From 1f03ebad2b37bc60ec320c4463aaa2e019b12e7b Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 2 Sep 2023 23:22:37 -0400 Subject: [PATCH] Update Source interface --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- .github/workflows/publish.yml | 2 +- build.gradle.kts | 10 ++- gradle/wrapper/gradle-wrapper.properties | 4 +- server/build.gradle.kts | 15 ++-- .../tachiyomi/source/CatalogueSource.kt | 2 +- .../eu/kanade/tachiyomi/source/Source.kt | 70 +++++++++++++++++-- .../source/online/ResolvableSource.kt | 26 +++++++ .../eu/kanade/tachiyomi/util/RxExtension.kt | 54 ++++++++++++++ settings.gradle.kts | 1 - 11 files changed, 160 insertions(+), 28 deletions(-) create mode 100644 server/src/main/kotlin/eu/kanade/tachiyomi/source/online/ResolvableSource.kt create mode 100644 server/src/main/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 1a5317f..781efa8 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -24,7 +24,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 distribution: adopt - name: Generate android.jar diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 6e06c11..02fd9e3 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -24,7 +24,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 distribution: adopt - name: Generate android.jar diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5e70135..ca0b0c2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,7 +26,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 distribution: adopt - name: Generate android.jar diff --git a/build.gradle.kts b/build.gradle.kts index 380f7e1..c67e30e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,13 +33,13 @@ configure(projects) { apply(plugin = "org.jetbrains.kotlin.jvm") java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } tasks.withType { kotlinOptions { - jvmTarget = JavaVersion.VERSION_11.toString() + jvmTarget = JavaVersion.VERSION_17.toString() } } @@ -59,7 +59,6 @@ configure(projects) { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion") implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion") - // Dependency Injection implementation("org.kodein.di:kodein-di-conf-jvm:7.11.0") @@ -71,7 +70,6 @@ configure(projects) { // ReactiveX implementation("io.reactivex:rxjava:1.3.8") implementation("io.reactivex:rxkotlin:1.0.0") - implementation("com.jakewharton.rxrelay:rxrelay:1.2.0") // JSoup implementation("org.jsoup:jsoup:1.15.3") @@ -81,7 +79,7 @@ configure(projects) { implementation("io.github.config4k:config4k:0.4.2") // dex2jar - val dex2jarVersion = "v59" + val dex2jarVersion = "v71" implementation("com.github.ThexXTURBOXx.dex2jar:dex-translator:$dex2jarVersion") implementation("com.github.ThexXTURBOXx.dex2jar:dex-tools:$dex2jarVersion") diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fc..ac72c34 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 1fd15b8..1b0a77d 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -29,11 +29,6 @@ dependencies { implementation("org.jsoup:jsoup:1.15.3") implementation("app.cash.quickjs:quickjs-jvm:0.9.2") - - // Source models and interfaces from Tachiyomi 1.x - // using source class from tachiyomi commit 9493577de27c40ce8b2b6122cc447d025e34c477 to not depend on tachiyomi.sourceapi -// implementation("tachiyomi.sourceapi:source-api:1.1") - // AndroidCompat implementation(project(":AndroidCompat")) implementation(project(":AndroidCompat:Config")) @@ -59,7 +54,7 @@ sourceSets { } // should be bumped with each stable release -val inspectorVersion = "v1.4.2" +val inspectorVersion = "v1.4.3" // counts commit count on master val inspectorRevision = runCatching { @@ -110,10 +105,10 @@ tasks { withType { kotlinOptions { freeCompilerArgs = listOf( - "-Xopt-in=kotlin.RequiresOptIn", - "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", - "-Xopt-in=kotlinx.coroutines.InternalCoroutinesApi", - "-Xopt-in=kotlin.io.path.ExperimentalPathApi", + "-Xopt-in=kotlin.RequiresOptIn", + "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-Xopt-in=kotlinx.coroutines.InternalCoroutinesApi", + "-Xopt-in=kotlin.io.path.ExperimentalPathApi", ) } } diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt index c78033e..f9e416d 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt @@ -9,7 +9,7 @@ interface CatalogueSource : Source { /** * An ISO 639-1 compliant language code (two letters in lower case). */ - val lang: String + override val lang: String /** * Whether the source has support for latest updates. diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/Source.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/Source.kt index 93a8a80..f140eda 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/source/Source.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/Source.kt @@ -3,15 +3,16 @@ package eu.kanade.tachiyomi.source import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.util.awaitSingle import rx.Observable /** - * A basic interface for creating a source. It could be an online source, a local source, etc... + * A basic interface for creating a source. It could be an online source, a local source, etc. */ interface Source { /** - * Id for the source. Must be unique. + * ID for the source. Must be unique. */ val id: Long @@ -20,24 +21,81 @@ interface Source { */ val name: String + val lang: String + get() = "" + + /** + * Get the updated details for a manga. + * + * @since extensions-lib 1.4 + * @param manga the manga to update. + * @return the updated manga. + */ + @Suppress("DEPRECATION") + suspend fun getMangaDetails(manga: SManga): SManga { + return fetchMangaDetails(manga).awaitSingle() + } + + /** + * Get all the available chapters for a manga. + * + * @since extensions-lib 1.4 + * @param manga the manga to update. + * @return the chapters for the manga. + */ + @Suppress("DEPRECATION") + suspend fun getChapterList(manga: SManga): List { + return fetchChapterList(manga).awaitSingle() + } + + /** + * Get the list of pages a chapter has. Pages should be returned + * in the expected order; the index is ignored. + * + * @since extensions-lib 1.4 + * @param chapter the chapter. + * @return the pages for the chapter. + */ + @Suppress("DEPRECATION") + suspend fun getPageList(chapter: SChapter): List { + return fetchPageList(chapter).awaitSingle() + } + /** * Returns an observable with the updated details for a manga. * * @param manga the manga to update. */ - fun fetchMangaDetails(manga: SManga): Observable + @Deprecated( + "Use the non-RxJava API instead", + ReplaceWith("getMangaDetails"), + ) + fun fetchMangaDetails(manga: SManga): Observable = throw IllegalStateException( + "Not used", + ) /** * Returns an observable with all the available chapters for a manga. * * @param manga the manga to update. */ - fun fetchChapterList(manga: SManga): Observable> + @Deprecated( + "Use the non-RxJava API instead", + ReplaceWith("getChapterList"), + ) + fun fetchChapterList(manga: SManga): Observable> = throw IllegalStateException( + "Not used", + ) /** - * Returns an observable with the list of pages a chapter has. + * Returns an observable with the list of pages a chapter has. Pages should be returned + * in the expected order; the index is ignored. * * @param chapter the chapter. */ - fun fetchPageList(chapter: SChapter): Observable> + @Deprecated( + "Use the non-RxJava API instead", + ReplaceWith("getPageList"), + ) + fun fetchPageList(chapter: SChapter): Observable> = Observable.empty() } diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/online/ResolvableSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/online/ResolvableSource.kt new file mode 100644 index 0000000..6a00c2e --- /dev/null +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/online/ResolvableSource.kt @@ -0,0 +1,26 @@ +package eu.kanade.tachiyomi.source.online + +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.model.SManga + +/** + * A source that may handle opening an SManga for a given URI. + * + * @since extensions-lib 1.5 + */ +interface ResolvableSource : Source { + + /** + * Whether this source may potentially handle the given URI. + * + * @since extensions-lib 1.5 + */ + fun canResolveUri(uri: String): Boolean + + /** + * Called if canHandleUri is true. Returns the corresponding SManga, if possible. + * + * @since extensions-lib 1.5 + */ + suspend fun getManga(uri: String): SManga? +} diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt new file mode 100644 index 0000000..d82e9e8 --- /dev/null +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt @@ -0,0 +1,54 @@ +package eu.kanade.tachiyomi.util + +import kotlinx.coroutines.CancellableContinuation +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.suspendCancellableCoroutine +import rx.Observable +import rx.Subscriber +import rx.Subscription +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException + +suspend fun Observable.awaitSingle(): T = single().awaitOne() + +@OptIn(InternalCoroutinesApi::class) +private suspend fun Observable.awaitOne(): T = suspendCancellableCoroutine { cont -> + cont.unsubscribeOnCancellation( + subscribe( + object : Subscriber() { + override fun onStart() { + request(1) + } + + override fun onNext(t: T) { + cont.resume(t) + } + + override fun onCompleted() { + if (cont.isActive) { + cont.resumeWithException( + IllegalStateException( + "Should have invoked onNext", + ), + ) + } + } + + override fun onError(e: Throwable) { + /* + * Rx1 observable throws NoSuchElementException if cancellation happened before + * element emission. To mitigate this we try to atomically resume continuation with exception: + * if resume failed, then we know that continuation successfully cancelled itself + */ + val token = cont.tryResumeWithException(e) + if (token != null) { + cont.completeResume(token) + } + } + }, + ), + ) +} + +private fun CancellableContinuation.unsubscribeOnCancellation(sub: Subscription) = + invokeOnCancellation { sub.unsubscribe() } diff --git a/settings.gradle.kts b/settings.gradle.kts index 041d99a..b63917d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,5 @@ rootProject.name = "Tachiyomi Extensions Inspector" include("server") - include("AndroidCompat") include("AndroidCompat:Config") \ No newline at end of file