Make ChapterRecognition return the result (#7279)

This commit is contained in:
AntsyLich 2022-06-10 19:26:56 +06:00 committed by GitHub
parent cf48bbc176
commit 06fdfcdb23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 135 deletions

View file

@ -193,7 +193,7 @@ class LocalSource(
} }
date_upload = chapterFile.lastModified() date_upload = chapterFile.lastModified()
ChapterRecognition.parseChapterNumber(this, sManga) chapter_number = ChapterRecognition.parseChapterNumber(sManga.title, this.name, this.chapter_number)
val format = getFormat(chapterFile) val format = getFormat(chapterFile)
if (format is Format.Epub) { if (format is Format.Epub) {

View file

@ -1,8 +1,5 @@
package eu.kanade.tachiyomi.util.chapter package eu.kanade.tachiyomi.util.chapter
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
/** /**
* -R> = regex conversion. * -R> = regex conversion.
*/ */
@ -37,14 +34,14 @@ object ChapterRecognition {
*/ */
private val unwantedWhiteSpace = Regex("""(\s)(extra|special|omake)""") private val unwantedWhiteSpace = Regex("""(\s)(extra|special|omake)""")
fun parseChapterNumber(chapter: SChapter, manga: SManga) { fun parseChapterNumber(mangaTitle: String, chapterName: String, chapterNumber: Float? = null): Float {
// If chapter number is known return. // If chapter number is known return.
if (chapter.chapter_number == -2f || chapter.chapter_number > -1f) { if (chapterNumber != null && (chapterNumber == -2f || chapterNumber > -1f)) {
return return chapterNumber
} }
// Get chapter title with lower case // Get chapter title with lower case
var name = chapter.name.lowercase() var name = chapterName.lowercase()
// Remove comma's or hyphens. // Remove comma's or hyphens.
name = name.replace(',', '.').replace('-', '.') name = name.replace(',', '.').replace('-', '.')
@ -60,9 +57,7 @@ object ChapterRecognition {
} }
// Check base case ch.xx // Check base case ch.xx
if (updateChapter(basic.find(name), chapter)) { getChapterNumberFromMatch(basic.find(name))?.let { return it }
return
}
// Check one number occurrence. // Check one number occurrence.
val occurrences: MutableList<MatchResult> = arrayListOf() val occurrences: MutableList<MatchResult> = arrayListOf()
@ -71,41 +66,34 @@ object ChapterRecognition {
} }
if (occurrences.size == 1) { if (occurrences.size == 1) {
if (updateChapter(occurrences[0], chapter)) { getChapterNumberFromMatch(occurrences[0])?.let { return it }
return
}
} }
// Remove manga title from chapter title. // Remove manga title from chapter title.
val nameWithoutManga = name.replace(manga.title.lowercase(), "").trim() val nameWithoutManga = name.replace(mangaTitle.lowercase(), "").trim()
// Check if first value is number after title remove. // Check if first value is number after title remove.
if (updateChapter(withoutManga.find(nameWithoutManga), chapter)) { getChapterNumberFromMatch(withoutManga.find(nameWithoutManga))?.let { return it }
return
}
// Take the first number encountered. // Take the first number encountered.
if (updateChapter(occurrence.find(nameWithoutManga), chapter)) { getChapterNumberFromMatch(occurrence.find(nameWithoutManga))?.let { return it }
return
} return chapterNumber ?: -1f
} }
/** /**
* Check if volume is found and update chapter * Check if chapter number is found and return it
* @param match result of regex * @param match result of regex
* @param chapter chapter object * @return chapter number if found else null
* @return true if volume is found
*/ */
private fun updateChapter(match: MatchResult?, chapter: SChapter): Boolean { private fun getChapterNumberFromMatch(match: MatchResult?): Float? {
match?.let { return match?.let {
val initial = it.groups[1]?.value?.toFloat()!! val initial = it.groups[1]?.value?.toFloat()!!
val subChapterDecimal = it.groups[2]?.value val subChapterDecimal = it.groups[2]?.value
val subChapterAlpha = it.groups[3]?.value val subChapterAlpha = it.groups[3]?.value
val addition = checkForDecimal(subChapterDecimal, subChapterAlpha) val addition = checkForDecimal(subChapterDecimal, subChapterAlpha)
chapter.chapter_number = initial.plus(addition) initial.plus(addition)
return true
} }
return false
} }
/** /**

View file

@ -70,7 +70,7 @@ fun syncChaptersWithSource(
source.prepareNewChapter(sourceChapter, manga) source.prepareNewChapter(sourceChapter, manga)
} }
// Recognize chapter number for the chapter. // Recognize chapter number for the chapter.
ChapterRecognition.parseChapterNumber(sourceChapter, manga) sourceChapter.chapter_number = ChapterRecognition.parseChapterNumber(manga.title, sourceChapter.name, sourceChapter.chapter_number)
val dbChapter = dbChapters.find { it.url == sourceChapter.url } val dbChapter = dbChapters.find { it.url == sourceChapter.url }

View file

@ -1,7 +1,5 @@
package eu.kanade.tachiyomi.data.database package eu.kanade.tachiyomi.data.database
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition.parseChapterNumber import eu.kanade.tachiyomi.util.chapter.ChapterRecognition.parseChapterNumber
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -13,154 +11,154 @@ class ChapterRecognitionTest {
@Test @Test
fun `Basic Ch prefix`() { fun `Basic Ch prefix`() {
val manga = createManga("Mokushiroku Alice") val mangaTitle = "Mokushiroku Alice"
assertChapter(manga, "Mokushiroku Alice Vol.1 Ch.4: Misrepresentation", 4f) assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4: Misrepresentation", 4f)
} }
@Test @Test
fun `Basic Ch prefix with space after period`() { fun `Basic Ch prefix with space after period`() {
val manga = createManga("Mokushiroku Alice") val mangaTitle = "Mokushiroku Alice"
assertChapter(manga, "Mokushiroku Alice Vol. 1 Ch. 4: Misrepresentation", 4f) assertChapter(mangaTitle, "Mokushiroku Alice Vol. 1 Ch. 4: Misrepresentation", 4f)
} }
@Test @Test
fun `Basic Ch prefix with decimal`() { fun `Basic Ch prefix with decimal`() {
val manga = createManga("Mokushiroku Alice") val mangaTitle = "Mokushiroku Alice"
assertChapter(manga, "Mokushiroku Alice Vol.1 Ch.4.1: Misrepresentation", 4.1f) assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4.1: Misrepresentation", 4.1f)
assertChapter(manga, "Mokushiroku Alice Vol.1 Ch.4.4: Misrepresentation", 4.4f) assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4.4: Misrepresentation", 4.4f)
} }
@Test @Test
fun `Basic Ch prefix with alpha postfix`() { fun `Basic Ch prefix with alpha postfix`() {
val manga = createManga("Mokushiroku Alice") val mangaTitle = "Mokushiroku Alice"
assertChapter(manga, "Mokushiroku Alice Vol.1 Ch.4.a: Misrepresentation", 4.1f) assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4.a: Misrepresentation", 4.1f)
assertChapter(manga, "Mokushiroku Alice Vol.1 Ch.4.b: Misrepresentation", 4.2f) assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4.b: Misrepresentation", 4.2f)
assertChapter(manga, "Mokushiroku Alice Vol.1 Ch.4.extra: Misrepresentation", 4.99f) assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4.extra: Misrepresentation", 4.99f)
} }
@Test @Test
fun `Name containing one number`() { fun `Name containing one number`() {
val manga = createManga("Bleach") val mangaTitle = "Bleach"
assertChapter(manga, "Bleach 567 Down With Snowwhite", 567f) assertChapter(mangaTitle, "Bleach 567 Down With Snowwhite", 567f)
} }
@Test @Test
fun `Name containing one number and decimal`() { fun `Name containing one number and decimal`() {
val manga = createManga("Bleach") val mangaTitle = "Bleach"
assertChapter(manga, "Bleach 567.1 Down With Snowwhite", 567.1f) assertChapter(mangaTitle, "Bleach 567.1 Down With Snowwhite", 567.1f)
assertChapter(manga, "Bleach 567.4 Down With Snowwhite", 567.4f) assertChapter(mangaTitle, "Bleach 567.4 Down With Snowwhite", 567.4f)
} }
@Test @Test
fun `Name containing one number and alpha`() { fun `Name containing one number and alpha`() {
val manga = createManga("Bleach") val mangaTitle = "Bleach"
assertChapter(manga, "Bleach 567.a Down With Snowwhite", 567.1f) assertChapter(mangaTitle, "Bleach 567.a Down With Snowwhite", 567.1f)
assertChapter(manga, "Bleach 567.b Down With Snowwhite", 567.2f) assertChapter(mangaTitle, "Bleach 567.b Down With Snowwhite", 567.2f)
assertChapter(manga, "Bleach 567.extra Down With Snowwhite", 567.99f) assertChapter(mangaTitle, "Bleach 567.extra Down With Snowwhite", 567.99f)
} }
@Test @Test
fun `Chapter containing manga title and number`() { fun `Chapter containing manga title and number`() {
val manga = createManga("Solanin") val mangaTitle = "Solanin"
assertChapter(manga, "Solanin 028 Vol. 2", 28f) assertChapter(mangaTitle, "Solanin 028 Vol. 2", 28f)
} }
@Test @Test
fun `Chapter containing manga title and number decimal`() { fun `Chapter containing manga title and number decimal`() {
val manga = createManga("Solanin") val mangaTitle = "Solanin"
assertChapter(manga, "Solanin 028.1 Vol. 2", 28.1f) assertChapter(mangaTitle, "Solanin 028.1 Vol. 2", 28.1f)
assertChapter(manga, "Solanin 028.4 Vol. 2", 28.4f) assertChapter(mangaTitle, "Solanin 028.4 Vol. 2", 28.4f)
} }
@Test @Test
fun `Chapter containing manga title and number alpha`() { fun `Chapter containing manga title and number alpha`() {
val manga = createManga("Solanin") val mangaTitle = "Solanin"
assertChapter(manga, "Solanin 028.a Vol. 2", 28.1f) assertChapter(mangaTitle, "Solanin 028.a Vol. 2", 28.1f)
assertChapter(manga, "Solanin 028.b Vol. 2", 28.2f) assertChapter(mangaTitle, "Solanin 028.b Vol. 2", 28.2f)
assertChapter(manga, "Solanin 028.extra Vol. 2", 28.99f) assertChapter(mangaTitle, "Solanin 028.extra Vol. 2", 28.99f)
} }
@Test @Test
fun `Extreme case`() { fun `Extreme case`() {
val manga = createManga("Onepunch-Man") val mangaTitle = "Onepunch-Man"
assertChapter(manga, "Onepunch-Man Punch Ver002 028", 28f) assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028", 28f)
} }
@Test @Test
fun `Extreme case with decimal`() { fun `Extreme case with decimal`() {
val manga = createManga("Onepunch-Man") val mangaTitle = "Onepunch-Man"
assertChapter(manga, "Onepunch-Man Punch Ver002 028.1", 28.1f) assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028.1", 28.1f)
assertChapter(manga, "Onepunch-Man Punch Ver002 028.4", 28.4f) assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028.4", 28.4f)
} }
@Test @Test
fun `Extreme case with alpha`() { fun `Extreme case with alpha`() {
val manga = createManga("Onepunch-Man") val mangaTitle = "Onepunch-Man"
assertChapter(manga, "Onepunch-Man Punch Ver002 028.a", 28.1f) assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028.a", 28.1f)
assertChapter(manga, "Onepunch-Man Punch Ver002 028.b", 28.2f) assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028.b", 28.2f)
assertChapter(manga, "Onepunch-Man Punch Ver002 028.extra", 28.99f) assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028.extra", 28.99f)
} }
@Test @Test
fun `Chapter containing dot v2`() { fun `Chapter containing dot v2`() {
val manga = createManga("random") val mangaTitle = "random"
assertChapter(manga, "Vol.1 Ch.5v.2: Alones", 5f) assertChapter(mangaTitle, "Vol.1 Ch.5v.2: Alones", 5f)
} }
@Test @Test
fun `Number in manga title`() { fun `Number in manga title`() {
val manga = createManga("Ayame 14") val mangaTitle = "Ayame 14"
assertChapter(manga, "Ayame 14 1 - The summer of 14", 1f) assertChapter(mangaTitle, "Ayame 14 1 - The summer of 14", 1f)
} }
@Test @Test
fun `Space between ch x`() { fun `Space between ch x`() {
val manga = createManga("Mokushiroku Alice") val mangaTitle = "Mokushiroku Alice"
assertChapter(manga, "Mokushiroku Alice Vol.1 Ch. 4: Misrepresentation", 4f) assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch. 4: Misrepresentation", 4f)
} }
@Test @Test
fun `Chapter title with ch substring`() { fun `Chapter title with ch substring`() {
val manga = createManga("Ayame 14") val mangaTitle = "Ayame 14"
assertChapter(manga, "Vol.1 Ch.1: March 25 (First Day Cohabiting)", 1f) assertChapter(mangaTitle, "Vol.1 Ch.1: March 25 (First Day Cohabiting)", 1f)
} }
@Test @Test
fun `Chapter containing multiple zeros`() { fun `Chapter containing multiple zeros`() {
val manga = createManga("random") val mangaTitle = "random"
assertChapter(manga, "Vol.001 Ch.003: Kaguya Doesn't Know Much", 3f) assertChapter(mangaTitle, "Vol.001 Ch.003: Kaguya Doesn't Know Much", 3f)
} }
@Test @Test
fun `Chapter with version before number`() { fun `Chapter with version before number`() {
val manga = createManga("Onepunch-Man") val mangaTitle = "Onepunch-Man"
assertChapter(manga, "Onepunch-Man Punch Ver002 086 : Creeping Darkness [3]", 86f) assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 086 : Creeping Darkness [3]", 86f)
} }
@Test @Test
fun `Version attached to chapter number`() { fun `Version attached to chapter number`() {
val manga = createManga("Ansatsu Kyoushitsu") val mangaTitle = "Ansatsu Kyoushitsu"
assertChapter(manga, "Ansatsu Kyoushitsu 011v002: Assembly Time", 11f) assertChapter(mangaTitle, "Ansatsu Kyoushitsu 011v002: Assembly Time", 11f)
} }
/** /**
@ -169,110 +167,95 @@ class ChapterRecognitionTest {
*/ */
@Test @Test
fun `Number after manga title with chapter in chapter title case`() { fun `Number after manga title with chapter in chapter title case`() {
val manga = createManga("Tokyo ESP") val mangaTitle = "Tokyo ESP"
assertChapter(manga, "Tokyo ESP 027: Part 002: Chapter 001", 027f) assertChapter(mangaTitle, "Tokyo ESP 027: Part 002: Chapter 001", 027f)
} }
@Test @Test
fun `Unparseable chapter`() { fun `Unparseable chapter`() {
val manga = createManga("random") val mangaTitle = "random"
assertChapter(manga, "Foo", -1f) assertChapter(mangaTitle, "Foo", -1f)
} }
@Test @Test
fun `Chapter with time in title`() { fun `Chapter with time in title`() {
val manga = createManga("random") val mangaTitle = "random"
assertChapter(manga, "Fairy Tail 404: 00:00", 404f) assertChapter(mangaTitle, "Fairy Tail 404: 00:00", 404f)
} }
@Test @Test
fun `Chapter with alpha without dot`() { fun `Chapter with alpha without dot`() {
val manga = createManga("random") val mangaTitle = "random"
assertChapter(manga, "Asu No Yoichi 19a", 19.1f) assertChapter(mangaTitle, "Asu No Yoichi 19a", 19.1f)
} }
@Test @Test
fun `Chapter title containing extra and vol`() { fun `Chapter title containing extra and vol`() {
val manga = createManga("Fairy Tail") val mangaTitle = "Fairy Tail"
assertChapter(manga, "Fairy Tail 404.extravol002", 404.99f) assertChapter(mangaTitle, "Fairy Tail 404.extravol002", 404.99f)
assertChapter(manga, "Fairy Tail 404 extravol002", 404.99f) assertChapter(mangaTitle, "Fairy Tail 404 extravol002", 404.99f)
assertChapter(manga, "Fairy Tail 404.evol002", 404.5f) assertChapter(mangaTitle, "Fairy Tail 404.evol002", 404.5f)
} }
@Test @Test
fun `Chapter title containing omake (japanese extra) and vol`() { fun `Chapter title containing omake (japanese extra) and vol`() {
val manga = createManga("Fairy Tail") val mangaTitle = "Fairy Tail"
assertChapter(manga, "Fairy Tail 404.omakevol002", 404.98f) assertChapter(mangaTitle, "Fairy Tail 404.omakevol002", 404.98f)
assertChapter(manga, "Fairy Tail 404 omakevol002", 404.98f) assertChapter(mangaTitle, "Fairy Tail 404 omakevol002", 404.98f)
assertChapter(manga, "Fairy Tail 404.ovol002", 404.15f) assertChapter(mangaTitle, "Fairy Tail 404.ovol002", 404.15f)
} }
@Test @Test
fun `Chapter title containing special and vol`() { fun `Chapter title containing special and vol`() {
val manga = createManga("Fairy Tail") val mangaTitle = "Fairy Tail"
assertChapter(manga, "Fairy Tail 404.specialvol002", 404.97f) assertChapter(mangaTitle, "Fairy Tail 404.specialvol002", 404.97f)
assertChapter(manga, "Fairy Tail 404 specialvol002", 404.97f) assertChapter(mangaTitle, "Fairy Tail 404 specialvol002", 404.97f)
assertChapter(manga, "Fairy Tail 404.svol002", 404.19f) assertChapter(mangaTitle, "Fairy Tail 404.svol002", 404.19f)
} }
@Test @Test
fun `Chapter title containing commas`() { fun `Chapter title containing commas`() {
val manga = createManga("One Piece") val mangaTitle = "One Piece"
assertChapter(manga, "One Piece 300,a", 300.1f) assertChapter(mangaTitle, "One Piece 300,a", 300.1f)
assertChapter(manga, "One Piece Ch,123,extra", 123.99f) assertChapter(mangaTitle, "One Piece Ch,123,extra", 123.99f)
assertChapter(manga, "One Piece the sunny, goes swimming 024,005", 24.005f) assertChapter(mangaTitle, "One Piece the sunny, goes swimming 024,005", 24.005f)
} }
@Test @Test
fun `Chapter title containing hyphens`() { fun `Chapter title containing hyphens`() {
val manga = createManga("Solo Leveling") val mangaTitle = "Solo Leveling"
assertChapter(manga, "ch 122-a", 122.1f) assertChapter(mangaTitle, "ch 122-a", 122.1f)
assertChapter(manga, "Solo Leveling Ch.123-extra", 123.99f) assertChapter(mangaTitle, "Solo Leveling Ch.123-extra", 123.99f)
assertChapter(manga, "Solo Leveling, 024-005", 24.005f) assertChapter(mangaTitle, "Solo Leveling, 024-005", 24.005f)
assertChapter(manga, "Ch.191-200 Read Online", 191.200f) assertChapter(mangaTitle, "Ch.191-200 Read Online", 191.200f)
} }
@Test @Test
fun `Chapters containing season`() { fun `Chapters containing season`() {
val manga = createManga("D.I.C.E") assertChapter("D.I.C.E", "D.I.C.E[Season 001] Ep. 007", 7f)
assertChapter(manga, "D.I.C.E[Season 001] Ep. 007", 7f)
} }
@Test @Test
fun `Chapters in format sx - chapter xx`() { fun `Chapters in format sx - chapter xx`() {
val manga = createManga("The Gamer") assertChapter("The Gamer", "S3 - Chapter 20", 20f)
assertChapter(manga, "S3 - Chapter 20", 20f)
} }
@Test @Test
fun `Chapters ending with s`() { fun `Chapters ending with s`() {
val manga = createManga("One Outs") assertChapter("One Outs", "One Outs 001", 1f)
assertChapter(manga, "One Outs 001", 1f)
} }
private fun assertChapter(manga: Manga, name: String, expected: Float) { private fun assertChapter(mangaTitle: String, name: String, expected: Float) {
val chapter = Chapter.create() val chapterNumber = parseChapterNumber(mangaTitle, name)
chapter.name = name assertEquals(chapterNumber, expected)
parseChapterNumber(chapter, manga)
assertEquals(expected, chapter.chapter_number)
} }
}
private fun createManga(title: String): Manga {
val manga = Manga.create(0)
manga.title = title
return manga
}
}