Use SQLDelight for most SY specific things
This commit is contained in:
parent
3cf4c3128f
commit
664f9b1484
14
app/src/main/java/eu/kanade/data/exh/FavoriteEntry.kt
Normal file
14
app/src/main/java/eu/kanade/data/exh/FavoriteEntry.kt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package eu.kanade.data.exh
|
||||||
|
|
||||||
|
import exh.favorites.sql.models.FavoriteEntry
|
||||||
|
|
||||||
|
val favoriteEntryMapper: (Long, String, String, String, Long) -> FavoriteEntry =
|
||||||
|
{ id, title, gid, token, category ->
|
||||||
|
FavoriteEntry(
|
||||||
|
id = id,
|
||||||
|
title = title,
|
||||||
|
gid = gid,
|
||||||
|
token = token,
|
||||||
|
category = category.toInt(),
|
||||||
|
)
|
||||||
|
}
|
@ -10,4 +10,10 @@ class RemoveHistoryById(
|
|||||||
suspend fun await(history: HistoryWithRelations) {
|
suspend fun await(history: HistoryWithRelations) {
|
||||||
repository.resetHistory(history.id)
|
repository.resetHistory(history.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SY -->
|
||||||
|
suspend fun await(historyId: Long) {
|
||||||
|
repository.resetHistory(historyId)
|
||||||
|
}
|
||||||
|
// SY <--
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,6 @@ import eu.kanade.tachiyomi.source.online.MetadataSource
|
|||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import exh.metadata.metadata.base.awaitFlatMetadataForManga
|
import exh.metadata.metadata.base.awaitFlatMetadataForManga
|
||||||
import exh.metadata.metadata.base.awaitInsertFlatMetadata
|
import exh.metadata.metadata.base.awaitInsertFlatMetadata
|
||||||
import exh.metadata.metadata.base.getFlatMetadataForManga
|
|
||||||
import exh.source.MERGED_SOURCE_ID
|
import exh.source.MERGED_SOURCE_ID
|
||||||
import exh.source.getMainSource
|
import exh.source.getMainSource
|
||||||
import exh.util.nullIfBlank
|
import exh.util.nullIfBlank
|
||||||
@ -198,7 +197,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
|
|||||||
|
|
||||||
val source = sourceManager.get(manga.source)?.getMainSource<MetadataSource<*, *>>()
|
val source = sourceManager.get(manga.source)?.getMainSource<MetadataSource<*, *>>()
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
handler.getFlatMetadataForManga(manga.id)?.let { flatMetadata ->
|
handler.awaitFlatMetadataForManga(manga.id)?.let { flatMetadata ->
|
||||||
mangaObject.flatMetadata = BackupFlatMetadata.copyFrom(flatMetadata)
|
mangaObject.flatMetadata = BackupFlatMetadata.copyFrom(flatMetadata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package eu.kanade.tachiyomi.source.online
|
package eu.kanade.tachiyomi.source.online
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import eu.kanade.data.DatabaseHandler
|
||||||
|
import eu.kanade.domain.manga.interactor.GetMangaByUrlAndSource
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
@ -12,8 +14,8 @@ import eu.kanade.tachiyomi.ui.manga.MangaScreenState
|
|||||||
import eu.kanade.tachiyomi.util.lang.awaitSingle
|
import eu.kanade.tachiyomi.util.lang.awaitSingle
|
||||||
import eu.kanade.tachiyomi.util.lang.runAsObservable
|
import eu.kanade.tachiyomi.util.lang.runAsObservable
|
||||||
import exh.metadata.metadata.base.RaisedSearchMetadata
|
import exh.metadata.metadata.base.RaisedSearchMetadata
|
||||||
import exh.metadata.metadata.base.getFlatMetadataForManga
|
import exh.metadata.metadata.base.awaitFlatMetadataForManga
|
||||||
import exh.metadata.metadata.base.insertFlatMetadata
|
import exh.metadata.metadata.base.awaitInsertFlatMetadata
|
||||||
import rx.Completable
|
import rx.Completable
|
||||||
import rx.Single
|
import rx.Single
|
||||||
import tachiyomi.source.model.MangaInfo
|
import tachiyomi.source.model.MangaInfo
|
||||||
@ -26,6 +28,8 @@ import kotlin.reflect.KClass
|
|||||||
*/
|
*/
|
||||||
interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
|
interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
|
||||||
val db: DatabaseHelper get() = Injekt.get()
|
val db: DatabaseHelper get() = Injekt.get()
|
||||||
|
val handler: DatabaseHandler get() = Injekt.get()
|
||||||
|
val getMangaByUrlAndSource: GetMangaByUrlAndSource get() = Injekt.get()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class of the metadata used by this source
|
* The class of the metadata used by this source
|
||||||
@ -59,14 +63,14 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
|
|||||||
suspend fun parseToManga(manga: MangaInfo, input: I): MangaInfo {
|
suspend fun parseToManga(manga: MangaInfo, input: I): MangaInfo {
|
||||||
val mangaId = manga.id()
|
val mangaId = manga.id()
|
||||||
val metadata = if (mangaId != null) {
|
val metadata = if (mangaId != null) {
|
||||||
val flatMetadata = db.getFlatMetadataForManga(mangaId).executeAsBlocking()
|
val flatMetadata = handler.awaitFlatMetadataForManga(mangaId)
|
||||||
flatMetadata?.raise(metaClass) ?: newMetaInstance()
|
flatMetadata?.raise(metaClass) ?: newMetaInstance()
|
||||||
} else newMetaInstance()
|
} else newMetaInstance()
|
||||||
|
|
||||||
parseIntoMetadata(metadata, input)
|
parseIntoMetadata(metadata, input)
|
||||||
if (mangaId != null) {
|
if (mangaId != null) {
|
||||||
metadata.mangaId = mangaId
|
metadata.mangaId = mangaId
|
||||||
db.insertFlatMetadata(metadata.flatten())
|
handler.awaitInsertFlatMetadata(metadata.flatten())
|
||||||
}
|
}
|
||||||
|
|
||||||
return metadata.createMangaInfo(manga)
|
return metadata.createMangaInfo(manga)
|
||||||
@ -95,7 +99,7 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
|
|||||||
*/
|
*/
|
||||||
suspend fun fetchOrLoadMetadata(mangaId: Long?, inputProducer: suspend () -> I): M {
|
suspend fun fetchOrLoadMetadata(mangaId: Long?, inputProducer: suspend () -> I): M {
|
||||||
val meta = if (mangaId != null) {
|
val meta = if (mangaId != null) {
|
||||||
val flatMetadata = db.getFlatMetadataForManga(mangaId).executeAsBlocking()
|
val flatMetadata = handler.awaitFlatMetadataForManga(mangaId)
|
||||||
flatMetadata?.raise(metaClass)
|
flatMetadata?.raise(metaClass)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
@ -106,15 +110,16 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
|
|||||||
parseIntoMetadata(newMeta, input)
|
parseIntoMetadata(newMeta, input)
|
||||||
if (mangaId != null) {
|
if (mangaId != null) {
|
||||||
newMeta.mangaId = mangaId
|
newMeta.mangaId = mangaId
|
||||||
db.insertFlatMetadata(newMeta.flatten()).let { newMeta }
|
handler.awaitInsertFlatMetadata(newMeta.flatten())
|
||||||
} else newMeta
|
}
|
||||||
|
newMeta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit)
|
fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit)
|
||||||
|
|
||||||
fun MangaInfo.id() = db.getManga(key, id).executeAsBlocking()?.id
|
suspend fun MangaInfo.id() = getMangaByUrlAndSource.await(key, id)?.id
|
||||||
val SManga.id get() = (this as? Manga)?.id
|
val SManga.id get() = (this as? Manga)?.id
|
||||||
val SChapter.mangaId get() = (this as? Chapter)?.manga_id
|
val SChapter.mangaId get() = (this as? Chapter)?.manga_id
|
||||||
}
|
}
|
||||||
|
@ -807,7 +807,7 @@ class LibraryController(
|
|||||||
?.setMessage(activity!!.getString(R.string.favorites_sync_bad_library_state, status.message))
|
?.setMessage(activity!!.getString(R.string.favorites_sync_bad_library_state, status.message))
|
||||||
?.setCancelable(false)
|
?.setCancelable(false)
|
||||||
?.setPositiveButton(R.string.show_gallery) { _, _ ->
|
?.setPositiveButton(R.string.show_gallery) { _, _ ->
|
||||||
openManga(status.manga)
|
openManga(status.manga.toDbManga())
|
||||||
presenter.favoritesSync.status.value = FavoritesSyncStatus.Idle(activity!!)
|
presenter.favoritesSync.status.value = FavoritesSyncStatus.Idle(activity!!)
|
||||||
}
|
}
|
||||||
?.setNegativeButton(android.R.string.ok) { _, _ ->
|
?.setNegativeButton(android.R.string.ok) { _, _ ->
|
||||||
|
@ -239,7 +239,7 @@ class MangaPresenter(
|
|||||||
if (chapters.isNotEmpty() && manga.isEhBasedManga() && DebugToggles.ENABLE_EXH_ROOT_REDIRECT.enabled) {
|
if (chapters.isNotEmpty() && manga.isEhBasedManga() && DebugToggles.ENABLE_EXH_ROOT_REDIRECT.enabled) {
|
||||||
// Check for gallery in library and accept manga with lowest id
|
// Check for gallery in library and accept manga with lowest id
|
||||||
// Find chapters sharing same root
|
// Find chapters sharing same root
|
||||||
updateHelper.findAcceptedRootAndDiscardOthers(manga.source, chapters.map { it.toDbChapter() })
|
updateHelper.findAcceptedRootAndDiscardOthers(manga.source, chapters)
|
||||||
.onEach { (acceptedChain, _) ->
|
.onEach { (acceptedChain, _) ->
|
||||||
// Redirect if we are not the accepted root
|
// Redirect if we are not the accepted root
|
||||||
if (manga.id != acceptedChain.manga.id && acceptedChain.manga.favorite) {
|
if (manga.id != acceptedChain.manga.id && acceptedChain.manga.favorite) {
|
||||||
@ -250,7 +250,7 @@ class MangaPresenter(
|
|||||||
val update = (ourChapterUrls - acceptedChapterUrls).isNotEmpty()
|
val update = (ourChapterUrls - acceptedChapterUrls).isNotEmpty()
|
||||||
redirectFlow.emit(
|
redirectFlow.emit(
|
||||||
EXHRedirect(
|
EXHRedirect(
|
||||||
acceptedChain.manga.id!!,
|
acceptedChain.manga.id,
|
||||||
update,
|
update,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -2,33 +2,38 @@ package exh
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import eu.kanade.data.DatabaseHandler
|
||||||
|
import eu.kanade.data.chapter.chapterMapper
|
||||||
|
import eu.kanade.data.manga.mangaMapper
|
||||||
|
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
||||||
|
import eu.kanade.domain.chapter.model.Chapter
|
||||||
|
import eu.kanade.domain.manga.interactor.GetMangaById
|
||||||
|
import eu.kanade.domain.manga.interactor.GetMangaByUrlAndSource
|
||||||
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
|
import eu.kanade.domain.manga.model.toMangaInfo
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.model.toSChapter
|
import eu.kanade.tachiyomi.source.model.toSChapter
|
||||||
import eu.kanade.tachiyomi.source.model.toSManga
|
|
||||||
import eu.kanade.tachiyomi.source.online.UrlImportableSource
|
import eu.kanade.tachiyomi.source.online.UrlImportableSource
|
||||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
|
||||||
import exh.log.xLogStack
|
import exh.log.xLogStack
|
||||||
import exh.source.getMainSource
|
import exh.source.getMainSource
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
|
|
||||||
class GalleryAdder {
|
class GalleryAdder(
|
||||||
|
private val handler: DatabaseHandler = Injekt.get(),
|
||||||
|
private val getMangaByUrlAndSource: GetMangaByUrlAndSource = Injekt.get(),
|
||||||
|
private val getMangaById: GetMangaById = Injekt.get(),
|
||||||
|
private val updateManga: UpdateManga = Injekt.get(),
|
||||||
|
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(),
|
||||||
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
|
) {
|
||||||
|
|
||||||
private val db: DatabaseHelper by injectLazy()
|
private val filters: Pair<Set<String>, Set<Long>> = Injekt.get<PreferencesHelper>().run {
|
||||||
|
enabledLanguages().get() to disabledSources().get().map { it.toLong() }.toSet()
|
||||||
private val sourceManager: SourceManager by injectLazy()
|
|
||||||
|
|
||||||
private val filters: Pair<Set<String>, Set<Long>> = run {
|
|
||||||
val preferences = Injekt.get<PreferencesHelper>()
|
|
||||||
preferences.enabledLanguages().get() to preferences.disabledSources().get().map { it.toLong() }.toSet()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val logger = xLogStack()
|
private val logger = xLogStack()
|
||||||
@ -115,33 +120,28 @@ class GalleryAdder {
|
|||||||
} ?: return GalleryAddEvent.Fail.UnknownType(url, context)
|
} ?: return GalleryAddEvent.Fail.UnknownType(url, context)
|
||||||
|
|
||||||
// Use manga in DB if possible, otherwise, make a new manga
|
// Use manga in DB if possible, otherwise, make a new manga
|
||||||
val manga = db.getManga(cleanedMangaUrl, source.id).executeAsBlocking()
|
var manga = getMangaByUrlAndSource.await(cleanedMangaUrl, source.id)
|
||||||
?: Manga.create(source.id).apply {
|
?: handler.awaitOne(true) {
|
||||||
this.url = cleanedMangaUrl
|
|
||||||
title = realMangaUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert created manga if not in DB before fetching details
|
// Insert created manga if not in DB before fetching details
|
||||||
// This allows us to keep the metadata when fetching details
|
// This allows us to keep the metadata when fetching details
|
||||||
if (manga.id == null) {
|
mangasQueries.insertEmpty(
|
||||||
db.insertManga(manga).executeAsBlocking().insertedId()?.let {
|
source = source.id,
|
||||||
manga.id = it
|
url = cleanedMangaUrl,
|
||||||
}
|
title = realMangaUrl,
|
||||||
|
)
|
||||||
|
mangasQueries.selectLastInsertRow(mangaMapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch and copy details
|
// Fetch and copy details
|
||||||
val newManga = source.getMangaDetails(manga.toMangaInfo())
|
val newManga = source.getMangaDetails(manga.toMangaInfo())
|
||||||
|
updateManga.awaitUpdateFromSource(manga, newManga, false, Injekt.get())
|
||||||
manga.copyFrom(newManga.toSManga())
|
manga = getMangaById.await(manga.id)!!
|
||||||
manga.initialized = true
|
|
||||||
|
|
||||||
if (fav) {
|
if (fav) {
|
||||||
manga.favorite = true
|
updateManga.awaitUpdateFavorite(manga.id, true)
|
||||||
manga.date_added = System.currentTimeMillis()
|
manga = manga.copy(favorite = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
db.insertManga(manga).executeAsBlocking()
|
|
||||||
|
|
||||||
// Fetch and copy chapters
|
// Fetch and copy chapters
|
||||||
try {
|
try {
|
||||||
val chapterList = if (source is EHentai) {
|
val chapterList = if (source is EHentai) {
|
||||||
@ -151,7 +151,7 @@ class GalleryAdder {
|
|||||||
}.map { it.toSChapter() }
|
}.map { it.toSChapter() }
|
||||||
|
|
||||||
if (chapterList.isNotEmpty()) {
|
if (chapterList.isNotEmpty()) {
|
||||||
syncChaptersWithSource(chapterList, manga, source)
|
syncChaptersWithSource.await(chapterList, manga, source)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.w(context.getString(R.string.gallery_adder_chapter_fetch_error, manga.title), e)
|
logger.w(context.getString(R.string.gallery_adder_chapter_fetch_error, manga.title), e)
|
||||||
@ -159,7 +159,7 @@ class GalleryAdder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return if (cleanedChapterUrl != null) {
|
return if (cleanedChapterUrl != null) {
|
||||||
val chapter = db.getChapter(cleanedChapterUrl, manga.id!!).executeAsBlocking()
|
val chapter = handler.awaitOneOrNull { chaptersQueries.getChapterByUrlAndMangaId(cleanedChapterUrl, manga.id, chapterMapper) }
|
||||||
if (chapter != null) {
|
if (chapter != null) {
|
||||||
GalleryAddEvent.Success(url, manga, context, chapter)
|
GalleryAddEvent.Success(url, manga, context, chapter)
|
||||||
} else {
|
} else {
|
||||||
|
@ -4,19 +4,21 @@ import android.app.Application
|
|||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.pushtorefresh.storio.sqlite.queries.RawQuery
|
import com.pushtorefresh.storio.sqlite.queries.RawQuery
|
||||||
import eu.kanade.data.DatabaseHandler
|
import eu.kanade.data.DatabaseHandler
|
||||||
|
import eu.kanade.data.manga.mangaMapper
|
||||||
|
import eu.kanade.domain.manga.interactor.GetFavorites
|
||||||
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
|
import eu.kanade.domain.manga.model.toMangaInfo
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.model.toSManga
|
|
||||||
import eu.kanade.tachiyomi.source.online.all.NHentai
|
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||||
import exh.EXHMigrations
|
import exh.EXHMigrations
|
||||||
import exh.eh.EHentaiThrottleManager
|
import exh.eh.EHentaiThrottleManager
|
||||||
import exh.eh.EHentaiUpdateWorker
|
import exh.eh.EHentaiUpdateWorker
|
||||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||||
import exh.metadata.metadata.base.getFlatMetadataForManga
|
import exh.metadata.metadata.base.awaitFlatMetadataForManga
|
||||||
import exh.metadata.metadata.base.insertFlatMetadataAsync
|
import exh.metadata.metadata.base.awaitInsertFlatMetadata
|
||||||
import exh.source.EH_SOURCE_ID
|
import exh.source.EH_SOURCE_ID
|
||||||
import exh.source.EXH_SOURCE_ID
|
import exh.source.EXH_SOURCE_ID
|
||||||
import exh.source.isEhBasedManga
|
import exh.source.isEhBasedManga
|
||||||
@ -35,9 +37,11 @@ import java.util.UUID
|
|||||||
object DebugFunctions {
|
object DebugFunctions {
|
||||||
val app: Application by injectLazy()
|
val app: Application by injectLazy()
|
||||||
val db: DatabaseHelper by injectLazy()
|
val db: DatabaseHelper by injectLazy()
|
||||||
val database: DatabaseHandler by injectLazy()
|
val handler: DatabaseHandler by injectLazy()
|
||||||
val prefs: PreferencesHelper by injectLazy()
|
val prefs: PreferencesHelper by injectLazy()
|
||||||
val sourceManager: SourceManager by injectLazy()
|
val sourceManager: SourceManager by injectLazy()
|
||||||
|
val updateManga: UpdateManga by injectLazy()
|
||||||
|
val getFavorites: GetFavorites by injectLazy()
|
||||||
|
|
||||||
fun forceUpgradeMigration() {
|
fun forceUpgradeMigration() {
|
||||||
prefs.ehLastVersionCode().set(1)
|
prefs.ehLastVersionCode().set(1)
|
||||||
@ -59,10 +63,10 @@ object DebugFunctions {
|
|||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
allManga.forEach { manga ->
|
allManga.forEach { manga ->
|
||||||
val meta = db.getFlatMetadataForManga(manga.id!!).executeOnIO()?.raise<EHentaiSearchMetadata>() ?: return@forEach
|
val meta = handler.awaitFlatMetadataForManga(manga.id!!)?.raise<EHentaiSearchMetadata>() ?: return@forEach
|
||||||
// remove age flag
|
// remove age flag
|
||||||
meta.aged = false
|
meta.aged = false
|
||||||
db.insertFlatMetadataAsync(meta.flatten()).await()
|
handler.awaitInsertFlatMetadata(meta.flatten())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,70 +77,50 @@ object DebugFunctions {
|
|||||||
fun resetEHGalleriesForUpdater() {
|
fun resetEHGalleriesForUpdater() {
|
||||||
throttleManager.resetThrottle()
|
throttleManager.resetThrottle()
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val metadataManga = db.getFavoriteMangaWithMetadata().executeOnIO()
|
val allManga = handler
|
||||||
|
.awaitList { mangasQueries.getEhMangaWithMetadata(EH_SOURCE_ID, EXH_SOURCE_ID, mangaMapper) }
|
||||||
|
|
||||||
val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga ->
|
|
||||||
if (manga.isEhBasedManga()) manga
|
|
||||||
else null
|
|
||||||
}.toList()
|
|
||||||
val eh = sourceManager.get(EH_SOURCE_ID)
|
val eh = sourceManager.get(EH_SOURCE_ID)
|
||||||
val ex = sourceManager.get(EXH_SOURCE_ID)
|
val ex = sourceManager.get(EXH_SOURCE_ID)
|
||||||
|
|
||||||
allManga.forEach { manga ->
|
allManga.forEach { manga ->
|
||||||
throttleManager.throttle()
|
throttleManager.throttle()
|
||||||
(
|
|
||||||
when (manga.source) {
|
val networkManga = when (manga.source) {
|
||||||
EH_SOURCE_ID -> eh
|
EH_SOURCE_ID -> eh
|
||||||
EXH_SOURCE_ID -> ex
|
EXH_SOURCE_ID -> ex
|
||||||
else -> return@forEach
|
else -> return@forEach
|
||||||
}
|
}?.getMangaDetails(manga.toMangaInfo()) ?: return@forEach
|
||||||
)?.getMangaDetails(manga.toMangaInfo())?.let { networkManga ->
|
|
||||||
manga.copyFrom(networkManga.toSManga())
|
updateManga.awaitUpdateFromSource(manga, networkManga, true)
|
||||||
manga.initialized = true
|
|
||||||
db.insertManga(manga).executeOnIO()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEHMangaListWithAgedFlagInfo(): String {
|
fun getEHMangaListWithAgedFlagInfo(): String {
|
||||||
val galleries = mutableListOf(String())
|
return runBlocking {
|
||||||
runBlocking {
|
val allManga = handler
|
||||||
val metadataManga = db.getFavoriteMangaWithMetadata().executeOnIO()
|
.awaitList { mangasQueries.getEhMangaWithMetadata(EH_SOURCE_ID, EXH_SOURCE_ID, mangaMapper) }
|
||||||
|
|
||||||
val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga ->
|
allManga.map { manga ->
|
||||||
if (manga.isEhBasedManga()) manga
|
val meta = handler.awaitFlatMetadataForManga(manga.id)?.raise<EHentaiSearchMetadata>() ?: return@map
|
||||||
else null
|
"Aged: ${meta.aged}\t Title: ${manga.title}"
|
||||||
}.toList()
|
|
||||||
|
|
||||||
allManga.forEach { manga ->
|
|
||||||
val meta = db.getFlatMetadataForManga(manga.id!!).executeOnIO()?.raise<EHentaiSearchMetadata>() ?: return@forEach
|
|
||||||
galleries += "Aged: ${meta.aged}\t Title: ${manga.title}"
|
|
||||||
}
|
}
|
||||||
}
|
}.joinToString(",\n")
|
||||||
return galleries.joinToString(",\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun countAgedFlagInEXHManga(): Int {
|
fun countAgedFlagInEXHManga(): Int {
|
||||||
var agedAmount = 0
|
return runBlocking {
|
||||||
runBlocking {
|
handler
|
||||||
val metadataManga = db.getFavoriteMangaWithMetadata().executeOnIO()
|
.awaitList { mangasQueries.getEhMangaWithMetadata(EH_SOURCE_ID, EXH_SOURCE_ID, mangaMapper) }
|
||||||
|
.count { manga ->
|
||||||
val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga ->
|
val meta = handler.awaitFlatMetadataForManga(manga.id)
|
||||||
if (manga.isEhBasedManga()) manga
|
?.raise<EHentaiSearchMetadata>()
|
||||||
else null
|
?: return@count false
|
||||||
}.toList()
|
meta.aged
|
||||||
|
|
||||||
allManga.forEach { manga ->
|
|
||||||
val meta = db.getFlatMetadataForManga(manga.id!!).executeOnIO()?.raise<EHentaiSearchMetadata>() ?: return@forEach
|
|
||||||
if (meta.aged) {
|
|
||||||
// remove age flag
|
|
||||||
agedAmount++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return agedAmount
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addAllMangaInDatabaseToLibrary() {
|
fun addAllMangaInDatabaseToLibrary() {
|
||||||
db.inTransaction {
|
db.inTransaction {
|
||||||
@ -154,7 +138,7 @@ object DebugFunctions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun countMangaInDatabaseInLibrary() = db.getMangas().executeAsBlocking().count { it.favorite }
|
fun countMangaInDatabaseInLibrary() = runBlocking { getFavorites.await().size }
|
||||||
|
|
||||||
fun countMangaInDatabaseNotInLibrary() = db.getMangas().executeAsBlocking().count { !it.favorite }
|
fun countMangaInDatabaseNotInLibrary() = db.getMangas().executeAsBlocking().count { !it.favorite }
|
||||||
|
|
||||||
@ -166,7 +150,7 @@ object DebugFunctions {
|
|||||||
it.favorite && db.getSearchMetadataForManga(it.id!!).executeAsBlocking() == null
|
it.favorite && db.getSearchMetadataForManga(it.id!!).executeAsBlocking() == null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearSavedSearches() = runBlocking { database.await { saved_searchQueries.deleteAll() } }
|
fun clearSavedSearches() = runBlocking { handler.await { saved_searchQueries.deleteAll() } }
|
||||||
|
|
||||||
fun listAllSources() = sourceManager.getCatalogueSources().joinToString("\n") {
|
fun listAllSources() = sourceManager.getCatalogueSources().joinToString("\n") {
|
||||||
"${it.id}: ${it.name} (${it.lang.uppercase()})"
|
"${it.id}: ${it.name} (${it.lang.uppercase()})"
|
||||||
|
@ -1,12 +1,23 @@
|
|||||||
package exh.eh
|
package exh.eh
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.data.DatabaseHandler
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.data.chapter.chapterMapper
|
||||||
import eu.kanade.tachiyomi.data.database.models.History
|
import eu.kanade.data.history.historyMapper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.domain.category.interactor.GetCategories
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
import eu.kanade.domain.category.interactor.SetMangaCategories
|
||||||
import exh.util.executeOnIO
|
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
||||||
|
import eu.kanade.domain.chapter.model.Chapter
|
||||||
|
import eu.kanade.domain.chapter.model.ChapterUpdate
|
||||||
|
import eu.kanade.domain.chapter.repository.ChapterRepository
|
||||||
|
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
|
||||||
|
import eu.kanade.domain.manga.interactor.GetMangaById
|
||||||
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
|
import eu.kanade.domain.manga.model.MangaUpdate
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
@ -24,7 +35,15 @@ class EHentaiUpdateHelper(context: Context) {
|
|||||||
File(context.filesDir, "exh-plt.maftable"),
|
File(context.filesDir, "exh-plt.maftable"),
|
||||||
GalleryEntry.Serializer(),
|
GalleryEntry.Serializer(),
|
||||||
)
|
)
|
||||||
private val db: DatabaseHelper by injectLazy()
|
private val handler: DatabaseHandler by injectLazy()
|
||||||
|
private val getChapterByMangaId: GetChapterByMangaId by injectLazy()
|
||||||
|
private val getMangaById: GetMangaById by injectLazy()
|
||||||
|
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()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param chapters Cannot be an empty list!
|
* @param chapters Cannot be an empty list!
|
||||||
@ -36,7 +55,8 @@ class EHentaiUpdateHelper(context: Context) {
|
|||||||
val chainsFlow = flowOf(chapters)
|
val chainsFlow = flowOf(chapters)
|
||||||
.map { chapterList ->
|
.map { chapterList ->
|
||||||
chapterList.flatMap { chapter ->
|
chapterList.flatMap { chapter ->
|
||||||
db.getChapters(chapter.url).executeOnIO().mapNotNull { it.manga_id }
|
handler.awaitList { chaptersQueries.getChapterByUrl(chapter.url, chapterMapper) }
|
||||||
|
.map { it.mangaId }
|
||||||
}.distinct()
|
}.distinct()
|
||||||
}
|
}
|
||||||
.map { mangaIds ->
|
.map { mangaIds ->
|
||||||
@ -44,13 +64,13 @@ class EHentaiUpdateHelper(context: Context) {
|
|||||||
.mapNotNull { mangaId ->
|
.mapNotNull { mangaId ->
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
val manga = async(Dispatchers.IO) {
|
val manga = async(Dispatchers.IO) {
|
||||||
db.getManga(mangaId).executeAsBlocking()
|
getMangaById.await(mangaId)
|
||||||
}
|
}
|
||||||
val chapterList = async(Dispatchers.IO) {
|
val chapterList = async(Dispatchers.IO) {
|
||||||
db.getChapters(mangaId).executeAsBlocking()
|
getChapterByMangaId.await(mangaId)
|
||||||
}
|
}
|
||||||
val history = async(Dispatchers.IO) {
|
val history = async(Dispatchers.IO) {
|
||||||
db.getHistoryByMangaId(mangaId).executeAsBlocking()
|
handler.awaitList { historyQueries.getHistoryByMangaId(mangaId, historyMapper) }
|
||||||
}
|
}
|
||||||
ChapterChain(
|
ChapterChain(
|
||||||
manga.await() ?: return@coroutineScope null,
|
manga.await() ?: return@coroutineScope null,
|
||||||
@ -64,66 +84,66 @@ class EHentaiUpdateHelper(context: Context) {
|
|||||||
|
|
||||||
// Accept oldest chain
|
// Accept oldest chain
|
||||||
val chainsWithAccepted = chainsFlow.map { chains ->
|
val chainsWithAccepted = chainsFlow.map { chains ->
|
||||||
val acceptedChain = chains.minByOrNull { it.manga.id!! }!!
|
val acceptedChain = chains.minBy { it.manga.id }
|
||||||
|
|
||||||
acceptedChain to chains
|
acceptedChain to chains
|
||||||
}
|
}
|
||||||
|
|
||||||
return chainsWithAccepted.map { (accepted, chains) ->
|
return chainsWithAccepted.map { (accepted, chains) ->
|
||||||
val toDiscard = chains.filter { it.manga.favorite && it.manga.id != accepted.manga.id }
|
val toDiscard = chains.filter { it.manga.favorite && it.manga.id != accepted.manga.id }
|
||||||
|
val mangaUpdates = mutableListOf<MangaUpdate>()
|
||||||
|
|
||||||
val chainsAsChapters = chains.flatMap { it.chapters }
|
val chainsAsChapters = chains.flatMap { it.chapters }
|
||||||
val chainsAsHistory = chains.flatMap { it.history }
|
val chainsAsHistory = chains.flatMap { it.history }
|
||||||
|
|
||||||
if (toDiscard.isNotEmpty()) {
|
if (toDiscard.isNotEmpty()) {
|
||||||
// Copy chain chapters to curChapters
|
// Copy chain chapters to curChapters
|
||||||
val (newChapters, new) = getChapterList(accepted, toDiscard, chainsAsChapters)
|
val (chapterUpdates, newChapters, new) = getChapterList(accepted, toDiscard, chainsAsChapters)
|
||||||
val (history, urlHistory, deleteHistory) = getHistory(newChapters, chainsAsChapters, chainsAsHistory)
|
|
||||||
|
|
||||||
toDiscard.forEach {
|
toDiscard.forEach {
|
||||||
it.manga.favorite = false
|
mangaUpdates += MangaUpdate(
|
||||||
it.manga.date_added = 0
|
id = it.manga.id,
|
||||||
|
favorite = false,
|
||||||
|
dateAdded = 0,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (!accepted.manga.favorite) {
|
if (!accepted.manga.favorite) {
|
||||||
accepted.manga.favorite = true
|
mangaUpdates += MangaUpdate(
|
||||||
accepted.manga.date_added = System.currentTimeMillis()
|
id = accepted.manga.id,
|
||||||
|
favorite = true,
|
||||||
|
dateAdded = System.currentTimeMillis(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val newAccepted = ChapterChain(accepted.manga, newChapters, history + urlHistory.map { it.second })
|
val newAccepted = ChapterChain(accepted.manga, newChapters, emptyList())
|
||||||
val rootsToMutate = toDiscard + newAccepted
|
val rootsToMutate = toDiscard + newAccepted
|
||||||
|
|
||||||
db.inTransaction {
|
|
||||||
// Apply changes to all manga
|
// Apply changes to all manga
|
||||||
db.insertMangas(rootsToMutate.map { it.manga }).executeAsBlocking()
|
updateManga.awaitAll(mangaUpdates)
|
||||||
// Insert new chapters for accepted manga
|
// Insert new chapters for accepted manga
|
||||||
val chapterPutResults = db.insertChapters(newAccepted.chapters).executeAsBlocking().results()
|
chapterRepository.updateAll(chapterUpdates)
|
||||||
|
chapterRepository.addAll(newChapters)
|
||||||
|
|
||||||
|
val (newHistory, deleteHistory) = getHistory(getChapterByMangaId.await(accepted.manga.id), chainsAsChapters, chainsAsHistory)
|
||||||
|
|
||||||
// Delete the duplicate history first
|
// Delete the duplicate history first
|
||||||
if (deleteHistory.isNotEmpty()) {
|
if (deleteHistory.isNotEmpty()) {
|
||||||
db.deleteHistoryIds(deleteHistory).executeAsBlocking()
|
deleteHistory.forEach {
|
||||||
|
removeHistoryById.await(it)
|
||||||
}
|
}
|
||||||
// Get a updated history list
|
|
||||||
val newHistory = urlHistory.mapNotNull { (url, history) ->
|
|
||||||
val result = chapterPutResults.firstNotNullOfOrNull { (chapter, result) ->
|
|
||||||
if (chapter.url == url) {
|
|
||||||
result.insertedId()
|
|
||||||
} else null
|
|
||||||
}
|
}
|
||||||
if (result != null) {
|
// Insert new history
|
||||||
history.chapter_id = result
|
newHistory.forEach {
|
||||||
history
|
upsertHistory.await(it)
|
||||||
} else null
|
}
|
||||||
} + history
|
|
||||||
// Copy the new history chapter ids
|
|
||||||
db.updateHistoryChapterIds(newHistory).executeAsBlocking()
|
|
||||||
|
|
||||||
// Copy categories from all chains to accepted manga
|
// Copy categories from all chains to accepted manga
|
||||||
|
|
||||||
val newCategories = rootsToMutate.flatMap {
|
val newCategories = rootsToMutate.flatMap {
|
||||||
db.getCategoriesForManga(it.manga).executeAsBlocking()
|
getCategories.await(it.manga.id).map { it.id }
|
||||||
}.distinctBy { it.id }.map {
|
}.distinct()
|
||||||
MangaCategory.create(newAccepted.manga, it)
|
rootsToMutate.forEach {
|
||||||
}
|
setMangaCategories.await(it.manga.id, newCategories)
|
||||||
db.setMangaCategories(newCategories, rootsToMutate.map { it.manga })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Triple(newAccepted, toDiscard, new)
|
Triple(newAccepted, toDiscard, new)
|
||||||
@ -140,105 +160,105 @@ class EHentaiUpdateHelper(context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class HistoryUpdates(
|
fun getHistory(
|
||||||
val history: List<History>,
|
currentChapters: List<Chapter>,
|
||||||
val urlHistory: List<Pair<String, History>>,
|
|
||||||
val historyToDelete: List<Long>,
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun getHistory(
|
|
||||||
newChapters: List<Chapter>,
|
|
||||||
chainsAsChapters: List<Chapter>,
|
chainsAsChapters: List<Chapter>,
|
||||||
chainsAsHistory: List<History>,
|
chainsAsHistory: List<History>,
|
||||||
): HistoryUpdates {
|
): Pair<List<HistoryUpdate>, List<Long>> {
|
||||||
val historyMap = chainsAsHistory
|
val history = chainsAsHistory.groupBy { history -> chainsAsChapters.find { it.id == history.chapterId }?.url }
|
||||||
.groupBy { history ->
|
val newHistory = currentChapters.mapNotNull { chapter ->
|
||||||
chainsAsChapters.find { it.id == history.chapter_id }?.url.orEmpty()
|
val newHistory = history[chapter.url]
|
||||||
|
?.maxByOrNull {
|
||||||
|
it.readAt?.time ?: 0
|
||||||
}
|
}
|
||||||
.filterKeys { it.isNotBlank() }
|
?.takeIf { it.chapterId != chapter.id && it.readAt != null }
|
||||||
val latestHistory = historyMap.mapValues { entry ->
|
if (newHistory != null) {
|
||||||
entry.value.maxByOrNull {
|
HistoryUpdate(chapter.id, newHistory.readAt!!, newHistory.readDuration)
|
||||||
it.time_read
|
} else null
|
||||||
}!!
|
|
||||||
}
|
}
|
||||||
val oldHistory = historyMap.flatMap { entry ->
|
val currentChapterIds = currentChapters.map { it.id }
|
||||||
val topEntry = entry.value.maxByOrNull {
|
val historyToDelete = chainsAsHistory.filterNot { it.chapterId in currentChapterIds }
|
||||||
it.time_read
|
.map { it.id }
|
||||||
}!!
|
return newHistory to historyToDelete
|
||||||
entry.value - topEntry
|
|
||||||
}.mapNotNull { it.id }
|
|
||||||
return HistoryUpdates(
|
|
||||||
latestHistory.filter { (_, history) ->
|
|
||||||
val oldChapter = chainsAsChapters.find { it.id == history.chapter_id }
|
|
||||||
val newChapter = newChapters.find { it.url == oldChapter?.url }
|
|
||||||
if (oldChapter != newChapter && newChapter?.id != null) {
|
|
||||||
history.chapter_id = newChapter.id!!
|
|
||||||
true
|
|
||||||
} else false
|
|
||||||
}.mapNotNull { it.value },
|
|
||||||
latestHistory.mapNotNull { (url, history) ->
|
|
||||||
val oldChapter = chainsAsChapters.find { it.id == history.chapter_id }
|
|
||||||
val newChapter = newChapters.find { it.url == oldChapter?.url }
|
|
||||||
if (oldChapter != newChapter && newChapter?.id == null) {
|
|
||||||
url to history
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
oldHistory,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getChapterList(
|
private fun getChapterList(
|
||||||
accepted: ChapterChain,
|
accepted: ChapterChain,
|
||||||
toDiscard: List<ChapterChain>,
|
toDiscard: List<ChapterChain>,
|
||||||
chainsAsChapters: List<Chapter>,
|
chainsAsChapters: List<Chapter>,
|
||||||
): Pair<List<Chapter>, Boolean> {
|
): Triple<List<ChapterUpdate>, List<Chapter>, Boolean> {
|
||||||
var new = false
|
var new = false
|
||||||
return toDiscard
|
return toDiscard
|
||||||
.flatMap { chain ->
|
.flatMap { chain ->
|
||||||
chain.chapters
|
chain.chapters
|
||||||
}
|
}
|
||||||
.fold(accepted.chapters) { curChapters, chapter ->
|
.fold(accepted.chapters) { curChapters, chapter ->
|
||||||
val existing = curChapters.find { it.url == chapter.url }
|
val newLastPageRead = chainsAsChapters.maxOfOrNull { it.lastPageRead }
|
||||||
|
|
||||||
val newLastPageRead = chainsAsChapters.maxOfOrNull { it.last_page_read }
|
if (curChapters.any { it.url == chapter.url }) {
|
||||||
|
curChapters.map {
|
||||||
if (existing != null) {
|
if (it.url == chapter.url) {
|
||||||
existing.read = existing.read || chapter.read
|
val read = it.read || chapter.read
|
||||||
existing.last_page_read = existing.last_page_read.coerceAtLeast(chapter.last_page_read)
|
var lastPageRead = it.lastPageRead.coerceAtLeast(chapter.lastPageRead)
|
||||||
if (newLastPageRead != null && existing.last_page_read <= 0) {
|
if (newLastPageRead != null && lastPageRead <= 0) {
|
||||||
existing.last_page_read = newLastPageRead
|
lastPageRead = newLastPageRead
|
||||||
|
}
|
||||||
|
val bookmark = it.bookmark || chapter.bookmark
|
||||||
|
it.copy(
|
||||||
|
read = read,
|
||||||
|
lastPageRead = lastPageRead,
|
||||||
|
bookmark = bookmark,
|
||||||
|
)
|
||||||
|
} else it
|
||||||
}
|
}
|
||||||
existing.bookmark = existing.bookmark || chapter.bookmark
|
|
||||||
curChapters
|
|
||||||
} else {
|
} else {
|
||||||
new = true
|
new = true
|
||||||
curChapters + Chapter.create().apply {
|
curChapters + Chapter(
|
||||||
manga_id = accepted.manga.id
|
id = -1,
|
||||||
url = chapter.url
|
mangaId = accepted.manga.id,
|
||||||
name = chapter.name
|
url = chapter.url,
|
||||||
read = chapter.read
|
name = chapter.name,
|
||||||
bookmark = chapter.bookmark
|
read = chapter.read,
|
||||||
|
bookmark = chapter.bookmark,
|
||||||
last_page_read = chapter.last_page_read
|
lastPageRead = if (newLastPageRead != null && chapter.lastPageRead <= 0) {
|
||||||
if (newLastPageRead != null && last_page_read <= 0) {
|
newLastPageRead
|
||||||
last_page_read = newLastPageRead
|
} else chapter.lastPageRead,
|
||||||
}
|
dateFetch = chapter.dateFetch,
|
||||||
|
dateUpload = chapter.dateUpload,
|
||||||
date_fetch = chapter.date_fetch
|
chapterNumber = -1F,
|
||||||
date_upload = chapter.date_upload
|
scanlator = null,
|
||||||
|
sourceOrder = -1,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.sortedBy { it.dateUpload }
|
||||||
.sortedBy { it.date_upload }
|
|
||||||
.let { chapters ->
|
.let { chapters ->
|
||||||
chapters.onEachIndexed { index, chapter ->
|
val updates = mutableListOf<ChapterUpdate>()
|
||||||
chapter.name = "v${index + 1}: " + chapter.name.substringAfter(" ")
|
val newChapters = mutableListOf<Chapter>()
|
||||||
chapter.chapter_number = index + 1f
|
chapters.mapIndexed { index, chapter ->
|
||||||
chapter.source_order = chapters.lastIndex - index
|
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 },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Triple(updates.toList(), newChapters.toList(), new)
|
||||||
}
|
}
|
||||||
} to new
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,29 +11,35 @@ import androidx.work.WorkManager
|
|||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import com.elvishew.xlog.Logger
|
import com.elvishew.xlog.Logger
|
||||||
import com.elvishew.xlog.XLog
|
import com.elvishew.xlog.XLog
|
||||||
|
import eu.kanade.data.DatabaseHandler
|
||||||
|
import eu.kanade.data.manga.mangaMapper
|
||||||
|
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
||||||
|
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
||||||
|
import eu.kanade.domain.chapter.model.Chapter
|
||||||
|
import eu.kanade.domain.chapter.model.toDbChapter
|
||||||
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
|
import eu.kanade.domain.manga.model.toMangaInfo
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateNotifier
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateNotifier
|
||||||
import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING
|
import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING
|
||||||
import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI
|
import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.model.toSChapter
|
import eu.kanade.tachiyomi.source.model.toSChapter
|
||||||
import eu.kanade.tachiyomi.source.model.toSManga
|
|
||||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
|
||||||
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
||||||
import exh.debug.DebugToggles
|
import exh.debug.DebugToggles
|
||||||
import exh.eh.EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION
|
import exh.eh.EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION
|
||||||
import exh.log.xLog
|
import exh.log.xLog
|
||||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||||
import exh.metadata.metadata.base.getFlatMetadataForManga
|
import exh.metadata.metadata.base.awaitFlatMetadataForManga
|
||||||
import exh.metadata.metadata.base.insertFlatMetadataAsync
|
import exh.metadata.metadata.base.insertFlatMetadataAsync
|
||||||
|
import exh.source.EH_SOURCE_ID
|
||||||
|
import exh.source.EXH_SOURCE_ID
|
||||||
import exh.source.isEhBasedManga
|
import exh.source.isEhBasedManga
|
||||||
import exh.util.cancellable
|
import exh.util.cancellable
|
||||||
import exh.util.executeOnIO
|
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.mapNotNull
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
import kotlinx.coroutines.flow.single
|
import kotlinx.coroutines.flow.single
|
||||||
@ -49,10 +55,14 @@ import kotlin.time.Duration.Companion.days
|
|||||||
class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerParameters) :
|
class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerParameters) :
|
||||||
CoroutineWorker(context, workerParams) {
|
CoroutineWorker(context, workerParams) {
|
||||||
private val db: DatabaseHelper by injectLazy()
|
private val db: DatabaseHelper by injectLazy()
|
||||||
|
private val handler: DatabaseHandler by injectLazy()
|
||||||
private val prefs: PreferencesHelper by injectLazy()
|
private val prefs: PreferencesHelper by injectLazy()
|
||||||
private val sourceManager: SourceManager by injectLazy()
|
private val sourceManager: SourceManager by injectLazy()
|
||||||
private val updateHelper: EHentaiUpdateHelper by injectLazy()
|
private val updateHelper: EHentaiUpdateHelper by injectLazy()
|
||||||
private val logger: Logger = xLog()
|
private val logger: Logger = xLog()
|
||||||
|
private val updateManga: UpdateManga by injectLazy()
|
||||||
|
private val syncChaptersWithSource: SyncChaptersWithSource by injectLazy()
|
||||||
|
private val getChapterByMangaId: GetChapterByMangaId by injectLazy()
|
||||||
|
|
||||||
private val updateNotifier by lazy { LibraryUpdateNotifier(context) }
|
private val updateNotifier by lazy { LibraryUpdateNotifier(context) }
|
||||||
|
|
||||||
@ -76,7 +86,7 @@ class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerPara
|
|||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
|
|
||||||
logger.d("Finding manga with metadata...")
|
logger.d("Finding manga with metadata...")
|
||||||
val metadataManga = db.getFavoriteMangaWithMetadata().executeOnIO()
|
val metadataManga = handler.awaitList { mangasQueries.getEhMangaWithMetadata(EH_SOURCE_ID, EXH_SOURCE_ID, mangaMapper) }
|
||||||
|
|
||||||
logger.d("Filtering manga and raising metadata...")
|
logger.d("Filtering manga and raising metadata...")
|
||||||
val curTime = System.currentTimeMillis()
|
val curTime = System.currentTimeMillis()
|
||||||
@ -85,7 +95,7 @@ class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerPara
|
|||||||
return@mapNotNull null
|
return@mapNotNull null
|
||||||
}
|
}
|
||||||
|
|
||||||
val meta = db.getFlatMetadataForManga(manga.id!!).executeOnIO()
|
val meta = handler.awaitFlatMetadataForManga(manga.id)
|
||||||
?: return@mapNotNull null
|
?: return@mapNotNull null
|
||||||
|
|
||||||
val raisedMeta = meta.raise<EHentaiSearchMetadata>()
|
val raisedMeta = meta.raise<EHentaiSearchMetadata>()
|
||||||
@ -95,8 +105,8 @@ class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerPara
|
|||||||
return@mapNotNull null
|
return@mapNotNull null
|
||||||
}
|
}
|
||||||
|
|
||||||
val chapter = db.getChapters(manga.id!!).executeOnIO().minByOrNull {
|
val chapter = getChapterByMangaId.await(manga.id).minByOrNull {
|
||||||
it.date_upload
|
it.dateUpload
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateEntry(manga, raisedMeta, chapter)
|
UpdateEntry(manga, raisedMeta, chapter)
|
||||||
@ -176,8 +186,8 @@ class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerPara
|
|||||||
updatedManga += acceptedRoot.manga to new.toTypedArray()
|
updatedManga += acceptedRoot.manga to new.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
modifiedThisIteration += acceptedRoot.manga.id!!
|
modifiedThisIteration += acceptedRoot.manga.id
|
||||||
modifiedThisIteration += discardedRoots.map { it.manga.id!! }
|
modifiedThisIteration += discardedRoots.map { it.manga.id }
|
||||||
updatedThisIteration++
|
updatedThisIteration++
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -192,7 +202,7 @@ class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerPara
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (updatedManga.isNotEmpty()) {
|
if (updatedManga.isNotEmpty()) {
|
||||||
updateNotifier.showUpdateNotifications(updatedManga)
|
updateNotifier.showUpdateNotifications(updatedManga.map { it.first.toDbManga() to it.second.map { it.toDbChapter() }.toTypedArray() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,17 +214,16 @@ class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerPara
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
val updatedManga = source.getMangaDetails(manga.toMangaInfo())
|
val updatedManga = source.getMangaDetails(manga.toMangaInfo())
|
||||||
manga.copyFrom(updatedManga.toSManga())
|
updateManga.awaitUpdateFromSource(manga, updatedManga, false)
|
||||||
db.insertManga(manga).executeOnIO()
|
|
||||||
|
|
||||||
val newChapters = source.getChapterList(manga.toMangaInfo())
|
val newChapters = source.getChapterList(manga.toMangaInfo())
|
||||||
.map { it.toSChapter() }
|
.map { it.toSChapter() }
|
||||||
|
|
||||||
val (new, _) = syncChaptersWithSource(newChapters, manga, source) // Not suspending, but does block, maybe fix this?
|
val (new, _) = syncChaptersWithSource.await(newChapters, manga, source)
|
||||||
return new to db.getChapters(manga).executeOnIO()
|
return new to getChapterByMangaId.await(manga.id)
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
if (t is EHentai.GalleryNotFoundException) {
|
if (t is EHentai.GalleryNotFoundException) {
|
||||||
val meta = db.getFlatMetadataForManga(manga.id!!).executeOnIO()?.raise<EHentaiSearchMetadata>()
|
val meta = handler.awaitFlatMetadataForManga(manga.id)?.raise<EHentaiSearchMetadata>()
|
||||||
if (meta != null) {
|
if (meta != null) {
|
||||||
// Age dead galleries
|
// Age dead galleries
|
||||||
logger.d("Aged %s - notfound", manga.id)
|
logger.d("Aged %s - notfound", manga.id)
|
||||||
|
@ -3,18 +3,23 @@ package exh.favorites
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.wifi.WifiManager
|
import android.net.wifi.WifiManager
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
|
import eu.kanade.data.AndroidDatabaseHandler
|
||||||
|
import eu.kanade.data.DatabaseHandler
|
||||||
|
import eu.kanade.domain.category.interactor.GetCategories
|
||||||
|
import eu.kanade.domain.category.interactor.SetMangaCategories
|
||||||
|
import eu.kanade.domain.category.model.Category
|
||||||
|
import eu.kanade.domain.manga.interactor.GetMangaByUrlAndSource
|
||||||
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||||
import eu.kanade.tachiyomi.data.database.models.Category
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.network.await
|
import eu.kanade.tachiyomi.network.await
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
|
||||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
import eu.kanade.tachiyomi.util.system.powerManager
|
import eu.kanade.tachiyomi.util.system.powerManager
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import exh.GalleryAddEvent
|
import exh.GalleryAddEvent
|
||||||
@ -29,13 +34,11 @@ import exh.source.isEhBasedManga
|
|||||||
import exh.util.ignore
|
import exh.util.ignore
|
||||||
import exh.util.wifiManager
|
import exh.util.wifiManager
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.newSingleThreadContext
|
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
@ -43,16 +46,18 @@ import uy.kohesive.injekt.api.get
|
|||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
// TODO only apply database changes after sync
|
||||||
class FavoritesSyncHelper(val context: Context) {
|
class FavoritesSyncHelper(val context: Context) {
|
||||||
private val db: DatabaseHelper by injectLazy()
|
private val handler: DatabaseHandler by injectLazy()
|
||||||
|
private val getCategories: GetCategories by injectLazy()
|
||||||
|
private val getMangaByUrlAndSource: GetMangaByUrlAndSource by injectLazy()
|
||||||
|
private val updateManga: UpdateManga by injectLazy()
|
||||||
|
private val setMangaCategories: SetMangaCategories by injectLazy()
|
||||||
|
|
||||||
private val prefs: PreferencesHelper by injectLazy()
|
private val prefs: PreferencesHelper by injectLazy()
|
||||||
|
|
||||||
private val scope = CoroutineScope(Job() + Dispatchers.Main)
|
private val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
|
||||||
private val dispatcher = newSingleThreadContext("Favorites-sync-worker")
|
|
||||||
|
|
||||||
private val exh by lazy {
|
private val exh by lazy {
|
||||||
Injekt.get<SourceManager>().get(EXH_SOURCE_ID) as? EHentai
|
Injekt.get<SourceManager>().get(EXH_SOURCE_ID) as? EHentai
|
||||||
?: EHentai(0, true, context)
|
?: EHentai(0, true, context)
|
||||||
@ -79,7 +84,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
status.value = FavoritesSyncStatus.Initializing(context)
|
status.value = FavoritesSyncStatus.Initializing(context)
|
||||||
|
|
||||||
scope.launch(dispatcher) { beginSync() }
|
scope.launch(Dispatchers.IO) { beginSync() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun beginSync() {
|
private suspend fun beginSync() {
|
||||||
@ -91,14 +96,14 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
// Validate library state
|
// Validate library state
|
||||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context)
|
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context)
|
||||||
val libraryManga = db.getLibraryMangas().executeAsBlocking()
|
val libraryManga = handler.awaitList { (handler as AndroidDatabaseHandler).getLibraryQuery() }
|
||||||
val seenManga = HashSet<Long>(libraryManga.size)
|
val seenManga = HashSet<Long>(libraryManga.size)
|
||||||
libraryManga.forEach {
|
libraryManga.forEach {
|
||||||
if (!it.isEhBasedManga()) return@forEach
|
if (!it.isEhBasedManga()) return@forEach
|
||||||
|
|
||||||
if (it.id in seenManga) {
|
if (it.id in seenManga) {
|
||||||
val inCategories = db.getCategoriesForManga(it).executeAsBlocking()
|
val inCategories = getCategories.await(it.id!!)
|
||||||
status.value = FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories(it, inCategories, context)
|
status.value = FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories(it.toDomainManga()!!, inCategories, context)
|
||||||
|
|
||||||
logger.w(context.getString(R.string.favorites_sync_manga_multiple_categories_error, it.id))
|
logger.w(context.getString(R.string.favorites_sync_manga_multiple_categories_error, it.id))
|
||||||
return
|
return
|
||||||
@ -139,7 +144,6 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
// Do not update galleries while syncing favorites
|
// Do not update galleries while syncing favorites
|
||||||
EHentaiUpdateWorker.cancelBackground(context)
|
EHentaiUpdateWorker.cancelBackground(context)
|
||||||
|
|
||||||
db.inTransaction {
|
|
||||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_calculating_remote_changes), context = context)
|
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_calculating_remote_changes), context = context)
|
||||||
val remoteChanges = storage.getChangedRemoteEntries(favorites.first)
|
val remoteChanges = storage.getChangedRemoteEntries(favorites.first)
|
||||||
val localChanges = if (prefs.exhReadOnlySync().get()) {
|
val localChanges = if (prefs.exhReadOnlySync().get()) {
|
||||||
@ -161,10 +165,9 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_cleaning_up), context = context)
|
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_cleaning_up), context = context)
|
||||||
storage.snapshotEntries()
|
storage.snapshotEntries()
|
||||||
}
|
|
||||||
|
|
||||||
launchUI {
|
withUIContext {
|
||||||
context.toast(context.getString(R.string.favorites_sync_complete))
|
context.toast(R.string.favorites_sync_complete)
|
||||||
}
|
}
|
||||||
} catch (e: IgnoredException) {
|
} catch (e: IgnoredException) {
|
||||||
// Do not display error as this error has already been reported
|
// Do not display error as this error has already been reported
|
||||||
@ -196,46 +199,33 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyRemoteCategories(categories: List<String>) {
|
private suspend fun applyRemoteCategories(categories: List<String>) {
|
||||||
val localCategories = db.getCategories().executeAsBlocking()
|
val localCategories = getCategories.await()
|
||||||
|
|
||||||
val newLocalCategories = localCategories.toMutableList()
|
val newLocalCategories = localCategories.toMutableList()
|
||||||
|
|
||||||
var changed = false
|
|
||||||
|
|
||||||
categories.forEachIndexed { index, remote ->
|
categories.forEachIndexed { index, remote ->
|
||||||
val local = localCategories.getOrElse(index) {
|
val local = localCategories.getOrElse(index) {
|
||||||
changed = true
|
val newCategoryId = handler.awaitOne(true) {
|
||||||
|
categoriesQueries.insert(remote, index.toLong(), 0L, emptyList())
|
||||||
Category.create(remote).apply {
|
categoriesQueries.selectLastInsertedRowId()
|
||||||
order = index
|
|
||||||
|
|
||||||
// Going through categories list from front to back
|
|
||||||
// If category does not exist, list size <= category index
|
|
||||||
// Thus, we can just add it here and not worry about indexing
|
|
||||||
newLocalCategories += this
|
|
||||||
}
|
}
|
||||||
|
Category(newCategoryId, remote, index.toLong(), 0L, emptyList())
|
||||||
|
.also { newLocalCategories += it }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local.name != remote) {
|
// Ensure consistent ordering and naming
|
||||||
changed = true
|
if (local.name != remote || local.order != index.toLong()) {
|
||||||
|
handler.await {
|
||||||
local.name = remote
|
categoriesQueries.update(
|
||||||
|
categoryId = local.id,
|
||||||
|
order = index.toLong().takeIf { it != local.order },
|
||||||
|
name = remote.takeIf { it != local.name },
|
||||||
|
flags = null,
|
||||||
|
mangaOrder = null,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure consistent ordering
|
|
||||||
newLocalCategories.forEachIndexed { index, category ->
|
|
||||||
if (category.order != index) {
|
|
||||||
changed = true
|
|
||||||
|
|
||||||
category.order = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only insert categories if changed
|
|
||||||
if (changed) {
|
|
||||||
db.insertCategories(newLocalCategories).executeAsBlocking()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,27 +329,25 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
// Consider both EX and EH sources
|
// Consider both EX and EH sources
|
||||||
listOf(
|
listOf(
|
||||||
db.getManga(url, EXH_SOURCE_ID),
|
EXH_SOURCE_ID,
|
||||||
db.getManga(url, EH_SOURCE_ID),
|
EH_SOURCE_ID,
|
||||||
).forEach {
|
).forEach {
|
||||||
val manga = it.executeAsBlocking()
|
val manga = getMangaByUrlAndSource.await(url, it)
|
||||||
|
|
||||||
if (manga?.favorite == true) {
|
if (manga?.favorite == true) {
|
||||||
manga.favorite = false
|
updateManga.awaitUpdateFavorite(manga.id, false)
|
||||||
manga.date_added = 0
|
|
||||||
db.updateMangaFavorite(manga).executeAsBlocking()
|
|
||||||
removedManga += manga
|
removedManga += manga
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't do too many DB OPs in one go
|
// Can't do too many DB OPs in one go
|
||||||
removedManga.chunked(10).forEach {
|
removedManga.forEach {
|
||||||
db.deleteOldMangasCategories(it).executeAsBlocking()
|
setMangaCategories.await(it.id, emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
val insertedMangaCategories = mutableListOf<Pair<MangaCategory, Manga>>()
|
val insertedMangaCategories = mutableListOf<Pair<Long, Manga>>()
|
||||||
val categories = db.getCategories().executeAsBlocking()
|
val categories = getCategories.await()
|
||||||
|
|
||||||
// Apply additions
|
// Apply additions
|
||||||
throttleManager.resetThrottle()
|
throttleManager.resetThrottle()
|
||||||
@ -402,18 +390,13 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
throw IgnoredException()
|
throw IgnoredException()
|
||||||
}
|
}
|
||||||
} else if (result is GalleryAddEvent.Success) {
|
} else if (result is GalleryAddEvent.Success) {
|
||||||
insertedMangaCategories += MangaCategory.create(
|
insertedMangaCategories += categories[it.category].id to result.manga
|
||||||
result.manga,
|
|
||||||
categories[it.category],
|
|
||||||
) to result.manga
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't do too many DB OPs in one go
|
// Can't do too many DB OPs in one go
|
||||||
insertedMangaCategories.chunked(10).map { mangaCategories ->
|
insertedMangaCategories.forEach { (category, manga) ->
|
||||||
mangaCategories.map { it.first } to mangaCategories.map { it.second }
|
setMangaCategories.await(manga.id, listOf(category))
|
||||||
}.forEach {
|
|
||||||
db.setMangaCategories(it.first, it.second)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,7 +407,6 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
fun onDestroy() {
|
fun onDestroy() {
|
||||||
scope.cancel()
|
scope.cancel()
|
||||||
dispatcher.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -1,56 +1,77 @@
|
|||||||
package exh.favorites
|
package exh.favorites
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.data.DatabaseHandler
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.data.exh.favoriteEntryMapper
|
||||||
|
import eu.kanade.domain.category.interactor.GetCategories
|
||||||
|
import eu.kanade.domain.manga.interactor.GetFavorites
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||||
import exh.favorites.sql.models.FavoriteEntry
|
import exh.favorites.sql.models.FavoriteEntry
|
||||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||||
import exh.source.isEhBasedManga
|
import exh.source.isEhBasedManga
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
|
import kotlinx.coroutines.flow.toList
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class LocalFavoritesStorage {
|
class LocalFavoritesStorage {
|
||||||
private val db: DatabaseHelper by injectLazy()
|
private val handler: DatabaseHandler by injectLazy()
|
||||||
|
private val getFavorites: GetFavorites by injectLazy()
|
||||||
|
private val getCategories: GetCategories by injectLazy()
|
||||||
|
|
||||||
fun getChangedDbEntries() = db.getFavoriteMangas()
|
suspend fun getChangedDbEntries() = getFavorites.await()
|
||||||
.executeAsBlocking()
|
.asFlow()
|
||||||
.asSequence()
|
|
||||||
.loadDbCategories()
|
.loadDbCategories()
|
||||||
.parseToFavoriteEntries()
|
.parseToFavoriteEntries()
|
||||||
.getChangedEntries()
|
.getChangedEntries()
|
||||||
|
|
||||||
fun getChangedRemoteEntries(entries: List<EHentai.ParsedManga>) = entries
|
suspend fun getChangedRemoteEntries(entries: List<EHentai.ParsedManga>) = entries
|
||||||
.asSequence()
|
.asFlow()
|
||||||
.map {
|
.map {
|
||||||
it.fav to it.manga.apply {
|
it.fav to it.manga.apply {
|
||||||
|
id = -1
|
||||||
favorite = true
|
favorite = true
|
||||||
date_added = System.currentTimeMillis()
|
date_added = System.currentTimeMillis()
|
||||||
}
|
}.toDomainManga()!!
|
||||||
}
|
}
|
||||||
.parseToFavoriteEntries()
|
.parseToFavoriteEntries()
|
||||||
.getChangedEntries()
|
.getChangedEntries()
|
||||||
|
|
||||||
fun snapshotEntries() {
|
suspend fun snapshotEntries() {
|
||||||
val dbMangas = db.getFavoriteMangas()
|
val dbMangas = getFavorites.await()
|
||||||
.executeAsBlocking()
|
.asFlow()
|
||||||
.asSequence()
|
|
||||||
.loadDbCategories()
|
.loadDbCategories()
|
||||||
.parseToFavoriteEntries()
|
.parseToFavoriteEntries()
|
||||||
|
|
||||||
// Delete old snapshot
|
// Delete old snapshot
|
||||||
db.deleteAllFavoriteEntries().executeAsBlocking()
|
handler.await { eh_favoritesQueries.deleteAll() }
|
||||||
|
|
||||||
// Insert new snapshots
|
// Insert new snapshots
|
||||||
db.insertFavoriteEntries(dbMangas.toList()).executeAsBlocking()
|
handler.await(true) {
|
||||||
|
dbMangas.toList().forEach {
|
||||||
|
eh_favoritesQueries.insertEhFavorites(
|
||||||
|
it.id,
|
||||||
|
it.title,
|
||||||
|
it.gid,
|
||||||
|
it.token,
|
||||||
|
it.category.toLong(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearSnapshots() {
|
suspend fun clearSnapshots() {
|
||||||
db.deleteAllFavoriteEntries().executeAsBlocking()
|
handler.await { eh_favoritesQueries.deleteAll() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Sequence<FavoriteEntry>.getChangedEntries(): ChangeSet {
|
private suspend fun Flow<FavoriteEntry>.getChangedEntries(): ChangeSet {
|
||||||
val terminated = toList()
|
val terminated = toList()
|
||||||
|
|
||||||
val databaseEntries = db.getFavoriteEntries().executeAsBlocking()
|
val databaseEntries = handler.awaitList { eh_favoritesQueries.selectAll(favoriteEntryMapper) }
|
||||||
|
|
||||||
val added = terminated.filter {
|
val added = terminated.filter {
|
||||||
queryListForEntry(databaseEntries, it) == null
|
queryListForEntry(databaseEntries, it) == null
|
||||||
@ -74,11 +95,11 @@ class LocalFavoritesStorage {
|
|||||||
it.category == entry.category
|
it.category == entry.category
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Sequence<Manga>.loadDbCategories(): Sequence<Pair<Int, Manga>> {
|
private suspend fun Flow<Manga>.loadDbCategories(): Flow<Pair<Int, Manga>> {
|
||||||
val dbCategories = db.getCategories().executeAsBlocking()
|
val dbCategories = getCategories.await()
|
||||||
|
|
||||||
return filter(::validateDbManga).mapNotNull {
|
return filter(::validateDbManga).mapNotNull {
|
||||||
val category = db.getCategoriesForManga(it).executeAsBlocking()
|
val category = getCategories.await(it.id)
|
||||||
|
|
||||||
dbCategories.indexOf(
|
dbCategories.indexOf(
|
||||||
category.firstOrNull()
|
category.firstOrNull()
|
||||||
@ -87,12 +108,12 @@ class LocalFavoritesStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Sequence<Pair<Int, Manga>>.parseToFavoriteEntries() =
|
private fun Flow<Pair<Int, Manga>>.parseToFavoriteEntries() =
|
||||||
filter { (_, manga) ->
|
filter { (_, manga) ->
|
||||||
validateDbManga(manga)
|
validateDbManga(manga)
|
||||||
}.mapNotNull { (categoryId, manga) ->
|
}.mapNotNull { (categoryId, manga) ->
|
||||||
FavoriteEntry(
|
FavoriteEntry(
|
||||||
title = manga.originalTitle,
|
title = manga.ogTitle,
|
||||||
gid = EHentaiSearchMetadata.galleryId(manga.url),
|
gid = EHentaiSearchMetadata.galleryId(manga.url),
|
||||||
token = EHentaiSearchMetadata.galleryToken(manga.url),
|
token = EHentaiSearchMetadata.galleryToken(manga.url),
|
||||||
category = categoryId,
|
category = categoryId,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package exh.md.handlers
|
package exh.md.handlers
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.data.DatabaseHandler
|
||||||
|
import eu.kanade.domain.manga.interactor.GetMangaByUrlAndSource
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import exh.log.xLogE
|
import exh.log.xLogE
|
||||||
import exh.md.dto.ChapterDataDto
|
import exh.md.dto.ChapterDataDto
|
||||||
@ -12,8 +13,8 @@ import exh.md.utils.MdUtil
|
|||||||
import exh.md.utils.asMdMap
|
import exh.md.utils.asMdMap
|
||||||
import exh.metadata.metadata.MangaDexSearchMetadata
|
import exh.metadata.metadata.MangaDexSearchMetadata
|
||||||
import exh.metadata.metadata.base.RaisedTag
|
import exh.metadata.metadata.base.RaisedTag
|
||||||
import exh.metadata.metadata.base.getFlatMetadataForManga
|
import exh.metadata.metadata.base.awaitFlatMetadataForManga
|
||||||
import exh.metadata.metadata.base.insertFlatMetadata
|
import exh.metadata.metadata.base.awaitInsertFlatMetadata
|
||||||
import exh.util.capitalize
|
import exh.util.capitalize
|
||||||
import exh.util.floor
|
import exh.util.floor
|
||||||
import exh.util.nullIfEmpty
|
import exh.util.nullIfEmpty
|
||||||
@ -25,7 +26,8 @@ import java.util.Locale
|
|||||||
class ApiMangaParser(
|
class ApiMangaParser(
|
||||||
private val lang: String,
|
private val lang: String,
|
||||||
) {
|
) {
|
||||||
val db: DatabaseHelper by injectLazy()
|
private val handler: DatabaseHandler by injectLazy()
|
||||||
|
private val getMangaByUrlAndSource: GetMangaByUrlAndSource by injectLazy()
|
||||||
|
|
||||||
val metaClass = MangaDexSearchMetadata::class
|
val metaClass = MangaDexSearchMetadata::class
|
||||||
|
|
||||||
@ -37,23 +39,23 @@ class ApiMangaParser(
|
|||||||
}?.call()
|
}?.call()
|
||||||
?: error("Could not find no-args constructor for meta class: ${metaClass.qualifiedName}!")
|
?: error("Could not find no-args constructor for meta class: ${metaClass.qualifiedName}!")
|
||||||
|
|
||||||
fun parseToManga(
|
suspend fun parseToManga(
|
||||||
manga: MangaInfo,
|
manga: MangaInfo,
|
||||||
sourceId: Long,
|
sourceId: Long,
|
||||||
input: MangaDto,
|
input: MangaDto,
|
||||||
simpleChapters: List<String>,
|
simpleChapters: List<String>,
|
||||||
statistics: StatisticsMangaDto?,
|
statistics: StatisticsMangaDto?,
|
||||||
): MangaInfo {
|
): MangaInfo {
|
||||||
val mangaId = db.getManga(manga.key, sourceId).executeAsBlocking()?.id
|
val mangaId = getMangaByUrlAndSource.await(manga.key, sourceId)?.id
|
||||||
val metadata = if (mangaId != null) {
|
val metadata = if (mangaId != null) {
|
||||||
val flatMetadata = db.getFlatMetadataForManga(mangaId).executeAsBlocking()
|
val flatMetadata = handler.awaitFlatMetadataForManga(mangaId)
|
||||||
flatMetadata?.raise(metaClass) ?: newMetaInstance()
|
flatMetadata?.raise(metaClass) ?: newMetaInstance()
|
||||||
} else newMetaInstance()
|
} else newMetaInstance()
|
||||||
|
|
||||||
parseIntoMetadata(metadata, input, simpleChapters, statistics)
|
parseIntoMetadata(metadata, input, simpleChapters, statistics)
|
||||||
if (mangaId != null) {
|
if (mangaId != null) {
|
||||||
metadata.mangaId = mangaId
|
metadata.mangaId = mangaId
|
||||||
db.insertFlatMetadata(metadata.flatten())
|
handler.awaitInsertFlatMetadata(metadata.flatten())
|
||||||
}
|
}
|
||||||
|
|
||||||
return metadata.createMangaInfo(manga)
|
return metadata.createMangaInfo(manga)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package exh.md.similar
|
package exh.md.similar
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
import eu.kanade.domain.manga.model.toMangaInfo
|
||||||
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
|
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
|
||||||
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException
|
import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException
|
||||||
|
@ -1,26 +1,30 @@
|
|||||||
package exh.md.similar
|
package exh.md.similar
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.domain.manga.interactor.GetMangaById
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
|
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
|
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
|
||||||
import exh.source.getMainSource
|
import exh.source.getMainSource
|
||||||
import uy.kohesive.injekt.injectLazy
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Presenter of [MangaDexSimilarController]. Inherit BrowseCataloguePresenter.
|
* Presenter of [MangaDexSimilarController]. Inherit BrowseCataloguePresenter.
|
||||||
*/
|
*/
|
||||||
class MangaDexSimilarPresenter(val mangaId: Long, sourceId: Long) : BrowseSourcePresenter(sourceId) {
|
class MangaDexSimilarPresenter(
|
||||||
|
val mangaId: Long,
|
||||||
|
sourceId: Long,
|
||||||
|
private val getMangaById: GetMangaById = Injekt.get(),
|
||||||
|
) : BrowseSourcePresenter(sourceId) {
|
||||||
|
|
||||||
var manga: Manga? = null
|
var manga: Manga? = null
|
||||||
|
|
||||||
val db: DatabaseHelper by injectLazy()
|
|
||||||
|
|
||||||
override fun createPager(query: String, filters: FilterList): Pager {
|
override fun createPager(query: String, filters: FilterList): Pager {
|
||||||
val sourceAsMangaDex = source.getMainSource() as MangaDex
|
val sourceAsMangaDex = source.getMainSource() as MangaDex
|
||||||
this.manga = db.getManga(mangaId).executeAsBlocking()
|
this.manga = runBlocking { getMangaById.await(mangaId) }
|
||||||
return MangaDexSimilarPager(manga!!, sourceAsMangaDex)
|
return MangaDexSimilarPager(manga!!, sourceAsMangaDex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package exh.recs
|
package exh.recs
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
@ -203,7 +203,7 @@ open class RecommendsPager(
|
|||||||
|
|
||||||
val recs = apiList.firstNotNullOfOrNull { (key, api) ->
|
val recs = apiList.firstNotNullOfOrNull { (key, api) ->
|
||||||
try {
|
try {
|
||||||
val recs = api.getRecsBySearch(manga.originalTitle)
|
val recs = api.getRecsBySearch(manga.ogTitle)
|
||||||
logcat { key.toString() + " > Results: " + recs.count() }
|
logcat { key.toString() + " > Results: " + recs.count() }
|
||||||
recs
|
recs
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -1,23 +1,27 @@
|
|||||||
package exh.recs
|
package exh.recs
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.domain.manga.interactor.GetMangaById
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
|
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
|
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
|
||||||
import uy.kohesive.injekt.injectLazy
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Presenter of [RecommendsController]. Inherit BrowseCataloguePresenter.
|
* Presenter of [RecommendsController]. Inherit BrowseCataloguePresenter.
|
||||||
*/
|
*/
|
||||||
class RecommendsPresenter(val mangaId: Long, sourceId: Long) : BrowseSourcePresenter(sourceId) {
|
class RecommendsPresenter(
|
||||||
|
val mangaId: Long,
|
||||||
|
sourceId: Long,
|
||||||
|
private val getMangaById: GetMangaById = Injekt.get(),
|
||||||
|
) : BrowseSourcePresenter(sourceId) {
|
||||||
|
|
||||||
var manga: Manga? = null
|
var manga: Manga? = null
|
||||||
|
|
||||||
val db: DatabaseHelper by injectLazy()
|
|
||||||
|
|
||||||
override fun createPager(query: String, filters: FilterList): Pager {
|
override fun createPager(query: String, filters: FilterList): Pager {
|
||||||
this.manga = db.getManga(mangaId).executeAsBlocking()
|
this.manga = runBlocking { getMangaById.await(mangaId) }
|
||||||
return RecommendsPager(manga!!)
|
return RecommendsPager(manga!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ import android.view.MenuItem
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import eu.kanade.domain.chapter.model.Chapter
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.databinding.EhActivityInterceptBinding
|
import eu.kanade.tachiyomi.databinding.EhActivityInterceptBinding
|
||||||
import eu.kanade.tachiyomi.source.online.UrlImportableSource
|
import eu.kanade.tachiyomi.source.online.UrlImportableSource
|
||||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||||
@ -70,7 +70,7 @@ class InterceptActivity : BaseActivity() {
|
|||||||
onBackPressed()
|
onBackPressed()
|
||||||
startActivity(
|
startActivity(
|
||||||
if (it.chapter != null) {
|
if (it.chapter != null) {
|
||||||
ReaderActivity.newIntent(this, it.manga.id!!, it.chapter.id!!)
|
ReaderActivity.newIntent(this, it.manga.id, it.chapter.id)
|
||||||
} else {
|
} else {
|
||||||
Intent(this, MainActivity::class.java)
|
Intent(this, MainActivity::class.java)
|
||||||
.setAction(MainActivity.SHORTCUT_MANGA)
|
.setAction(MainActivity.SHORTCUT_MANGA)
|
||||||
@ -133,9 +133,7 @@ class InterceptActivity : BaseActivity() {
|
|||||||
val result = galleryAdder.addGallery(this@InterceptActivity, gallery, forceSource = source)
|
val result = galleryAdder.addGallery(this@InterceptActivity, gallery, forceSource = source)
|
||||||
|
|
||||||
status.value = when (result) {
|
status.value = when (result) {
|
||||||
is GalleryAddEvent.Success -> result.manga.id?.let {
|
is GalleryAddEvent.Success -> InterceptResult.Success(result.manga.id, result.manga, result.chapter)
|
||||||
InterceptResult.Success(it, result.manga, result.chapter)
|
|
||||||
} ?: InterceptResult.Failure(getString(R.string.manga_id_is_null))
|
|
||||||
is GalleryAddEvent.Fail -> InterceptResult.Failure(result.logMessage)
|
is GalleryAddEvent.Fail -> InterceptResult.Failure(result.logMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
|||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import eu.kanade.domain.manga.model.Manga as DomainManga
|
||||||
|
|
||||||
fun Manga.mangaType(context: Context): String {
|
fun Manga.mangaType(context: Context): String {
|
||||||
return context.getString(
|
return context.getString(
|
||||||
@ -48,6 +49,30 @@ fun Manga.mangaType(sourceName: String? = Injekt.get<SourceManager>().get(source
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun DomainManga.mangaType(sourceName: String? = Injekt.get<SourceManager>().get(source)?.name): MangaType {
|
||||||
|
val currentTags = genre.orEmpty()
|
||||||
|
return when {
|
||||||
|
currentTags.any { tag -> isMangaTag(tag) } -> {
|
||||||
|
MangaType.TYPE_MANGA
|
||||||
|
}
|
||||||
|
currentTags.any { tag -> isWebtoonTag(tag) } || sourceName?.let { isWebtoonSource(it) } == true -> {
|
||||||
|
MangaType.TYPE_WEBTOON
|
||||||
|
}
|
||||||
|
currentTags.any { tag -> isComicTag(tag) } || sourceName?.let { isComicSource(it) } == true -> {
|
||||||
|
MangaType.TYPE_COMIC
|
||||||
|
}
|
||||||
|
currentTags.any { tag -> isManhuaTag(tag) } || sourceName?.let { isManhuaSource(it) } == true -> {
|
||||||
|
MangaType.TYPE_MANHUA
|
||||||
|
}
|
||||||
|
currentTags.any { tag -> isManhwaTag(tag) } || sourceName?.let { isManhwaSource(it) } == true -> {
|
||||||
|
MangaType.TYPE_MANHWA
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
MangaType.TYPE_MANGA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type the reader should use. Different from manga type as certain manga has different
|
* The type the reader should use. Different from manga type as certain manga has different
|
||||||
* read types
|
* read types
|
||||||
|
@ -24,7 +24,7 @@ fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: Strin
|
|||||||
.map { res ->
|
.map { res ->
|
||||||
MangasPage(
|
MangasPage(
|
||||||
if (res is GalleryAddEvent.Success) {
|
if (res is GalleryAddEvent.Success) {
|
||||||
listOf(res.manga)
|
listOf(res.manga.toSManga())
|
||||||
} else {
|
} else {
|
||||||
emptyList()
|
emptyList()
|
||||||
},
|
},
|
||||||
|
@ -30,6 +30,9 @@ insert:
|
|||||||
INSERT INTO mangas(source,url,artist,author,description,genre,title,status,thumbnail_url,favorite,last_update,next_update,initialized,viewer,chapter_flags,cover_last_modified,date_added)
|
INSERT INTO mangas(source,url,artist,author,description,genre,title,status,thumbnail_url,favorite,last_update,next_update,initialized,viewer,chapter_flags,cover_last_modified,date_added)
|
||||||
VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnail_url,:favorite,:last_update,:next_update,:initialized,:viewer,:chapter_flags,:cover_last_modified,:date_added);
|
VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnail_url,:favorite,:last_update,:next_update,:initialized,:viewer,:chapter_flags,:cover_last_modified,:date_added);
|
||||||
|
|
||||||
|
insertEmpty:
|
||||||
|
INSERT INTO mangas (source, url, title, artist, author, description, genre, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, filtered_scanlators) VALUES (?, ?, ?, NULL, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, 0, 0, 0, 0, 0, NULL);
|
||||||
|
|
||||||
getMangaById:
|
getMangaById:
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM mangas
|
FROM mangas
|
||||||
@ -186,3 +189,14 @@ WHERE _id = :mangaId;
|
|||||||
|
|
||||||
selectLastInsertedRowId:
|
selectLastInsertedRowId:
|
||||||
SELECT last_insert_rowid();
|
SELECT last_insert_rowid();
|
||||||
|
|
||||||
|
getEhMangaWithMetadata:
|
||||||
|
SELECT mangas.* FROM mangas
|
||||||
|
INNER JOIN search_metadata
|
||||||
|
ON mangas._id = search_metadata.manga_id
|
||||||
|
WHERE mangas.favorite = 1 AND (mangas.source = :eh OR mangas.source = :exh);
|
||||||
|
|
||||||
|
selectLastInsertRow:
|
||||||
|
SELECT *
|
||||||
|
FROM mangas
|
||||||
|
WHERE _id = last_insert_rowid();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user