2016-05-20 14:22:10 +02:00
|
|
|
package eu.kanade.tachiyomi.util
|
|
|
|
|
|
|
|
|
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
|
|
|
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
|
|
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
2017-01-20 21:24:31 +01:00
|
|
|
import eu.kanade.tachiyomi.source.Source
|
|
|
|
|
import eu.kanade.tachiyomi.source.model.SChapter
|
2017-01-20 21:27:53 +01:00
|
|
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
2016-05-20 14:22:10 +02:00
|
|
|
import java.util.*
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper method for syncing the list of chapters from the source with the ones from the database.
|
|
|
|
|
*
|
|
|
|
|
* @param db the database.
|
2017-01-08 18:12:19 +01:00
|
|
|
* @param rawSourceChapters a list of chapters from the source.
|
2016-05-20 14:22:10 +02:00
|
|
|
* @param manga the manga of the chapters.
|
|
|
|
|
* @param source the source of the chapters.
|
|
|
|
|
* @return a pair of new insertions and deletions.
|
|
|
|
|
*/
|
|
|
|
|
fun syncChaptersWithSource(db: DatabaseHelper,
|
2017-01-08 18:12:19 +01:00
|
|
|
rawSourceChapters: List<SChapter>,
|
2016-05-20 14:22:10 +02:00
|
|
|
manga: Manga,
|
2017-08-28 09:10:19 +02:00
|
|
|
source: Source): Pair<List<Chapter>, List<Chapter>> {
|
2016-05-20 14:22:10 +02:00
|
|
|
|
2017-01-08 18:12:19 +01:00
|
|
|
if (rawSourceChapters.isEmpty()) {
|
|
|
|
|
throw Exception("No chapters found")
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-20 14:22:10 +02:00
|
|
|
// Chapters from db.
|
|
|
|
|
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
|
|
|
|
|
2017-01-08 18:12:19 +01:00
|
|
|
val sourceChapters = rawSourceChapters.mapIndexed { i, sChapter ->
|
|
|
|
|
Chapter.create().apply {
|
|
|
|
|
copyFrom(sChapter)
|
|
|
|
|
manga_id = manga.id
|
|
|
|
|
source_order = i
|
|
|
|
|
}
|
2016-05-20 14:22:10 +02:00
|
|
|
}
|
|
|
|
|
|
2017-08-26 12:50:52 +02:00
|
|
|
// Chapters from the source not in db.
|
2017-08-26 12:46:35 +02:00
|
|
|
val toAdd = mutableListOf<Chapter>()
|
|
|
|
|
|
|
|
|
|
// Chapters whose metadata have changed.
|
|
|
|
|
val toChange = mutableListOf<Chapter>()
|
|
|
|
|
|
|
|
|
|
for (sourceChapter in sourceChapters) {
|
|
|
|
|
val dbChapter = dbChapters.find { it.url == sourceChapter.url }
|
|
|
|
|
|
|
|
|
|
// Add the chapter if not in db already, or update if the metadata changed.
|
|
|
|
|
if (dbChapter == null) {
|
|
|
|
|
toAdd.add(sourceChapter)
|
2018-02-18 19:20:05 +01:00
|
|
|
} else {
|
|
|
|
|
//this forces metadata update for the main viewable things in the chapter list
|
2019-03-25 14:53:17 +01:00
|
|
|
if (source is HttpSource) {
|
|
|
|
|
source.prepareNewChapter(sourceChapter, manga)
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-18 19:20:05 +01:00
|
|
|
ChapterRecognition.parseChapterNumber(sourceChapter, manga)
|
2019-03-25 14:53:17 +01:00
|
|
|
|
2018-02-18 19:20:05 +01:00
|
|
|
if (shouldUpdateDbChapter(dbChapter, sourceChapter)) {
|
|
|
|
|
dbChapter.scanlator = sourceChapter.scanlator
|
|
|
|
|
dbChapter.name = sourceChapter.name
|
|
|
|
|
dbChapter.date_upload = sourceChapter.date_upload
|
|
|
|
|
dbChapter.chapter_number = sourceChapter.chapter_number
|
|
|
|
|
toChange.add(dbChapter)
|
|
|
|
|
}
|
2017-08-26 12:46:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
2016-05-20 14:22:10 +02:00
|
|
|
|
|
|
|
|
// Recognize number for new chapters.
|
|
|
|
|
toAdd.forEach {
|
2017-01-20 21:27:53 +01:00
|
|
|
if (source is HttpSource) {
|
2016-11-12 14:04:25 +01:00
|
|
|
source.prepareNewChapter(it, manga)
|
2016-03-22 19:00:05 +01:00
|
|
|
}
|
2016-05-20 14:22:10 +02:00
|
|
|
ChapterRecognition.parseChapterNumber(it, manga)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Chapters from the db not in the source.
|
2017-08-26 12:46:35 +02:00
|
|
|
val toDelete = dbChapters.filterNot { dbChapter ->
|
|
|
|
|
sourceChapters.any { sourceChapter ->
|
|
|
|
|
dbChapter.url == sourceChapter.url
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-20 14:22:10 +02:00
|
|
|
|
2017-08-26 12:46:35 +02:00
|
|
|
// Return if there's nothing to add, delete or change, avoiding unnecessary db transactions.
|
|
|
|
|
if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) {
|
2017-01-01 20:54:41 +01:00
|
|
|
return Pair(emptyList(), emptyList())
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-06 17:22:03 +01:00
|
|
|
val readded = mutableListOf<Chapter>()
|
2016-05-20 14:22:10 +02:00
|
|
|
|
|
|
|
|
db.inTransaction {
|
2016-12-06 17:22:03 +01:00
|
|
|
val deletedChapterNumbers = TreeSet<Float>()
|
2016-05-20 14:22:10 +02:00
|
|
|
val deletedReadChapterNumbers = TreeSet<Float>()
|
2020-01-08 01:20:08 +01:00
|
|
|
if (toDelete.isNotEmpty()) {
|
2016-05-20 14:22:10 +02:00
|
|
|
for (c in toDelete) {
|
|
|
|
|
if (c.read) {
|
|
|
|
|
deletedReadChapterNumbers.add(c.chapter_number)
|
|
|
|
|
}
|
2016-12-06 17:22:03 +01:00
|
|
|
deletedChapterNumbers.add(c.chapter_number)
|
2016-05-20 14:22:10 +02:00
|
|
|
}
|
2016-12-06 17:22:03 +01:00
|
|
|
db.deleteChapters(toDelete).executeAsBlocking()
|
2016-05-20 14:22:10 +02:00
|
|
|
}
|
|
|
|
|
|
2020-01-08 01:20:08 +01:00
|
|
|
if (toAdd.isNotEmpty()) {
|
2016-05-20 14:22:10 +02:00
|
|
|
// Set the date fetch for new items in reverse order to allow another sorting method.
|
|
|
|
|
// Sources MUST return the chapters from most to less recent, which is common.
|
|
|
|
|
var now = Date().time
|
|
|
|
|
|
|
|
|
|
for (i in toAdd.indices.reversed()) {
|
|
|
|
|
val c = toAdd[i]
|
|
|
|
|
c.date_fetch = now++
|
|
|
|
|
// Try to mark already read chapters as read when the source deletes them
|
2016-05-26 16:03:55 +02:00
|
|
|
if (c.isRecognizedNumber && c.chapter_number in deletedReadChapterNumbers) {
|
2016-05-20 14:22:10 +02:00
|
|
|
c.read = true
|
2016-12-06 17:22:03 +01:00
|
|
|
}
|
|
|
|
|
if (c.isRecognizedNumber && c.chapter_number in deletedChapterNumbers) {
|
|
|
|
|
readded.add(c)
|
2016-05-20 14:22:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
2016-12-06 17:22:03 +01:00
|
|
|
db.insertChapters(toAdd).executeAsBlocking()
|
2016-05-20 14:22:10 +02:00
|
|
|
}
|
|
|
|
|
|
2020-01-08 01:20:08 +01:00
|
|
|
if (toChange.isNotEmpty()) {
|
2017-08-26 12:46:35 +02:00
|
|
|
db.insertChapters(toChange).executeAsBlocking()
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-20 14:22:10 +02:00
|
|
|
// Fix order in source.
|
|
|
|
|
db.fixChaptersSourceOrder(sourceChapters).executeAsBlocking()
|
2018-07-07 14:05:02 +02:00
|
|
|
|
|
|
|
|
// Set this manga as updated since chapters were changed
|
|
|
|
|
manga.last_update = Date().time
|
|
|
|
|
db.updateLastUpdated(manga).executeAsBlocking()
|
2016-05-20 14:22:10 +02:00
|
|
|
}
|
2017-08-28 09:10:19 +02:00
|
|
|
|
2020-01-08 01:20:08 +01:00
|
|
|
return Pair(toAdd.subtract(readded).toList(), toDelete.subtract(readded).toList())
|
2016-05-20 14:22:10 +02:00
|
|
|
}
|
2018-02-18 19:20:05 +01:00
|
|
|
|
|
|
|
|
//checks if the chapter in db needs updated
|
|
|
|
|
private fun shouldUpdateDbChapter(dbChapter: Chapter, sourceChapter: SChapter): Boolean {
|
|
|
|
|
return dbChapter.scanlator != sourceChapter.scanlator || dbChapter.name != sourceChapter.name ||
|
|
|
|
|
dbChapter.date_upload != sourceChapter.date_upload ||
|
|
|
|
|
dbChapter.chapter_number != sourceChapter.chapter_number
|
2020-01-08 01:20:08 +01:00
|
|
|
}
|