Use SQLDelight for more SY specific things

This commit is contained in:
Jobobby04 2022-07-03 13:37:27 -04:00
parent e71c9e2775
commit d88c769d3b
83 changed files with 678 additions and 1504 deletions

View File

@ -91,5 +91,9 @@ class AndroidDatabaseHandler(
// SY --> // SY -->
fun getLibraryQuery() = LibraryQuery(driver) fun getLibraryQuery() = LibraryQuery(driver)
fun rawQuery(query: (SqlDriver) -> Unit) {
return query(driver)
}
// SY <-- // SY <--
} }

View File

@ -0,0 +1,26 @@
package eu.kanade.data.manga
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.favoriteEntryMapper
import eu.kanade.domain.manga.repository.FavoritesEntryRepository
import exh.favorites.sql.models.FavoriteEntry
class FavoritesEntryRepositoryImpl(
private val handler: DatabaseHandler,
) : FavoritesEntryRepository {
override suspend fun deleteAll() {
handler.await { eh_favoritesQueries.deleteAll() }
}
override suspend fun insertAll(favoriteEntries: List<FavoriteEntry>) {
handler.await(true) {
favoriteEntries.forEach {
eh_favoritesQueries.insertEhFavorites(it.id, it.title, it.gid, it.token, it.category.toLong())
}
}
}
override suspend fun selectAll(): List<FavoriteEntry> {
return handler.awaitList { eh_favoritesQueries.selectAll(favoriteEntryMapper) }
}
}

View File

@ -2,10 +2,14 @@ package eu.kanade.data.manga
import eu.kanade.data.DatabaseHandler import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.mergedMangaReferenceMapper import eu.kanade.data.exh.mergedMangaReferenceMapper
import eu.kanade.data.toLong
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MergeMangaSettingsUpdate
import eu.kanade.domain.manga.repository.MangaMergeRepository import eu.kanade.domain.manga.repository.MangaMergeRepository
import eu.kanade.tachiyomi.util.system.logcat
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import logcat.LogPriority
class MangaMergeRepositoryImpl( class MangaMergeRepositoryImpl(
private val handler: DatabaseHandler, private val handler: DatabaseHandler,
@ -34,4 +38,38 @@ class MangaMergeRepositoryImpl(
override suspend fun subscribeReferencesById(id: Long): Flow<List<MergedMangaReference>> { override suspend fun subscribeReferencesById(id: Long): Flow<List<MergedMangaReference>> {
return handler.subscribeToList { mergedQueries.selectByMergeId(id, mergedMangaReferenceMapper) } return handler.subscribeToList { mergedQueries.selectByMergeId(id, mergedMangaReferenceMapper) }
} }
override suspend fun updateSettings(update: MergeMangaSettingsUpdate): Boolean {
return try {
partialUpdate(update)
true
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
false
}
}
override suspend fun updateAllSettings(values: List<MergeMangaSettingsUpdate>): Boolean {
return try {
partialUpdate(*values.toTypedArray())
true
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
false
}
}
private suspend fun partialUpdate(vararg values: MergeMangaSettingsUpdate) {
handler.await(inTransaction = true) {
values.forEach { value ->
mergedQueries.updateSettingsById(
id = value.id,
getChapterUpdates = value.getChapterUpdates?.toLong(),
downloadChapters = value.downloadChapters?.toLong(),
infoManga = value.isInfoManga?.toLong(),
chapterPriority = value.chapterPriority?.toLong(),
)
}
}
}
} }

View File

@ -4,10 +4,14 @@ import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.searchMetadataMapper import eu.kanade.data.exh.searchMetadataMapper
import eu.kanade.data.exh.searchTagMapper import eu.kanade.data.exh.searchTagMapper
import eu.kanade.data.exh.searchTitleMapper import eu.kanade.data.exh.searchTitleMapper
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaMetadataRepository import eu.kanade.domain.manga.repository.MangaMetadataRepository
import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.sql.models.SearchMetadata import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.models.SearchTag import exh.metadata.sql.models.SearchTag
import exh.metadata.sql.models.SearchTitle import exh.metadata.sql.models.SearchTitle
import exh.source.EH_SOURCE_ID
import exh.source.EXH_SOURCE_ID
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
class MangaMetadataRepositoryImpl( class MangaMetadataRepositoryImpl(
@ -37,4 +41,34 @@ class MangaMetadataRepositoryImpl(
override suspend fun subscribeTitlesById(id: Long): Flow<List<SearchTitle>> { override suspend fun subscribeTitlesById(id: Long): Flow<List<SearchTitle>> {
return handler.subscribeToList { search_titlesQueries.selectByMangaId(id, searchTitleMapper) } return handler.subscribeToList { search_titlesQueries.selectByMangaId(id, searchTitleMapper) }
} }
override suspend fun insertFlatMetadata(flatMetadata: FlatMetadata) {
require(flatMetadata.metadata.mangaId != -1L)
handler.await(true) {
flatMetadata.metadata.run {
search_metadataQueries.upsert(mangaId, uploader, extra, indexedExtra, extraVersion)
}
search_tagsQueries.deleteByManga(flatMetadata.metadata.mangaId)
flatMetadata.tags.forEach {
search_tagsQueries.insert(it.mangaId, it.namespace, it.name, it.type)
}
search_titlesQueries.deleteByManga(flatMetadata.metadata.mangaId)
flatMetadata.titles.forEach {
search_titlesQueries.insert(it.mangaId, it.title, it.type)
}
}
}
override suspend fun getExhFavoriteMangaWithMetadata(): List<Manga> {
return handler.awaitList { mangasQueries.getEhMangaWithMetadata(EH_SOURCE_ID, EXH_SOURCE_ID, mangaMapper) }
}
override suspend fun getIdsOfFavoriteMangaWithMetadata(): List<Long> {
return handler.awaitList { mangasQueries.getIdsOfFavoriteMangaWithMetadata() }
}
override suspend fun getSearchMetadata(): List<SearchMetadata> {
return handler.awaitList { search_metadataQueries.selectAll(searchMetadataMapper) }
}
} }

View File

@ -114,4 +114,12 @@ class MangaRepositoryImpl(
} }
} }
} }
override suspend fun getMangaBySource(sourceId: Long): List<Manga> {
return handler.awaitList { mangasQueries.getBySource(sourceId, mangaMapper) }
}
override suspend fun getAll(): List<Manga> {
return handler.awaitList { mangasQueries.getAll(mangaMapper) }
}
} }

View File

@ -32,7 +32,7 @@ import eu.kanade.domain.history.interactor.UpsertHistory
import eu.kanade.domain.history.repository.HistoryRepository import eu.kanade.domain.history.repository.HistoryRepository
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
import eu.kanade.domain.manga.interactor.GetFavorites import eu.kanade.domain.manga.interactor.GetFavorites
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.GetMangaWithChapters import eu.kanade.domain.manga.interactor.GetMangaWithChapters
import eu.kanade.domain.manga.interactor.ResetViewerFlags import eu.kanade.domain.manga.interactor.ResetViewerFlags
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
@ -72,7 +72,7 @@ class DomainModule : InjektModule {
addFactory { GetDuplicateLibraryManga(get()) } addFactory { GetDuplicateLibraryManga(get()) }
addFactory { GetFavorites(get()) } addFactory { GetFavorites(get()) }
addFactory { GetMangaWithChapters(get(), get()) } addFactory { GetMangaWithChapters(get(), get()) }
addFactory { GetMangaById(get()) } addFactory { GetManga(get()) }
addFactory { GetNextChapter(get()) } addFactory { GetNextChapter(get()) }
addFactory { ResetViewerFlags(get()) } addFactory { ResetViewerFlags(get()) }
addFactory { SetMangaChapterFlags(get()) } addFactory { SetMangaChapterFlags(get()) }

View File

@ -1,14 +1,25 @@
package eu.kanade.domain package eu.kanade.domain
import eu.kanade.data.manga.FavoritesEntryRepositoryImpl
import eu.kanade.data.manga.MangaMergeRepositoryImpl import eu.kanade.data.manga.MangaMergeRepositoryImpl
import eu.kanade.data.manga.MangaMetadataRepositoryImpl import eu.kanade.data.manga.MangaMetadataRepositoryImpl
import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId
import eu.kanade.domain.manga.interactor.DeleteFavoriteEntries
import eu.kanade.domain.manga.interactor.GetAllManga
import eu.kanade.domain.manga.interactor.GetExhFavoriteMangaWithMetadata
import eu.kanade.domain.manga.interactor.GetFavoriteEntries
import eu.kanade.domain.manga.interactor.GetFlatMetadataById import eu.kanade.domain.manga.interactor.GetFlatMetadataById
import eu.kanade.domain.manga.interactor.GetMangaByUrlAndSource import eu.kanade.domain.manga.interactor.GetIdsOfFavoriteMangaWithMetadata
import eu.kanade.domain.manga.interactor.GetMangaBySource
import eu.kanade.domain.manga.interactor.GetMergedManga import eu.kanade.domain.manga.interactor.GetMergedManga
import eu.kanade.domain.manga.interactor.GetMergedMangaById import eu.kanade.domain.manga.interactor.GetMergedMangaById
import eu.kanade.domain.manga.interactor.GetMergedReferencesById import eu.kanade.domain.manga.interactor.GetMergedReferencesById
import eu.kanade.domain.manga.interactor.GetSearchTags
import eu.kanade.domain.manga.interactor.GetSearchTitles
import eu.kanade.domain.manga.interactor.InsertFavoriteEntries
import eu.kanade.domain.manga.interactor.InsertFlatMetadata
import eu.kanade.domain.manga.interactor.SetMangaFilteredScanlators import eu.kanade.domain.manga.interactor.SetMangaFilteredScanlators
import eu.kanade.domain.manga.repository.FavoritesEntryRepository
import eu.kanade.domain.manga.repository.MangaMergeRepository import eu.kanade.domain.manga.repository.MangaMergeRepository
import eu.kanade.domain.manga.repository.MangaMetadataRepository import eu.kanade.domain.manga.repository.MangaMetadataRepository
import eu.kanade.domain.source.interactor.GetShowLatest import eu.kanade.domain.source.interactor.GetShowLatest
@ -31,15 +42,26 @@ class SYDomainModule : InjektModule {
addFactory { SetSourceCategories(get()) } addFactory { SetSourceCategories(get()) }
addFactory { ToggleSources(get()) } addFactory { ToggleSources(get()) }
addFactory { SetMangaFilteredScanlators(get()) } addFactory { SetMangaFilteredScanlators(get()) }
addFactory { GetMangaByUrlAndSource(get()) } addFactory { GetAllManga(get()) }
addFactory { GetMangaBySource(get()) }
addSingletonFactory<MangaMetadataRepository> { MangaMetadataRepositoryImpl(get()) } addSingletonFactory<MangaMetadataRepository> { MangaMetadataRepositoryImpl(get()) }
addFactory { GetFlatMetadataById(get()) } addFactory { GetFlatMetadataById(get()) }
addFactory { InsertFlatMetadata(get()) }
addFactory { GetExhFavoriteMangaWithMetadata(get()) }
addFactory { GetSearchTags(get()) }
addFactory { GetSearchTitles(get()) }
addFactory { GetIdsOfFavoriteMangaWithMetadata(get()) }
addSingletonFactory<MangaMergeRepository> { MangaMergeRepositoryImpl(get()) } addSingletonFactory<MangaMergeRepository> { MangaMergeRepositoryImpl(get()) }
addFactory { GetMergedManga(get()) } addFactory { GetMergedManga(get()) }
addFactory { GetMergedMangaById(get()) } addFactory { GetMergedMangaById(get()) }
addFactory { GetMergedReferencesById(get()) } addFactory { GetMergedReferencesById(get()) }
addFactory { GetMergedChapterByMangaId(get()) } addFactory { GetMergedChapterByMangaId(get()) }
addSingletonFactory<FavoritesEntryRepository> { FavoritesEntryRepositoryImpl(get()) }
addFactory { GetFavoriteEntries(get()) }
addFactory { InsertFavoriteEntries(get()) }
addFactory { DeleteFavoriteEntries(get()) }
} }
} }

View File

@ -0,0 +1,12 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.FavoritesEntryRepository
class DeleteFavoriteEntries(
private val favoriteEntryRepository: FavoritesEntryRepository,
) {
suspend fun await() {
return favoriteEntryRepository.deleteAll()
}
}

View File

@ -0,0 +1,13 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository
class GetAllManga(
private val mangaRepository: MangaRepository,
) {
suspend fun await(): List<Manga> {
return mangaRepository.getAll()
}
}

View File

@ -0,0 +1,13 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaMetadataRepository
class GetExhFavoriteMangaWithMetadata(
private val mangaMetadataRepository: MangaMetadataRepository,
) {
suspend fun await(): List<Manga> {
return mangaMetadataRepository.getExhFavoriteMangaWithMetadata()
}
}

View File

@ -0,0 +1,13 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.FavoritesEntryRepository
import exh.favorites.sql.models.FavoriteEntry
class GetFavoriteEntries(
private val favoriteEntryRepository: FavoritesEntryRepository,
) {
suspend fun await(): List<FavoriteEntry> {
return favoriteEntryRepository.selectAll()
}
}

View File

@ -0,0 +1,12 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.MangaMetadataRepository
class GetIdsOfFavoriteMangaWithMetadata(
private val mangaMetadataRepository: MangaMetadataRepository,
) {
suspend fun await(): List<Long> {
return mangaMetadataRepository.getIdsOfFavoriteMangaWithMetadata()
}
}

View File

@ -6,10 +6,23 @@ import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import logcat.LogPriority import logcat.LogPriority
class GetMangaByUrlAndSource( class GetManga(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
) { ) {
suspend fun await(id: Long): Manga? {
return try {
mangaRepository.getMangaById(id)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
null
}
}
suspend fun subscribe(id: Long): Flow<Manga> {
return mangaRepository.subscribeMangaById(id)
}
suspend fun await(url: String, sourceId: Long): Manga? { suspend fun await(url: String, sourceId: Long): Manga? {
return try { return try {
mangaRepository.getMangaByUrlAndSource(url, sourceId) mangaRepository.getMangaByUrlAndSource(url, sourceId)

View File

@ -1,25 +0,0 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.flow.Flow
import logcat.LogPriority
class GetMangaById(
private val mangaRepository: MangaRepository,
) {
suspend fun await(id: Long): Manga? {
return try {
mangaRepository.getMangaById(id)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
null
}
}
suspend fun subscribe(id: Long): Flow<Manga> {
return mangaRepository.subscribeMangaById(id)
}
}

View File

@ -0,0 +1,13 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository
class GetMangaBySource(
private val mangaRepository: MangaRepository,
) {
suspend fun await(sourceId: Long): List<Manga> {
return mangaRepository.getMangaBySource(sourceId)
}
}

View File

@ -0,0 +1,17 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.MangaMetadataRepository
import exh.metadata.sql.models.SearchMetadata
class GetSearchMetadata(
private val mangaMetadataRepository: MangaMetadataRepository,
) {
suspend fun await(mangaId: Long): SearchMetadata? {
return mangaMetadataRepository.getMetadataById(mangaId)
}
suspend fun await(): List<SearchMetadata> {
return mangaMetadataRepository.getSearchMetadata()
}
}

View File

@ -0,0 +1,13 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.MangaMetadataRepository
import exh.metadata.sql.models.SearchTag
class GetSearchTags(
private val mangaMetadataRepository: MangaMetadataRepository,
) {
suspend fun await(mangaId: Long): List<SearchTag> {
return mangaMetadataRepository.getTagsById(mangaId)
}
}

View File

@ -0,0 +1,13 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.MangaMetadataRepository
import exh.metadata.sql.models.SearchTitle
class GetSearchTitles(
private val mangaMetadataRepository: MangaMetadataRepository,
) {
suspend fun await(mangaId: Long): List<SearchTitle> {
return mangaMetadataRepository.getTitlesById(mangaId)
}
}

View File

@ -0,0 +1,13 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.FavoritesEntryRepository
import exh.favorites.sql.models.FavoriteEntry
class InsertFavoriteEntries(
private val favoriteEntryRepository: FavoritesEntryRepository,
) {
suspend fun await(entries: List<FavoriteEntry>) {
return favoriteEntryRepository.insertAll(entries)
}
}

View File

@ -0,0 +1,28 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.MangaMetadataRepository
import eu.kanade.tachiyomi.util.system.logcat
import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata
import logcat.LogPriority
class InsertFlatMetadata(
private val mangaMetadataRepository: MangaMetadataRepository,
) {
suspend fun await(flatMetadata: FlatMetadata) {
try {
mangaMetadataRepository.insertFlatMetadata(flatMetadata)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
}
}
suspend fun await(metadata: RaisedSearchMetadata) {
try {
mangaMetadataRepository.insertMetadata(metadata)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
}
}
}

View File

@ -0,0 +1,9 @@
package eu.kanade.domain.manga.model
data class MergeMangaSettingsUpdate(
val id: Long,
var isInfoManga: Boolean?,
var getChapterUpdates: Boolean?,
var chapterPriority: Int?,
var downloadChapters: Boolean?,
)

View File

@ -0,0 +1,11 @@
package eu.kanade.domain.manga.repository
import exh.favorites.sql.models.FavoriteEntry
interface FavoritesEntryRepository {
suspend fun deleteAll()
suspend fun insertAll(favoriteEntries: List<FavoriteEntry>)
suspend fun selectAll(): List<FavoriteEntry>
}

View File

@ -1,6 +1,7 @@
package eu.kanade.domain.manga.repository package eu.kanade.domain.manga.repository
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MergeMangaSettingsUpdate
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -16,4 +17,8 @@ interface MangaMergeRepository {
suspend fun getReferencesById(id: Long): List<MergedMangaReference> suspend fun getReferencesById(id: Long): List<MergedMangaReference>
suspend fun subscribeReferencesById(id: Long): Flow<List<MergedMangaReference>> suspend fun subscribeReferencesById(id: Long): Flow<List<MergedMangaReference>>
suspend fun updateSettings(update: MergeMangaSettingsUpdate): Boolean
suspend fun updateAllSettings(values: List<MergeMangaSettingsUpdate>): Boolean
} }

View File

@ -1,5 +1,8 @@
package eu.kanade.domain.manga.repository package eu.kanade.domain.manga.repository
import eu.kanade.domain.manga.model.Manga
import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.sql.models.SearchMetadata import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.models.SearchTag import exh.metadata.sql.models.SearchTag
import exh.metadata.sql.models.SearchTitle import exh.metadata.sql.models.SearchTitle
@ -17,4 +20,14 @@ interface MangaMetadataRepository {
suspend fun getTitlesById(id: Long): List<SearchTitle> suspend fun getTitlesById(id: Long): List<SearchTitle>
suspend fun subscribeTitlesById(id: Long): Flow<List<SearchTitle>> suspend fun subscribeTitlesById(id: Long): Flow<List<SearchTitle>>
suspend fun insertFlatMetadata(flatMetadata: FlatMetadata)
suspend fun insertMetadata(metadata: RaisedSearchMetadata) = insertFlatMetadata(metadata.flatten())
suspend fun getExhFavoriteMangaWithMetadata(): List<Manga>
suspend fun getIdsOfFavoriteMangaWithMetadata(): List<Long>
suspend fun getSearchMetadata(): List<SearchMetadata>
} }

View File

@ -29,4 +29,8 @@ interface MangaRepository {
suspend fun update(update: MangaUpdate): Boolean suspend fun update(update: MangaUpdate): Boolean
suspend fun updateAll(values: List<MangaUpdate>): Boolean suspend fun updateAll(values: List<MangaUpdate>): Boolean
suspend fun getMangaBySource(sourceId: Long): List<Manga>
suspend fun getAll(): List<Manga>
} }

View File

@ -14,31 +14,11 @@ import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.queries.CategoryQueries
import eu.kanade.tachiyomi.data.database.queries.ChapterQueries import eu.kanade.tachiyomi.data.database.queries.ChapterQueries
import eu.kanade.tachiyomi.data.database.queries.HistoryQueries
import eu.kanade.tachiyomi.data.database.queries.MangaCategoryQueries
import eu.kanade.tachiyomi.data.database.queries.MangaQueries import eu.kanade.tachiyomi.data.database.queries.MangaQueries
import eu.kanade.tachiyomi.data.database.queries.TrackQueries
import exh.favorites.sql.mappers.FavoriteEntryTypeMapping
import exh.favorites.sql.models.FavoriteEntry
import exh.favorites.sql.queries.FavoriteEntryQueries
import exh.merged.sql.mappers.MergedMangaTypeMapping import exh.merged.sql.mappers.MergedMangaTypeMapping
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import exh.merged.sql.queries.MergedQueries import exh.merged.sql.queries.MergedQueries
import exh.metadata.sql.mappers.SearchMetadataTypeMapping
import exh.metadata.sql.mappers.SearchTagTypeMapping
import exh.metadata.sql.mappers.SearchTitleTypeMapping
import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.models.SearchTag
import exh.metadata.sql.models.SearchTitle
import exh.metadata.sql.queries.SearchMetadataQueries
import exh.metadata.sql.queries.SearchTagQueries
import exh.metadata.sql.queries.SearchTitleQueries
import exh.savedsearches.mappers.FeedSavedSearchTypeMapping
import exh.savedsearches.mappers.SavedSearchTypeMapping
import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch
/** /**
* This class provides operations to manage the database through its interfaces. * This class provides operations to manage the database through its interfaces.
@ -48,16 +28,8 @@ class DatabaseHelper(
) : ) :
MangaQueries, MangaQueries,
ChapterQueries, ChapterQueries,
TrackQueries,
CategoryQueries,
MangaCategoryQueries,
HistoryQueries,
/* SY --> */ /* SY --> */
SearchMetadataQueries, MergedQueries
SearchTagQueries,
SearchTitleQueries,
MergedQueries,
FavoriteEntryQueries
/* SY <-- */ { /* SY <-- */ {
override val db = DefaultStorIOSQLite.builder() override val db = DefaultStorIOSQLite.builder()
@ -69,13 +41,7 @@ class DatabaseHelper(
.addTypeMapping(MangaCategory::class.java, MangaCategoryTypeMapping()) .addTypeMapping(MangaCategory::class.java, MangaCategoryTypeMapping())
.addTypeMapping(History::class.java, HistoryTypeMapping()) .addTypeMapping(History::class.java, HistoryTypeMapping())
// SY --> // SY -->
.addTypeMapping(SearchMetadata::class.java, SearchMetadataTypeMapping())
.addTypeMapping(SearchTag::class.java, SearchTagTypeMapping())
.addTypeMapping(SearchTitle::class.java, SearchTitleTypeMapping())
.addTypeMapping(MergedMangaReference::class.java, MergedMangaTypeMapping()) .addTypeMapping(MergedMangaReference::class.java, MergedMangaTypeMapping())
.addTypeMapping(FavoriteEntry::class.java, FavoriteEntryTypeMapping())
.addTypeMapping(SavedSearch::class.java, SavedSearchTypeMapping())
.addTypeMapping(FeedSavedSearch::class.java, FeedSavedSearchTypeMapping())
// SY <-- // SY <--
.build() .build()

View File

@ -1,33 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
interface CategoryQueries : DbProvider {
fun getCategories() = db.get()
.listOfObjects(Category::class.java)
.withQuery(
Query.builder()
.table(CategoryTable.TABLE)
.orderBy(CategoryTable.COL_ORDER)
.build(),
)
.prepare()
fun getCategoriesForManga(manga: Manga) = db.get()
.listOfObjects(Category::class.java)
.withQuery(
RawQuery.builder()
.query(getCategoriesForMangaQuery())
.args(manga.id)
.build(),
)
.prepare()
fun insertCategories(categories: List<Category>) = db.put().objects(categories).prepare()
}

View File

@ -23,54 +23,6 @@ interface ChapterQueries : DbProvider {
.prepare() .prepare()
// SY <-- // SY <--
fun getChapter(id: Long) = db.get()
.`object`(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_ID} = ?")
.whereArgs(id)
.build(),
)
.prepare()
fun getChapter(url: String, mangaId: Long) = db.get()
.`object`(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?")
.whereArgs(url, mangaId)
.build(),
)
.prepare()
// SY -->
fun getChapters(url: String) = db.get()
.listOfObjects(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ?")
.whereArgs(url)
.build(),
)
.prepare()
fun getChaptersReadByUrls(urls: List<String>) = db.get()
.listOfObjects(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} IN (?) AND (${ChapterTable.COL_READ} = 1 OR ${ChapterTable.COL_LAST_PAGE_READ} != 0)")
.whereArgs(urls.joinToString { "\"$it\"" })
.build(),
)
.prepare()
// SY <--
fun insertChapters(chapters: List<Chapter>) = db.put().objects(chapters).prepare()
fun deleteChapters(chapters: List<Chapter>) = db.delete().objects(chapters).prepare() fun deleteChapters(chapters: List<Chapter>) = db.delete().objects(chapters).prepare()
fun updateChapterProgress(chapter: Chapter) = db.put() fun updateChapterProgress(chapter: Chapter) = db.put()

View File

@ -1,50 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.resolvers.HistoryChapterIdPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.HistoryUpsertResolver
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
interface HistoryQueries : DbProvider {
fun getHistoryByMangaId(mangaId: Long) = db.get()
.listOfObjects(History::class.java)
.withQuery(
RawQuery.builder()
.query(getHistoryByMangaId())
.args(mangaId)
.observesTables(HistoryTable.TABLE)
.build(),
)
.prepare()
/**
* Updates the history last read.
* Inserts history object if not yet in database
* @param historyList history object list
*/
fun upsertHistoryLastRead(historyList: List<History>) = db.put()
.objects(historyList)
.withPutResolver(HistoryUpsertResolver())
.prepare()
// SY -->
fun updateHistoryChapterIds(history: List<History>) = db.put()
.objects(history)
.withPutResolver(HistoryChapterIdPutResolver())
.prepare()
fun deleteHistoryIds(ids: List<Long>) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(HistoryTable.TABLE)
.where("${HistoryTable.COL_ID} IN (?)")
.whereArgs(ids.joinToString())
.build(),
)
.prepare()
// SY <--
}

View File

@ -1,37 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.Queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.inTransaction
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
interface MangaCategoryQueries : DbProvider {
fun insertMangasCategories(mangasCategories: List<MangaCategory>) = db.put().objects(mangasCategories).prepare()
fun deleteOldMangasCategories(mangas: List<Manga>) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(MangaCategoryTable.TABLE)
.where("${MangaCategoryTable.COL_MANGA_ID} IN (${Queries.placeholders(mangas.size)})")
.whereArgs(*mangas.map { it.id }.toTypedArray())
.build(),
)
.prepare()
fun setMangaCategories(mangasCategories: List<MangaCategory>, mangas: List<Manga>) {
db.inTransaction {
// SY -->
mangas.chunked(100) { chunk ->
deleteOldMangasCategories(chunk).executeAsBlocking()
}
mangasCategories.chunked(100) { chunk ->
insertMangasCategories(chunk).executeAsBlocking()
}
// SY <--
}
}
}

View File

@ -6,17 +6,11 @@ import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.resolvers.LibraryMangaGetResolver import eu.kanade.tachiyomi.data.database.resolvers.LibraryMangaGetResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaFavoritePutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaFilteredScanlatorsPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaInfoPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaMigrationPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaThumbnailPutResolver
import eu.kanade.tachiyomi.data.database.tables.CategoryTable import eu.kanade.tachiyomi.data.database.tables.CategoryTable
import eu.kanade.tachiyomi.data.database.tables.ChapterTable import eu.kanade.tachiyomi.data.database.tables.ChapterTable
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
import eu.kanade.tachiyomi.data.database.tables.MangaTable import eu.kanade.tachiyomi.data.database.tables.MangaTable
import exh.metadata.sql.tables.SearchMetadataTable
interface MangaQueries : DbProvider { interface MangaQueries : DbProvider {
@ -64,50 +58,8 @@ interface MangaQueries : DbProvider {
) )
.prepare() .prepare()
// SY -->
fun getMangas() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.build(),
)
.prepare()
fun getReadNotInLibraryMangas() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
RawQuery.builder()
.query(getReadMangaNotInLibraryQuery())
.build(),
)
.prepare()
fun updateMangaInfo(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaInfoPutResolver())
.prepare()
fun resetMangaInfo(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaInfoPutResolver(true))
.prepare()
fun updateMangaMigrate(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaMigrationPutResolver())
.prepare()
fun updateMangaThumbnail(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaThumbnailPutResolver())
.prepare()
// SY <--
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare() fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
fun insertMangas(mangas: List<Manga>) = db.put().objects(mangas).prepare()
fun updateChapterFlags(manga: Manga) = db.put() fun updateChapterFlags(manga: Manga) = db.put()
.`object`(manga) .`object`(manga)
.withPutResolver(MangaFlagsPutResolver(MangaTable.COL_CHAPTER_FLAGS, Manga::chapter_flags)) .withPutResolver(MangaFlagsPutResolver(MangaTable.COL_CHAPTER_FLAGS, Manga::chapter_flags))
@ -123,69 +75,5 @@ interface MangaQueries : DbProvider {
.withPutResolver(MangaFlagsPutResolver(MangaTable.COL_VIEWER, Manga::viewer_flags)) .withPutResolver(MangaFlagsPutResolver(MangaTable.COL_VIEWER, Manga::viewer_flags))
.prepare() .prepare()
fun updateMangaFavorite(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaFavoritePutResolver())
.prepare()
// SY -->
fun updateMangaFilteredScanlators(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaFilteredScanlatorsPutResolver())
.prepare()
// SY <--
fun deleteManga(manga: Manga) = db.delete().`object`(manga).prepare() fun deleteManga(manga: Manga) = db.delete().`object`(manga).prepare()
// SY -->
fun getMangaWithMetadata() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
RawQuery.builder()
.query(
"""
SELECT ${MangaTable.TABLE}.* FROM ${MangaTable.TABLE}
INNER JOIN ${SearchMetadataTable.TABLE}
ON ${MangaTable.TABLE}.${MangaTable.COL_ID} = ${SearchMetadataTable.TABLE}.${SearchMetadataTable.COL_MANGA_ID}
ORDER BY ${MangaTable.TABLE}.${MangaTable.COL_ID}
""".trimIndent(),
)
.build(),
)
.prepare()
fun getFavoriteMangaWithMetadata() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
RawQuery.builder()
.query(
"""
SELECT ${MangaTable.TABLE}.* FROM ${MangaTable.TABLE}
INNER JOIN ${SearchMetadataTable.TABLE}
ON ${MangaTable.TABLE}.${MangaTable.COL_ID} = ${SearchMetadataTable.TABLE}.${SearchMetadataTable.COL_MANGA_ID}
WHERE ${MangaTable.TABLE}.${MangaTable.COL_FAVORITE} = 1
ORDER BY ${MangaTable.TABLE}.${MangaTable.COL_ID}
""".trimIndent(),
)
.build(),
)
.prepare()
fun getIdsOfFavoriteMangaWithMetadata() = db.get()
.cursor()
.withQuery(
RawQuery.builder()
.query(
"""
SELECT ${MangaTable.TABLE}.${MangaTable.COL_ID} FROM ${MangaTable.TABLE}
INNER JOIN ${SearchMetadataTable.TABLE}
ON ${MangaTable.TABLE}.${MangaTable.COL_ID} = ${SearchMetadataTable.TABLE}.${SearchMetadataTable.COL_MANGA_ID}
WHERE ${MangaTable.TABLE}.${MangaTable.COL_FAVORITE} = 1
ORDER BY ${MangaTable.TABLE}.${MangaTable.COL_ID}
""".trimIndent(),
)
.build(),
)
.prepare()
// SY <--
} }

View File

@ -1,9 +1,7 @@
package eu.kanade.tachiyomi.data.database.queries package eu.kanade.tachiyomi.data.database.queries
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import eu.kanade.tachiyomi.data.database.tables.CategoryTable as Category
import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter
import eu.kanade.tachiyomi.data.database.tables.HistoryTable as History
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable as MangaCategory import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable as MangaCategory
import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga
import exh.merged.sql.tables.MergedTable as Merged import exh.merged.sql.tables.MergedTable as Merged
@ -74,18 +72,6 @@ fun getMergedChaptersQuery() =
ON ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = M.${Merged.COL_MANGA_ID} ON ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = M.${Merged.COL_MANGA_ID}
""" """
/**
* Query to get manga that are not in library, but have read chapters
*/
fun getReadMangaNotInLibraryQuery() =
"""
SELECT ${Manga.TABLE}.*
FROM ${Manga.TABLE}
WHERE ${Manga.COL_FAVORITE} = 0 AND ${Manga.COL_ID} IN(
SELECT ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} FROM ${Chapter.TABLE} WHERE ${Chapter.COL_READ} = 1 OR ${Chapter.COL_LAST_PAGE_READ} != 0
)
"""
/** /**
* Query to get the manga from the library, with their categories, read and unread count. * Query to get the manga from the library, with their categories, read and unread count.
*/ */
@ -142,24 +128,4 @@ val libraryQuery =
ON MC.${MangaCategory.COL_MANGA_ID} = M.${Manga.COL_ID}; ON MC.${MangaCategory.COL_MANGA_ID} = M.${Manga.COL_ID};
""" """
fun getHistoryByMangaId() =
"""
SELECT ${History.TABLE}.*
FROM ${History.TABLE}
JOIN ${Chapter.TABLE}
ON ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
WHERE ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
"""
/**
* Query to get the categories for a manga.
*/
fun getCategoriesForMangaQuery() =
"""
SELECT ${Category.TABLE}.* FROM ${Category.TABLE}
JOIN ${MangaCategory.TABLE} ON ${Category.TABLE}.${Category.COL_ID} =
${MangaCategory.TABLE}.${MangaCategory.COL_CATEGORY_ID}
WHERE ${MangaCategory.COL_MANGA_ID} = ?
"""
// SY <-- // SY <--

View File

@ -1,33 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.tables.TrackTable
interface TrackQueries : DbProvider {
fun getTracks() = db.get()
.listOfObjects(Track::class.java)
.withQuery(
Query.builder()
.table(TrackTable.TABLE)
.build(),
)
.prepare()
fun getTracks(mangaId: Long?) = db.get()
.listOfObjects(Track::class.java)
.withQuery(
Query.builder()
.table(TrackTable.TABLE)
.where("${TrackTable.COL_MANGA_ID} = ?")
.whereArgs(mangaId)
.build(),
)
.prepare()
fun insertTrack(track: Track) = db.put().`object`(track).prepare()
fun insertTracks(tracks: List<Track>) = db.put().objects(tracks).prepare()
}

View File

@ -4,7 +4,7 @@ import android.content.Context
import androidx.core.content.edit import androidx.core.content.edit
import eu.kanade.domain.chapter.interactor.GetChapter import eu.kanade.domain.chapter.interactor.GetChapter
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.toDbManga import eu.kanade.domain.manga.model.toDbManga
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
@ -34,7 +34,7 @@ class DownloadStore(
private val json: Json by injectLazy() private val json: Json by injectLazy()
private val getMangaById: GetMangaById by injectLazy() private val getManga: GetManga by injectLazy()
private val getChapter: GetChapter by injectLazy() private val getChapter: GetChapter by injectLazy()
/** /**
@ -96,7 +96,7 @@ class DownloadStore(
val cachedManga = mutableMapOf<Long, Manga?>() val cachedManga = mutableMapOf<Long, Manga?>()
for ((mangaId, chapterId) in objs) { for ((mangaId, chapterId) in objs) {
val manga = cachedManga.getOrPut(mangaId) { val manga = cachedManga.getOrPut(mangaId) {
runBlocking { getMangaById.await(mangaId)?.toDbManga() } runBlocking { getManga.await(mangaId)?.toDbManga() }
} ?: continue } ?: continue
val source = sourceManager.get(manga.source) as? HttpSource ?: continue val source = sourceManager.get(manga.source) as? HttpSource ?: continue
val chapter = runBlocking { getChapter.await(chapterId) }?.toDbChapter() ?: continue val chapter = runBlocking { getChapter.await(chapterId) }?.toDbChapter() ?: continue

View File

@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.download.model
import eu.kanade.domain.chapter.interactor.GetChapter import eu.kanade.domain.chapter.interactor.GetChapter
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.toDbManga import eu.kanade.domain.manga.model.toDbManga
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
@ -69,11 +69,11 @@ data class Download(
suspend fun fromChapterId( suspend fun fromChapterId(
chapterId: Long, chapterId: Long,
getChapter: GetChapter = Injekt.get(), getChapter: GetChapter = Injekt.get(),
getMangaById: GetMangaById = Injekt.get(), getManga: GetManga = Injekt.get(),
sourceManager: SourceManager = Injekt.get(), sourceManager: SourceManager = Injekt.get(),
): Download? { ): Download? {
val chapter = getChapter.await(chapterId) ?: return null val chapter = getChapter.await(chapterId) ?: return null
val manga = getMangaById.await(chapter.mangaId) ?: return null val manga = getManga.await(chapter.mangaId) ?: return null
val source = sourceManager.get(manga.source) as? HttpSource ?: return null val source = sourceManager.get(manga.source) as? HttpSource ?: return null
return Download(source, manga.toDbManga(), chapter.toDbChapter()) return Download(source, manga.toDbManga(), chapter.toDbChapter())

View File

@ -13,7 +13,9 @@ import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetFavorites
import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.InsertFlatMetadata
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDbManga import eu.kanade.domain.manga.model.toDbManga
import eu.kanade.domain.manga.model.toMangaInfo import eu.kanade.domain.manga.model.toMangaInfo
@ -61,7 +63,6 @@ import eu.kanade.tachiyomi.util.system.logcat
import exh.log.xLogE import exh.log.xLogE
import exh.md.utils.FollowStatus import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.metadata.metadata.base.insertFlatMetadataAsync
import exh.source.LIBRARY_UPDATE_EXCLUDED_SOURCES import exh.source.LIBRARY_UPDATE_EXCLUDED_SOURCES
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import exh.source.isMdBasedSource import exh.source.isMdBasedSource
@ -77,6 +78,7 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.sync.withPermit
@ -106,7 +108,7 @@ class LibraryUpdateService(
val downloadManager: DownloadManager = Injekt.get(), val downloadManager: DownloadManager = Injekt.get(),
val trackManager: TrackManager = Injekt.get(), val trackManager: TrackManager = Injekt.get(),
val coverCache: CoverCache = Injekt.get(), val coverCache: CoverCache = Injekt.get(),
private val getMangaById: GetMangaById = Injekt.get(), private val getManga: GetManga = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(),
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(), private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(), private val getCategories: GetCategories = Injekt.get(),
@ -114,6 +116,10 @@ class LibraryUpdateService(
private val getTracks: GetTracks = Injekt.get(), private val getTracks: GetTracks = Injekt.get(),
private val insertTrack: InsertTrack = Injekt.get(), private val insertTrack: InsertTrack = Injekt.get(),
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(), private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(),
// SY -->
private val getFavorites: GetFavorites = Injekt.get(),
private val insertFlatMetadata: InsertFlatMetadata = Injekt.get(),
// SY <--
) : Service() { ) : Service() {
private lateinit var wakeLock: PowerManager.WakeLock private lateinit var wakeLock: PowerManager.WakeLock
@ -327,11 +333,11 @@ class LibraryUpdateService(
when (group) { when (group) {
LibraryGroup.BY_TRACK_STATUS -> { LibraryGroup.BY_TRACK_STATUS -> {
val trackingExtra = groupExtra?.toIntOrNull() ?: -1 val trackingExtra = groupExtra?.toIntOrNull() ?: -1
val tracks = db.getTracks().executeAsBlocking().groupBy { it.manga_id } val tracks = runBlocking { getTracks.await() }.groupBy { it.mangaId }
libraryManga.filter { manga -> libraryManga.filter { manga ->
val status = tracks[manga.id]?.firstNotNullOfOrNull { track -> val status = tracks[manga.id]?.firstNotNullOfOrNull { track ->
TrackStatus.parseTrackerStatus(track.sync_id.toLong(), track.status) TrackStatus.parseTrackerStatus(track.syncId, track.status)
} ?: TrackStatus.OTHER } ?: TrackStatus.OTHER
status.int == trackingExtra status.int == trackingExtra
} }
@ -404,7 +410,7 @@ class LibraryUpdateService(
} }
// Don't continue to update if manga not in library // Don't continue to update if manga not in library
manga.id?.let { getMangaById.await(it) } ?: return@forEach manga.id?.let { getManga.await(it) } ?: return@forEach
withUpdateNotification( withUpdateNotification(
currentlyUpdatingManga, currentlyUpdatingManga,
@ -536,7 +542,7 @@ class LibraryUpdateService(
val tracks = getTracks.await(manga.id) val tracks = getTracks.await(manga.id)
if (tracks.isEmpty() || tracks.none { it.syncId == TrackManager.MDLIST }) { if (tracks.isEmpty() || tracks.none { it.syncId == TrackManager.MDLIST }) {
val track = trackManager.mdList.createInitialTracker(manga.toDbManga()) val track = trackManager.mdList.createInitialTracker(manga.toDbManga())
db.insertTrack(trackManager.mdList.refresh(track)).executeAsBlocking() insertTrack.await(trackManager.mdList.refresh(track).toDomainTrack(false)!!)
} }
} }
} }
@ -550,7 +556,7 @@ class LibraryUpdateService(
.map { it.toSChapter() } .map { it.toSChapter() }
// Get manga from database to account for if it was removed during the update // Get manga from database to account for if it was removed during the update
val dbManga = getMangaById.await(manga.id) val dbManga = getManga.await(manga.id)
?: return Pair(emptyList(), emptyList()) ?: return Pair(emptyList(), emptyList())
// [dbmanga] was used so that manga data doesn't get overwritten // [dbmanga] was used so that manga data doesn't get overwritten
@ -724,7 +730,7 @@ class LibraryUpdateService(
val id = db.insertManga(dbManga).executeOnIO().insertedId() val id = db.insertManga(dbManga).executeOnIO().insertedId()
if (id != null) { if (id != null) {
metadata.mangaId = id metadata.mangaId = id
db.insertFlatMetadataAsync(metadata.flatten()).await() insertFlatMetadata.await(metadata)
} }
} }
@ -736,7 +742,7 @@ class LibraryUpdateService(
*/ */
private suspend fun pushFavorites() { private suspend fun pushFavorites() {
var count = 0 var count = 0
val listManga = db.getFavoriteMangas().executeAsBlocking().filter { it.source in mangaDexSourceIds } val listManga = getFavorites.await().filter { it.source in mangaDexSourceIds }
// filter all follows from Mangadex and only add reading or rereading manga to library // filter all follows from Mangadex and only add reading or rereading manga to library
if (trackManager.mdList.isLogged) { if (trackManager.mdList.isLogged) {
@ -746,18 +752,18 @@ class LibraryUpdateService(
} }
count++ count++
notifier.showProgressNotification(listOf(manga), count, listManga.size) notifier.showProgressNotification(listOf(manga.toDbManga()), count, listManga.size)
// Get this manga's trackers from the database // Get this manga's trackers from the database
val dbTracks = getTracks.await(manga.id!!) val dbTracks = getTracks.await(manga.id)
// find the mdlist entry if its unfollowed the follow it // find the mdlist entry if its unfollowed the follow it
val tracker = TrackItem(dbTracks.firstOrNull { it.syncId == TrackManager.MDLIST }?.toDbTrack() ?: trackManager.mdList.createInitialTracker(manga), trackManager.mdList) val tracker = TrackItem(dbTracks.firstOrNull { it.syncId == TrackManager.MDLIST }?.toDbTrack() ?: trackManager.mdList.createInitialTracker(manga.toDbManga()), trackManager.mdList)
if (tracker.track?.status == FollowStatus.UNFOLLOWED.int) { if (tracker.track?.status == FollowStatus.UNFOLLOWED.int) {
tracker.track.status = FollowStatus.READING.int tracker.track.status = FollowStatus.READING.int
val updatedTrack = tracker.service.update(tracker.track) val updatedTrack = tracker.service.update(tracker.track)
db.insertTrack(updatedTrack).executeOnIO() insertTrack.await(updatedTrack.toDomainTrack(false)!!)
} }
} }
} }

View File

@ -11,7 +11,7 @@ import eu.kanade.domain.chapter.interactor.GetChapter
import eu.kanade.domain.chapter.interactor.UpdateChapter import eu.kanade.domain.chapter.interactor.UpdateChapter
import eu.kanade.domain.chapter.model.toChapterUpdate import eu.kanade.domain.chapter.model.toChapterUpdate
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.toDbManga import eu.kanade.domain.manga.model.toDbManga
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.BackupRestoreService import eu.kanade.tachiyomi.data.backup.BackupRestoreService
@ -46,7 +46,7 @@ import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
*/ */
class NotificationReceiver : BroadcastReceiver() { class NotificationReceiver : BroadcastReceiver() {
private val getMangaById: GetMangaById by injectLazy() private val getManga: GetManga by injectLazy()
private val getChapter: GetChapter by injectLazy() private val getChapter: GetChapter by injectLazy()
private val updateChapter: UpdateChapter by injectLazy() private val updateChapter: UpdateChapter by injectLazy()
private val downloadManager: DownloadManager by injectLazy() private val downloadManager: DownloadManager by injectLazy()
@ -178,7 +178,7 @@ class NotificationReceiver : BroadcastReceiver() {
* @param chapterId id of chapter * @param chapterId id of chapter
*/ */
private fun openChapter(context: Context, mangaId: Long, chapterId: Long) { private fun openChapter(context: Context, mangaId: Long, chapterId: Long) {
val manga = runBlocking { getMangaById.await(mangaId) } val manga = runBlocking { getManga.await(mangaId) }
val chapter = runBlocking { getChapter.await(chapterId) } val chapter = runBlocking { getChapter.await(chapterId) }
if (manga != null && chapter != null) { if (manga != null && chapter != null) {
val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply { val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply {
@ -248,7 +248,7 @@ class NotificationReceiver : BroadcastReceiver() {
.map { .map {
val chapter = it.copy(read = true) val chapter = it.copy(read = true)
if (preferences.removeAfterMarkedAsRead()) { if (preferences.removeAfterMarkedAsRead()) {
val manga = getMangaById.await(mangaId) val manga = getManga.await(mangaId)
if (manga != null) { if (manga != null) {
val source = sourceManager.get(manga.source) val source = sourceManager.get(manga.source)
if (source != null) { if (source != null) {
@ -270,7 +270,7 @@ class NotificationReceiver : BroadcastReceiver() {
*/ */
private fun downloadChapters(chapterUrls: Array<String>, mangaId: Long) { private fun downloadChapters(chapterUrls: Array<String>, mangaId: Long) {
launchIO { launchIO {
val manga = getMangaById.await(mangaId)?.toDbManga() val manga = getManga.await(mangaId)?.toDbManga()
val chapters = chapterUrls.mapNotNull { getChapter.await(it, mangaId)?.toDbChapter() } val chapters = chapterUrls.mapNotNull { getChapter.await(it, mangaId)?.toDbChapter() }
if (manga != null && chapters.isNotEmpty()) { if (manga != null && chapters.isNotEmpty()) {
downloadManager.downloadChapters(manga, chapters) downloadManager.downloadChapters(manga, chapters)

View File

@ -21,7 +21,8 @@ enum class TrackStatus(val int: Int, @StringRes val res: Int) {
OTHER(7, R.string.not_tracked); OTHER(7, R.string.not_tracked);
companion object { companion object {
fun parseTrackerStatus(tracker: Long, status: Int): TrackStatus? { fun parseTrackerStatus(tracker: Long, statusLong: Long): TrackStatus? {
val status = statusLong.toInt()
return when (tracker) { return when (tracker) {
TrackManager.MDLIST -> { TrackManager.MDLIST -> {
when (FollowStatus.fromInt(status)) { when (FollowStatus.fromInt(status)) {

View File

@ -9,7 +9,7 @@ import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.track.interactor.GetTracks import eu.kanade.domain.track.interactor.GetTracks
import eu.kanade.domain.track.interactor.InsertTrack import eu.kanade.domain.track.interactor.InsertTrack
import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDbTrack
@ -26,7 +26,7 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
CoroutineWorker(context, workerParams) { CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
val getMangaById = Injekt.get<GetMangaById>() val getManga = Injekt.get<GetManga>()
val getTracks = Injekt.get<GetTracks>() val getTracks = Injekt.get<GetTracks>()
val insertTrack = Injekt.get<InsertTrack>() val insertTrack = Injekt.get<InsertTrack>()
@ -35,7 +35,7 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val tracks = delayedTrackingStore.getItems().mapNotNull { val tracks = delayedTrackingStore.getItems().mapNotNull {
val manga = getMangaById.await(it.mangaId) ?: return@withContext val manga = getManga.await(it.mangaId) ?: return@withContext
getTracks.await(manga.id) getTracks.await(manga.id)
.find { track -> track.id == it.trackId } .find { track -> track.id == it.trackId }
?.copy(lastChapterRead = it.lastChapterRead.toDouble()) ?.copy(lastChapterRead = it.lastChapterRead.toDouble())

View File

@ -1,9 +1,9 @@
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.GetFlatMetadataById
import eu.kanade.domain.manga.interactor.GetMangaByUrlAndSource import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.domain.manga.interactor.InsertFlatMetadata
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
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
@ -14,8 +14,6 @@ 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.awaitFlatMetadataForManga
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
@ -27,9 +25,9 @@ import kotlin.reflect.KClass
* LEWD! * LEWD!
*/ */
interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource { interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
val db: DatabaseHelper get() = Injekt.get() val getManga: GetManga get() = Injekt.get()
val handler: DatabaseHandler get() = Injekt.get() val insertFlatMetadata: InsertFlatMetadata get() = Injekt.get()
val getMangaByUrlAndSource: GetMangaByUrlAndSource get() = Injekt.get() val getFlatMetadataById: GetFlatMetadataById get() = Injekt.get()
/** /**
* The class of the metadata used by this source * The class of the metadata used by this source
@ -63,14 +61,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 = handler.awaitFlatMetadataForManga(mangaId) val flatMetadata = getFlatMetadataById.await(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
handler.awaitInsertFlatMetadata(metadata.flatten()) insertFlatMetadata.await(metadata)
} }
return metadata.createMangaInfo(manga) return metadata.createMangaInfo(manga)
@ -99,7 +97,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 = handler.awaitFlatMetadataForManga(mangaId) val flatMetadata = getFlatMetadataById.await(mangaId)
flatMetadata?.raise(metaClass) flatMetadata?.raise(metaClass)
} else { } else {
null null
@ -110,7 +108,7 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
parseIntoMetadata(newMeta, input) parseIntoMetadata(newMeta, input)
if (mangaId != null) { if (mangaId != null) {
newMeta.mangaId = mangaId newMeta.mangaId = mangaId
handler.awaitInsertFlatMetadata(newMeta.flatten()) insertFlatMetadata.await(newMeta)
} }
newMeta newMeta
} }
@ -119,7 +117,7 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
@Composable @Composable
fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit)
suspend fun MangaInfo.id() = getMangaByUrlAndSource.await(key, id)?.id suspend fun MangaInfo.id() = getManga.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
} }

View File

@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.migration.advanced.process package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class MigratingManga( class MigratingManga(
private val getMangaById: GetMangaById, private val getManga: GetManga,
private val sourceManager: SourceManager, private val sourceManager: SourceManager,
val mangaId: Long, val mangaId: Long,
parentContext: CoroutineContext, parentContext: CoroutineContext,
@ -28,7 +28,7 @@ class MigratingManga(
@Volatile @Volatile
private var manga: Manga? = null private var manga: Manga? = null
suspend fun manga(): Manga? { suspend fun manga(): Manga? {
if (manga == null) manga = getMangaById.await(mangaId) if (manga == null) manga = getManga.await(mangaId)
return manga return manga
} }

View File

@ -16,7 +16,7 @@ import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.chrisbanes.insetter.applyInsetter import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDbManga import eu.kanade.domain.manga.model.toDbManga
import eu.kanade.domain.manga.model.toMangaInfo import eu.kanade.domain.manga.model.toMangaInfo
@ -75,7 +75,7 @@ class MigrationListController(bundle: Bundle? = null) :
private val smartSearchEngine = SmartSearchEngine(config?.extraSearchParams) private val smartSearchEngine = SmartSearchEngine(config?.extraSearchParams)
private val syncChaptersWithSource: SyncChaptersWithSource by injectLazy() private val syncChaptersWithSource: SyncChaptersWithSource by injectLazy()
private val getMangaById: GetMangaById by injectLazy() private val getManga: GetManga by injectLazy()
private val updateManga: UpdateManga by injectLazy() private val updateManga: UpdateManga by injectLazy()
private val migrationScope = CoroutineScope(Job() + Dispatchers.IO) private val migrationScope = CoroutineScope(Job() + Dispatchers.IO)
@ -109,7 +109,7 @@ class MigrationListController(bundle: Bundle? = null) :
val newMigratingManga = migratingManga ?: run { val newMigratingManga = migratingManga ?: run {
val new = config.mangaIds.map { val new = config.mangaIds.map {
MigratingManga(getMangaById, sourceManager, it, migrationScope.coroutineContext) MigratingManga(getManga, sourceManager, it, migrationScope.coroutineContext)
} }
migratingManga = new.toMutableList() migratingManga = new.toMutableList()
new new
@ -409,7 +409,7 @@ class MigrationListController(bundle: Bundle? = null) :
val hasDetails = router.backstack.any { it.controller is MangaController } val hasDetails = router.backstack.any { it.controller is MangaController }
if (hasDetails) { if (hasDetails) {
val manga = migratingManga?.firstOrNull()?.searchResult?.get()?.let { val manga = migratingManga?.firstOrNull()?.searchResult?.get()?.let {
getMangaById.await(it) getManga.await(it)
} }
if (manga != null) { if (manga != null) {
val newStack = router.backstack.filter { val newStack = router.backstack.filter {

View File

@ -12,7 +12,7 @@ import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.model.ChapterUpdate import eu.kanade.domain.chapter.model.ChapterUpdate
import eu.kanade.domain.history.interactor.UpsertHistory import eu.kanade.domain.history.interactor.UpsertHistory
import eu.kanade.domain.history.model.HistoryUpdate import eu.kanade.domain.history.model.HistoryUpdate
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MangaUpdate import eu.kanade.domain.manga.model.MangaUpdate
@ -35,7 +35,7 @@ class MigrationProcessAdapter(
private val handler: DatabaseHandler by injectLazy() private val handler: DatabaseHandler by injectLazy()
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()
private val coverCache: CoverCache by injectLazy() private val coverCache: CoverCache by injectLazy()
private val getMangaById: GetMangaById by injectLazy() private val getManga: GetManga by injectLazy()
private val updateManga: UpdateManga by injectLazy() private val updateManga: UpdateManga by injectLazy()
private val updateChapter: UpdateChapter by injectLazy() private val updateChapter: UpdateChapter by injectLazy()
private val getChapterByMangaId: GetChapterByMangaId by injectLazy() private val getChapterByMangaId: GetChapterByMangaId by injectLazy()
@ -81,7 +81,7 @@ class MigrationProcessAdapter(
currentItems.forEach { migratingManga -> currentItems.forEach { migratingManga ->
val manga = migratingManga.manga val manga = migratingManga.manga
if (manga.searchResult.initialized) { if (manga.searchResult.initialized) {
val toMangaObj = getMangaById.await(manga.searchResult.get() ?: return@forEach) val toMangaObj = getManga.await(manga.searchResult.get() ?: return@forEach)
?: return@forEach ?: return@forEach
migrateMangaInternal( migrateMangaInternal(
manga.manga() ?: return@forEach, manga.manga() ?: return@forEach,
@ -97,7 +97,7 @@ class MigrationProcessAdapter(
launchUI { launchUI {
val manga = getItem(position)?.manga ?: return@launchUI val manga = getItem(position)?.manga ?: return@launchUI
val toMangaObj = getMangaById.await(manga.searchResult.get() ?: return@launchUI) val toMangaObj = getManga.await(manga.searchResult.get() ?: return@launchUI)
?: return@launchUI ?: return@launchUI
migrateMangaInternal( migrateMangaInternal(
manga.manga() ?: return@launchUI, manga.manga() ?: return@launchUI,

View File

@ -7,7 +7,7 @@ import androidx.core.view.isVisible
import coil.dispose import coil.dispose
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.GetMergedReferencesById import eu.kanade.domain.manga.interactor.GetMergedReferencesById
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -32,7 +32,7 @@ class MigrationProcessHolder(
private val adapter: MigrationProcessAdapter, private val adapter: MigrationProcessAdapter,
) : FlexibleViewHolder(view, adapter) { ) : FlexibleViewHolder(view, adapter) {
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
private val getMangaById: GetMangaById by injectLazy() private val getManga: GetManga by injectLazy()
private val getChapterByMangaId: GetChapterByMangaId by injectLazy() private val getChapterByMangaId: GetChapterByMangaId by injectLazy()
private val getMergedReferencesById: GetMergedReferencesById by injectLazy() private val getMergedReferencesById: GetMergedReferencesById by injectLazy()
@ -89,7 +89,7 @@ class MigrationProcessHolder(
}*/ }*/
val searchResult = item.manga.searchResult.get()?.let { val searchResult = item.manga.searchResult.get()?.let {
getMangaById.await(it) getManga.await(it)
} }
val resultSource = searchResult?.source?.let { val resultSource = searchResult?.source?.let {
sourceManager.get(it) sourceManager.get(it)

View File

@ -5,9 +5,9 @@ import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import eu.kanade.domain.manga.interactor.GetFavorites
import eu.kanade.presentation.browse.MigrateSourceScreen import eu.kanade.presentation.browse.MigrateSourceScreen
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.base.controller.ComposeController import eu.kanade.tachiyomi.ui.base.controller.ComposeController
import eu.kanade.tachiyomi.ui.base.controller.pushController import eu.kanade.tachiyomi.ui.base.controller.pushController
@ -54,10 +54,9 @@ class MigrationSourcesController : ComposeController<MigrationSourcesPresenter>(
onClickAll = { source -> onClickAll = { source ->
// TODO: Jay wtf, need to clean this up sometime // TODO: Jay wtf, need to clean this up sometime
launchIO { launchIO {
val manga = Injekt.get<DatabaseHelper>().getFavoriteMangas().executeAsBlocking() val manga = Injekt.get<GetFavorites>().await()
val sourceMangas = val sourceMangas =
manga.asSequence().filter { it.source == source.id }.mapNotNull { it.id } manga.asSequence().filter { it.source == source.id }.map { it.id }.toList()
.toList()
withUIContext { withUIContext {
PreMigrationController.navigateToMigration( PreMigrationController.navigateToMigration(
Injekt.get<PreferencesHelper>().skipPreMigration().get(), Injekt.get<PreferencesHelper>().skipPreMigration().get(),

View File

@ -1,11 +1,12 @@
package eu.kanade.tachiyomi.ui.library package eu.kanade.tachiyomi.ui.library
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.domain.manga.interactor.GetIdsOfFavoriteMangaWithMetadata
import eu.kanade.domain.manga.interactor.GetSearchTags
import eu.kanade.domain.manga.interactor.GetSearchTitles
import eu.kanade.domain.track.interactor.GetTracks import eu.kanade.domain.track.interactor.GetTracks
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
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.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
@ -21,7 +22,6 @@ import exh.search.SearchEngine
import exh.search.Text import exh.search.Text
import exh.source.isMetadataSource import exh.source.isMetadataSource
import exh.util.cancellable import exh.util.cancellable
import exh.util.executeOnIO
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -41,7 +41,6 @@ import uy.kohesive.injekt.injectLazy
class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryController) : class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryController) :
FlexibleAdapter<LibraryItem>(null, view, true) { FlexibleAdapter<LibraryItem>(null, view, true) {
// EXH --> // EXH -->
private val db: DatabaseHelper by injectLazy()
private val searchEngine = SearchEngine() private val searchEngine = SearchEngine()
private var lastFilterJob: Job? = null private var lastFilterJob: Job? = null
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
@ -50,8 +49,13 @@ class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryC
private val hasLoggedServices by lazy { private val hasLoggedServices by lazy {
trackManager.hasLoggedServices() trackManager.hasLoggedServices()
} }
private val services = trackManager.services.map { service -> service.id to controller.activity!!.getString(service.nameRes()) }.toMap() private val services = trackManager.services.associate { service ->
service.id to controller.activity!!.getString(service.nameRes())
}
private val getIdsOfFavoriteMangaWithMetadata: GetIdsOfFavoriteMangaWithMetadata by injectLazy()
private val getTracks: GetTracks by injectLazy() private val getTracks: GetTracks by injectLazy()
private val getSearchTags: GetSearchTags by injectLazy()
private val getSearchTitles: GetSearchTitles by injectLazy()
// Keep compatibility as searchText field was replaced when we upgraded FlexibleAdapter // Keep compatibility as searchText field was replaced when we upgraded FlexibleAdapter
var searchText var searchText
@ -116,19 +120,7 @@ class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryC
val newManga = try { val newManga = try {
// Prepare filter object // Prepare filter object
val parsedQuery = searchEngine.parseQuery(savedSearchText) val parsedQuery = searchEngine.parseQuery(savedSearchText)
val mangaWithMetaIds = getIdsOfFavoriteMangaWithMetadata.await()
val mangaWithMetaIdsQuery = db.getIdsOfFavoriteMangaWithMetadata().executeOnIO()
val mangaWithMetaIds = LongArray(mangaWithMetaIdsQuery.count)
if (mangaWithMetaIds.isNotEmpty()) {
val mangaIdCol = mangaWithMetaIdsQuery.getColumnIndex(MangaTable.COL_ID)
mangaWithMetaIdsQuery.moveToFirst()
while (!mangaWithMetaIdsQuery.isAfterLast) {
ensureActive() // Fail early when cancelled
mangaWithMetaIds[mangaWithMetaIdsQuery.position] = mangaWithMetaIdsQuery.getLong(mangaIdCol)
mangaWithMetaIdsQuery.moveToNext()
}
}
ensureActive() // Fail early when cancelled ensureActive() // Fail early when cancelled
@ -140,8 +132,8 @@ class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryC
// No meta? Filter using title // No meta? Filter using title
filterManga(parsedQuery, item.manga) filterManga(parsedQuery, item.manga)
} else { } else {
val tags = db.getSearchTagsForManga(mangaId).executeAsBlocking() val tags = getSearchTags.await(mangaId)
val titles = db.getSearchTitlesForManga(mangaId).executeAsBlocking() val titles = getSearchTitles.await(mangaId)
filterManga(parsedQuery, item.manga, false, tags, titles) filterManga(parsedQuery, item.manga, false, tags, titles)
} }
} else { } else {

View File

@ -934,7 +934,7 @@ class LibraryPresenter(
val tracks = runBlocking { getTracks.await() }.groupBy { it.mangaId } val tracks = runBlocking { getTracks.await() }.groupBy { it.mangaId }
libraryManga.forEach { libraryItem -> libraryManga.forEach { libraryItem ->
val status = tracks[libraryItem.manga.id]?.firstNotNullOfOrNull { track -> val status = tracks[libraryItem.manga.id]?.firstNotNullOfOrNull { track ->
TrackStatus.parseTrackerStatus(track.syncId, track.status.toInt()) TrackStatus.parseTrackerStatus(track.syncId, track.status)
} ?: TrackStatus.OTHER } ?: TrackStatus.OTHER
map.getOrPut(status.int.toLong()) { mutableListOf() } += libraryItem map.getOrPut(status.int.toLong()) { mutableListOf() } += libraryItem

View File

@ -13,7 +13,7 @@ import coil.transform.RoundedCornersTransformation
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.EditMangaDialogBinding import eu.kanade.tachiyomi.databinding.EditMangaDialogBinding
@ -52,7 +52,7 @@ class EditMangaDialog : DialogController {
@Suppress("unused") @Suppress("unused")
constructor(bundle: Bundle) : super(bundle) { constructor(bundle: Bundle) : super(bundle) {
manga = runBlocking { Injekt.get<GetMangaById>().await(bundle.getLong(KEY_MANGA))!! } manga = runBlocking { Injekt.get<GetManga>().await(bundle.getLong(KEY_MANGA))!! }
} }
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {

View File

@ -14,8 +14,7 @@ import eu.kanade.domain.chapter.model.ChapterUpdate
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
import eu.kanade.domain.manga.interactor.GetFlatMetadataById import eu.kanade.domain.manga.interactor.GetFlatMetadataById
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.GetMangaByUrlAndSource
import eu.kanade.domain.manga.interactor.GetMangaWithChapters import eu.kanade.domain.manga.interactor.GetMangaWithChapters
import eu.kanade.domain.manga.interactor.GetMergedMangaById import eu.kanade.domain.manga.interactor.GetMergedMangaById
import eu.kanade.domain.manga.interactor.GetMergedReferencesById import eu.kanade.domain.manga.interactor.GetMergedReferencesById
@ -123,8 +122,7 @@ class MangaPresenter(
// SY <-- // SY <--
private val getMangaAndChapters: GetMangaWithChapters = Injekt.get(), private val getMangaAndChapters: GetMangaWithChapters = Injekt.get(),
// SY --> // SY -->
private val getMangaById: GetMangaById = Injekt.get(), private val getManga: GetManga = Injekt.get(),
private val getMangaByUrlAndSource: GetMangaByUrlAndSource = Injekt.get(),
private val setMangaFilteredScanlators: SetMangaFilteredScanlators = Injekt.get(), private val setMangaFilteredScanlators: SetMangaFilteredScanlators = Injekt.get(),
private val getMergedChapterByMangaId: GetMergedChapterByMangaId = Injekt.get(), private val getMergedChapterByMangaId: GetMergedChapterByMangaId = Injekt.get(),
private val getMergedMangaById: GetMergedMangaById = Injekt.get(), private val getMergedMangaById: GetMergedMangaById = Injekt.get(),
@ -435,7 +433,7 @@ class MangaPresenter(
suspend fun smartSearchMerge(context: Context, manga: DomainManga, originalMangaId: Long): DomainManga { suspend fun smartSearchMerge(context: Context, manga: DomainManga, originalMangaId: Long): DomainManga {
val db = Injekt.get<DatabaseHelper>() val db = Injekt.get<DatabaseHelper>()
val originalManga = getMangaById.await(originalMangaId) val originalManga = getManga.await(originalMangaId)
?: throw IllegalArgumentException(context.getString(R.string.merge_unknown_manga, originalMangaId)) ?: throw IllegalArgumentException(context.getString(R.string.merge_unknown_manga, originalMangaId))
if (originalManga.source == MERGED_SOURCE_ID) { if (originalManga.source == MERGED_SOURCE_ID) {
val children = getMergedReferencesById.await(originalMangaId) val children = getMergedReferencesById.await(originalMangaId)
@ -490,7 +488,7 @@ class MangaPresenter(
date_added = System.currentTimeMillis() date_added = System.currentTimeMillis()
} }
var existingManga = getMangaByUrlAndSource.await(mergedManga.url, mergedManga.source) var existingManga = getManga.await(mergedManga.url, mergedManga.source)
while (existingManga != null) { while (existingManga != null) {
if (existingManga.favorite) { if (existingManga.favorite) {
throw IllegalArgumentException(context.getString(R.string.merge_duplicate)) throw IllegalArgumentException(context.getString(R.string.merge_duplicate))
@ -500,7 +498,7 @@ class MangaPresenter(
db.deleteMangaForMergedManga(existingManga!!.id).executeAsBlocking() db.deleteMangaForMergedManga(existingManga!!.id).executeAsBlocking()
} }
} }
existingManga = getMangaByUrlAndSource.await(mergedManga.url, mergedManga.source) existingManga = getManga.await(mergedManga.url, mergedManga.source)
} }
// Reload chapters immediately // Reload chapters immediately
@ -744,7 +742,7 @@ class MangaPresenter(
tracks tracks
.filter { it.syncId in loggedServicesId } .filter { it.syncId in loggedServicesId }
// SY --> // SY -->
.filterNot { it.syncId == TrackManager.MDLIST.toLong() && it.status == FollowStatus.UNFOLLOWED.int.toLong() } .filterNot { it.syncId == TrackManager.MDLIST && it.status == FollowStatus.UNFOLLOWED.int.toLong() }
// SY <-- // SY <--
.size .size
} }

View File

@ -20,7 +20,7 @@ import androidx.core.os.bundleOf
import coil.imageLoader import coil.imageLoader
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.size.Size import coil.size.Size
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.manga.model.hasCustomCover
@ -161,7 +161,7 @@ class MangaFullCoverDialog : FullComposeController<MangaFullCoverDialog.MangaFul
inner class MangaFullCoverPresenter( inner class MangaFullCoverPresenter(
private val mangaId: Long, private val mangaId: Long,
private val getMangaById: GetMangaById = Injekt.get(), private val getManga: GetManga = Injekt.get(),
) : Presenter<MangaFullCoverDialog>() { ) : Presenter<MangaFullCoverDialog>() {
private var presenterScope: CoroutineScope = MainScope() private var presenterScope: CoroutineScope = MainScope()
@ -176,7 +176,7 @@ class MangaFullCoverDialog : FullComposeController<MangaFullCoverDialog.MangaFul
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
presenterScope.launchIO { presenterScope.launchIO {
getMangaById.subscribe(mangaId) getManga.subscribe(mangaId)
.collect { _mangaFlow.value = it } .collect { _mangaFlow.value = it }
} }
} }

View File

@ -13,7 +13,7 @@ import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.history.interactor.UpsertHistory import eu.kanade.domain.history.interactor.UpsertHistory
import eu.kanade.domain.history.model.HistoryUpdate import eu.kanade.domain.history.model.HistoryUpdate
import eu.kanade.domain.manga.interactor.GetFlatMetadataById import eu.kanade.domain.manga.interactor.GetFlatMetadataById
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.isLocal import eu.kanade.domain.manga.model.isLocal
import eu.kanade.domain.manga.model.toDbManga import eu.kanade.domain.manga.model.toDbManga
import eu.kanade.domain.track.interactor.GetTracks import eu.kanade.domain.track.interactor.GetTracks
@ -91,7 +91,7 @@ class ReaderPresenter(
private val downloadManager: DownloadManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(),
private val preferences: PreferencesHelper = Injekt.get(), private val preferences: PreferencesHelper = Injekt.get(),
private val delayedTrackingStore: DelayedTrackingStore = Injekt.get(), private val delayedTrackingStore: DelayedTrackingStore = Injekt.get(),
private val getMangaById: GetMangaById = Injekt.get(), private val getManga: GetManga = Injekt.get(),
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(), private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
private val getTracks: GetTracks = Injekt.get(), private val getTracks: GetTracks = Injekt.get(),
private val insertTrack: InsertTrack = Injekt.get(), private val insertTrack: InsertTrack = Injekt.get(),
@ -282,7 +282,7 @@ class ReaderPresenter(
launchIO { launchIO {
try { try {
// SY --> // SY -->
val manga = getMangaById.await(mangaId) ?: return@launchIO val manga = getManga.await(mangaId) ?: return@launchIO
val source = sourceManager.get(manga.source)?.getMainSource<MetadataSource<*, *>>() val source = sourceManager.get(manga.source)?.getMainSource<MetadataSource<*, *>>()
val metadata = if (source != null) { val metadata = if (source != null) {
getFlatMetadataById.await(mangaId)?.raise(source.metaClass) getFlatMetadataById.await(mangaId)?.raise(source.metaClass)

View File

@ -14,10 +14,13 @@ import androidx.core.net.toUri
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.manga.interactor.GetAllManga
import eu.kanade.domain.manga.model.toDbManga
import eu.kanade.domain.manga.repository.MangaRepository import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Target import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Target
@ -82,7 +85,8 @@ class SettingsAdvancedController(
private val network: NetworkHelper by injectLazy() private val network: NetworkHelper by injectLazy()
private val chapterCache: ChapterCache by injectLazy() private val chapterCache: ChapterCache by injectLazy()
private val trackManager: TrackManager by injectLazy() private val trackManager: TrackManager by injectLazy()
private val db: DatabaseHelper by injectLazy() private val getAllManga: GetAllManga by injectLazy()
private val getChapterByMangaId: GetChapterByMangaId by injectLazy()
@SuppressLint("BatteryLife") @SuppressLint("BatteryLife")
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
@ -475,7 +479,7 @@ class SettingsAdvancedController(
if (job?.isActive == true) return if (job?.isActive == true) return
activity?.toast(R.string.starting_cleanup) activity?.toast(R.string.starting_cleanup)
job = launchIO { job = launchIO {
val mangaList = db.getMangas().executeAsBlocking() val mangaList = getAllManga.await()
val downloadManager: DownloadManager = Injekt.get() val downloadManager: DownloadManager = Injekt.get()
var foldersCleared = 0 var foldersCleared = 0
Injekt.get<SourceManager>().getOnlineSources().forEach { source -> Injekt.get<SourceManager>().getOnlineSources().forEach { source ->
@ -483,7 +487,7 @@ class SettingsAdvancedController(
val sourceManga = mangaList val sourceManga = mangaList
.asSequence() .asSequence()
.filter { it.source == source.id } .filter { it.source == source.id }
.map { it to DiskUtil.buildValidFilename(it.originalTitle) } .map { it to DiskUtil.buildValidFilename(it.ogTitle) }
.toList() .toList()
mangaFolders.forEach mangaFolder@{ mangaFolder -> mangaFolders.forEach mangaFolder@{ mangaFolder ->
@ -493,8 +497,8 @@ class SettingsAdvancedController(
foldersCleared += 1 + (mangaFolder.listFiles().orEmpty().size) foldersCleared += 1 + (mangaFolder.listFiles().orEmpty().size)
mangaFolder.delete() mangaFolder.delete()
} else { } else {
val chapterList = db.getChapters(manga).executeAsBlocking() val chapterList = getChapterByMangaId.await(manga.id)
foldersCleared += downloadManager.cleanupChapters(chapterList, manga, source, removeRead, removeNonFavorite) foldersCleared += downloadManager.cleanupChapters(chapterList.map { it.toDbChapter() }, manga.toDbManga(), source, removeRead, removeNonFavorite)
} }
} }
} }

View File

@ -14,15 +14,19 @@ import androidx.core.widget.doAfterTextChanged
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.fredporciuncula.flow.preferences.Preference import com.fredporciuncula.flow.preferences.Preference
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.manga.mangaMapper
import eu.kanade.domain.manga.interactor.DeleteFavoriteEntries
import eu.kanade.domain.manga.interactor.GetFlatMetadataById
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
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.databinding.DialogStubTextinputBinding import eu.kanade.tachiyomi.databinding.DialogStubTextinputBinding
import eu.kanade.tachiyomi.ui.setting.eh.FrontPageCategoriesDialog import eu.kanade.tachiyomi.ui.setting.eh.FrontPageCategoriesDialog
import eu.kanade.tachiyomi.ui.setting.eh.LanguagesDialog import eu.kanade.tachiyomi.ui.setting.eh.LanguagesDialog
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.preference.bindTo import eu.kanade.tachiyomi.util.preference.bindTo
import eu.kanade.tachiyomi.util.preference.entriesRes import eu.kanade.tachiyomi.util.preference.entriesRes
import eu.kanade.tachiyomi.util.preference.intListPreference import eu.kanade.tachiyomi.util.preference.intListPreference
@ -42,11 +46,10 @@ import exh.eh.EHentaiUpdaterStats
import exh.favorites.FavoritesIntroDialog import exh.favorites.FavoritesIntroDialog
import exh.log.xLogD import exh.log.xLogD
import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.source.EH_SOURCE_ID
import exh.source.isEhBasedManga import exh.source.EXH_SOURCE_ID
import exh.uconfig.WarnConfigureDialogController import exh.uconfig.WarnConfigureDialogController
import exh.ui.login.EhLoginActivity import exh.ui.login.EhLoginActivity
import exh.util.executeOnIO
import exh.util.nullIfBlank import exh.util.nullIfBlank
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -69,7 +72,9 @@ import kotlin.time.Duration.Companion.seconds
*/ */
class SettingsEhController : SettingsController() { class SettingsEhController : SettingsController() {
private val db: DatabaseHelper by injectLazy() private val handler: DatabaseHandler by injectLazy()
private val getFlatMetadataById: GetFlatMetadataById by injectLazy()
private val deleteFavoriteEntries: DeleteFavoriteEntries by injectLazy()
fun Preference<*>.reconfigure(): Boolean { fun Preference<*>.reconfigure(): Boolean {
// Listen for change commit // Listen for change commit
@ -360,10 +365,12 @@ class SettingsEhController : SettingsController() {
.setTitle(R.string.favorites_sync_reset) .setTitle(R.string.favorites_sync_reset)
.setMessage(R.string.favorites_sync_reset_message) .setMessage(R.string.favorites_sync_reset_message)
.setPositiveButton(android.R.string.ok) { _, _ -> .setPositiveButton(android.R.string.ok) { _, _ ->
db.inTransaction { launchIO {
db.deleteAllFavoriteEntries().executeAsBlocking() deleteFavoriteEntries.await()
withUIContext {
activity.toast(context.getString(R.string.sync_state_reset), Toast.LENGTH_LONG)
}
} }
activity.toast(context.getString(R.string.sync_state_reset), Toast.LENGTH_LONG)
} }
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.setCancelable(false) .setCancelable(false)
@ -468,12 +475,12 @@ class SettingsEhController : SettingsController() {
context.getString(R.string.gallery_updater_stats_text, getRelativeTimeString(getRelativeTimeFromNow(stats.startTime.milliseconds), context), stats.updateCount, stats.possibleUpdates) context.getString(R.string.gallery_updater_stats_text, getRelativeTimeString(getRelativeTimeFromNow(stats.startTime.milliseconds), context), stats.updateCount, stats.possibleUpdates)
} else context.getString(R.string.gallery_updater_not_ran_yet) } else context.getString(R.string.gallery_updater_not_ran_yet)
val allMeta = db.getFavoriteMangaWithMetadata().executeOnIO() val allMeta = handler
.filter(Manga::isEhBasedManga) .awaitList { mangasQueries.getEhMangaWithMetadata(EH_SOURCE_ID, EXH_SOURCE_ID, mangaMapper) }
.mapNotNull { .mapNotNull {
db.getFlatMetadataForManga(it.id!!).executeOnIO() getFlatMetadataById.await(it.id)
?.raise<EHentaiSearchMetadata>() ?.raise<EHentaiSearchMetadata>()
}.toList() }
fun metaInRelativeDuration(duration: Duration): Int { fun metaInRelativeDuration(duration: Duration): Int {
val durationMs = duration.inWholeMilliseconds val durationMs = duration.inWholeMilliseconds

View File

@ -5,19 +5,18 @@ package exh
import android.content.Context import android.content.Context
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.data.DatabaseHandler import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.GetMangaBySource
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.MangaUpdate
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
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
import eu.kanade.tachiyomi.data.database.resolvers.MangaUrlPutResolver
import eu.kanade.tachiyomi.data.database.tables.ChapterTable import eu.kanade.tachiyomi.data.database.tables.ChapterTable
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.data.database.tables.TrackTable
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.preference.MANGA_NON_COMPLETED import eu.kanade.tachiyomi.data.preference.MANGA_NON_COMPLETED
import eu.kanade.tachiyomi.data.preference.PreferenceKeys import eu.kanade.tachiyomi.data.preference.PreferenceKeys
@ -29,7 +28,6 @@ import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.all.Hitomi import eu.kanade.tachiyomi.source.online.all.Hitomi
import eu.kanade.tachiyomi.source.online.all.NHentai import eu.kanade.tachiyomi.source.online.all.NHentai
import eu.kanade.tachiyomi.ui.library.LibrarySort import eu.kanade.tachiyomi.ui.library.LibrarySort
@ -68,11 +66,15 @@ import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.net.URI import java.net.URI
import java.net.URISyntaxException import java.net.URISyntaxException
import eu.kanade.domain.manga.model.Manga as DomainManga
object EXHMigrations { object EXHMigrations {
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
private val database: DatabaseHandler by injectLazy() private val handler: DatabaseHandler by injectLazy()
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
private val getManga: GetManga by injectLazy()
private val getMangaBySource: GetMangaBySource by injectLazy()
private val updateManga: UpdateManga by injectLazy()
/** /**
* Performs a migration when the application is updated. * Performs a migration when the application is updated.
@ -102,68 +104,41 @@ object EXHMigrations {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
if (oldVersion under 4) { if (oldVersion under 4) {
db.inTransaction { updateSourceId(HBROWSE_SOURCE_ID, 6912)
updateSourceId(HBROWSE_SOURCE_ID, 6912) // Migrate BHrowse URLs
// Migrate BHrowse URLs val hBrowseManga = runBlocking { getMangaBySource.await(HBROWSE_SOURCE_ID) }
val hBrowseManga = db.db.get() val mangaUpdates = hBrowseManga.map {
.listOfObjects(Manga::class.java) MangaUpdate(it.id, url = it.url + "/c00001/")
.withQuery( }
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_SOURCE} = $HBROWSE_SOURCE_ID")
.build(),
)
.prepare()
.executeAsBlocking()
hBrowseManga.forEach {
it.url = it.url + "/c00001/"
}
db.db.put() runBlocking {
.objects(hBrowseManga) updateManga.awaitAll(mangaUpdates)
// Extremely slow without the resolver :/
.withPutResolver(MangaUrlPutResolver())
.prepare()
.executeAsBlocking()
} }
} }
if (oldVersion under 5) { if (oldVersion under 5) {
db.inTransaction { // Migrate Hitomi source IDs
// Migrate Hitomi source IDs updateSourceId(Hitomi.otherId, 6910)
updateSourceId(Hitomi.otherId, 6910)
}
} }
if (oldVersion under 6) { if (oldVersion under 6) {
db.inTransaction { updateSourceId(PERV_EDEN_EN_SOURCE_ID, 6905)
updateSourceId(PERV_EDEN_EN_SOURCE_ID, 6905) updateSourceId(PERV_EDEN_IT_SOURCE_ID, 6906)
updateSourceId(PERV_EDEN_IT_SOURCE_ID, 6906) updateSourceId(NHentai.otherId, 6907)
updateSourceId(NHentai.otherId, 6907)
}
} }
if (oldVersion under 7) { if (oldVersion under 7) {
db.inTransaction { db.inTransaction {
val mergedMangas = db.db.get() val mergedMangas = runBlocking { getMangaBySource.await(MERGED_SOURCE_ID) }
.listOfObjects(Manga::class.java)
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_SOURCE} = $MERGED_SOURCE_ID")
.build(),
)
.prepare()
.executeAsBlocking()
if (mergedMangas.isNotEmpty()) { if (mergedMangas.isNotEmpty()) {
val mangaConfigs = mergedMangas.mapNotNull { mergedManga -> readMangaConfig(mergedManga)?.let { mergedManga to it } } val mangaConfigs = mergedMangas.mapNotNull { mergedManga -> readMangaConfig(mergedManga)?.let { mergedManga to it } }
if (mangaConfigs.isNotEmpty()) { if (mangaConfigs.isNotEmpty()) {
val mangaToUpdate = mutableListOf<Manga>() val mangaToUpdate = mutableListOf<MangaUpdate>()
val mergedMangaReferences = mutableListOf<MergedMangaReference>() val mergedMangaReferences = mutableListOf<MergedMangaReference>()
mangaConfigs.onEach { mergedManga -> mangaConfigs.onEach { mergedManga ->
mergedManga.second.children.firstOrNull()?.url?.let { val newFirst = mergedManga.second.children.firstOrNull()?.url?.let {
if (db.getManga(it, MERGED_SOURCE_ID).executeAsBlocking() != null) return@onEach if (runBlocking { getManga.await(it, MERGED_SOURCE_ID) } != null) return@onEach
mergedManga.first.url = it mangaToUpdate += MangaUpdate(id = mergedManga.first.id, url = it)
} mergedManga.first.copy(url = it)
mangaToUpdate += mergedManga.first } ?: mergedManga.first
mergedMangaReferences += MergedMangaReference( mergedMangaReferences += MergedMangaReference(
id = null, id = null,
isInfoManga = false, isInfoManga = false,
@ -171,9 +146,9 @@ object EXHMigrations {
chapterSortMode = 0, chapterSortMode = 0,
chapterPriority = 0, chapterPriority = 0,
downloadChapters = false, downloadChapters = false,
mergeId = mergedManga.first.id!!, mergeId = mergedManga.first.id,
mergeUrl = mergedManga.first.url, mergeUrl = mergedManga.first.url,
mangaId = mergedManga.first.id!!, mangaId = mergedManga.first.id,
mangaUrl = mergedManga.first.url, mangaUrl = mergedManga.first.url,
mangaSourceId = MERGED_SOURCE_ID, mangaSourceId = MERGED_SOURCE_ID,
) )
@ -186,7 +161,7 @@ object EXHMigrations {
chapterSortMode = 0, chapterSortMode = 0,
chapterPriority = 0, chapterPriority = 0,
downloadChapters = true, downloadChapters = true,
mergeId = mergedManga.first.id!!, mergeId = mergedManga.first.id,
mergeUrl = mergedManga.first.url, mergeUrl = mergedManga.first.url,
mangaId = load.manga.id!!, mangaId = load.manga.id!!,
mangaUrl = load.manga.url, mangaUrl = load.manga.url,
@ -194,12 +169,9 @@ object EXHMigrations {
) )
} }
} }
db.db.put() runBlocking {
.objects(mangaToUpdate) updateManga.awaitAll(mangaToUpdate)
// Extremely slow without the resolver :/ }
.withPutResolver(MangaUrlPutResolver())
.prepare()
.executeAsBlocking()
db.insertMergedMangas(mergedMangaReferences).executeAsBlocking() db.insertMergedMangas(mergedMangaReferences).executeAsBlocking()
val loadedMangaList = mangaConfigs.map { it.second.children }.flatten().mapNotNull { it.load(db, sourceManager) }.distinct() val loadedMangaList = mangaConfigs.map { it.second.children }.flatten().mapNotNull { it.load(db, sourceManager) }.distinct()
@ -208,7 +180,7 @@ object EXHMigrations {
.withQuery( .withQuery(
Query.builder() Query.builder()
.table(ChapterTable.TABLE) .table(ChapterTable.TABLE)
.where("${ChapterTable.COL_MANGA_ID} IN (${mergedMangas.filter { it.id != null }.joinToString { it.id.toString() }})") .where("${ChapterTable.COL_MANGA_ID} IN (${mergedMangas.joinToString { it.id.toString() }})")
.build(), .build(),
) )
.prepare() .prepare()
@ -289,13 +261,9 @@ object EXHMigrations {
} }
// Delete old mangadex trackers // Delete old mangadex trackers
db.db.lowLevel().delete( runBlocking {
DeleteQuery.builder() handler.await { ehQueries.deleteBySyncId(6) }
.table(TrackTable.TABLE) }
.where("${TrackTable.COL_SYNC_ID} = ?")
.whereArgs(6)
.build(),
)
} }
if (oldVersion under 18) { if (oldVersion under 18) {
val readerTheme = preferences.readerTheme().get() val readerTheme = preferences.readerTheme().get()
@ -407,7 +375,7 @@ object EXHMigrations {
} }
if (oldVersion under 31) { if (oldVersion under 31) {
runBlocking { runBlocking {
database.await(true) { handler.await(true) {
prefs.getStringSet("eh_saved_searches", emptySet())?.forEach { prefs.getStringSet("eh_saved_searches", emptySet())?.forEach {
kotlin.runCatching { kotlin.runCatching {
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':')) val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
@ -421,7 +389,7 @@ object EXHMigrations {
} }
} }
} }
database.await(true) { handler.await(true) {
prefs.getStringSet("latest_tab_sources", emptySet())?.forEach { prefs.getStringSet("latest_tab_sources", emptySet())?.forEach {
feed_saved_searchQueries.insertFeedSavedSearch( feed_saved_searchQueries.insertFeedSavedSearch(
_id = null, _id = null,
@ -557,7 +525,7 @@ object EXHMigrations {
} }
} }
private fun readMangaConfig(manga: SManga): MangaConfig? { private fun readMangaConfig(manga: DomainManga): MangaConfig? {
return MangaConfig.readFromUrl(manga.url) return MangaConfig.readFromUrl(manga.url)
} }
@ -586,17 +554,8 @@ object EXHMigrations {
private data class LoadedMangaSource(val source: Source, val manga: Manga) private data class LoadedMangaSource(val source: Source, val manga: Manga)
private fun updateSourceId(newId: Long, oldId: Long) { private fun updateSourceId(newId: Long, oldId: Long) {
db.lowLevel().executeSQL( runBlocking {
RawQuery.builder() handler.await { ehQueries.migrateSource(newId, oldId) }
.query( }
"""
UPDATE ${MangaTable.TABLE}
SET ${MangaTable.COL_SOURCE} = $newId
WHERE ${MangaTable.COL_SOURCE} = $oldId
""".trimIndent(),
)
.affectsTables(MangaTable.TABLE)
.build(),
)
} }
} }

View File

@ -7,8 +7,7 @@ import eu.kanade.data.chapter.chapterMapper
import eu.kanade.data.manga.mangaMapper import eu.kanade.data.manga.mangaMapper
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.GetMangaByUrlAndSource
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.toMangaInfo import eu.kanade.domain.manga.model.toMangaInfo
@ -25,8 +24,7 @@ import uy.kohesive.injekt.api.get
class GalleryAdder( class GalleryAdder(
private val handler: DatabaseHandler = Injekt.get(), private val handler: DatabaseHandler = Injekt.get(),
private val getMangaByUrlAndSource: GetMangaByUrlAndSource = Injekt.get(), private val getManga: GetManga = Injekt.get(),
private val getMangaById: GetMangaById = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(),
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(), private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
@ -120,7 +118,7 @@ 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
var manga = getMangaByUrlAndSource.await(cleanedMangaUrl, source.id) var manga = getManga.await(cleanedMangaUrl, source.id)
?: handler.awaitOne(true) { ?: handler.awaitOne(true) {
// 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
@ -135,7 +133,7 @@ class GalleryAdder(
// 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()) updateManga.awaitUpdateFromSource(manga, newManga, false, Injekt.get())
manga = getMangaById.await(manga.id)!! manga = getManga.await(manga.id)!!
if (fav) { if (fav) {
updateManga.awaitUpdateFavorite(manga.id, true) updateManga.awaitUpdateFavorite(manga.id, true)

View File

@ -2,13 +2,16 @@ package exh.debug
import android.app.Application import android.app.Application
import androidx.work.WorkManager import androidx.work.WorkManager
import com.pushtorefresh.storio.sqlite.queries.RawQuery import eu.kanade.data.AndroidDatabaseHandler
import eu.kanade.data.DatabaseHandler import eu.kanade.data.DatabaseHandler
import eu.kanade.data.manga.mangaMapper import eu.kanade.domain.manga.interactor.GetAllManga
import eu.kanade.domain.manga.interactor.GetExhFavoriteMangaWithMetadata
import eu.kanade.domain.manga.interactor.GetFavorites import eu.kanade.domain.manga.interactor.GetFavorites
import eu.kanade.domain.manga.interactor.GetFlatMetadataById
import eu.kanade.domain.manga.interactor.GetSearchMetadata
import eu.kanade.domain.manga.interactor.InsertFlatMetadata
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toMangaInfo import eu.kanade.domain.manga.model.toMangaInfo
import eu.kanade.tachiyomi.data.database.DatabaseHelper
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
@ -17,18 +20,10 @@ 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.awaitFlatMetadataForManga
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.nHentaiSourceIds import exh.source.nHentaiSourceIds
import exh.util.cancellable
import exh.util.executeOnIO
import exh.util.jobScheduler import exh.util.jobScheduler
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.UUID import java.util.UUID
@ -36,12 +31,16 @@ import java.util.UUID
@Suppress("unused") @Suppress("unused")
object DebugFunctions { object DebugFunctions {
val app: Application by injectLazy() val app: Application by injectLazy()
val db: DatabaseHelper by injectLazy()
val handler: 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 updateManga: UpdateManga by injectLazy()
val getFavorites: GetFavorites by injectLazy() val getFavorites: GetFavorites by injectLazy()
val getFlatMetadataById: GetFlatMetadataById by injectLazy()
val insertFlatMetadata: InsertFlatMetadata by injectLazy()
val getExhFavoriteMangaWithMetadata: GetExhFavoriteMangaWithMetadata by injectLazy()
val getSearchMetadata: GetSearchMetadata by injectLazy()
val getAllManga: GetAllManga by injectLazy()
fun forceUpgradeMigration() { fun forceUpgradeMigration() {
prefs.ehLastVersionCode().set(1) prefs.ehLastVersionCode().set(1)
@ -55,18 +54,11 @@ object DebugFunctions {
fun resetAgedFlagInEXHManga() { fun resetAgedFlagInEXHManga() {
runBlocking { runBlocking {
val metadataManga = db.getFavoriteMangaWithMetadata().executeOnIO() getExhFavoriteMangaWithMetadata.await().forEach { manga ->
val meta = getFlatMetadataById.await(manga.id)?.raise<EHentaiSearchMetadata>() ?: return@forEach
val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga ->
if (manga.isEhBasedManga()) manga
else null
}.toList()
allManga.forEach { manga ->
val meta = handler.awaitFlatMetadataForManga(manga.id!!)?.raise<EHentaiSearchMetadata>() ?: return@forEach
// remove age flag // remove age flag
meta.aged = false meta.aged = false
handler.awaitInsertFlatMetadata(meta.flatten()) insertFlatMetadata.await(meta)
} }
} }
} }
@ -77,8 +69,7 @@ object DebugFunctions {
fun resetEHGalleriesForUpdater() { fun resetEHGalleriesForUpdater() {
throttleManager.resetThrottle() throttleManager.resetThrottle()
runBlocking { runBlocking {
val allManga = handler val allManga = getExhFavoriteMangaWithMetadata.await()
.awaitList { mangasQueries.getEhMangaWithMetadata(EH_SOURCE_ID, EXH_SOURCE_ID, mangaMapper) }
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)
@ -99,11 +90,8 @@ object DebugFunctions {
fun getEHMangaListWithAgedFlagInfo(): String { fun getEHMangaListWithAgedFlagInfo(): String {
return runBlocking { return runBlocking {
val allManga = handler getExhFavoriteMangaWithMetadata.await().map { manga ->
.awaitList { mangasQueries.getEhMangaWithMetadata(EH_SOURCE_ID, EXH_SOURCE_ID, mangaMapper) } val meta = getFlatMetadataById.await(manga.id)?.raise<EHentaiSearchMetadata>() ?: return@map
allManga.map { manga ->
val meta = handler.awaitFlatMetadataForManga(manga.id)?.raise<EHentaiSearchMetadata>() ?: return@map
"Aged: ${meta.aged}\t Title: ${manga.title}" "Aged: ${meta.aged}\t Title: ${manga.title}"
} }
}.joinToString(",\n") }.joinToString(",\n")
@ -111,10 +99,9 @@ object DebugFunctions {
fun countAgedFlagInEXHManga(): Int { fun countAgedFlagInEXHManga(): Int {
return runBlocking { return runBlocking {
handler getExhFavoriteMangaWithMetadata.await()
.awaitList { mangasQueries.getEhMangaWithMetadata(EH_SOURCE_ID, EXH_SOURCE_ID, mangaMapper) }
.count { manga -> .count { manga ->
val meta = handler.awaitFlatMetadataForManga(manga.id) val meta = getFlatMetadataById.await(manga.id)
?.raise<EHentaiSearchMetadata>() ?.raise<EHentaiSearchMetadata>()
?: return@count false ?: return@count false
meta.aged meta.aged
@ -123,31 +110,30 @@ object DebugFunctions {
} }
fun addAllMangaInDatabaseToLibrary() { fun addAllMangaInDatabaseToLibrary() {
db.inTransaction { (handler as AndroidDatabaseHandler).rawQuery {
db.lowLevel().executeSQL( it.execute(
RawQuery.builder() null,
.query( """
""" UPDATE ${MangaTable.TABLE}
UPDATE ${MangaTable.TABLE} SET ${MangaTable.COL_FAVORITE} = 1
SET ${MangaTable.COL_FAVORITE} = 1 """.trimIndent(),
""".trimIndent(), 0,
)
.affectsTables(MangaTable.TABLE)
.build(),
) )
} }
} }
fun countMangaInDatabaseInLibrary() = runBlocking { getFavorites.await().size } fun countMangaInDatabaseInLibrary() = runBlocking { getFavorites.await().size }
fun countMangaInDatabaseNotInLibrary() = db.getMangas().executeAsBlocking().count { !it.favorite } fun countMangaInDatabaseNotInLibrary() = runBlocking { getAllManga.await() }.count { !it.favorite }
fun countMangaInDatabase() = db.getMangas().executeAsBlocking().size fun countMangaInDatabase() = runBlocking { getAllManga.await() }.size
fun countMetadataInDatabase() = db.getSearchMetadata().executeAsBlocking().size fun countMetadataInDatabase() = runBlocking { getSearchMetadata.await().size }
fun countMangaInLibraryWithMissingMetadata() = db.getMangas().executeAsBlocking().count { fun countMangaInLibraryWithMissingMetadata() = runBlocking {
it.favorite && db.getSearchMetadataForManga(it.id!!).executeAsBlocking() == null runBlocking { getAllManga.await() }.count {
it.favorite && getSearchMetadata.await(it.id) == null
}
} }
fun clearSavedSearches() = runBlocking { handler.await { saved_searchQueries.deleteAll() } } fun clearSavedSearches() = runBlocking { handler.await { saved_searchQueries.deleteAll() } }
@ -214,18 +200,17 @@ object DebugFunctions {
fun cancelAllScheduledJobs() = app.jobScheduler.cancelAll() fun cancelAllScheduledJobs() = app.jobScheduler.cancelAll()
private fun convertSources(from: Long, to: Long) { private fun convertSources(from: Long, to: Long) {
db.lowLevel().executeSQL( (handler as AndroidDatabaseHandler).rawQuery {
RawQuery.builder() it.execute(
.query( null,
""" """
UPDATE ${MangaTable.TABLE} UPDATE ${MangaTable.TABLE}
SET ${MangaTable.COL_SOURCE} = $to SET ${MangaTable.COL_SOURCE} = $to
WHERE ${MangaTable.COL_SOURCE} = $from WHERE ${MangaTable.COL_SOURCE} = $from
""".trimIndent(), """.trimIndent(),
) 0,
.affectsTables(MangaTable.TABLE) )
.build(), }
)
} }
/*fun copyEHentaiSavedSearchesToExhentai() { /*fun copyEHentaiSavedSearchesToExhentai() {
@ -307,34 +292,28 @@ object DebugFunctions {
}*/ }*/
fun fixReaderViewerBackupBug() { fun fixReaderViewerBackupBug() {
db.inTransaction { (handler as AndroidDatabaseHandler).rawQuery {
db.lowLevel().executeSQL( it.execute(
RawQuery.builder() null,
.query( """
""" UPDATE ${MangaTable.TABLE}
UPDATE ${MangaTable.TABLE} SET ${MangaTable.COL_VIEWER} = 0
SET ${MangaTable.COL_VIEWER} = 0 WHERE ${MangaTable.COL_VIEWER} = -1
WHERE ${MangaTable.COL_VIEWER} = -1 """.trimIndent(),
""".trimIndent(), 0,
)
.affectsTables(MangaTable.TABLE)
.build(),
) )
} }
} }
fun resetReaderViewerForAllManga() { fun resetReaderViewerForAllManga() {
db.inTransaction { (handler as AndroidDatabaseHandler).rawQuery {
db.lowLevel().executeSQL( it.execute(
RawQuery.builder() null,
.query( """
""" UPDATE ${MangaTable.TABLE}
UPDATE ${MangaTable.TABLE} SET ${MangaTable.COL_VIEWER} = 0
SET ${MangaTable.COL_VIEWER} = 0 """.trimIndent(),
""".trimIndent(), 0,
)
.affectsTables(MangaTable.TABLE)
.build(),
) )
} }
} }
@ -344,34 +323,28 @@ object DebugFunctions {
.also { it.remove(NHentai.otherId) } .also { it.remove(NHentai.otherId) }
.joinToString(separator = ",") .joinToString(separator = ",")
db.inTransaction { (handler as AndroidDatabaseHandler).rawQuery {
db.lowLevel().executeSQL( it.execute(
RawQuery.builder() null,
.query( """
""" UPDATE ${MangaTable.TABLE}
UPDATE ${MangaTable.TABLE} SET ${MangaTable.COL_SOURCE} = ${NHentai.otherId}
SET ${MangaTable.COL_SOURCE} = ${NHentai.otherId} WHERE ${MangaTable.COL_FAVORITE} = 1 AND ${MangaTable.COL_SOURCE} in ($sources)
WHERE ${MangaTable.COL_FAVORITE} = 1 AND ${MangaTable.COL_SOURCE} in ($sources) """.trimIndent(),
""".trimIndent(), 0,
)
.affectsTables(MangaTable.TABLE)
.build(),
) )
} }
} }
fun resetFilteredScanlatorsForAllManga() { fun resetFilteredScanlatorsForAllManga() {
db.inTransaction { (handler as AndroidDatabaseHandler).rawQuery {
db.lowLevel().executeSQL( it.execute(
RawQuery.builder() null,
.query( """
""" UPDATE ${MangaTable.TABLE}
UPDATE ${MangaTable.TABLE} SET ${MangaTable.COL_FILTERED_SCANLATORS} = NULL
SET ${MangaTable.COL_FILTERED_SCANLATORS} = NULL """.trimIndent(),
""".trimIndent(), 0,
)
.affectsTables(MangaTable.TABLE)
.build(),
) )
} }
} }

View File

@ -14,7 +14,7 @@ import eu.kanade.domain.history.interactor.RemoveHistoryById
import eu.kanade.domain.history.interactor.UpsertHistory import eu.kanade.domain.history.interactor.UpsertHistory
import eu.kanade.domain.history.model.History import eu.kanade.domain.history.model.History
import eu.kanade.domain.history.model.HistoryUpdate import eu.kanade.domain.history.model.HistoryUpdate
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MangaUpdate import eu.kanade.domain.manga.model.MangaUpdate
@ -37,7 +37,7 @@ class EHentaiUpdateHelper(context: Context) {
) )
private val handler: DatabaseHandler by injectLazy() private val handler: DatabaseHandler by injectLazy()
private val getChapterByMangaId: GetChapterByMangaId by injectLazy() private val getChapterByMangaId: GetChapterByMangaId by injectLazy()
private val getMangaById: GetMangaById by injectLazy() private val getManga: GetManga by injectLazy()
private val updateManga: UpdateManga by injectLazy() private val updateManga: UpdateManga by injectLazy()
private val setMangaCategories: SetMangaCategories by injectLazy() private val setMangaCategories: SetMangaCategories by injectLazy()
private val getCategories: GetCategories by injectLazy() private val getCategories: GetCategories by injectLazy()
@ -64,7 +64,7 @@ class EHentaiUpdateHelper(context: Context) {
.mapNotNull { mangaId -> .mapNotNull { mangaId ->
coroutineScope { coroutineScope {
val manga = async(Dispatchers.IO) { val manga = async(Dispatchers.IO) {
getMangaById.await(mangaId) getManga.await(mangaId)
} }
val chapterList = async(Dispatchers.IO) { val chapterList = async(Dispatchers.IO) {
getChapterByMangaId.await(mangaId) getChapterByMangaId.await(mangaId)

View File

@ -21,7 +21,6 @@ import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.toDbManga import eu.kanade.domain.manga.model.toDbManga
import eu.kanade.domain.manga.model.toMangaInfo import eu.kanade.domain.manga.model.toMangaInfo
import eu.kanade.tachiyomi.data.database.DatabaseHelper
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
@ -35,7 +34,7 @@ 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.awaitFlatMetadataForManga 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
@ -54,7 +53,6 @@ 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 handler: DatabaseHandler 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()
@ -228,7 +226,7 @@ class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerPara
// Age dead galleries // Age dead galleries
logger.d("Aged %s - notfound", manga.id) logger.d("Aged %s - notfound", manga.id)
meta.aged = true meta.aged = true
db.insertFlatMetadataAsync(meta.flatten()).await() handler.awaitInsertFlatMetadata(meta.flatten())
} }
throw GalleryNotUpdatedException(false, t) throw GalleryNotUpdatedException(false, t)
} }

View File

@ -8,7 +8,7 @@ import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.category.interactor.GetCategories import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.interactor.SetMangaCategories import eu.kanade.domain.category.interactor.SetMangaCategories
import eu.kanade.domain.category.model.Category import eu.kanade.domain.category.model.Category
import eu.kanade.domain.manga.interactor.GetMangaByUrlAndSource import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -50,7 +50,7 @@ import kotlin.time.Duration.Companion.seconds
class FavoritesSyncHelper(val context: Context) { class FavoritesSyncHelper(val context: Context) {
private val handler: DatabaseHandler by injectLazy() private val handler: DatabaseHandler by injectLazy()
private val getCategories: GetCategories by injectLazy() private val getCategories: GetCategories by injectLazy()
private val getMangaByUrlAndSource: GetMangaByUrlAndSource by injectLazy() private val getManga: GetManga by injectLazy()
private val updateManga: UpdateManga by injectLazy() private val updateManga: UpdateManga by injectLazy()
private val setMangaCategories: SetMangaCategories by injectLazy() private val setMangaCategories: SetMangaCategories by injectLazy()
@ -332,7 +332,7 @@ class FavoritesSyncHelper(val context: Context) {
EXH_SOURCE_ID, EXH_SOURCE_ID,
EH_SOURCE_ID, EH_SOURCE_ID,
).forEach { ).forEach {
val manga = getMangaByUrlAndSource.await(url, it) val manga = getManga.await(url, it)
if (manga?.favorite == true) { if (manga?.favorite == true) {
updateManga.awaitUpdateFavorite(manga.id, false) updateManga.awaitUpdateFavorite(manga.id, false)

View File

@ -1,9 +1,10 @@
package exh.favorites package exh.favorites
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.favoriteEntryMapper
import eu.kanade.domain.category.interactor.GetCategories import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.manga.interactor.DeleteFavoriteEntries
import eu.kanade.domain.manga.interactor.GetFavoriteEntries
import eu.kanade.domain.manga.interactor.GetFavorites import eu.kanade.domain.manga.interactor.GetFavorites
import eu.kanade.domain.manga.interactor.InsertFavoriteEntries
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.data.database.models.toDomainManga import eu.kanade.tachiyomi.data.database.models.toDomainManga
import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.source.online.all.EHentai
@ -19,9 +20,11 @@ import kotlinx.coroutines.flow.toList
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class LocalFavoritesStorage { class LocalFavoritesStorage {
private val handler: DatabaseHandler by injectLazy()
private val getFavorites: GetFavorites by injectLazy() private val getFavorites: GetFavorites by injectLazy()
private val getCategories: GetCategories by injectLazy() private val getCategories: GetCategories by injectLazy()
private val deleteFavoriteEntries: DeleteFavoriteEntries by injectLazy()
private val getFavoriteEntries: GetFavoriteEntries by injectLazy()
private val insertFavoriteEntries: InsertFavoriteEntries by injectLazy()
suspend fun getChangedDbEntries() = getFavorites.await() suspend fun getChangedDbEntries() = getFavorites.await()
.asFlow() .asFlow()
@ -48,30 +51,20 @@ class LocalFavoritesStorage {
.parseToFavoriteEntries() .parseToFavoriteEntries()
// Delete old snapshot // Delete old snapshot
handler.await { eh_favoritesQueries.deleteAll() } deleteFavoriteEntries.await()
// Insert new snapshots // Insert new snapshots
handler.await(true) { insertFavoriteEntries.await(dbMangas.toList())
dbMangas.toList().forEach {
eh_favoritesQueries.insertEhFavorites(
it.id,
it.title,
it.gid,
it.token,
it.category.toLong(),
)
}
}
} }
suspend fun clearSnapshots() { suspend fun clearSnapshots() {
handler.await { eh_favoritesQueries.deleteAll() } deleteFavoriteEntries.await()
} }
private suspend fun Flow<FavoriteEntry>.getChangedEntries(): ChangeSet { private suspend fun Flow<FavoriteEntry>.getChangedEntries(): ChangeSet {
val terminated = toList() val terminated = toList()
val databaseEntries = handler.awaitList { eh_favoritesQueries.selectAll(favoriteEntryMapper) } val databaseEntries = getFavoriteEntries.await()
val added = terminated.filter { val added = terminated.filter {
queryListForEntry(databaseEntries, it) == null queryListForEntry(databaseEntries, it) == null

View File

@ -1,65 +0,0 @@
package exh.favorites.sql.mappers
import android.database.Cursor
import androidx.core.content.contentValuesOf
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import exh.favorites.sql.models.FavoriteEntry
import exh.favorites.sql.tables.FavoriteEntryTable.COL_CATEGORY
import exh.favorites.sql.tables.FavoriteEntryTable.COL_GID
import exh.favorites.sql.tables.FavoriteEntryTable.COL_ID
import exh.favorites.sql.tables.FavoriteEntryTable.COL_TITLE
import exh.favorites.sql.tables.FavoriteEntryTable.COL_TOKEN
import exh.favorites.sql.tables.FavoriteEntryTable.TABLE
class FavoriteEntryTypeMapping : SQLiteTypeMapping<FavoriteEntry>(
FavoriteEntryPutResolver(),
FavoriteEntryGetResolver(),
FavoriteEntryDeleteResolver(),
)
class FavoriteEntryPutResolver : DefaultPutResolver<FavoriteEntry>() {
override fun mapToInsertQuery(obj: FavoriteEntry) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: FavoriteEntry) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
override fun mapToContentValues(obj: FavoriteEntry) = contentValuesOf(
COL_ID to obj.id,
COL_TITLE to obj.title,
COL_GID to obj.gid,
COL_TOKEN to obj.token,
COL_CATEGORY to obj.category,
)
}
class FavoriteEntryGetResolver : DefaultGetResolver<FavoriteEntry>() {
override fun mapFromCursor(cursor: Cursor): FavoriteEntry = FavoriteEntry(
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID)),
title = cursor.getString(cursor.getColumnIndexOrThrow(COL_TITLE)),
gid = cursor.getString(cursor.getColumnIndexOrThrow(COL_GID)),
token = cursor.getString(cursor.getColumnIndexOrThrow(COL_TOKEN)),
category = cursor.getInt(cursor.getColumnIndexOrThrow(COL_CATEGORY)),
)
}
class FavoriteEntryDeleteResolver : DefaultDeleteResolver<FavoriteEntry>() {
override fun mapToDeleteQuery(obj: FavoriteEntry) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
}

View File

@ -1,30 +0,0 @@
package exh.favorites.sql.queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider
import exh.favorites.sql.models.FavoriteEntry
import exh.favorites.sql.tables.FavoriteEntryTable
interface FavoriteEntryQueries : DbProvider {
fun getFavoriteEntries() = db.get()
.listOfObjects(FavoriteEntry::class.java)
.withQuery(
Query.builder()
.table(FavoriteEntryTable.TABLE)
.build(),
)
.prepare()
fun insertFavoriteEntries(favoriteEntries: List<FavoriteEntry>) = db.put()
.objects(favoriteEntries)
.prepare()
fun deleteAllFavoriteEntries() = db.delete()
.byQuery(
DeleteQuery.builder()
.table(FavoriteEntryTable.TABLE)
.build(),
)
.prepare()
}

View File

@ -1,16 +0,0 @@
package exh.favorites.sql.tables
object FavoriteEntryTable {
const val TABLE = "eh_favorites"
const val COL_ID = "_id"
const val COL_TITLE = "title"
const val COL_GID = "gid"
const val COL_TOKEN = "token"
const val COL_CATEGORY = "category"
}

View File

@ -1,7 +1,8 @@
package exh.md.handlers package exh.md.handlers
import eu.kanade.data.DatabaseHandler import eu.kanade.domain.manga.interactor.GetFlatMetadataById
import eu.kanade.domain.manga.interactor.GetMangaByUrlAndSource import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.InsertFlatMetadata
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
@ -13,8 +14,6 @@ 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.awaitFlatMetadataForManga
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
@ -26,8 +25,9 @@ import java.util.Locale
class ApiMangaParser( class ApiMangaParser(
private val lang: String, private val lang: String,
) { ) {
private val handler: DatabaseHandler by injectLazy() private val getManga: GetManga by injectLazy()
private val getMangaByUrlAndSource: GetMangaByUrlAndSource by injectLazy() private val insertFlatMetadata: InsertFlatMetadata by injectLazy()
private val getFlatMetadataById: GetFlatMetadataById by injectLazy()
val metaClass = MangaDexSearchMetadata::class val metaClass = MangaDexSearchMetadata::class
@ -46,16 +46,16 @@ class ApiMangaParser(
simpleChapters: List<String>, simpleChapters: List<String>,
statistics: StatisticsMangaDto?, statistics: StatisticsMangaDto?,
): MangaInfo { ): MangaInfo {
val mangaId = getMangaByUrlAndSource.await(manga.key, sourceId)?.id val mangaId = getManga.await(manga.key, sourceId)?.id
val metadata = if (mangaId != null) { val metadata = if (mangaId != null) {
val flatMetadata = handler.awaitFlatMetadataForManga(mangaId) val flatMetadata = getFlatMetadataById.await(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
handler.awaitInsertFlatMetadata(metadata.flatten()) insertFlatMetadata.await(metadata.flatten())
} }
return metadata.createMangaInfo(manga) return metadata.createMangaInfo(manga)

View File

@ -1,6 +1,6 @@
package exh.md.similar package exh.md.similar
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.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
@ -17,14 +17,14 @@ import uy.kohesive.injekt.api.get
class MangaDexSimilarPresenter( class MangaDexSimilarPresenter(
val mangaId: Long, val mangaId: Long,
sourceId: Long, sourceId: Long,
private val getMangaById: GetMangaById = Injekt.get(), private val getManga: GetManga = Injekt.get(),
) : BrowseSourcePresenter(sourceId) { ) : BrowseSourcePresenter(sourceId) {
var manga: Manga? = null var manga: Manga? = null
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 = runBlocking { getMangaById.await(mangaId) } this.manga = runBlocking { getManga.await(mangaId) }
return MangaDexSimilarPager(manga!!, sourceAsMangaDex) return MangaDexSimilarPager(manga!!, sourceAsMangaDex)
} }
} }

View File

@ -4,15 +4,9 @@ import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DbProvider import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.inTransaction
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.queries.getAllMergedMangaQuery
import eu.kanade.tachiyomi.data.database.queries.getMergedChaptersQuery
import eu.kanade.tachiyomi.data.database.queries.getMergedMangaForDownloadingQuery import eu.kanade.tachiyomi.data.database.queries.getMergedMangaForDownloadingQuery
import eu.kanade.tachiyomi.data.database.queries.getMergedMangaFromUrlQuery
import eu.kanade.tachiyomi.data.database.queries.getMergedMangaQuery import eu.kanade.tachiyomi.data.database.queries.getMergedMangaQuery
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import exh.merged.sql.resolvers.MergeMangaSettingsPutResolver import exh.merged.sql.resolvers.MergeMangaSettingsPutResolver
import exh.merged.sql.resolvers.MergedMangaIdPutResolver import exh.merged.sql.resolvers.MergedMangaIdPutResolver
@ -31,17 +25,6 @@ interface MergedQueries : DbProvider {
) )
.prepare() .prepare()
fun getMergedMangaReferences(mergedMangaUrl: String) = db.get()
.listOfObjects(MergedMangaReference::class.java)
.withQuery(
Query.builder()
.table(MergedTable.TABLE)
.where("${MergedTable.COL_MERGE_URL} = ?")
.whereArgs(mergedMangaUrl)
.build(),
)
.prepare()
fun deleteMangaForMergedManga(mergedMangaId: Long) = db.delete() fun deleteMangaForMergedManga(mergedMangaId: Long) = db.delete()
.byQuery( .byQuery(
DeleteQuery.builder() DeleteQuery.builder()
@ -72,56 +55,6 @@ interface MergedQueries : DbProvider {
) )
.prepare() .prepare()
fun getMergedMangas(mergedMangaUrl: String) = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
RawQuery.builder()
.query(getMergedMangaFromUrlQuery())
.args(mergedMangaUrl)
.build(),
)
.prepare()
fun getMergedMangas() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
RawQuery.builder()
.query(getAllMergedMangaQuery())
.build(),
)
.prepare()
fun deleteMangaForMergedManga(mergedMangaUrl: String) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(MergedTable.TABLE)
.where("${MergedTable.COL_MERGE_URL} = ?")
.whereArgs(mergedMangaUrl)
.build(),
)
.prepare()
fun getMergedMangaReferences() = db.get()
.listOfObjects(MergedMangaReference::class.java)
.withQuery(
Query.builder()
.table(MergedTable.TABLE)
.orderBy(MergedTable.COL_ID)
.build(),
)
.prepare()
fun getChaptersByMergedMangaId(mergedMangaId: Long) = db.get()
.listOfObjects(Chapter::class.java)
.withQuery(
RawQuery.builder()
.query(getMergedChaptersQuery())
.args(mergedMangaId)
.observesTables(ChapterTable.TABLE, MergedTable.TABLE)
.build(),
)
.prepare()
fun insertMergedManga(mergedManga: MergedMangaReference) = db.put().`object`(mergedManga).prepare() fun insertMergedManga(mergedManga: MergedMangaReference) = db.put().`object`(mergedManga).prepare()
fun insertNewMergedMangaId(mergedManga: MergedMangaReference) = db.put().`object`(mergedManga).withPutResolver(MergedMangaIdPutResolver()).prepare() fun insertNewMergedMangaId(mergedManga: MergedMangaReference) = db.put().`object`(mergedManga).withPutResolver(MergedMangaIdPutResolver()).prepare()
@ -133,20 +66,4 @@ interface MergedQueries : DbProvider {
fun updateMergeMangaSettings(mergeManga: MergedMangaReference) = db.put().`object`(mergeManga).withPutResolver(MergeMangaSettingsPutResolver()).prepare() fun updateMergeMangaSettings(mergeManga: MergedMangaReference) = db.put().`object`(mergeManga).withPutResolver(MergeMangaSettingsPutResolver()).prepare()
fun deleteMergedManga(mergedManga: MergedMangaReference) = db.delete().`object`(mergedManga).prepare() fun deleteMergedManga(mergedManga: MergedMangaReference) = db.delete().`object`(mergedManga).prepare()
fun deleteAllMergedManga() = db.delete().byQuery(
DeleteQuery.builder()
.table(MergedTable.TABLE)
.build(),
)
.prepare()
fun setMangasForMergedManga(mergedMangaId: Long, mergedMangas: List<MergedMangaReference>) {
db.inTransaction {
deleteMangaForMergedManga(mergedMangaId).executeAsBlocking()
mergedMangas.chunked(100) { chunk ->
insertMergedMangas(chunk).executeAsBlocking()
}
}
}
} }

View File

@ -1,23 +1,15 @@
package exh.metadata.metadata.base package exh.metadata.metadata.base
import com.pushtorefresh.storio.operations.PreparedOperation
import eu.kanade.data.AndroidDatabaseHandler
import eu.kanade.data.DatabaseHandler import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.searchMetadataMapper import eu.kanade.data.exh.searchMetadataMapper
import eu.kanade.data.exh.searchTagMapper import eu.kanade.data.exh.searchTagMapper
import eu.kanade.data.exh.searchTitleMapper import eu.kanade.data.exh.searchTitleMapper
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import exh.metadata.sql.models.SearchMetadata import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.models.SearchTag import exh.metadata.sql.models.SearchTag
import exh.metadata.sql.models.SearchTitle import exh.metadata.sql.models.SearchTitle
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer import kotlinx.serialization.serializer
import rx.Completable
import rx.Single
import kotlin.reflect.KClass import kotlin.reflect.KClass
@Serializable @Serializable
@ -36,17 +28,7 @@ data class FlatMetadata(
} }
} }
fun DatabaseHandler.getFlatMetadataForManga(mangaId: Long): FlatMetadata? { @Deprecated("Replace with GetFlatMetadataById")
this as AndroidDatabaseHandler
val meta = db.search_metadataQueries.selectByMangaId(mangaId, searchMetadataMapper).executeAsOneOrNull()
return if (meta != null) {
val tags = db.search_tagsQueries.selectByMangaId(mangaId, searchTagMapper).executeAsList()
val titles = db.search_titlesQueries.selectByMangaId(mangaId, searchTitleMapper).executeAsList()
FlatMetadata(meta, tags, titles)
} else null
}
suspend fun DatabaseHandler.awaitFlatMetadataForManga(mangaId: Long): FlatMetadata? { suspend fun DatabaseHandler.awaitFlatMetadataForManga(mangaId: Long): FlatMetadata? {
return await { return await {
val meta = search_metadataQueries.selectByMangaId(mangaId, searchMetadataMapper).executeAsOneOrNull() val meta = search_metadataQueries.selectByMangaId(mangaId, searchMetadataMapper).executeAsOneOrNull()
@ -59,86 +41,7 @@ suspend fun DatabaseHandler.awaitFlatMetadataForManga(mangaId: Long): FlatMetada
} }
} }
fun DatabaseHelper.getFlatMetadataForManga(mangaId: Long): PreparedOperation<FlatMetadata?> { @Deprecated("Replace with InsertFlatMetadata")
// We have to use fromCallable because StorIO messes up the thread scheduling if we use their rx functions
val single = Single.fromCallable {
val meta = getSearchMetadataForManga(mangaId).executeAsBlocking()
if (meta != null) {
val tags = getSearchTagsForManga(mangaId).executeAsBlocking()
val titles = getSearchTitlesForManga(mangaId).executeAsBlocking()
FlatMetadata(meta, tags, titles)
} else null
}
return preparedOperationFromSingle(single)
}
private fun <T> preparedOperationFromSingle(single: Single<T>): PreparedOperation<T> {
return object : PreparedOperation<T> {
/**
* Creates [rx.Observable] that emits result of Operation.
*
*
* Observable may be "Hot" or "Cold", please read documentation of the concrete implementation.
*
* @return observable result of operation with only one [rx.Observer.onNext] call.
*/
override fun createObservable() = single.toObservable()
/**
* Executes operation synchronously in current thread.
*
*
* Notice: Blocking I/O operation should not be executed on the Main Thread,
* it can cause ANR (Activity Not Responding dialog), block the UI and drop animations frames.
* So please, execute blocking I/O operation only from background thread.
* See [androidx.annotation.WorkerThread].
*
* @return nullable result of operation.
*/
override fun executeAsBlocking() = single.toBlocking().value()
/**
* Creates [rx.Observable] that emits result of Operation.
*
*
* Observable may be "Hot" (usually "Warm") or "Cold", please read documentation of the concrete implementation.
*
* @return observable result of operation with only one [rx.Observer.onNext] call.
*/
override fun asRxObservable() = single.toObservable()
/**
* Creates [rx.Single] that emits result of Operation lazily when somebody subscribes to it.
*
*
*
* @return single result of operation.
*/
override fun asRxSingle() = single
}
}
fun DatabaseHandler.insertFlatMetadata(flatMetadata: FlatMetadata) {
require(flatMetadata.metadata.mangaId != -1L)
this as AndroidDatabaseHandler // todo remove when legacy backup is dead
db.transaction {
flatMetadata.metadata.let {
db.search_metadataQueries.upsert(it.mangaId, it.uploader, it.extra, it.indexedExtra, it.extraVersion)
}
db.search_tagsQueries.deleteByManga(flatMetadata.metadata.mangaId)
flatMetadata.tags.forEach {
db.search_tagsQueries.insert(it.mangaId, it.namespace, it.name, it.type)
}
db.search_titlesQueries.deleteByManga(flatMetadata.metadata.mangaId)
flatMetadata.titles.forEach {
db.search_titlesQueries.insert(it.mangaId, it.title, it.type)
}
}
}
suspend fun DatabaseHandler.awaitInsertFlatMetadata(flatMetadata: FlatMetadata) { suspend fun DatabaseHandler.awaitInsertFlatMetadata(flatMetadata: FlatMetadata) {
require(flatMetadata.metadata.mangaId != -1L) require(flatMetadata.metadata.mangaId != -1L)
@ -156,29 +59,3 @@ suspend fun DatabaseHandler.awaitInsertFlatMetadata(flatMetadata: FlatMetadata)
} }
} }
} }
fun DatabaseHelper.insertFlatMetadata(flatMetadata: FlatMetadata) {
require(flatMetadata.metadata.mangaId != -1L)
inTransaction {
insertSearchMetadata(flatMetadata.metadata).executeAsBlocking()
setSearchTagsForManga(flatMetadata.metadata.mangaId, flatMetadata.tags)
setSearchTitlesForManga(flatMetadata.metadata.mangaId, flatMetadata.titles)
}
}
fun DatabaseHelper.insertFlatMetadataCompletable(flatMetadata: FlatMetadata): Completable = Completable.fromCallable {
insertFlatMetadata(flatMetadata)
}
suspend fun DatabaseHelper.insertFlatMetadataAsync(flatMetadata: FlatMetadata): Deferred<Unit> = coroutineScope {
async {
require(flatMetadata.metadata.mangaId != -1L)
inTransaction {
insertSearchMetadata(flatMetadata.metadata).executeAsBlocking()
setSearchTagsForManga(flatMetadata.metadata.mangaId, flatMetadata.tags)
setSearchTitlesForManga(flatMetadata.metadata.mangaId, flatMetadata.titles)
}
}
}

View File

@ -1,66 +0,0 @@
package exh.metadata.sql.mappers
import android.database.Cursor
import androidx.core.content.contentValuesOf
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.tables.SearchMetadataTable.COL_EXTRA
import exh.metadata.sql.tables.SearchMetadataTable.COL_EXTRA_VERSION
import exh.metadata.sql.tables.SearchMetadataTable.COL_INDEXED_EXTRA
import exh.metadata.sql.tables.SearchMetadataTable.COL_MANGA_ID
import exh.metadata.sql.tables.SearchMetadataTable.COL_UPLOADER
import exh.metadata.sql.tables.SearchMetadataTable.TABLE
class SearchMetadataTypeMapping : SQLiteTypeMapping<SearchMetadata>(
SearchMetadataPutResolver(),
SearchMetadataGetResolver(),
SearchMetadataDeleteResolver(),
)
class SearchMetadataPutResolver : DefaultPutResolver<SearchMetadata>() {
override fun mapToInsertQuery(obj: SearchMetadata) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: SearchMetadata) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_MANGA_ID = ?")
.whereArgs(obj.mangaId)
.build()
override fun mapToContentValues(obj: SearchMetadata) = contentValuesOf(
COL_MANGA_ID to obj.mangaId,
COL_UPLOADER to obj.uploader,
COL_EXTRA to obj.extra,
COL_INDEXED_EXTRA to obj.indexedExtra,
COL_EXTRA_VERSION to obj.extraVersion,
)
}
class SearchMetadataGetResolver : DefaultGetResolver<SearchMetadata>() {
override fun mapFromCursor(cursor: Cursor): SearchMetadata =
SearchMetadata(
mangaId = cursor.getLong(cursor.getColumnIndexOrThrow(COL_MANGA_ID)),
uploader = cursor.getString(cursor.getColumnIndexOrThrow(COL_UPLOADER)),
extra = cursor.getString(cursor.getColumnIndexOrThrow(COL_EXTRA)),
indexedExtra = cursor.getString(cursor.getColumnIndexOrThrow(COL_INDEXED_EXTRA)),
extraVersion = cursor.getInt(cursor.getColumnIndexOrThrow(COL_EXTRA_VERSION)),
)
}
class SearchMetadataDeleteResolver : DefaultDeleteResolver<SearchMetadata>() {
override fun mapToDeleteQuery(obj: SearchMetadata) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_MANGA_ID = ?")
.whereArgs(obj.mangaId)
.build()
}

View File

@ -1,65 +0,0 @@
package exh.metadata.sql.mappers
import android.database.Cursor
import androidx.core.content.contentValuesOf
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import exh.metadata.sql.models.SearchTag
import exh.metadata.sql.tables.SearchTagTable.COL_ID
import exh.metadata.sql.tables.SearchTagTable.COL_MANGA_ID
import exh.metadata.sql.tables.SearchTagTable.COL_NAME
import exh.metadata.sql.tables.SearchTagTable.COL_NAMESPACE
import exh.metadata.sql.tables.SearchTagTable.COL_TYPE
import exh.metadata.sql.tables.SearchTagTable.TABLE
class SearchTagTypeMapping : SQLiteTypeMapping<SearchTag>(
SearchTagPutResolver(),
SearchTagGetResolver(),
SearchTagDeleteResolver(),
)
class SearchTagPutResolver : DefaultPutResolver<SearchTag>() {
override fun mapToInsertQuery(obj: SearchTag) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: SearchTag) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
override fun mapToContentValues(obj: SearchTag) = contentValuesOf(
COL_ID to obj.id,
COL_MANGA_ID to obj.mangaId,
COL_NAMESPACE to obj.namespace,
COL_NAME to obj.name,
COL_TYPE to obj.type,
)
}
class SearchTagGetResolver : DefaultGetResolver<SearchTag>() {
override fun mapFromCursor(cursor: Cursor): SearchTag = SearchTag(
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID)),
mangaId = cursor.getLong(cursor.getColumnIndexOrThrow(COL_MANGA_ID)),
namespace = cursor.getString(cursor.getColumnIndexOrThrow(COL_NAMESPACE)),
name = cursor.getString(cursor.getColumnIndexOrThrow(COL_NAME)),
type = cursor.getInt(cursor.getColumnIndexOrThrow(COL_TYPE)),
)
}
class SearchTagDeleteResolver : DefaultDeleteResolver<SearchTag>() {
override fun mapToDeleteQuery(obj: SearchTag) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
}

View File

@ -1,62 +0,0 @@
package exh.metadata.sql.mappers
import android.database.Cursor
import androidx.core.content.contentValuesOf
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import exh.metadata.sql.models.SearchTitle
import exh.metadata.sql.tables.SearchTitleTable.COL_ID
import exh.metadata.sql.tables.SearchTitleTable.COL_MANGA_ID
import exh.metadata.sql.tables.SearchTitleTable.COL_TITLE
import exh.metadata.sql.tables.SearchTitleTable.COL_TYPE
import exh.metadata.sql.tables.SearchTitleTable.TABLE
class SearchTitleTypeMapping : SQLiteTypeMapping<SearchTitle>(
SearchTitlePutResolver(),
SearchTitleGetResolver(),
SearchTitleDeleteResolver(),
)
class SearchTitlePutResolver : DefaultPutResolver<SearchTitle>() {
override fun mapToInsertQuery(obj: SearchTitle) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: SearchTitle) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
override fun mapToContentValues(obj: SearchTitle) = contentValuesOf(
COL_ID to obj.id,
COL_MANGA_ID to obj.mangaId,
COL_TITLE to obj.title,
COL_TYPE to obj.type,
)
}
class SearchTitleGetResolver : DefaultGetResolver<SearchTitle>() {
override fun mapFromCursor(cursor: Cursor): SearchTitle = SearchTitle(
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID)),
mangaId = cursor.getLong(cursor.getColumnIndexOrThrow(COL_MANGA_ID)),
title = cursor.getString(cursor.getColumnIndexOrThrow(COL_TITLE)),
type = cursor.getInt(cursor.getColumnIndexOrThrow(COL_TYPE)),
)
}
class SearchTitleDeleteResolver : DefaultDeleteResolver<SearchTitle>() {
override fun mapToDeleteQuery(obj: SearchTitle) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
}

View File

@ -1,52 +0,0 @@
package exh.metadata.sql.queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider
import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.tables.SearchMetadataTable
interface SearchMetadataQueries : DbProvider {
fun getSearchMetadataForManga(mangaId: Long) = db.get()
.`object`(SearchMetadata::class.java)
.withQuery(
Query.builder()
.table(SearchMetadataTable.TABLE)
.where("${SearchMetadataTable.COL_MANGA_ID} = ?")
.whereArgs(mangaId)
.build(),
)
.prepare()
fun getSearchMetadata() = db.get()
.listOfObjects(SearchMetadata::class.java)
.withQuery(
Query.builder()
.table(SearchMetadataTable.TABLE)
.build(),
)
.prepare()
fun getSearchMetadataByIndexedExtra(extra: String) = db.get()
.listOfObjects(SearchMetadata::class.java)
.withQuery(
Query.builder()
.table(SearchMetadataTable.TABLE)
.where("${SearchMetadataTable.COL_INDEXED_EXTRA} = ?")
.whereArgs(extra)
.build(),
)
.prepare()
fun insertSearchMetadata(metadata: SearchMetadata) = db.put().`object`(metadata).prepare()
fun deleteSearchMetadata(metadata: SearchMetadata) = db.delete().`object`(metadata).prepare()
fun deleteAllSearchMetadata() = db.delete().byQuery(
DeleteQuery.builder()
.table(SearchMetadataTable.TABLE)
.build(),
)
.prepare()
}

View File

@ -1,53 +0,0 @@
package exh.metadata.sql.queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.inTransaction
import exh.metadata.sql.models.SearchTag
import exh.metadata.sql.tables.SearchTagTable
interface SearchTagQueries : DbProvider {
fun getSearchTagsForManga(mangaId: Long) = db.get()
.listOfObjects(SearchTag::class.java)
.withQuery(
Query.builder()
.table(SearchTagTable.TABLE)
.where("${SearchTagTable.COL_MANGA_ID} = ?")
.whereArgs(mangaId)
.build(),
)
.prepare()
fun deleteSearchTagsForManga(mangaId: Long) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(SearchTagTable.TABLE)
.where("${SearchTagTable.COL_MANGA_ID} = ?")
.whereArgs(mangaId)
.build(),
)
.prepare()
fun insertSearchTag(searchTag: SearchTag) = db.put().`object`(searchTag).prepare()
fun insertSearchTags(searchTags: List<SearchTag>) = db.put().objects(searchTags).prepare()
fun deleteSearchTag(searchTag: SearchTag) = db.delete().`object`(searchTag).prepare()
fun deleteAllSearchTags() = db.delete().byQuery(
DeleteQuery.builder()
.table(SearchTagTable.TABLE)
.build(),
)
.prepare()
fun setSearchTagsForManga(mangaId: Long, tags: List<SearchTag>) {
db.inTransaction {
deleteSearchTagsForManga(mangaId).executeAsBlocking()
tags.chunked(100) { chunk ->
insertSearchTags(chunk).executeAsBlocking()
}
}
}
}

View File

@ -1,53 +0,0 @@
package exh.metadata.sql.queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.inTransaction
import exh.metadata.sql.models.SearchTitle
import exh.metadata.sql.tables.SearchTitleTable
interface SearchTitleQueries : DbProvider {
fun getSearchTitlesForManga(mangaId: Long) = db.get()
.listOfObjects(SearchTitle::class.java)
.withQuery(
Query.builder()
.table(SearchTitleTable.TABLE)
.where("${SearchTitleTable.COL_MANGA_ID} = ?")
.whereArgs(mangaId)
.build(),
)
.prepare()
fun deleteSearchTitlesForManga(mangaId: Long) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(SearchTitleTable.TABLE)
.where("${SearchTitleTable.COL_MANGA_ID} = ?")
.whereArgs(mangaId)
.build(),
)
.prepare()
fun insertSearchTitle(searchTitle: SearchTitle) = db.put().`object`(searchTitle).prepare()
fun insertSearchTitles(searchTitles: List<SearchTitle>) = db.put().objects(searchTitles).prepare()
fun deleteSearchTitle(searchTitle: SearchTitle) = db.delete().`object`(searchTitle).prepare()
fun deleteAllSearchTitle() = db.delete().byQuery(
DeleteQuery.builder()
.table(SearchTitleTable.TABLE)
.build(),
)
.prepare()
fun setSearchTitlesForManga(mangaId: Long, titles: List<SearchTitle>) {
db.inTransaction {
deleteSearchTitlesForManga(mangaId).executeAsBlocking()
titles.chunked(100) { chunk ->
insertSearchTitles(chunk).executeAsBlocking()
}
}
}
}

View File

@ -1,15 +0,0 @@
package exh.metadata.sql.tables
object SearchMetadataTable {
const val TABLE = "search_metadata"
const val COL_MANGA_ID = "manga_id"
const val COL_UPLOADER = "uploader"
const val COL_EXTRA = "extra"
const val COL_INDEXED_EXTRA = "indexed_extra"
const val COL_EXTRA_VERSION = "extra_version"
}

View File

@ -1,15 +0,0 @@
package exh.metadata.sql.tables
object SearchTagTable {
const val TABLE = "search_tags"
const val COL_ID = "_id"
const val COL_MANGA_ID = "manga_id"
const val COL_NAMESPACE = "namespace"
const val COL_NAME = "name"
const val COL_TYPE = "type"
}

View File

@ -1,13 +0,0 @@
package exh.metadata.sql.tables
object SearchTitleTable {
const val TABLE = "search_titles"
const val COL_ID = "_id"
const val COL_MANGA_ID = "manga_id"
const val COL_TITLE = "title"
const val COL_TYPE = "type"
}

View File

@ -1,6 +1,6 @@
package exh.recs package exh.recs
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.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
@ -15,13 +15,13 @@ import uy.kohesive.injekt.api.get
class RecommendsPresenter( class RecommendsPresenter(
val mangaId: Long, val mangaId: Long,
sourceId: Long, sourceId: Long,
private val getMangaById: GetMangaById = Injekt.get(), private val getManga: GetManga = Injekt.get(),
) : BrowseSourcePresenter(sourceId) { ) : BrowseSourcePresenter(sourceId) {
var manga: Manga? = null var manga: Manga? = null
override fun createPager(query: String, filters: FilterList): Pager { override fun createPager(query: String, filters: FilterList): Pager {
this.manga = runBlocking { getMangaById.await(mangaId) } this.manga = runBlocking { getManga.await(mangaId) }
return RecommendsPager(manga!!) return RecommendsPager(manga!!)
} }
} }

View File

@ -1,8 +1,5 @@
package exh.search package exh.search
import exh.metadata.sql.tables.SearchMetadataTable
import exh.metadata.sql.tables.SearchTagTable
import exh.metadata.sql.tables.SearchTitleTable
import java.util.Locale import java.util.Locale
class SearchEngine { class SearchEngine {
@ -23,16 +20,16 @@ class SearchEngine {
val params = mutableListOf<String>() val params = mutableListOf<String>()
it.joinToString(separator = " OR ", prefix = "(", postfix = ")") { q -> it.joinToString(separator = " OR ", prefix = "(", postfix = ")") { q ->
params += q params += q
"${SearchTagTable.TABLE}.${SearchTagTable.COL_NAME} LIKE ?" "search_tags.name LIKE ?"
} to params } to params
} }
return when { return when {
namespace != null -> { namespace != null -> {
var query = var query =
""" """
(SELECT ${SearchTagTable.COL_MANGA_ID} AS $COL_MANGA_ID FROM ${SearchTagTable.TABLE} (SELECT ${"manga_id"} AS $COL_MANGA_ID FROM ${"search_tags"}
WHERE ${SearchTagTable.COL_NAMESPACE} IS NOT NULL WHERE ${"namespace"} IS NOT NULL
AND ${SearchTagTable.COL_NAMESPACE} LIKE ? AND ${"namespace"} LIKE ?
""".trimIndent() """.trimIndent()
val params = mutableListOf(escapeLike(namespace)) val params = mutableListOf(escapeLike(namespace))
if (componentTagQuery != null) { if (componentTagQuery != null) {
@ -46,14 +43,14 @@ class SearchEngine {
// Match title + tags // Match title + tags
val tagQuery = val tagQuery =
""" """
SELECT ${SearchTagTable.COL_MANGA_ID} AS $COL_MANGA_ID FROM ${SearchTagTable.TABLE} SELECT ${"manga_id"} AS $COL_MANGA_ID FROM ${"search_tags"}
WHERE ${componentTagQuery!!.first} WHERE ${componentTagQuery!!.first}
""".trimIndent() to componentTagQuery.second """.trimIndent() to componentTagQuery.second
val titleQuery = val titleQuery =
""" """
SELECT ${SearchTitleTable.COL_MANGA_ID} AS $COL_MANGA_ID FROM ${SearchTitleTable.TABLE} SELECT ${"manga_id"} AS $COL_MANGA_ID FROM ${"search_titles"}
WHERE ${SearchTitleTable.COL_TITLE} LIKE ? WHERE ${"title"} LIKE ?
""".trimIndent() to listOf(component.asLenientTitleQuery()) """.trimIndent() to listOf(component.asLenientTitleQuery())
"(${tagQuery.first} UNION ${titleQuery.first})".trimIndent() to "(${tagQuery.first} UNION ${titleQuery.first})".trimIndent() to
@ -75,7 +72,7 @@ class SearchEngine {
textToSubQueries(null, component) textToSubQueries(null, component)
} else if (component is Namespace) { } else if (component is Namespace) {
if (component.namespace == "uploader") { if (component.namespace == "uploader") {
wheres += "meta.${SearchMetadataTable.COL_UPLOADER} LIKE ?" wheres += "meta.uploader LIKE ?"
whereParams += component.tag!!.rawTextEscapedForLike() whereParams += component.tag!!.rawTextEscapedForLike()
null null
} else { } else {
@ -97,15 +94,15 @@ class SearchEngine {
val completeParams = mutableListOf<String>() val completeParams = mutableListOf<String>()
var baseQuery = var baseQuery =
""" """
SELECT ${SearchMetadataTable.COL_MANGA_ID} SELECT ${"manga_id"}
FROM ${SearchMetadataTable.TABLE} meta FROM ${"search_metadata"} meta
""".trimIndent() """.trimIndent()
include.forEachIndexed { index, pair -> include.forEachIndexed { index, pair ->
baseQuery += "\n" + ( baseQuery += "\n" + (
""" """
INNER JOIN ${pair.first} i$index INNER JOIN ${pair.first} i$index
ON i$index.$COL_MANGA_ID = meta.${SearchMetadataTable.COL_MANGA_ID} ON i$index.$COL_MANGA_ID = meta.${"manga_id"}
""".trimIndent() """.trimIndent()
) )
completeParams += pair.second completeParams += pair.second
@ -113,7 +110,7 @@ class SearchEngine {
exclude.forEach { exclude.forEach {
wheres += """ wheres += """
(meta.${SearchMetadataTable.COL_MANGA_ID} NOT IN ${it.first}) (meta.${"manga_id"} NOT IN ${it.first})
""".trimIndent() """.trimIndent()
whereParams += it.second whereParams += it.second
} }
@ -122,7 +119,7 @@ class SearchEngine {
baseQuery += "\nWHERE\n" baseQuery += "\nWHERE\n"
baseQuery += wheres.joinToString("\nAND\n") baseQuery += wheres.joinToString("\nAND\n")
} }
baseQuery += "\nORDER BY ${SearchMetadataTable.COL_MANGA_ID}" baseQuery += "\nORDER BY manga_id"
return baseQuery to completeParams return baseQuery to completeParams
} }

View File

@ -1,5 +1,6 @@
package exh.smartsearch package exh.smartsearch
import eu.kanade.data.DatabaseHandler
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
@ -18,6 +19,7 @@ class SmartSearchEngine(
private val extraSearchParams: String? = null, private val extraSearchParams: String? = null,
) { ) {
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
private val handler: DatabaseHandler by injectLazy()
private val normalizedLevenshtein = NormalizedLevenshtein() private val normalizedLevenshtein = NormalizedLevenshtein()

View File

@ -6,7 +6,7 @@ import android.view.View
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import dev.chrisbanes.insetter.applyInsetter import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.manga.interactor.GetMangaById import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.databinding.MetadataViewControllerBinding import eu.kanade.tachiyomi.databinding.MetadataViewControllerBinding
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
@ -29,7 +29,7 @@ class MetadataViewController : NucleusController<MetadataViewControllerBinding,
} }
constructor(mangaId: Long) : this( constructor(mangaId: Long) : this(
runBlocking { Injekt.get<GetMangaById>().await(mangaId)!! }, runBlocking { Injekt.get<GetManga>().await(mangaId)!! },
) )
@Suppress("unused") @Suppress("unused")

View File

@ -0,0 +1,7 @@
deleteBySyncId:
DELETE FROM manga_sync WHERE sync_id = :syncId;
migrateSource:
UPDATE mangas
SET source = :newId
WHERE source = :oldId;

View File

@ -196,6 +196,18 @@ INNER JOIN search_metadata
ON mangas._id = search_metadata.manga_id ON mangas._id = search_metadata.manga_id
WHERE mangas.favorite = 1 AND (mangas.source = :eh OR mangas.source = :exh); WHERE mangas.favorite = 1 AND (mangas.source = :eh OR mangas.source = :exh);
getIdsOfFavoriteMangaWithMetadata:
SELECT mangas._id FROM mangas
INNER JOIN search_metadata
ON mangas._id = search_metadata.manga_id
WHERE mangas.favorite = 1;
getBySource:
SELECT * FROM mangas WHERE source = :sourceId;
getAll:
SELECT * FROM mangas;
selectLastInsertRow: selectLastInsertRow:
SELECT * SELECT *
FROM mangas FROM mangas

View File

@ -80,11 +80,11 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
updateSettingsById: updateSettingsById:
UPDATE merged UPDATE merged
SET SET
get_chapter_updates = ?, get_chapter_updates = coalesce(:getChapterUpdates, get_chapter_updates),
download_chapters = ?, download_chapters = coalesce(:downloadChapters, download_chapters),
info_manga = ?, info_manga = coalesce(:infoManga, info_manga),
chapter_priority = ? chapter_priority = coalesce(:chapterPriority, chapter_priority)
WHERE _id = ?; WHERE _id = :id;
deleteById: deleteById:
DELETE FROM merged WHERE _id = ?; DELETE FROM merged WHERE _id = ?;