From 4c9be5557daf5c128e5963f2592c7669401ea0c0 Mon Sep 17 00:00:00 2001 From: NerdNumber9 Date: Mon, 29 Jul 2019 19:36:16 -0400 Subject: [PATCH] Initial work on merged sources --- app/build.gradle | 1 + .../tachiyomi/source/online/HttpSource.kt | 2 +- .../source/online/all/MergedSource.kt | 165 ++++++++++++++++++ app/src/main/java/exh/EXHMigrations.kt | 2 + 4 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt diff --git a/app/build.gradle b/app/build.gradle index 4e86e420d..d58a13402 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -257,6 +257,7 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$coroutines_version" // Text distance (EH) implementation 'info.debatty:java-string-similarity:1.2.1' diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt index 46e6d8357..c47a9d380 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt @@ -314,7 +314,7 @@ abstract class HttpSource : CatalogueSource { * * @param page the page whose source image has to be downloaded. */ - fun fetchImage(page: Page): Observable { + open fun fetchImage(page: Page): Observable { return client.newCallWithProgress(imageRequest(page), page) .asObservableSuccess() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt new file mode 100644 index 000000000..5abcacbc4 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt @@ -0,0 +1,165 @@ +package eu.kanade.tachiyomi.source.online.all + +import com.github.salomonbrys.kotson.fromJson +import com.google.gson.Gson +import com.google.gson.annotations.SerializedName +import com.lvla.rxjava.interopkt.toV1Observable +import com.lvla.rxjava.interopkt.toV1Single +import eu.kanade.tachiyomi.data.database.DatabaseHelper +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.source.model.* +import eu.kanade.tachiyomi.source.online.HttpSource +import exh.util.await +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.rx2.asFlowable +import kotlinx.coroutines.rx2.asSingle +import okhttp3.Response +import rx.Observable +import rx.schedulers.Schedulers +import uy.kohesive.injekt.injectLazy + +// TODO LocalSource compatibility +// TODO Disable clear database option +class MergedSource : HttpSource() { + private val db: DatabaseHelper by injectLazy() + private val sourceManager: SourceManager by injectLazy() + private val gson: Gson by injectLazy() + + override val baseUrl = "" + + override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException() + override fun popularMangaParse(response: Response) = throw UnsupportedOperationException() + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException() + override fun searchMangaParse(response: Response) = throw UnsupportedOperationException() + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() + override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException() + + override fun fetchMangaDetails(manga: SManga): Observable { + return readMangaConfig(manga).load(db, sourceManager).take(1).map { loaded -> + SManga.create().apply { + this.copyFrom(loaded.manga) + url = manga.url + } + }.asFlowable().toV1Observable() + } + override fun fetchChapterList(manga: SManga): Observable> { + return GlobalScope.async(Dispatchers.IO) { + val loadedMangas = readMangaConfig(manga).load(db, sourceManager).buffer() + loadedMangas.map { loadedManga -> + loadedManga.source.fetchChapterList(loadedManga.manga).map { chapterList -> + chapterList.map { chapter -> + chapter.apply { + url = writeUrlConfig(UrlConfig(loadedManga.source.id, url, loadedManga.manga.url)) + } + } + } + }.buffer().map { it.toSingle().await(Schedulers.io()) }.toList().flatten() + }.asSingle(Dispatchers.IO).toV1Single().toObservable() + } + override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException() + override fun chapterListParse(response: Response) = throw UnsupportedOperationException() + + override fun fetchPageList(chapter: SChapter): Observable> { + val config = readUrlConfig(chapter.url) + val source = sourceManager.getOrStub(config.source) + return source.fetchPageList(SChapter.create().apply { + copyFrom(chapter) + url = config.url + }).map { pages -> + pages.map { page -> + page.copyWithUrl(writeUrlConfig(UrlConfig(config.source, page.url, config.mangaUrl))) + } + } + } + override fun fetchImageUrl(page: Page): Observable { + val config = readUrlConfig(page.url) + val source = sourceManager.getOrStub(config.source) as? HttpSource + ?: throw UnsupportedOperationException("This source does not support this operation!") + return source.fetchImageUrl(page.copyWithUrl(config.url)) + } + override fun pageListParse(response: Response) = throw UnsupportedOperationException() + override fun imageUrlParse(response: Response) = throw UnsupportedOperationException() + + override fun fetchImage(page: Page): Observable { + val config = readUrlConfig(page.url) + val source = sourceManager.getOrStub(config.source) as? HttpSource + ?: throw UnsupportedOperationException("This source does not support this operation!") + return source.fetchImage(page.copyWithUrl(config.url)) + } + override fun prepareNewChapter(chapter: SChapter, manga: SManga) { + val chapterConfig = readUrlConfig(chapter.url) + val source = sourceManager.getOrStub(chapterConfig.source) as? HttpSource + ?: throw UnsupportedOperationException("This source does not support this operation!") + val copiedManga = SManga.create().apply { + this.copyFrom(manga) + url = chapterConfig.mangaUrl + } + chapter.url = chapterConfig.url + source.prepareNewChapter(chapter, copiedManga) + chapter.url = writeUrlConfig(UrlConfig(source.id, chapter.url, chapterConfig.mangaUrl)) + chapter.scanlator = "${source.name}: ${chapter.scanlator}" + } + + fun readMangaConfig(manga: SManga): MangaConfig { + return gson.fromJson(manga.url) + } + fun readUrlConfig(url: String): UrlConfig { + return gson.fromJson(url) + } + fun writeUrlConfig(urlConfig: UrlConfig): String { + return gson.toJson(urlConfig) + } + data class LoadedMangaSource(val source: Source, val manga: Manga) + data class MangaSource( + @SerializedName("s") + val source: Long, + @SerializedName("u") + val url: String + ) { + suspend fun load(db: DatabaseHelper, sourceManager: SourceManager): LoadedMangaSource? { + val manga = db.getManga(url, source).await() ?: return null + val source = sourceManager.getOrStub(source) + return LoadedMangaSource(source, manga) + } + companion object { + fun fromLoadedMangaSource(loadedMS: LoadedMangaSource): MangaSource { + return MangaSource(loadedMS.source.id, loadedMS.manga.url) + } + } + } + data class MangaConfig( + @SerializedName("c") + val children: List + ) { + fun load(db: DatabaseHelper, sourceManager: SourceManager): Flow { + return children.asFlow().map { mangaSource -> + mangaSource.load(db, sourceManager) + ?: throw IllegalStateException("Missing source manga: $mangaSource") + } + } + } + data class UrlConfig( + @SerializedName("s") + val source: Long, + @SerializedName("u") + val url: String, + @SerializedName("m") + val mangaUrl: String + ) + + fun Page.copyWithUrl(newUrl: String) = Page( + index, + newUrl, + imageUrl, + uri + ) + + override val lang = "all" + override val supportsLatest = false + override val name = "MergedSource" +} \ No newline at end of file diff --git a/app/src/main/java/exh/EXHMigrations.kt b/app/src/main/java/exh/EXHMigrations.kt index 181e4c137..8eb47a3d0 100644 --- a/app/src/main/java/exh/EXHMigrations.kt +++ b/app/src/main/java/exh/EXHMigrations.kt @@ -77,6 +77,8 @@ object EXHMigrations { backupDatabase(context, oldVersion) } + // TODO BE CAREFUL TO NOT FUCK UP MergedSources IF CHANGING URLs + preferences.eh_lastVersionCode().set(BuildConfig.VERSION_CODE) return true