2019-04-18 23:40:13 +02:00
|
|
|
package exh.eh
|
|
|
|
|
|
|
|
|
|
import android.content.Context
|
2022-07-03 05:52:03 +02:00
|
|
|
import eu.kanade.domain.category.interactor.GetCategories
|
|
|
|
|
import eu.kanade.domain.category.interactor.SetMangaCategories
|
|
|
|
|
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
2022-07-05 00:37:30 +02:00
|
|
|
import eu.kanade.domain.chapter.interactor.GetChapterByUrl
|
2022-07-03 05:52:03 +02:00
|
|
|
import eu.kanade.domain.chapter.model.Chapter
|
|
|
|
|
import eu.kanade.domain.chapter.model.ChapterUpdate
|
|
|
|
|
import eu.kanade.domain.chapter.repository.ChapterRepository
|
2022-07-05 00:37:30 +02:00
|
|
|
import eu.kanade.domain.history.interactor.GetHistoryByMangaId
|
2022-07-03 05:52:03 +02:00
|
|
|
import eu.kanade.domain.history.interactor.RemoveHistoryById
|
|
|
|
|
import eu.kanade.domain.history.interactor.UpsertHistory
|
|
|
|
|
import eu.kanade.domain.history.model.History
|
|
|
|
|
import eu.kanade.domain.history.model.HistoryUpdate
|
2022-07-03 19:37:27 +02:00
|
|
|
import eu.kanade.domain.manga.interactor.GetManga
|
2022-07-03 05:52:03 +02:00
|
|
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
|
|
|
|
import eu.kanade.domain.manga.model.Manga
|
|
|
|
|
import eu.kanade.domain.manga.model.MangaUpdate
|
2022-02-09 01:47:40 +01:00
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
|
import kotlinx.coroutines.async
|
|
|
|
|
import kotlinx.coroutines.coroutineScope
|
2020-10-24 22:44:20 +02:00
|
|
|
import kotlinx.coroutines.flow.Flow
|
2020-10-25 01:41:16 +02:00
|
|
|
import kotlinx.coroutines.flow.flowOf
|
2020-10-24 22:44:20 +02:00
|
|
|
import kotlinx.coroutines.flow.map
|
2019-04-18 23:40:13 +02:00
|
|
|
import uy.kohesive.injekt.injectLazy
|
2020-09-14 00:48:20 +02:00
|
|
|
import java.io.File
|
2019-04-18 23:40:13 +02:00
|
|
|
|
2022-02-09 01:47:40 +01:00
|
|
|
data class ChapterChain(val manga: Manga, val chapters: List<Chapter>, val history: List<History>)
|
2019-04-18 23:40:13 +02:00
|
|
|
|
|
|
|
|
class EHentaiUpdateHelper(context: Context) {
|
|
|
|
|
val parentLookupTable =
|
2020-05-02 06:46:24 +02:00
|
|
|
MemAutoFlushingLookupTable(
|
|
|
|
|
File(context.filesDir, "exh-plt.maftable"),
|
2022-04-08 21:30:30 +02:00
|
|
|
GalleryEntry.Serializer(),
|
2020-05-02 06:46:24 +02:00
|
|
|
)
|
2022-07-05 00:37:30 +02:00
|
|
|
private val getChapterByUrl: GetChapterByUrl by injectLazy()
|
2022-07-03 05:52:03 +02:00
|
|
|
private val getChapterByMangaId: GetChapterByMangaId by injectLazy()
|
2022-07-03 19:37:27 +02:00
|
|
|
private val getManga: GetManga by injectLazy()
|
2022-07-03 05:52:03 +02:00
|
|
|
private val updateManga: UpdateManga by injectLazy()
|
|
|
|
|
private val setMangaCategories: SetMangaCategories by injectLazy()
|
|
|
|
|
private val getCategories: GetCategories by injectLazy()
|
|
|
|
|
private val chapterRepository: ChapterRepository by injectLazy()
|
|
|
|
|
private val upsertHistory: UpsertHistory by injectLazy()
|
|
|
|
|
private val removeHistoryById: RemoveHistoryById by injectLazy()
|
2022-07-05 00:37:30 +02:00
|
|
|
private val getHistoryByMangaId: GetHistoryByMangaId by injectLazy()
|
2019-04-18 23:40:13 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param chapters Cannot be an empty list!
|
|
|
|
|
*
|
2019-08-09 02:04:31 +02:00
|
|
|
* @return Triple<Accepted, Discarded, HasNew>
|
2019-04-18 23:40:13 +02:00
|
|
|
*/
|
2020-10-24 22:44:20 +02:00
|
|
|
fun findAcceptedRootAndDiscardOthers(sourceId: Long, chapters: List<Chapter>): Flow<Triple<ChapterChain, List<ChapterChain>, Boolean>> {
|
2019-04-18 23:40:13 +02:00
|
|
|
// Find other chains
|
2020-10-25 01:41:16 +02:00
|
|
|
val chainsFlow = flowOf(chapters)
|
|
|
|
|
.map { chapterList ->
|
|
|
|
|
chapterList.flatMap { chapter ->
|
2022-07-05 00:37:30 +02:00
|
|
|
getChapterByUrl.await(chapter.url).map { it.mangaId }
|
2020-10-25 01:41:16 +02:00
|
|
|
}.distinct()
|
2020-05-02 06:46:24 +02:00
|
|
|
}
|
2020-10-24 22:44:20 +02:00
|
|
|
.map { mangaIds ->
|
|
|
|
|
mangaIds
|
|
|
|
|
.mapNotNull { mangaId ->
|
2022-02-09 01:47:40 +01:00
|
|
|
coroutineScope {
|
|
|
|
|
val manga = async(Dispatchers.IO) {
|
2022-07-03 19:37:27 +02:00
|
|
|
getManga.await(mangaId)
|
2022-02-09 01:47:40 +01:00
|
|
|
}
|
|
|
|
|
val chapterList = async(Dispatchers.IO) {
|
2022-07-03 05:52:03 +02:00
|
|
|
getChapterByMangaId.await(mangaId)
|
2022-02-09 01:47:40 +01:00
|
|
|
}
|
|
|
|
|
val history = async(Dispatchers.IO) {
|
2022-07-05 00:37:30 +02:00
|
|
|
getHistoryByMangaId.await(mangaId)
|
2022-02-09 01:47:40 +01:00
|
|
|
}
|
|
|
|
|
ChapterChain(
|
|
|
|
|
manga.await() ?: return@coroutineScope null,
|
|
|
|
|
chapterList.await(),
|
2022-04-08 21:30:30 +02:00
|
|
|
history.await(),
|
2022-02-09 01:47:40 +01:00
|
|
|
)
|
|
|
|
|
}
|
2020-10-24 22:44:20 +02:00
|
|
|
}
|
|
|
|
|
.filter { it.manga.source == sourceId }
|
|
|
|
|
}
|
2019-04-18 23:40:13 +02:00
|
|
|
|
|
|
|
|
// Accept oldest chain
|
2020-10-24 22:44:20 +02:00
|
|
|
val chainsWithAccepted = chainsFlow.map { chains ->
|
2022-07-03 05:52:03 +02:00
|
|
|
val acceptedChain = chains.minBy { it.manga.id }
|
2019-04-18 23:40:13 +02:00
|
|
|
|
|
|
|
|
acceptedChain to chains
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return chainsWithAccepted.map { (accepted, chains) ->
|
|
|
|
|
val toDiscard = chains.filter { it.manga.favorite && it.manga.id != accepted.manga.id }
|
2022-07-03 05:52:03 +02:00
|
|
|
val mangaUpdates = mutableListOf<MangaUpdate>()
|
2019-04-18 23:40:13 +02:00
|
|
|
|
2019-08-05 03:27:59 +02:00
|
|
|
val chainsAsChapters = chains.flatMap { it.chapters }
|
2022-02-09 01:47:40 +01:00
|
|
|
val chainsAsHistory = chains.flatMap { it.history }
|
2019-08-05 03:27:59 +02:00
|
|
|
|
2020-04-04 22:30:05 +02:00
|
|
|
if (toDiscard.isNotEmpty()) {
|
2019-04-18 23:40:13 +02:00
|
|
|
// Copy chain chapters to curChapters
|
2022-07-03 05:52:03 +02:00
|
|
|
val (chapterUpdates, newChapters, new) = getChapterList(accepted, toDiscard, chainsAsChapters)
|
2019-04-18 23:40:13 +02:00
|
|
|
|
2020-07-10 19:08:21 +02:00
|
|
|
toDiscard.forEach {
|
2022-07-03 05:52:03 +02:00
|
|
|
mangaUpdates += MangaUpdate(
|
|
|
|
|
id = it.manga.id,
|
|
|
|
|
favorite = false,
|
|
|
|
|
dateAdded = 0,
|
|
|
|
|
)
|
2020-07-10 19:08:21 +02:00
|
|
|
}
|
2022-02-09 01:47:40 +01:00
|
|
|
if (!accepted.manga.favorite) {
|
2022-07-03 05:52:03 +02:00
|
|
|
mangaUpdates += MangaUpdate(
|
|
|
|
|
id = accepted.manga.id,
|
|
|
|
|
favorite = true,
|
|
|
|
|
dateAdded = System.currentTimeMillis(),
|
|
|
|
|
)
|
2022-02-09 01:47:40 +01:00
|
|
|
}
|
2019-04-18 23:40:13 +02:00
|
|
|
|
2022-07-03 05:52:03 +02:00
|
|
|
val newAccepted = ChapterChain(accepted.manga, newChapters, emptyList())
|
2019-04-18 23:40:13 +02:00
|
|
|
val rootsToMutate = toDiscard + newAccepted
|
|
|
|
|
|
2022-07-03 05:52:03 +02:00
|
|
|
// Apply changes to all manga
|
|
|
|
|
updateManga.awaitAll(mangaUpdates)
|
|
|
|
|
// Insert new chapters for accepted manga
|
|
|
|
|
chapterRepository.updateAll(chapterUpdates)
|
|
|
|
|
chapterRepository.addAll(newChapters)
|
2022-02-09 01:47:40 +01:00
|
|
|
|
2022-07-03 05:52:03 +02:00
|
|
|
val (newHistory, deleteHistory) = getHistory(getChapterByMangaId.await(accepted.manga.id), chainsAsChapters, chainsAsHistory)
|
2022-02-09 01:47:40 +01:00
|
|
|
|
2022-07-03 05:52:03 +02:00
|
|
|
// Delete the duplicate history first
|
2022-07-12 01:42:53 +02:00
|
|
|
deleteHistory.forEach {
|
|
|
|
|
removeHistoryById.await(it)
|
2022-07-03 05:52:03 +02:00
|
|
|
}
|
2022-07-12 01:42:53 +02:00
|
|
|
|
2022-07-03 05:52:03 +02:00
|
|
|
// Insert new history
|
|
|
|
|
newHistory.forEach {
|
|
|
|
|
upsertHistory.await(it)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy categories from all chains to accepted manga
|
|
|
|
|
|
2022-07-05 00:37:30 +02:00
|
|
|
val newCategories = rootsToMutate.flatMap { chapterChain ->
|
|
|
|
|
getCategories.await(chapterChain.manga.id).map { it.id }
|
2022-07-03 05:52:03 +02:00
|
|
|
}.distinct()
|
|
|
|
|
rootsToMutate.forEach {
|
|
|
|
|
setMangaCategories.await(it.manga.id, newCategories)
|
2019-04-18 23:40:13 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-09 02:04:31 +02:00
|
|
|
Triple(newAccepted, toDiscard, new)
|
2020-10-25 01:42:43 +02:00
|
|
|
} else {
|
2020-10-25 04:56:40 +01:00
|
|
|
/*val notNeeded = chains.filter { it.manga.id != accepted.manga.id }
|
2020-10-25 01:42:43 +02:00
|
|
|
val (newChapters, new) = getChapterList(accepted, notNeeded, chainsAsChapters)
|
|
|
|
|
val newAccepted = ChapterChain(accepted.manga, newChapters)
|
|
|
|
|
|
|
|
|
|
// Insert new chapters for accepted manga
|
2020-10-25 04:56:40 +01:00
|
|
|
db.insertChapters(newAccepted.chapters).await()*/
|
2020-10-25 01:42:43 +02:00
|
|
|
|
|
|
|
|
Triple(accepted, emptyList(), false)
|
|
|
|
|
}
|
2020-10-24 22:44:20 +02:00
|
|
|
}
|
2019-04-18 23:40:13 +02:00
|
|
|
}
|
2020-10-25 01:42:43 +02:00
|
|
|
|
2022-07-03 05:52:03 +02:00
|
|
|
fun getHistory(
|
|
|
|
|
currentChapters: List<Chapter>,
|
2022-02-09 01:47:40 +01:00
|
|
|
chainsAsChapters: List<Chapter>,
|
2022-04-08 21:30:30 +02:00
|
|
|
chainsAsHistory: List<History>,
|
2022-07-03 05:52:03 +02:00
|
|
|
): Pair<List<HistoryUpdate>, List<Long>> {
|
|
|
|
|
val history = chainsAsHistory.groupBy { history -> chainsAsChapters.find { it.id == history.chapterId }?.url }
|
|
|
|
|
val newHistory = currentChapters.mapNotNull { chapter ->
|
|
|
|
|
val newHistory = history[chapter.url]
|
|
|
|
|
?.maxByOrNull {
|
|
|
|
|
it.readAt?.time ?: 0
|
2022-03-04 18:27:18 +01:00
|
|
|
}
|
2022-07-03 05:52:03 +02:00
|
|
|
?.takeIf { it.chapterId != chapter.id && it.readAt != null }
|
|
|
|
|
if (newHistory != null) {
|
|
|
|
|
HistoryUpdate(chapter.id, newHistory.readAt!!, newHistory.readDuration)
|
2022-09-12 01:43:45 +02:00
|
|
|
} else {
|
|
|
|
|
null
|
|
|
|
|
}
|
2022-07-03 05:52:03 +02:00
|
|
|
}
|
|
|
|
|
val currentChapterIds = currentChapters.map { it.id }
|
|
|
|
|
val historyToDelete = chainsAsHistory.filterNot { it.chapterId in currentChapterIds }
|
|
|
|
|
.map { it.id }
|
|
|
|
|
return newHistory to historyToDelete
|
2022-02-09 01:47:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun getChapterList(
|
|
|
|
|
accepted: ChapterChain,
|
|
|
|
|
toDiscard: List<ChapterChain>,
|
2022-04-08 21:30:30 +02:00
|
|
|
chainsAsChapters: List<Chapter>,
|
2022-07-03 05:52:03 +02:00
|
|
|
): Triple<List<ChapterUpdate>, List<Chapter>, Boolean> {
|
2020-10-25 01:42:43 +02:00
|
|
|
var new = false
|
|
|
|
|
return toDiscard
|
|
|
|
|
.flatMap { chain ->
|
|
|
|
|
chain.chapters
|
|
|
|
|
}
|
|
|
|
|
.fold(accepted.chapters) { curChapters, chapter ->
|
2022-07-03 05:52:03 +02:00
|
|
|
val newLastPageRead = chainsAsChapters.maxOfOrNull { it.lastPageRead }
|
2020-10-25 01:42:43 +02:00
|
|
|
|
2022-07-03 05:52:03 +02:00
|
|
|
if (curChapters.any { it.url == chapter.url }) {
|
|
|
|
|
curChapters.map {
|
|
|
|
|
if (it.url == chapter.url) {
|
|
|
|
|
val read = it.read || chapter.read
|
|
|
|
|
var lastPageRead = it.lastPageRead.coerceAtLeast(chapter.lastPageRead)
|
|
|
|
|
if (newLastPageRead != null && lastPageRead <= 0) {
|
|
|
|
|
lastPageRead = newLastPageRead
|
|
|
|
|
}
|
|
|
|
|
val bookmark = it.bookmark || chapter.bookmark
|
|
|
|
|
it.copy(
|
|
|
|
|
read = read,
|
|
|
|
|
lastPageRead = lastPageRead,
|
|
|
|
|
bookmark = bookmark,
|
|
|
|
|
)
|
2022-09-12 01:43:45 +02:00
|
|
|
} else {
|
|
|
|
|
it
|
|
|
|
|
}
|
2020-10-25 01:42:43 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
new = true
|
2022-07-03 05:52:03 +02:00
|
|
|
curChapters + Chapter(
|
|
|
|
|
id = -1,
|
|
|
|
|
mangaId = accepted.manga.id,
|
|
|
|
|
url = chapter.url,
|
|
|
|
|
name = chapter.name,
|
|
|
|
|
read = chapter.read,
|
|
|
|
|
bookmark = chapter.bookmark,
|
|
|
|
|
lastPageRead = if (newLastPageRead != null && chapter.lastPageRead <= 0) {
|
|
|
|
|
newLastPageRead
|
2022-09-12 01:43:45 +02:00
|
|
|
} else {
|
|
|
|
|
chapter.lastPageRead
|
|
|
|
|
},
|
2022-07-03 05:52:03 +02:00
|
|
|
dateFetch = chapter.dateFetch,
|
|
|
|
|
dateUpload = chapter.dateUpload,
|
|
|
|
|
chapterNumber = -1F,
|
|
|
|
|
scanlator = null,
|
|
|
|
|
sourceOrder = -1,
|
|
|
|
|
)
|
2020-10-25 01:42:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-07-03 05:52:03 +02:00
|
|
|
.sortedBy { it.dateUpload }
|
2022-02-09 01:47:40 +01:00
|
|
|
.let { chapters ->
|
2022-07-03 05:52:03 +02:00
|
|
|
val updates = mutableListOf<ChapterUpdate>()
|
|
|
|
|
val newChapters = mutableListOf<Chapter>()
|
|
|
|
|
chapters.mapIndexed { index, chapter ->
|
|
|
|
|
val name = "v${index + 1}: " + chapter.name.substringAfter(" ")
|
|
|
|
|
val chapterNumber = index + 1f
|
|
|
|
|
val sourceOrder = chapters.lastIndex - index.toLong()
|
|
|
|
|
when (chapter.id) {
|
|
|
|
|
-1L -> newChapters.add(
|
|
|
|
|
chapter.copy(
|
|
|
|
|
name = name,
|
|
|
|
|
chapterNumber = chapterNumber,
|
|
|
|
|
sourceOrder = sourceOrder,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
else -> updates.add(
|
|
|
|
|
ChapterUpdate(
|
|
|
|
|
id = chapter.id,
|
|
|
|
|
name = name.takeUnless { chapter.name == it },
|
|
|
|
|
chapterNumber = chapterNumber.takeUnless { chapter.chapterNumber == it },
|
|
|
|
|
sourceOrder = sourceOrder.takeUnless { chapter.sourceOrder == it },
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
}
|
2020-10-25 01:42:43 +02:00
|
|
|
}
|
2022-07-03 05:52:03 +02:00
|
|
|
Triple(updates.toList(), newChapters.toList(), new)
|
|
|
|
|
}
|
2020-10-25 01:42:43 +02:00
|
|
|
}
|
2019-04-18 23:40:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data class GalleryEntry(val gId: String, val gToken: String) {
|
2020-04-04 22:30:05 +02:00
|
|
|
class Serializer : MemAutoFlushingLookupTable.EntrySerializer<GalleryEntry> {
|
2019-04-18 23:40:13 +02:00
|
|
|
/**
|
|
|
|
|
* Serialize an entry as a String.
|
|
|
|
|
*/
|
|
|
|
|
override fun write(entry: GalleryEntry) = with(entry) { "$gId:$gToken" }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read an entry from a String.
|
|
|
|
|
*/
|
|
|
|
|
override fun read(string: String): GalleryEntry {
|
|
|
|
|
val colonIndex = string.indexOf(':')
|
|
|
|
|
return GalleryEntry(
|
2020-05-02 06:46:24 +02:00
|
|
|
string.substring(0, colonIndex),
|
2022-04-08 21:30:30 +02:00
|
|
|
string.substring(colonIndex + 1, string.length),
|
2019-04-18 23:40:13 +02:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|