Recognize volumes and use neighbouring chapters for chapter numbers

Sync now recognizes the chapters of a manga together, which tells what a
name alone can't:
- Numbers every similar name shares, like the group in "Group 2 Chapter
  386", aren't the chapter number.
- Chapters without a number get one from the chapters around them, as long
  as none of them is in another volume, since numbers may start over in the
  next one. Between two chapters they get the missing whole numbers when
  those fit (2, ?, 4 -> 3), or .5, .6 and so on (10, ?, ?, 11 -> 10.5,
  10.6). Bonus chapters after the newest chapter go above it. A prologue
  gets 0, and volume 0 when the chapter after it has a volume.

Recognition returns a ChapterNumbering of the volume and chapter number as
strings, null when there is none. Sub-chapters keep their letter (19a) and
parts become one (Chapter 1.5 Part I -> 1.5a); sync turns the chapter
number into a double the way letters always counted. Volumes, seasons
included, are recognized from names and are no longer taken as the chapter
number. Sync only uses the chapter number for now.
This commit is contained in:
AntsyLich
2026-10-10 17:37:44 +06:00
parent 6c1b0702ba
commit e42e01049d
3 changed files with 577 additions and 208 deletions
@@ -84,30 +84,35 @@ class SyncChaptersWithSource(
// to a higher value than newer chapters
var maxSeenUploadDate: Instant? = null
for (sourceChapter in sourceChapters) {
var chapter = sourceChapter
// Update metadata from source if necessary.
if (source is HttpSource) {
// Update metadata from source if necessary.
val preparedChapters = if (source is HttpSource) {
val sManga = manga.toSManga()
sourceChapters.map { chapter ->
val sChapter = chapter.toSChapter()
@Suppress("DEPRECATION")
source.prepareNewChapter(sChapter, manga.toSManga())
chapter = chapter.copyFromSChapter(sChapter)
source.prepareNewChapter(sChapter, sManga)
chapter.copyFromSChapter(sChapter)
}
} else {
sourceChapters
}
// Recognize chapter numbers, from the whole list since names alone don't always tell.
val recognizedChapters = preparedChapters
.zip(ChapterRecognition.parseChapterNumbers(manga.title, preparedChapters)) { chapter, numbering ->
chapter.copy(chapterNumber = numbering.chapter?.let(ChapterRecognition::toDouble) ?: -1.0)
}
for (chapter in recognizedChapters) {
if (!sourceUrls.add(chapter.url)) continue
// Recognize chapter number for the chapter.
val chapterNumber = ChapterRecognition.parseChapterNumber(manga.title, chapter.name, chapter.chapterNumber)
chapter = chapter.copy(chapterNumber = chapterNumber)
val dbChapter = dbChaptersByUrl[chapter.url]
if (dbChapter == null) {
val toAddChapter = if (chapter.dateUpload == null) {
chapter.copy(dateUpload = maxSeenUploadDate ?: nowInstant)
} else {
maxSeenUploadDate = listOfNotNull(maxSeenUploadDate, sourceChapter.dateUpload).maxOrNull()
maxSeenUploadDate = listOfNotNull(maxSeenUploadDate, chapter.dateUpload).maxOrNull()
chapter
}
newChapters.add(toAddChapter)