Compare commits

...

9 Commits
v0.7.2 ... main

Author SHA1 Message Date
arkon
29e358953e Misc cleanup 2023-09-14 22:24:21 -04:00
arkon
1aa6775836 Revert RxJava API changes for 1.4
These will return for 1.5
2023-09-14 22:05:47 -04:00
arkon
1f03ebad2b Update Source interface 2023-09-02 23:22:37 -04:00
arkon
e301768a5d Bump dex2jar 2023-02-11 14:52:54 -05:00
arkon
b0e09fe721 Release v1.4.1 2023-02-11 09:37:39 -05:00
Alessandro Jean
ad84ee8857
Update extensions-lib version check to match the app ones. (#10) 2023-02-11 09:36:36 -05:00
arkon
42a23ee871 Release v1.4.0
Changing versioning to match the extensions-lib (so this is rev0 for extensions-lib 1.4).
2023-02-08 21:18:07 -05:00
arkon
2314d3e432
Add extensions-lib 1.4 bits (#9) 2023-02-07 17:56:49 -05:00
arkon
b220571647
Update workflows (#8) 2023-02-07 09:31:25 -05:00
73 changed files with 294 additions and 290 deletions

View File

@ -1,7 +0,0 @@
org.gradle.daemon=false
org.gradle.jvmargs=-Xmx5120m
org.gradle.workers.max=5
org.gradle.parallel=true
kotlin.incremental=false
kotlin.compiler.execution.strategy=in-process

View File

@ -3,19 +3,18 @@ name: CI Pull Request
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
build:
name: Build pull request
runs-on: ubuntu-latest
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.9.0
with:
access_token: ${{ github.token }}
- name: Checkout pull request
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
@ -23,14 +22,10 @@ jobs:
uses: gradle/wrapper-validation-action@v1
- name: Set up JDK
uses: actions/setup-java@v1
uses: actions/setup-java@v3
with:
java-version: 11
- name: Copy CI gradle.properties
run: |
mkdir -p ~/.gradle
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
java-version: 17
distribution: adopt
- name: Generate android.jar
run: |
@ -39,4 +34,4 @@ jobs:
- name: Build project
uses: gradle/gradle-command-action@v2
with:
arguments: :server:shadowJar
arguments: :inspector:shadowJar

View File

@ -3,7 +3,11 @@ name: CI build
on:
push:
branches:
- master
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
@ -11,26 +15,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.9.0
with:
access_token: ${{ github.token }}
- name: Clone repo
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Set up JDK
uses: actions/setup-java@v1
uses: actions/setup-java@v3
with:
java-version: 11
- name: Copy CI gradle.properties
run: |
mkdir -p ~/.gradle
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
java-version: 17
distribution: adopt
- name: Generate android.jar
run: |
@ -39,4 +34,4 @@ jobs:
- name: Build project
uses: gradle/gradle-command-action@v2
with:
arguments: :server:shadowJar
arguments: :inspector:shadowJar

View File

@ -5,19 +5,18 @@ on:
tags:
- 'v*'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Build artifacts and create draft release
runs-on: ubuntu-latest
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.9.0
with:
access_token: ${{ github.token }}
- name: Checkout ${{ github.ref }}
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
@ -25,14 +24,10 @@ jobs:
uses: gradle/wrapper-validation-action@v1
- name: Set up JDK
uses: actions/setup-java@v1
uses: actions/setup-java@v3
with:
java-version: 11
- name: Copy CI gradle.properties
run: |
mkdir -p ~/.gradle
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
java-version: 17
distribution: adopt
- name: Generate android.jar
run: |
@ -41,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
View File

@ -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-*

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,10 +1,10 @@
package android.text;
import android.graphics.drawable.Drawable;
import org.jetbrains.annotations.NotNull;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
import org.jsoup.safety.Safelist;
import org.xml.sax.XMLReader;
/**
@ -18,7 +18,7 @@ import org.xml.sax.XMLReader;
public class Html {
public static Spanned fromHtml(String source) {
return new FakeSpanned(Jsoup.clean(source, Whitelist.none()));
return new FakeSpanned(Jsoup.clean(source, Safelist.none()));
}
public static Spanned fromHtml(String source, Html.ImageGetter imageGetter, Html.TagHandler tagHandler) {

View File

@ -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!"); }

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>()

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -1,17 +1,15 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.6.10"
kotlin("plugin.serialization") version "1.6.10" apply false
kotlin("jvm") version "1.8.0"
kotlin("plugin.serialization") version "1.8.0" apply false
id("org.jmailen.kotlinter") version "3.9.0" apply false
id("com.github.gmazzo.buildconfig") version "3.0.3" apply false
id("com.github.johnrengelman.shadow") version "7.1.2" apply false
id("com.github.ben-manes.versions") version "0.39.0"
}
allprojects {
group = "suwayomi"
group = "tachiyomi"
version = "1.0"
repositories {
@ -27,20 +25,20 @@ allprojects {
val projects = listOf(
project(":AndroidCompat"),
project(":AndroidCompat:Config"),
project(":server")
project(":inspector")
)
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<KotlinCompile> {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
jvmTarget = JavaVersion.VERSION_17.toString()
}
}
@ -48,19 +46,17 @@ configure(projects) {
// Kotlin
implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect"))
testImplementation(kotlin("test"))
// coroutines
val coroutinesVersion = "1.6.0"
val coroutinesVersion = "1.6.4"
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion")
val kotlinSerializationVersion = "1.3.2"
val kotlinSerializationVersion = "1.4.0"
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")
@ -72,17 +68,16 @@ 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.14.3")
implementation("org.jsoup:jsoup:1.15.3")
// dependency of :AndroidCompat:Config
implementation("com.typesafe:config:1.4.2")
implementation("io.github.config4k:config4k:0.4.2")
// dex2jar
val dex2jarVersion = "v35"
val dex2jarVersion = "v71"
implementation("com.github.ThexXTURBOXx.dex2jar:dex-translator:$dex2jarVersion")
implementation("com.github.ThexXTURBOXx.dex2jar:dex-tools:$dex2jarVersion")

View File

@ -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

View File

@ -15,37 +15,28 @@ plugins {
dependencies {
// okhttp
val okhttpVersion = "4.9.3" // version is locked by Tachiyomi extensions
val okhttpVersion = "5.0.0-alpha.11" // version is locked by Tachiyomi extensions
implementation("com.squareup.okhttp3:okhttp:$okhttpVersion")
implementation("com.squareup.okhttp3:logging-interceptor:$okhttpVersion")
implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:$okhttpVersion")
implementation("com.squareup.okio:okio:3.0.0")
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")
implementation("io.reactivex:rxjava:1.3.8")
implementation("org.jsoup:jsoup:1.14.3")
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"))
// 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)
}
@ -59,7 +50,7 @@ sourceSets {
}
// should be bumped with each stable release
val inspectorVersion = "v0.7.2"
val inspectorVersion = "v1.4.3"
// counts commit count on master
val inspectorRevision = runCatching {
@ -80,7 +71,7 @@ val String.wrapped get() = """"$this""""
buildConfig {
className("BuildConfig")
packageName("suwayomi.server")
packageName("inspector")
useKotlinOutput()

View File

@ -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

View File

@ -5,34 +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()
}
}

View File

@ -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()
}
}

View File

@ -0,0 +1,26 @@
package eu.kanade.tachiyomi.network
import android.content.Context
import app.cash.quickjs.QuickJs
import eu.kanade.tachiyomi.util.lang.withIOContext
/**
* Util for evaluating JavaScript in sources.
*/
class JavaScriptEngine(context: Context) {
/**
* Evaluate arbitrary JavaScript code and get the result as a primtive type
* (e.g., String, Int).
*
* @since extensions-lib 1.4
* @param script JavaScript to execute.
* @return Result of JavaScript code as a primitive type.
*/
@Suppress("UNUSED", "UNCHECKED_CAST")
suspend fun <T> evaluate(script: String): T = withIOContext {
QuickJs.create().use {
it.evaluate(script) as T
}
}
}

View File

@ -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

View File

@ -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

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.network
import okhttp3.CacheControl
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.Request
import okhttp3.RequestBody
import java.util.concurrent.TimeUnit.MINUTES
@ -23,6 +24,21 @@ fun GET(
.build()
}
/**
* @since extensions-lib 1.4
*/
fun GET(
url: HttpUrl,
headers: Headers = DEFAULT_HEADERS,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
.url(url)
.headers(headers)
.cacheControl(cache)
.build()
}
fun POST(
url: String,
headers: Headers = DEFAULT_HEADERS,

View File

@ -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

View File

@ -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.

View File

@ -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")
}

View File

@ -20,6 +20,9 @@ interface Source {
*/
val name: String
val lang: String
get() = ""
/**
* Returns an observable with the updated details for a manga.
*

View File

@ -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) {

View File

@ -20,6 +20,8 @@ interface SManga : Serializable {
var thumbnail_url: String?
var update_strategy: UpdateStrategy
var initialized: Boolean
fun copyFrom(other: SManga) {

View File

@ -18,5 +18,7 @@ class SMangaImpl : SManga {
override var thumbnail_url: String? = null
override var update_strategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE
override var initialized: Boolean = false
}

View File

@ -0,0 +1,6 @@
package eu.kanade.tachiyomi.source.model
enum class UpdateStrategy {
ALWAYS_UPDATE,
ONLY_FETCH_ONCE
}

View File

@ -54,9 +54,10 @@ abstract class HttpSource : CatalogueSource {
* Note the generated id sets the sign bit to 0.
*/
override val id by lazy {
val key = "${name.toLowerCase()}/$lang/$versionId"
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
}
/**
@ -80,7 +81,7 @@ abstract class HttpSource : CatalogueSource {
/**
* Visible name of the source.
*/
override fun toString() = "$name (${lang.toUpperCase()})"
override fun toString() = "$name (${lang.uppercase()})"
/**
* Returns an observable containing a page with a list of manga. Normally it's not needed to
@ -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.
@ -355,6 +364,28 @@ abstract class HttpSource : CatalogueSource {
}
}
/**
* Returns the url of the provided manga
*
* @since extensions-lib 1.4
* @param manga the manga
* @return url of the manga
*/
open fun getMangaUrl(manga: SManga): String {
return mangaDetailsRequest(manga).url.toString()
}
/**
* Returns the url of the provided chapter
*
* @since extensions-lib 1.4
* @param chapter the chapter
* @return url of the chapter
*/
open fun getChapterUrl(chapter: SChapter): String {
return pageListRequest(chapter).url.toString()
}
/**
* Called before inserting a new chapter into database. Use it if you need to override chapter
* fields, like the title or the chapter number. Do not change anything to [manga].
@ -362,8 +393,7 @@ abstract class HttpSource : CatalogueSource {
* @param chapter the chapter to be added.
* @param manga the manga of the chapter.
*/
open fun prepareNewChapter(chapter: SChapter, manga: SManga) {
}
open fun prepareNewChapter(chapter: SChapter, manga: SManga) {}
/**
* Returns the list of filters for the source.

View File

@ -22,5 +22,5 @@ fun Element.attrOrText(css: String): String {
* @param html the body of the response. Use only if the body was read before calling this method.
*/
fun Response.asJsoup(html: String? = null): Document {
return Jsoup.parse(html ?: body!!.string(), request.url.toString())
return Jsoup.parse(html ?: body.string(), request.url.toString())
}

View File

@ -0,0 +1,8 @@
package eu.kanade.tachiyomi.util.lang
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) =
withContext(Dispatchers.IO, block)

View File

@ -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

View File

@ -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}")

View File

@ -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
@ -32,8 +33,8 @@ object PackageTools {
const val EXTENSION_FEATURE = "tachiyomi.extension"
const val METADATA_SOURCE_CLASS = "tachiyomi.extension.class"
const val LIB_VERSION_MIN = 1.2
const val LIB_VERSION_MAX = 1.3
const val LIB_VERSION_MIN = 1.3
const val LIB_VERSION_MAX = 1.4
/**
* Convert dex to jar, a wrapper for the dex2jar library
@ -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

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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())
}

View File

@ -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

View File

@ -1,6 +1,5 @@
rootProject.name = "Tachiyomi Extensions Inspector"
include("server")
include("AndroidCompat")
include("AndroidCompat:Config")
include("inspector")