mirror of
https://github.com/tachiyomiorg/tachiyomi-extensions-inspector.git
synced 2024-10-31 22:45:06 +01:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
29e358953e | ||
|
1aa6775836 |
4
.github/workflows/build_pull_request.yml
vendored
4
.github/workflows/build_pull_request.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout pull request
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
@ -34,4 +34,4 @@ jobs:
|
||||
- name: Build project
|
||||
uses: gradle/gradle-command-action@v2
|
||||
with:
|
||||
arguments: :server:shadowJar
|
||||
arguments: :inspector:shadowJar
|
||||
|
4
.github/workflows/build_push.yml
vendored
4
.github/workflows/build_push.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
@ -34,4 +34,4 @@ jobs:
|
||||
- name: Build project
|
||||
uses: gradle/gradle-command-action@v2
|
||||
with:
|
||||
arguments: :server:shadowJar
|
||||
arguments: :inspector:shadowJar
|
||||
|
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout ${{ github.ref }}
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
@ -36,7 +36,7 @@ jobs:
|
||||
- name: Build project
|
||||
uses: gradle/gradle-command-action@v2
|
||||
with:
|
||||
arguments: :server:shadowJar
|
||||
arguments: :inspector:shadowJar
|
||||
|
||||
- name: Upload Release
|
||||
uses: xresloader/upload-to-github-release@v1
|
||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -7,11 +7,3 @@ local.properties
|
||||
# Ignore Gradle build output directory
|
||||
build
|
||||
|
||||
server/src/main/resources/webUI
|
||||
server/tmp/
|
||||
server/tachiserver-data/
|
||||
|
||||
# bundle asset downlaods
|
||||
OpenJDK*.zip
|
||||
electron-*.zip
|
||||
rcedit-*
|
||||
|
@ -5,7 +5,8 @@ package xyz.nulldev.ts.config
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import ch.qos.logback.classic.Level
|
||||
import com.typesafe.config.Config
|
||||
|
@ -5,7 +5,8 @@ package xyz.nulldev.ts.config
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import com.typesafe.config.Config
|
||||
|
||||
|
@ -5,7 +5,8 @@ package xyz.nulldev.ts.config
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import ch.qos.logback.classic.Level
|
||||
import mu.KotlinLogging
|
||||
|
@ -5,7 +5,8 @@ package android.widget;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
public class EditText {
|
||||
public EditText(android.content.Context context) { throw new RuntimeException("Stub!"); }
|
||||
|
@ -5,7 +5,8 @@ package android.widget;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
public class Toast {
|
||||
public static final int LENGTH_LONG = 1;
|
||||
|
@ -5,7 +5,8 @@ package androidx.preference;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -5,7 +5,8 @@ package androidx.preference;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -5,7 +5,8 @@ package androidx.preference;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
@ -5,7 +5,8 @@ package androidx.preference;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
@ -5,7 +5,8 @@ package androidx.preference;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -5,7 +5,8 @@ package androidx.preference;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
@ -5,7 +5,8 @@ package androidx.preference;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -5,7 +5,8 @@ package androidx.preference;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -5,7 +5,8 @@ package androidx.preference;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -5,7 +5,8 @@ package xyz.nulldev.androidcompat.io.sharedprefs
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.russhwolf.settings.ExperimentalSettingsApi
|
||||
@ -23,7 +24,7 @@ import java.util.prefs.Preferences
|
||||
|
||||
@OptIn(ExperimentalSettingsImplementation::class, ExperimentalSerializationApi::class, ExperimentalSettingsApi::class)
|
||||
class JavaSharedPreferences(key: String) : SharedPreferences {
|
||||
private val javaPreferences = Preferences.userRoot().node("suwayomi/tachidesk/$key")
|
||||
private val javaPreferences = Preferences.userRoot().node("inspector/$key")
|
||||
private val preferences = JvmPreferencesSettings(javaPreferences)
|
||||
private val listeners = mutableMapOf<SharedPreferences.OnSharedPreferenceChangeListener, PreferenceChangeListener>()
|
||||
|
||||
|
@ -5,7 +5,8 @@ package xyz.nulldev.androidcompat.replace.java.text;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import com.ibm.icu.text.DisplayContext;
|
||||
import com.ibm.icu.util.Currency;
|
||||
|
@ -5,7 +5,8 @@ package xyz.nulldev.androidcompat.replace.java.text;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import com.ibm.icu.text.DateFormatSymbols;
|
||||
import com.ibm.icu.text.DisplayContext;
|
||||
|
@ -5,7 +5,8 @@ package xyz.nulldev.androidcompat.replace.java.util;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import com.ibm.icu.text.DateFormat;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
@ -5,7 +5,8 @@ package xyz.nulldev.androidcompat.replace.java.util;
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
|
@ -9,8 +9,7 @@ plugins {
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = "suwayomi"
|
||||
|
||||
group = "tachiyomi"
|
||||
version = "1.0"
|
||||
|
||||
repositories {
|
||||
@ -26,7 +25,7 @@ allprojects {
|
||||
val projects = listOf(
|
||||
project(":AndroidCompat"),
|
||||
project(":AndroidCompat:Config"),
|
||||
project(":server")
|
||||
project(":inspector")
|
||||
)
|
||||
|
||||
configure(projects) {
|
||||
@ -47,7 +46,6 @@ configure(projects) {
|
||||
// Kotlin
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
implementation(kotlin("reflect"))
|
||||
testImplementation(kotlin("test"))
|
||||
|
||||
// coroutines
|
||||
val coroutinesVersion = "1.6.4"
|
||||
|
@ -21,7 +21,6 @@ dependencies {
|
||||
implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:$okhttpVersion")
|
||||
implementation("com.squareup.okio:okio:3.3.0")
|
||||
|
||||
|
||||
// dependencies of Tachiyomi extensions, some are duplicate, keeping it here for reference
|
||||
implementation("com.github.inorichi.injekt:injekt-core:65b0440")
|
||||
implementation("com.squareup.okhttp3:okhttp:$okhttpVersion")
|
||||
@ -35,12 +34,9 @@ dependencies {
|
||||
|
||||
// uncomment to test extensions directly
|
||||
// implementation(fileTree("lib/"))
|
||||
|
||||
// Testing
|
||||
testImplementation(kotlin("test-junit5"))
|
||||
}
|
||||
|
||||
val MainClass = "suwayomi.tachidesk.MainKt"
|
||||
val MainClass = "inspector.MainKt"
|
||||
application {
|
||||
mainClass.set(MainClass)
|
||||
}
|
||||
@ -75,7 +71,7 @@ val String.wrapped get() = """"$this""""
|
||||
|
||||
buildConfig {
|
||||
className("BuildConfig")
|
||||
packageName("suwayomi.server")
|
||||
packageName("inspector")
|
||||
|
||||
useKotlinOutput()
|
||||
|
@ -5,7 +5,8 @@ package eu.kanade.tachiyomi
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
@ -5,36 +5,26 @@ package eu.kanade.tachiyomi
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.app.Application
|
||||
import eu.kanade.tachiyomi.network.JavaScriptEngine
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import kotlinx.serialization.json.Json
|
||||
import rx.Observable
|
||||
import rx.schedulers.Schedulers
|
||||
import uy.kohesive.injekt.api.InjektModule
|
||||
import uy.kohesive.injekt.api.InjektRegistrar
|
||||
import uy.kohesive.injekt.api.addSingleton
|
||||
import uy.kohesive.injekt.api.addSingletonFactory
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class AppModule(val app: Application) : InjektModule {
|
||||
|
||||
override fun InjektRegistrar.registerInjectables() {
|
||||
|
||||
addSingleton(app)
|
||||
|
||||
addSingletonFactory { NetworkHelper(app) }
|
||||
addSingletonFactory { JavaScriptEngine(app) }
|
||||
|
||||
addSingletonFactory { Json { ignoreUnknownKeys = true } }
|
||||
|
||||
// Asynchronously init expensive components for a faster cold start
|
||||
rxAsync { get<NetworkHelper>() }
|
||||
}
|
||||
|
||||
private fun rxAsync(block: () -> Unit) {
|
||||
Observable.fromCallable { block() }.subscribeOn(Schedulers.computation()).subscribe()
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package eu.kanade.tachiyomi
|
||||
|
||||
class BuildConfig {
|
||||
companion object {
|
||||
const val VERSION_NAME = inspector.BuildConfig.NAME
|
||||
val VERSION_CODE = inspector.BuildConfig.REVISION.trimStart('r').toInt()
|
||||
}
|
||||
}
|
@ -5,7 +5,8 @@ package eu.kanade.tachiyomi.network
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
@ -5,7 +5,8 @@ package eu.kanade.tachiyomi.network
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
|
@ -38,6 +38,7 @@ fun GET(
|
||||
.cacheControl(cache)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun POST(
|
||||
url: String,
|
||||
headers: Headers = DEFAULT_HEADERS,
|
@ -5,7 +5,8 @@ package eu.kanade.tachiyomi.network.interceptor
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
@ -29,11 +29,16 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
override fun fetchSearchManga(
|
||||
page: Int,
|
||||
query: String,
|
||||
filters: FilterList
|
||||
): Observable<MangasPage> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
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 rx.Observable
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
val id: Long
|
||||
|
||||
/**
|
||||
* Name of the source.
|
||||
*/
|
||||
val name: String
|
||||
|
||||
val lang: String
|
||||
get() = ""
|
||||
|
||||
/**
|
||||
* Returns an observable with the updated details for a manga.
|
||||
*
|
||||
* @param manga the manga to update.
|
||||
*/
|
||||
fun fetchMangaDetails(manga: SManga): Observable<SManga>
|
||||
|
||||
/**
|
||||
* Returns an observable with all the available chapters for a manga.
|
||||
*
|
||||
* @param manga the manga to update.
|
||||
*/
|
||||
fun fetchChapterList(manga: SManga): Observable<List<SChapter>>
|
||||
|
||||
/**
|
||||
* Returns an observable with the list of pages a chapter has.
|
||||
*
|
||||
* @param chapter the chapter.
|
||||
*/
|
||||
fun fetchPageList(chapter: SChapter): Observable<List<Page>>
|
||||
}
|
@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.source.model
|
||||
sealed class Filter<T>(val name: String, var state: T) {
|
||||
open class Header(name: String) : Filter<Any>(name, 0)
|
||||
open class Separator(name: String = "") : Filter<Any>(name, 0)
|
||||
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state)
|
||||
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) :
|
||||
Filter<Int>(name, state)
|
||||
|
||||
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
|
||||
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
|
||||
abstract class TriState(name: String, state: Int = STATE_IGNORE) : Filter<Int>(name, state) {
|
@ -56,7 +56,8 @@ abstract class HttpSource : CatalogueSource {
|
||||
override val id by lazy {
|
||||
val key = "${name.lowercase()}/$lang/$versionId"
|
||||
val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray())
|
||||
(0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE
|
||||
(0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }
|
||||
.reduce(Long::or) and Long.MAX_VALUE
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,7 +119,11 @@ abstract class HttpSource : CatalogueSource {
|
||||
* @param query the search query.
|
||||
* @param filters the list of filters to apply.
|
||||
*/
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
override fun fetchSearchManga(
|
||||
page: Int,
|
||||
query: String,
|
||||
filters: FilterList
|
||||
): Observable<MangasPage> {
|
||||
return client.newCall(searchMangaRequest(page, query, filters))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
@ -133,7 +138,11 @@ abstract class HttpSource : CatalogueSource {
|
||||
* @param query the search query.
|
||||
* @param filters the list of filters to apply.
|
||||
*/
|
||||
protected abstract fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request
|
||||
protected abstract fun searchMangaRequest(
|
||||
page: Int,
|
||||
query: String,
|
||||
filters: FilterList
|
||||
): Request
|
||||
|
||||
/**
|
||||
* Parses the response from the site and returns a [MangasPage] object.
|
@ -4,4 +4,5 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.IO, block)
|
||||
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) =
|
||||
withContext(Dispatchers.IO, block)
|
@ -1,4 +1,4 @@
|
||||
package suwayomi.tachidesk
|
||||
package inspector
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@ -8,14 +8,18 @@ package suwayomi.tachidesk
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import inspector.util.Extension
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import mu.KotlinLogging
|
||||
import suwayomi.tachidesk.manga.impl.extension.Extension
|
||||
import suwayomi.tachidesk.server.applicationSetup
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import xyz.nulldev.androidcompat.AndroidCompat
|
||||
import xyz.nulldev.androidcompat.AndroidCompatInitializer
|
||||
import xyz.nulldev.ts.config.ConfigKodeinModule
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
@ -23,18 +27,20 @@ import kotlin.io.path.extension
|
||||
import kotlin.streams.asSequence
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
private val androidCompat by lazy { AndroidCompat() }
|
||||
|
||||
suspend fun main(args: Array<String>) {
|
||||
if (args.size < 3) {
|
||||
throw RuntimeException("Inspector must be given the path of apks directory, output json, and a tmp dir")
|
||||
}
|
||||
|
||||
applicationSetup()
|
||||
|
||||
val (apksPath, outputPath, tmpDirPath) = args
|
||||
|
||||
initApplication()
|
||||
|
||||
val tmpDir = File(tmpDirPath, "tmp").also { it.mkdir() }
|
||||
val extensions = Files.find(Paths.get(apksPath), 2, { _, fileAttributes -> fileAttributes.isRegularFile })
|
||||
val extensions =
|
||||
Files.find(Paths.get(apksPath), 2, { _, fileAttributes -> fileAttributes.isRegularFile })
|
||||
.asSequence()
|
||||
.filter { it.extension == "apk" }
|
||||
.toList()
|
||||
@ -43,21 +49,31 @@ suspend fun main(args: Array<String>) {
|
||||
|
||||
val extensionsInfo = extensions.associate {
|
||||
logger.debug("Installing $it")
|
||||
val (pkgName, sources) = Extension.installAPK(tmpDir) { it.toFile() }
|
||||
val (pkgName, sources) = Extension.installApk(tmpDir) { it.toFile() }
|
||||
pkgName to sources.map { source -> SourceJson(source) }
|
||||
}
|
||||
|
||||
File(outputPath).writeText(Json.encodeToString(extensionsInfo))
|
||||
}
|
||||
|
||||
private fun initApplication() {
|
||||
logger.info("Running Inspector ${BuildConfig.VERSION} revision ${BuildConfig.REVISION}")
|
||||
|
||||
// Load config API
|
||||
DI.global.addImport(ConfigKodeinModule().create())
|
||||
// Load Android compatibility dependencies
|
||||
AndroidCompatInitializer().init()
|
||||
// start app
|
||||
androidCompat.startApp(App())
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class SourceJson(
|
||||
private data class SourceJson(
|
||||
val name: String,
|
||||
val lang: String,
|
||||
val id: String,
|
||||
val baseUrl: String,
|
||||
val versionId: Int,
|
||||
val hasCloudflare: Short
|
||||
) {
|
||||
constructor(source: HttpSource) :
|
||||
this(
|
||||
@ -66,10 +82,5 @@ data class SourceJson(
|
||||
source.id.toString(),
|
||||
source.baseUrl,
|
||||
source.versionId,
|
||||
source.client.interceptors
|
||||
.any { it is CloudflareInterceptor }
|
||||
.toShort()
|
||||
)
|
||||
}
|
||||
|
||||
private fun Boolean.toShort(): Short = if (this) 1 else 0
|
@ -1,29 +1,33 @@
|
||||
package suwayomi.tachidesk.manga.impl.extension
|
||||
package inspector.util
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import inspector.util.PackageTools.EXTENSION_FEATURE
|
||||
import inspector.util.PackageTools.LIB_VERSION_MAX
|
||||
import inspector.util.PackageTools.LIB_VERSION_MIN
|
||||
import inspector.util.PackageTools.METADATA_SOURCE_CLASS
|
||||
import inspector.util.PackageTools.dex2jar
|
||||
import inspector.util.PackageTools.getPackageInfo
|
||||
import inspector.util.PackageTools.loadExtensionSources
|
||||
import mu.KotlinLogging
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.EXTENSION_FEATURE
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.LIB_VERSION_MAX
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.LIB_VERSION_MIN
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.METADATA_SOURCE_CLASS
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.dex2jar
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.getPackageInfo
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.loadExtensionSources
|
||||
import java.io.File
|
||||
|
||||
object Extension {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
suspend fun installAPK(tmpDir: File, fetcher: suspend () -> File): Pair<String, List<HttpSource>> {
|
||||
suspend fun installApk(
|
||||
tmpDir: File,
|
||||
fetcher: suspend () -> File
|
||||
): Pair<String, List<HttpSource>> {
|
||||
val apkFile = fetcher()
|
||||
|
||||
val jarFile = File(tmpDir, "${apkFile.nameWithoutExtension}.jar")
|
||||
@ -43,14 +47,19 @@ object Extension {
|
||||
)
|
||||
}
|
||||
|
||||
val className = packageInfo.packageName + packageInfo.applicationInfo.metaData.getString(METADATA_SOURCE_CLASS)
|
||||
val className = packageInfo.packageName + packageInfo.applicationInfo.metaData.getString(
|
||||
METADATA_SOURCE_CLASS
|
||||
)
|
||||
|
||||
logger.trace("Main class for extension is $className")
|
||||
|
||||
dex2jar(apkFile, jarFile)
|
||||
|
||||
// collect sources from the extension
|
||||
return packageInfo.packageName to when (val instance = loadExtensionSources(jarFile.absolutePath, className)) {
|
||||
return packageInfo.packageName to when (
|
||||
val instance =
|
||||
loadExtensionSources(jarFile.absolutePath, className)
|
||||
) {
|
||||
is Source -> listOf(instance).filterIsInstance<HttpSource>()
|
||||
is SourceFactory -> instance.createSources().filterIsInstance<HttpSource>()
|
||||
else -> throw RuntimeException("Unknown source class type! ${instance.javaClass}")
|
@ -1,11 +1,12 @@
|
||||
package suwayomi.tachidesk.manga.impl.util
|
||||
package inspector.util
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.Signature
|
||||
@ -57,7 +58,8 @@ object PackageTools {
|
||||
.skipExceptions(false)
|
||||
.to(jarFilePath)
|
||||
if (handler.hasException()) {
|
||||
val errorFile: Path = jarFilePath.parent.resolve("${dexFile.nameWithoutExtension}-error.txt")
|
||||
val errorFile: Path =
|
||||
jarFilePath.parent.resolve("${dexFile.nameWithoutExtension}-error.txt")
|
||||
logger.error(
|
||||
"""
|
||||
Detail Error Information in File $errorFile
|
@ -8,7 +8,7 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="Exposed" level="ERROR"/>
|
||||
<logger name="Exposed" level="ERROR" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
@ -1,8 +0,0 @@
|
||||
package eu.kanade.tachiyomi
|
||||
|
||||
class BuildConfig {
|
||||
companion object {
|
||||
const val VERSION_NAME = suwayomi.server.BuildConfig.NAME
|
||||
val VERSION_CODE = suwayomi.server.BuildConfig.REVISION.trimStart('r').toInt()
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
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.
|
||||
*/
|
||||
interface Source {
|
||||
|
||||
/**
|
||||
* ID for the source. Must be unique.
|
||||
*/
|
||||
val id: Long
|
||||
|
||||
/**
|
||||
* Name of the 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<SChapter> {
|
||||
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<Page> {
|
||||
return fetchPageList(chapter).awaitSingle()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an observable with the updated details for a manga.
|
||||
*
|
||||
* @param manga the manga to update.
|
||||
*/
|
||||
@Deprecated(
|
||||
"Use the non-RxJava API instead",
|
||||
ReplaceWith("getMangaDetails"),
|
||||
)
|
||||
fun fetchMangaDetails(manga: SManga): Observable<SManga> = throw IllegalStateException(
|
||||
"Not used",
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns an observable with all the available chapters for a manga.
|
||||
*
|
||||
* @param manga the manga to update.
|
||||
*/
|
||||
@Deprecated(
|
||||
"Use the non-RxJava API instead",
|
||||
ReplaceWith("getChapterList"),
|
||||
)
|
||||
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = throw IllegalStateException(
|
||||
"Not used",
|
||||
)
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Deprecated(
|
||||
"Use the non-RxJava API instead",
|
||||
ReplaceWith("getPageList"),
|
||||
)
|
||||
fun fetchPageList(chapter: SChapter): Observable<List<Page>> = Observable.empty()
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
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?
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
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 <T> Observable<T>.awaitSingle(): T = single().awaitOne()
|
||||
|
||||
@OptIn(InternalCoroutinesApi::class)
|
||||
private suspend fun <T> Observable<T>.awaitOne(): T = suspendCancellableCoroutine { cont ->
|
||||
cont.unsubscribeOnCancellation(
|
||||
subscribe(
|
||||
object : Subscriber<T>() {
|
||||
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 <T> CancellableContinuation<T>.unsubscribeOnCancellation(sub: Subscription) =
|
||||
invokeOnCancellation { sub.unsubscribe() }
|
@ -1,44 +0,0 @@
|
||||
package eu.kanade.tachiyomi.util.lang
|
||||
|
||||
import java.security.MessageDigest
|
||||
|
||||
object Hash {
|
||||
|
||||
private val chars = charArrayOf(
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'a', 'b', 'c', 'd', 'e', 'f'
|
||||
)
|
||||
|
||||
private val MD5 get() = MessageDigest.getInstance("MD5")
|
||||
|
||||
private val SHA256 get() = MessageDigest.getInstance("SHA-256")
|
||||
|
||||
fun sha256(bytes: ByteArray): String {
|
||||
return encodeHex(SHA256.digest(bytes))
|
||||
}
|
||||
|
||||
fun sha256(string: String): String {
|
||||
return sha256(string.toByteArray())
|
||||
}
|
||||
|
||||
fun md5(bytes: ByteArray): String {
|
||||
return encodeHex(MD5.digest(bytes))
|
||||
}
|
||||
|
||||
fun md5(string: String): String {
|
||||
return md5(string.toByteArray())
|
||||
}
|
||||
|
||||
private fun encodeHex(data: ByteArray): String {
|
||||
val l = data.size
|
||||
val out = CharArray(l shl 1)
|
||||
var i = 0
|
||||
var j = 0
|
||||
while (i < l) {
|
||||
out[j++] = chars[(240 and data[i].toInt()).ushr(4)]
|
||||
out[j++] = chars[15 and data[i].toInt()]
|
||||
i++
|
||||
}
|
||||
return String(out)
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package suwayomi.tachidesk.server
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import eu.kanade.tachiyomi.App
|
||||
import mu.KotlinLogging
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import suwayomi.server.BuildConfig
|
||||
import xyz.nulldev.androidcompat.AndroidCompat
|
||||
import xyz.nulldev.androidcompat.AndroidCompatInitializer
|
||||
import xyz.nulldev.ts.config.ConfigKodeinModule
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
val androidCompat by lazy { AndroidCompat() }
|
||||
|
||||
fun applicationSetup() {
|
||||
logger.info("Running Inspector ${BuildConfig.VERSION} revision ${BuildConfig.REVISION}")
|
||||
|
||||
// Load config API
|
||||
DI.global.addImport(ConfigKodeinModule().create())
|
||||
// Load Android compatibility dependencies
|
||||
AndroidCompatInitializer().init()
|
||||
// start app
|
||||
androidCompat.startApp(App())
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
# Server ip and port bindings
|
||||
server.ip = "0.0.0.0"
|
||||
server.port = 4567
|
||||
|
||||
# Socks5 proxy
|
||||
server.socksProxy = false
|
||||
server.socksProxyHost = ""
|
||||
server.socksProxyPort = ""
|
||||
|
||||
# misc
|
||||
server.debugLogsEnabled = true
|
||||
server.systemTrayEnabled = false
|
||||
server.initialOpenInBrowserEnabled = true
|
@ -1,5 +1,5 @@
|
||||
rootProject.name = "Tachiyomi Extensions Inspector"
|
||||
|
||||
include("server")
|
||||
include("AndroidCompat")
|
||||
include("AndroidCompat:Config")
|
||||
include("inspector")
|
||||
|
Loading…
Reference in New Issue
Block a user