Compare commits

..

46 Commits

Author SHA1 Message Date
Aria Moradi a325440f24 bump to v0.4.3
CI Publish / Validate Gradle Wrapper (push) Successful in 10s
CI Publish / Build artifacts and release (push) Failing after 22s
2021-06-17 00:28:19 +04:30
Aria Moradi 14072bb5a0 fix manga extensions not loading 2021-06-17 00:26:34 +04:30
Aria Moradi 7fc33ba8db fix cache 2021-06-06 03:19:03 +04:30
Aria Moradi 47e51b6615 fix naming 2021-06-06 03:13:09 +04:30
Aria Moradi 857562eaff add build flexiblity for Equinox 2021-06-06 02:48:26 +04:30
Aria Moradi bace854b50 rm dummy 2021-06-06 02:05:47 +04:30
Aria Moradi e4a404472d Merge branch 'master' of github.com:Suwayomi/Tachidesk 2021-06-06 02:05:26 +04:30
Aria Moradi 7bfa215b4c change old paths 2021-06-06 01:52:05 +04:30
Aria Moradi ab7af4b80b fix typo 2021-06-06 01:35:39 +04:30
Aria Moradi 2c7ebd8ece prepare for integration with Equinox 2021-06-06 01:34:49 +04:30
Syer10 c96da79058 Fix MacOS crashing on launch (#132) 2021-06-05 22:36:36 +04:30
Aria Moradi 8f09ebacf5 dummy file to trigger gh actions 2021-06-04 23:10:40 +04:30
Aria Moradi e21f3b9c75 closes #130 2021-06-04 21:51:48 +04:30
Aria Moradi 37eeef06e2 correct spelling 2021-06-04 16:34:19 +04:30
Aria Moradi b7fe56687c Bump styfle/cancel-workflow-action from 0.5.0 to 0.9.0 2021-06-04 13:35:20 +04:30
Aria Moradi 60565729ca lint by linter 2021-06-04 13:35:07 +04:30
Aria Moradi 36f4e1c340 move all packages to 'suwayomi.tachidesk' 2021-06-04 13:08:20 +04:30
Aria Moradi abc2a5214b Merge branch 'master' of github.com:Suwayomi/Tachidesk 2021-06-04 10:43:39 +04:30
Aria Moradi a29010e0d7 fix chached image returning file type with extra . 2021-06-04 10:43:03 +04:30
arbuilder db99ab66ae Update build_push.yml (#124)
* Update build_push.yml

docker workflow for preview build

* Update build_push.yml

remove cd master

* Update build_push.yml

Change access token
2021-06-01 13:27:57 +04:30
arbuilder 84cc73c149 Update publish.yml (#123)
* Update publish.yml

add docker build workflow

* Update publish.yml

Remove cd master

* Update publish.yml

Change access token
2021-06-01 13:25:38 +04:30
Aria Moradi 10a29cab33 Merge branch 'master' of github.com:Suwayomi/Tachidesk 2021-05-30 04:56:59 +04:30
Aria Moradi 849e2f103a [SKIP CI] download chapters for real now 2021-05-30 04:24:21 +04:30
Syer10 6c22fe193a Add meta info for clients to store custom data in (#113)
* Add meta info for clients to store custom data in

* PR comments

* Really update migration
2021-05-30 04:18:08 +04:30
Syer10 e69dbbf418 Working shared preferences (#112)
* Working shared preferences

* Remove unneeded prefs dir

* Todo
2021-05-30 04:05:56 +04:30
Aria Moradi dfa59a1691 bump version to v0.4.2
CI Publish / Validate Gradle Wrapper (push) Successful in 11s
CI Publish / Build artifacts and release (push) Failing after 17s
2021-05-30 04:04:11 +04:30
Aria Moradi 5023e96301 Implemented Dowloads front-end 2021-05-30 04:01:49 +04:30
Aria Moradi 224c24ee9f a little reminder 2021-05-30 02:21:43 +04:30
Aria Moradi e3b154cf9e Merge branch 'master' of github.com:Suwayomi/Tachidesk 2021-05-29 23:59:29 +04:30
Aria Moradi d249867c4c finishing touches of download backend, done @jipfr's requests 2021-05-29 23:57:22 +04:30
Aria Moradi b56045e984 downloader backend done 2021-05-29 23:05:51 +04:30
Manchewable 3777cc646e Improve continuous horizontal reader (#110)
* differentiate ContinuesHorizontalLTR and ContinuesHorizontalRTL

* fix displaying pages in horizontal viewer

* add scroll handler for horizontal mode

* update curPage when images pass through center of the screen

* add click events to navigate pages

* remove console.log

* fix click mapping for ContinuesHorizontalRTL

* remove disable eslint inline comment

* fix ContinuesHorizontalRTL not updating curPage on scroll

* add ability to click to drag

* add margin in between images
2021-05-29 19:41:59 +04:30
Manchewable aa5a1083d0 fit images to height (#108) 2021-05-28 23:27:31 +04:30
Manchewable 2ae5e0742e reference to img elements directly (#106) 2021-05-28 23:25:04 +04:30
Aria Moradi e5e875c54a closes #100 2021-05-28 20:21:05 +04:30
Aria Moradi 1a99ec76e4 spinner image, closes #77 2021-05-28 19:37:26 +04:30
Manchewable 1b122d1157 Add a Double Page Viewer (#105)
* add double page reader

* implement singleRTL

* add on image load handler

* add retry display time interval

* remove comments

* add double page wrapper

* fix image getting out of bounds

* remove comments

* remove unused styles

* return imageStyle as type CSSProperties

* rename DoublePagedReader to DoublePagedPager
2021-05-28 17:06:55 +04:30
Aria Moradi 77f2f8cc18 add copyright notice to files that miss it 2021-05-28 16:23:26 +04:30
Aria Moradi f0a99980b6 fixed issue with clearing up orphan chapters 2021-05-28 03:46:32 +04:30
Aria Moradi b0d43ffe69 anime filter everywhere
CI Publish / Validate Gradle Wrapper (push) Successful in 12s
CI Publish / Build artifacts and release (push) Failing after 17s
2021-05-28 03:02:14 +04:30
Aria Moradi 16cb0184a4 fix catalog source imports 2021-05-28 02:53:36 +04:30
Aria Moradi f211a33ea3 bump to v0.4.1 2021-05-28 02:49:01 +04:30
Aria Moradi 440c815189 missed from previous commit 2021-05-28 02:46:19 +04:30
Aria Moradi 25829aacfd new anime library 2021-05-28 02:43:30 +04:30
Aria Moradi 700a739f95 probably fixes http leaks (by @Syer10) 2021-05-27 22:45:44 +04:30
Aria Moradi d9620bec05 fix getManga returning false for inLibrary 2021-05-27 22:30:29 +04:30
221 changed files with 2387 additions and 852 deletions
+4 -4
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0
uses: styfle/cancel-workflow-action@0.9.0
with:
access_token: ${{ github.token }}
@@ -48,14 +48,14 @@ jobs:
- name: Download android.jar
run: |
cd master
curl https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
curl https://raw.githubusercontent.com/Suwayomi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
- name: Cache node_modules
uses: actions/cache@v2
with:
path: |
**/react/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }}
**/webUI/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/webUI/yarn.lock') }}
- name: Build and copy webUI, Build Jar
uses: eskatos/gradle-command-action@v1
+9 -5
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0
uses: styfle/cancel-workflow-action@0.9.0
with:
access_token: ${{ github.token }}
@@ -50,19 +50,19 @@ jobs:
- name: Download android.jar
run: |
cd master
curl https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
curl https://raw.githubusercontent.com/Suwayomi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
- name: Cache node_modules
uses: actions/cache@v2
with:
path: |
**/react/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }}
**/webUI/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/webUI/yarn.lock') }}
- name: Build and copy webUI, Build Jar
uses: eskatos/gradle-command-action@v1
env:
TachideskBuildType: "Preview"
ProductBuildType: "Preview"
with:
build-root-directory: master
wrapper-directory: master
@@ -127,3 +127,7 @@ jobs:
owner: "Suwayomi"
repo: "Tachidesk-preview"
tag: ${{ steps.GenTagName.outputs.value }}
- name: Run Docker build workflow
run: |
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token ${{ secrets.DEPLOY_PREVIEW_TOKEN }}" -d '{"ref":"main", "inputs":{"tachidesk_release_type": "preview"}}' https://api.github.com/repos/suwayomi/docker-tachidesk/actions/workflows/build_container_images.yml/dispatches
+10 -5
View File
@@ -24,7 +24,7 @@ jobs:
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0
uses: styfle/cancel-workflow-action@0.9.0
with:
access_token: ${{ github.token }}
@@ -49,19 +49,19 @@ jobs:
- name: Download android.jar
run: |
cd master
curl https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
curl https://raw.githubusercontent.com/Suwayomi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
- name: Cache node_modules
uses: actions/cache@v2
with:
path: |
**/react/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }}
**/webUI/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/webUI/yarn.lock') }}
- name: Build and copy webUI, Build Jar
uses: eskatos/gradle-command-action@v1
env:
TachideskBuildType: "Stable"
ProductBuildType: "Stable"
with:
build-root-directory: master
wrapper-directory: master
@@ -85,3 +85,8 @@ jobs:
tags: true
draft: true
verbose: true
- name: Run Docker build workflow
run: |
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token ${{ secrets.DEPLOY_PREVIEW_TOKEN }}" -d '{"ref":"main", "inputs":{"tachidesk_release_type": "stable"}}' https://api.github.com/repos/suwayomi/docker-tachidesk/actions/workflows/build_container_images.yml/dispatches
+1 -1
View File
@@ -6,7 +6,7 @@ gradle.properties
# Ignore Gradle build output directory
build
server/src/main/resources/react
server/src/main/resources/webUI
server/tmp/
server/tachiserver-data/
@@ -12,7 +12,7 @@ import net.harawata.appdirs.AppDirsFactory
val ApplicationRootDir: String
get(): String {
return System.getProperty(
"suwayomi.server.rootDir",
"suwayomi.tachidesk.server.rootDir",
AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)
)
}
@@ -17,4 +17,4 @@ fun setLogLevel(level: Level) {
}
fun debugLogsEnabled(config: Config)
= System.getProperty("suwayomi.server.debugLogsEnabled", config.getString("server.debugLogsEnabled")).toBoolean()
= System.getProperty("suwayomi.tachidesk.server.debugLogsEnabled", config.getString("server.debugLogsEnabled")).toBoolean()
+12
View File
@@ -1,6 +1,7 @@
plugins {
application
kotlin("plugin.serialization")
}
@@ -46,6 +47,17 @@ dependencies {
implementation("org.mozilla:rhino-runtime:1.7.13")
// 'org.mozilla:rhino-engine' provides the same interface as 'javax.script' a.k.a Nashorn
implementation("org.mozilla:rhino-engine:1.7.13")
// Kotlin wrapper around Java Preferences, makes certain things easier
val multiplatformSettingsVersion = "0.7.7"
implementation("com.russhwolf:multiplatform-settings-jvm:$multiplatformSettingsVersion")
implementation("com.russhwolf:multiplatform-settings-serialization-jvm:$multiplatformSettingsVersion")
}
tasks {
withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn")
}
}
//def fatJarTask = tasks.getByPath(':AndroidCompat:JVMPatch:fatJar')
@@ -38,7 +38,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl;
import xyz.nulldev.androidcompat.io.AndroidFiles;
import xyz.nulldev.androidcompat.io.sharedprefs.JsonSharedPreferences;
import xyz.nulldev.androidcompat.io.sharedprefs.JavaSharedPreferences;
import xyz.nulldev.androidcompat.service.ServiceSupport;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
@@ -165,23 +165,22 @@ public class CustomContext extends Context implements DIAware {
/** Fake shared prefs! **/
private Map<String, SharedPreferences> prefs = new HashMap<>(); //Cache
private File sharedPrefsFileFromString(String s) {
return new File(androidFiles.getPrefsDir(), s + ".json");
}
@Override
public synchronized SharedPreferences getSharedPreferences(String s, int i) {
SharedPreferences preferences = prefs.get(s);
//Create new shared preferences if one does not exist
if(preferences == null) {
preferences = getSharedPreferences(sharedPrefsFileFromString(s), i);
preferences = new JavaSharedPreferences(s);
prefs.put(s, preferences);
}
return preferences;
}
public SharedPreferences getSharedPreferences(File file, int mode) {
return new JsonSharedPreferences(file);
@Override
public SharedPreferences getSharedPreferences(@NotNull File file, int mode) {
String path = file.getAbsolutePath().replace('\\', '/');
int firstSlash = path.indexOf("/");
return new JavaSharedPreferences(path.substring(firstSlash));
}
@Override
@@ -191,8 +190,8 @@ public class CustomContext extends Context implements DIAware {
@Override
public boolean deleteSharedPreferences(String name) {
prefs.remove(name);
return sharedPrefsFileFromString(name).delete();
JavaSharedPreferences item = (JavaSharedPreferences) prefs.remove(name);
return item.deleteAll();
}
@Override
@@ -26,8 +26,6 @@ class AndroidFiles(val configManager: ConfigManager = GlobalConfigManager) {
val downloadCacheDir: File get() = registerFile(filesConfig.downloadCacheDir)
val databasesDir: File get() = registerFile(filesConfig.databasesDir)
val prefsDir: File get() = registerFile(filesConfig.prefsDir)
val packagesDir: File get() = registerFile(filesConfig.packageDir)
fun registerFile(file: String): File {
@@ -0,0 +1,168 @@
package xyz.nulldev.androidcompat.io.sharedprefs
import android.content.SharedPreferences
import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.ExperimentalSettingsImplementation
import com.russhwolf.settings.JvmPreferencesSettings
import com.russhwolf.settings.serialization.decodeValue
import com.russhwolf.settings.serialization.encodeValue
import com.russhwolf.settings.set
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.builtins.SetSerializer
import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.builtins.serializer
import java.util.prefs.PreferenceChangeListener
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 preferences = JvmPreferencesSettings(javaPreferences)
private val listeners = mutableMapOf<SharedPreferences.OnSharedPreferenceChangeListener, PreferenceChangeListener>()
// TODO: 2021-05-29 Need to find a way to get this working with all pref types
override fun getAll(): MutableMap<String, *> {
return preferences.keys.associateWith { preferences.getStringOrNull(it) }.toMutableMap()
}
override fun getString(key: String, defValue: String?): String? {
return if (defValue != null) {
preferences.getString(key, defValue)
} else {
preferences.getStringOrNull(key)
}
}
override fun getStringSet(key: String, defValues: MutableSet<String>?): MutableSet<String>? {
try {
return if (defValues != null) {
preferences.decodeValue(SetSerializer(String.serializer()).nullable, key, defValues)
} else {
preferences.decodeValue(SetSerializer(String.serializer()).nullable, key, null)
}?.toMutableSet()
} catch (e: SerializationException) {
throw ClassCastException("$key was not a StringSet")
}
}
override fun getInt(key: String, defValue: Int): Int {
return preferences.getInt(key, defValue)
}
override fun getLong(key: String, defValue: Long): Long {
return preferences.getLong(key, defValue)
}
override fun getFloat(key: String, defValue: Float): Float {
return preferences.getFloat(key, defValue)
}
override fun getBoolean(key: String, defValue: Boolean): Boolean {
return preferences.getBoolean(key, defValue)
}
override fun contains(key: String): Boolean {
return key in preferences.keys
}
override fun edit(): SharedPreferences.Editor {
return Editor(preferences)
}
class Editor(private val preferences: JvmPreferencesSettings) : SharedPreferences.Editor {
val itemsToAdd = mutableMapOf<String, Any>()
override fun putString(key: String, value: String?): SharedPreferences.Editor {
if (value != null) {
itemsToAdd[key] = value
} else {
remove(key)
}
return this
}
override fun putStringSet(
key: String,
values: MutableSet<String>?
): SharedPreferences.Editor {
if (values != null) {
itemsToAdd[key] = values
} else {
remove(key)
}
return this
}
override fun putInt(key: String, value: Int): SharedPreferences.Editor {
itemsToAdd[key] = value
return this
}
override fun putLong(key: String, value: Long): SharedPreferences.Editor {
itemsToAdd[key] = value
return this
}
override fun putFloat(key: String, value: Float): SharedPreferences.Editor {
itemsToAdd[key] = value
return this
}
override fun putBoolean(key: String, value: Boolean): SharedPreferences.Editor {
itemsToAdd[key] = value
return this
}
override fun remove(key: String): SharedPreferences.Editor {
itemsToAdd.remove(key)
return this
}
override fun clear(): SharedPreferences.Editor {
itemsToAdd.clear()
return this
}
override fun commit(): Boolean {
addToPreferences()
return true
}
override fun apply() {
addToPreferences()
}
private fun addToPreferences() {
itemsToAdd.forEach { (key, value) ->
@Suppress("UNCHECKED_CAST")
when (value) {
is Set<*> -> preferences.encodeValue(SetSerializer(String.serializer()), key, value as Set<String>)
else -> {
preferences[key] = value
}
}
}
}
}
override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
val javaListener = PreferenceChangeListener {
listener.onSharedPreferenceChanged(this, it.key)
}
listeners[listener] = javaListener
javaPreferences.addPreferenceChangeListener(javaListener)
}
override fun unregisterOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
val registeredListener = listeners.remove(listener)
if (registeredListener != null) {
javaPreferences.removePreferenceChangeListener(registeredListener)
}
}
fun deleteAll(): Boolean {
javaPreferences.removeNode()
return true
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ Here is a list of current features:
- A library to save your mangas and categories to put them into.
- Searching and browsing installed sources.
- A decent chapter reader.
- Ability to download Mangas for offline read(This partially works)
- Ability to download Mangas for offline read
- Backup and restore support powered by Tachiyomi Legacy Backups
**Note:** Keep in mind that Tachidesk is alpha software and can break rarely and/or with each update. See [Troubleshooting](https://github.com/Suwayomi/Tachidesk/wiki/Troubleshooting) if it happens.
+5
View File
@@ -2,6 +2,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.4.32"
kotlin("plugin.serialization") version "1.4.32" apply false
}
allprojects {
@@ -50,6 +51,10 @@ configure(projects) {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion")
val kotlinSerializationVersion = "1.1.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.5.0")
@@ -1 +1 @@
jre\bin\java -Dsuwayomi.server.debugLogsEnabled=true -jar Tachidesk.jar
jre\bin\java -Dsuwayomi.tachidesk.server.debugLogsEnabled=true -jar Tachidesk.jar
@@ -1 +1 @@
jre\bin\javaw "-Dsuwayomi.server.webInterface=electron" "-Dsuwayomi.server.electronPath=electron/electron.exe" -jar Tachidesk.jar
jre\bin\javaw "-Dsuwayomi.tachidesk.server.webInterface=electron" "-Dsuwayomi.tachidesk.server.electronPath=electron/electron.exe" -jar Tachidesk.jar
+2 -2
View File
@@ -22,9 +22,9 @@ jre_dir="jdk8u292-b10-jre"
echo "creating windows bundle"
jar=$(ls ../server/build/Tachidesk-*.jar)
jar=$(ls ../server/build/*.jar | tail -n1)
jar_name=$(echo $jar | cut -d'/' -f4)
release_name=$(echo $jar_name | cut -d'.' -f4 --complement)-$arch
release_name=$(echo $jar_name | sed 's/.jar//')-$arch
# make release dir
+4 -5
View File
@@ -73,7 +73,7 @@ dependencies {
testImplementation(kotlin("test-junit5"))
}
val MainClass = "suwayomi.MainKt"
val MainClass = "suwayomi.tachidesk.MainKt"
application {
mainClass.set(MainClass)
@@ -93,11 +93,11 @@ sourceSets {
}
// should be bumped with each stable release
val tachideskVersion = "v0.4.0"
val tachideskVersion = System.getenv("ProductVersion") ?: "v0.4.3"
// counts commit count on master
val tachideskRevision = runCatching {
Runtime
System.getenv("ProductRevision") ?: Runtime
.getRuntime()
.exec("git rev-list HEAD --count")
.let { process ->
@@ -107,7 +107,6 @@ val tachideskRevision = runCatching {
}
process.destroy()
"r" + output.trim()
}
}.getOrDefault("r0")
@@ -119,7 +118,7 @@ buildConfig {
buildConfigField("String", "NAME", rootProject.name)
buildConfigField("String", "VERSION", tachideskVersion)
buildConfigField("String", "REVISION", tachideskRevision)
buildConfigField("String", "BUILD_TYPE", if (System.getenv("TachideskBuildType") == "Stable") "Stable" else "Preview")
buildConfigField("String", "BUILD_TYPE", if (System.getenv("ProductBuildType") == "Stable") "Stable" else "Preview")
buildConfigField("long", "BUILD_TIME", Instant.now().epochSecond.toString())
buildConfigField("String", "GITHUB", "https://github.com/Suwayomi/Tachidesk")
@@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.source
package eu.kanade.tachiyomi.animesource
import eu.kanade.tachiyomi.source.model.AnimesPage
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import rx.Observable
interface AnimeCatalogueSource : AnimeSource {
@@ -30,7 +30,7 @@ interface AnimeCatalogueSource : AnimeSource {
* @param query the search query.
* @param filters the list of filters to apply.
*/
fun fetchSearchAnime(page: Int, query: String, filters: FilterList): Observable<AnimesPage>
fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage>
/**
* Returns an observable containing a page with a list of latest anime updates.
@@ -42,5 +42,5 @@ interface AnimeCatalogueSource : AnimeSource {
/**
* Returns the list of filters for the source.
*/
fun getFilterList(): FilterList
fun getFilterList(): AnimeFilterList
}
@@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.source
package eu.kanade.tachiyomi.animesource
import eu.kanade.tachiyomi.source.model.SAnime
import eu.kanade.tachiyomi.source.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import rx.Observable
/**
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.source
package eu.kanade.tachiyomi.animesource
/**
* A factory for creating sources at runtime.
@@ -1,9 +1,9 @@
package eu.kanade.tachiyomi.source
package eu.kanade.tachiyomi.animesource
import android.content.Context
import eu.kanade.tachiyomi.source.model.SAnime
import eu.kanade.tachiyomi.source.model.SEpisode
import eu.kanade.tachiyomi.source.online.AnimeHttpSource
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import rx.Observable
open class AnimeSourceManager(private val context: Context) {
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.source
package eu.kanade.tachiyomi.animesource
import android.support.v7.preference.PreferenceScreen
@@ -0,0 +1,40 @@
package eu.kanade.tachiyomi.animesource.model
sealed class AnimeFilter<T>(val name: String, var state: T) {
open class Header(name: String) : AnimeFilter<Any>(name, 0)
open class Separator(name: String = "") : AnimeFilter<Any>(name, 0)
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) : AnimeFilter<Int>(name, state)
abstract class Text(name: String, state: String = "") : AnimeFilter<String>(name, state)
abstract class CheckBox(name: String, state: Boolean = false) : AnimeFilter<Boolean>(name, state)
abstract class TriState(name: String, state: Int = STATE_IGNORE) : AnimeFilter<Int>(name, state) {
fun isIgnored() = state == STATE_IGNORE
fun isIncluded() = state == STATE_INCLUDE
fun isExcluded() = state == STATE_EXCLUDE
companion object {
const val STATE_IGNORE = 0
const val STATE_INCLUDE = 1
const val STATE_EXCLUDE = 2
}
}
abstract class Group<V>(name: String, state: List<V>) : AnimeFilter<List<V>>(name, state)
abstract class Sort(name: String, val values: Array<String>, state: Selection? = null) :
AnimeFilter<Sort.Selection?>(name, state) {
data class Selection(val index: Int, val ascending: Boolean)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is AnimeFilter<*>) return false
return name == other.name && state == other.state
}
override fun hashCode(): Int {
var result = name.hashCode()
result = 31 * result + (state?.hashCode() ?: 0)
return result
}
}
@@ -0,0 +1,6 @@
package eu.kanade.tachiyomi.animesource.model
data class AnimeFilterList(val list: List<AnimeFilter<*>>) : List<AnimeFilter<*>> by list {
constructor(vararg fs: AnimeFilter<*>) : this(if (fs.isNotEmpty()) fs.asList() else emptyList())
}
@@ -1,3 +1,3 @@
package eu.kanade.tachiyomi.source.model
package eu.kanade.tachiyomi.animesource.model
data class AnimesPage(val animes: List<SAnime>, val hasNextPage: Boolean)
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.source.model
package eu.kanade.tachiyomi.animesource.model
import java.io.Serializable
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.source.model
package eu.kanade.tachiyomi.animesource.model
class SAnimeImpl : SAnime {
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.source.model
package eu.kanade.tachiyomi.animesource.model
import java.io.Serializable
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.source.model
package eu.kanade.tachiyomi.animesource.model
class SEpisodeImpl : SEpisode {
@@ -1,15 +1,15 @@
package eu.kanade.tachiyomi.source.online
package eu.kanade.tachiyomi.animesource.online
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.newCallWithProgress
import eu.kanade.tachiyomi.source.AnimeCatalogueSource
import eu.kanade.tachiyomi.source.model.AnimesPage
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SAnime
import eu.kanade.tachiyomi.source.model.SEpisode
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
@@ -118,7 +118,7 @@ abstract class AnimeHttpSource : AnimeCatalogueSource {
* @param query the search query.
* @param filters the list of filters to apply.
*/
override fun fetchSearchAnime(page: Int, query: String, filters: FilterList): Observable<AnimesPage> {
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {
return client.newCall(searchAnimeRequest(page, query, filters))
.asObservableSuccess()
.map { response ->
@@ -133,7 +133,7 @@ abstract class AnimeHttpSource : AnimeCatalogueSource {
* @param query the search query.
* @param filters the list of filters to apply.
*/
protected abstract fun searchAnimeRequest(page: Int, query: String, filters: FilterList): Request
protected abstract fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request
/**
* Parses the response from the site and returns a [AnimesPage] object.
@@ -380,7 +380,7 @@ abstract class AnimeHttpSource : AnimeCatalogueSource {
/**
* Returns the list of filters for the source.
*/
override fun getFilterList() = FilterList()
override fun getFilterList() = AnimeFilterList()
companion object {
const val DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.63"
@@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.source.online
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.source.model.Page
import rx.Observable
@@ -1,9 +1,9 @@
package eu.kanade.tachiyomi.source.online
package eu.kanade.tachiyomi.animesource.online
import eu.kanade.tachiyomi.source.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SAnime
import eu.kanade.tachiyomi.source.model.SEpisode
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Response
import org.jsoup.nodes.Document
@@ -1,12 +0,0 @@
package suwayomi.anime.impl.extension.github
data class OnlineExtension(
val name: String,
val pkgName: String,
val versionName: String,
val versionCode: Int,
val lang: String,
val isNsfw: Boolean,
val apkName: String,
val iconUrl: String
)
@@ -1,4 +1,4 @@
package suwayomi
package suwayomi.tachidesk
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -7,8 +7,8 @@ package suwayomi
* 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 suwayomi.server.JavalinSetup.javalinSetup
import suwayomi.server.applicationSetup
import suwayomi.tachidesk.server.JavalinSetup.javalinSetup
import suwayomi.tachidesk.server.applicationSetup
fun main() {
applicationSetup()
@@ -1,4 +1,4 @@
package suwayomi.anime
package suwayomi.tachidesk.anime
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -8,20 +8,20 @@ package suwayomi.anime
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import io.javalin.Javalin
import suwayomi.anime.impl.Anime.getAnime
import suwayomi.anime.impl.Anime.getAnimeThumbnail
import suwayomi.anime.impl.AnimeList.getAnimeList
import suwayomi.anime.impl.Episode.getEpisode
import suwayomi.anime.impl.Episode.getEpisodeList
import suwayomi.anime.impl.Episode.modifyEpisode
import suwayomi.anime.impl.Source.getAnimeSource
import suwayomi.anime.impl.Source.getSourceList
import suwayomi.anime.impl.extension.Extension.getExtensionIcon
import suwayomi.anime.impl.extension.Extension.installExtension
import suwayomi.anime.impl.extension.Extension.uninstallExtension
import suwayomi.anime.impl.extension.Extension.updateExtension
import suwayomi.anime.impl.extension.ExtensionsList.getExtensionList
import suwayomi.server.JavalinSetup.future
import suwayomi.tachidesk.anime.impl.Anime.getAnime
import suwayomi.tachidesk.anime.impl.Anime.getAnimeThumbnail
import suwayomi.tachidesk.anime.impl.AnimeList.getAnimeList
import suwayomi.tachidesk.anime.impl.Episode.getEpisode
import suwayomi.tachidesk.anime.impl.Episode.getEpisodeList
import suwayomi.tachidesk.anime.impl.Episode.modifyEpisode
import suwayomi.tachidesk.anime.impl.Source.getAnimeSource
import suwayomi.tachidesk.anime.impl.Source.getSourceList
import suwayomi.tachidesk.anime.impl.extension.Extension.getExtensionIcon
import suwayomi.tachidesk.anime.impl.extension.Extension.installExtension
import suwayomi.tachidesk.anime.impl.extension.Extension.uninstallExtension
import suwayomi.tachidesk.anime.impl.extension.Extension.updateExtension
import suwayomi.tachidesk.anime.impl.extension.ExtensionsList.getExtensionList
import suwayomi.tachidesk.server.JavalinSetup.future
object AnimeAPI {
fun defineEndpoints(app: Javalin) {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl
package suwayomi.tachidesk.anime.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -7,25 +7,25 @@ package suwayomi.anime.impl
* 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.animesource.model.SAnime
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.SAnime
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.anime.impl.AnimeList.proxyThumbnailUrl
import suwayomi.anime.impl.Source.getAnimeSource
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.AnimeDataClass
import suwayomi.anime.model.table.AnimeStatus
import suwayomi.anime.model.table.AnimeTable
import suwayomi.server.ApplicationDirs
import suwayomi.tachidesk.impl.util.lang.awaitSingle
import suwayomi.tachidesk.impl.util.network.await
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.clearCachedImage
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.anime.impl.AnimeList.proxyThumbnailUrl
import suwayomi.tachidesk.anime.impl.Source.getAnimeSource
import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.tachidesk.anime.model.dataclass.AnimeDataClass
import suwayomi.tachidesk.anime.model.table.AnimeStatus
import suwayomi.tachidesk.anime.model.table.AnimeTable
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.clearCachedImage
import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.server.ApplicationDirs
import java.io.InputStream
object Anime {
@@ -102,7 +102,7 @@ object Anime {
fetchedAnime.description,
fetchedAnime.genre,
AnimeStatus.valueOf(fetchedAnime.status).name,
false,
animeEntry[AnimeTable.inLibrary],
getAnimeSource(animeEntry[AnimeTable.sourceReference]),
true
)
@@ -1,4 +1,4 @@
package suwayomi.anime.impl
package suwayomi.tachidesk.anime.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -7,16 +7,16 @@ package suwayomi.anime.impl
* 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.source.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.AnimeDataClass
import suwayomi.anime.model.dataclass.PagedAnimeListDataClass
import suwayomi.anime.model.table.AnimeStatus
import suwayomi.anime.model.table.AnimeTable
import suwayomi.tachidesk.impl.util.lang.awaitSingle
import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.tachidesk.anime.model.dataclass.AnimeDataClass
import suwayomi.tachidesk.anime.model.dataclass.PagedAnimeListDataClass
import suwayomi.tachidesk.anime.model.table.AnimeStatus
import suwayomi.tachidesk.anime.model.table.AnimeTable
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
object AnimeList {
fun proxyThumbnailUrl(animeId: Int): String {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl
package suwayomi.tachidesk.anime.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -7,8 +7,8 @@ package suwayomi.anime.impl
* 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.source.model.SAnime
import eu.kanade.tachiyomi.source.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import org.jetbrains.exposed.sql.SortOrder.DESC
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere
@@ -16,13 +16,13 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.anime.impl.Anime.getAnime
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.EpisodeDataClass
import suwayomi.anime.model.table.AnimeTable
import suwayomi.anime.model.table.EpisodeTable
import suwayomi.anime.model.table.toDataClass
import suwayomi.tachidesk.impl.util.lang.awaitSingle
import suwayomi.tachidesk.anime.impl.Anime.getAnime
import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.tachidesk.anime.model.dataclass.EpisodeDataClass
import suwayomi.tachidesk.anime.model.table.AnimeTable
import suwayomi.tachidesk.anime.model.table.EpisodeTable
import suwayomi.tachidesk.anime.model.table.toDataClass
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
object Episode {
/** get episode list when showing an anime */
@@ -1,4 +1,4 @@
package suwayomi.anime.impl
package suwayomi.tachidesk.anime.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -11,11 +11,11 @@ import mu.KotlinLogging
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.anime.impl.extension.Extension.getExtensionIconUrl
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.AnimeSourceDataClass
import suwayomi.anime.model.table.AnimeExtensionTable
import suwayomi.anime.model.table.AnimeSourceTable
import suwayomi.tachidesk.anime.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.tachidesk.anime.model.dataclass.AnimeSourceDataClass
import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import suwayomi.tachidesk.anime.model.table.AnimeSourceTable
object Source {
private val logger = KotlinLogging.logger {}
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.extension
package suwayomi.tachidesk.anime.impl.extension
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -8,11 +8,11 @@ package suwayomi.anime.impl.extension
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.net.Uri
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
import eu.kanade.tachiyomi.animesource.AnimeSource
import eu.kanade.tachiyomi.animesource.AnimeSourceFactory
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.AnimeCatalogueSource
import eu.kanade.tachiyomi.source.AnimeSource
import eu.kanade.tachiyomi.source.AnimeSourceFactory
import mu.KotlinLogging
import okhttp3.Request
import okio.buffer
@@ -25,23 +25,23 @@ import org.jetbrains.exposed.sql.update
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.anime.impl.extension.ExtensionsList.extensionTableAsDataClass
import suwayomi.anime.impl.extension.github.ExtensionGithubApi
import suwayomi.anime.impl.util.PackageTools.EXTENSION_FEATURE
import suwayomi.anime.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.anime.impl.util.PackageTools.LIB_VERSION_MIN
import suwayomi.anime.impl.util.PackageTools.METADATA_NSFW
import suwayomi.anime.impl.util.PackageTools.METADATA_SOURCE_CLASS
import suwayomi.anime.impl.util.PackageTools.dex2jar
import suwayomi.anime.impl.util.PackageTools.getPackageInfo
import suwayomi.anime.impl.util.PackageTools.getSignatureHash
import suwayomi.anime.impl.util.PackageTools.loadExtensionSources
import suwayomi.anime.impl.util.PackageTools.trustedSignatures
import suwayomi.anime.model.table.AnimeExtensionTable
import suwayomi.anime.model.table.AnimeSourceTable
import suwayomi.server.ApplicationDirs
import suwayomi.tachidesk.impl.util.network.await
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.anime.impl.extension.ExtensionsList.extensionTableAsDataClass
import suwayomi.tachidesk.anime.impl.extension.github.ExtensionGithubApi
import suwayomi.tachidesk.anime.impl.util.PackageTools.EXTENSION_FEATURE
import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MIN
import suwayomi.tachidesk.anime.impl.util.PackageTools.METADATA_NSFW
import suwayomi.tachidesk.anime.impl.util.PackageTools.METADATA_SOURCE_CLASS
import suwayomi.tachidesk.anime.impl.util.PackageTools.dex2jar
import suwayomi.tachidesk.anime.impl.util.PackageTools.getPackageInfo
import suwayomi.tachidesk.anime.impl.util.PackageTools.getSignatureHash
import suwayomi.tachidesk.anime.impl.util.PackageTools.loadExtensionSources
import suwayomi.tachidesk.anime.impl.util.PackageTools.trustedSignatures
import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import suwayomi.tachidesk.anime.model.table.AnimeSourceTable
import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.InputStream
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.extension
package suwayomi.tachidesk.anime.impl.extension
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -14,11 +14,11 @@ import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.anime.impl.extension.Extension.getExtensionIconUrl
import suwayomi.anime.impl.extension.github.ExtensionGithubApi
import suwayomi.anime.impl.extension.github.OnlineExtension
import suwayomi.anime.model.dataclass.AnimeExtensionDataClass
import suwayomi.anime.model.table.AnimeExtensionTable
import suwayomi.tachidesk.anime.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.anime.impl.extension.github.ExtensionGithubApi
import suwayomi.tachidesk.anime.impl.extension.github.OnlineExtension
import suwayomi.tachidesk.anime.model.dataclass.AnimeExtensionDataClass
import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import java.util.concurrent.ConcurrentHashMap
object ExtensionsList {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.extension.github
package suwayomi.tachidesk.anime.impl.extension.github
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -13,23 +13,22 @@ import com.google.gson.JsonArray
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.NetworkHelper
import okhttp3.Request
import suwayomi.anime.model.dataclass.AnimeExtensionDataClass
import suwayomi.tachidesk.impl.util.network.UnzippingInterceptor
import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MIN
import suwayomi.tachidesk.anime.model.dataclass.AnimeExtensionDataClass
import suwayomi.tachidesk.manga.impl.util.network.UnzippingInterceptor
import uy.kohesive.injekt.injectLazy
object ExtensionGithubApi {
const val BASE_URL = "https://raw.githubusercontent.com"
const val REPO_URL_PREFIX = "$BASE_URL/jmir1/tachiyomi-extensions/repo"
private const val LIB_VERSION_MIN = 1.3
private const val LIB_VERSION_MAX = 1.3
private const val BASE_URL = "https://raw.githubusercontent.com"
private const val REPO_URL_PREFIX = "$BASE_URL/jmir1/tachiyomi-extensions/repo"
private fun parseResponse(json: JsonArray): List<OnlineExtension> {
return json
.map { it.asJsonObject }
.filter { element ->
val versionName = element["version"].string
val libVersion = versionName.substringBeforeLast('.').toDouble()
val libVersion = versionName.substringBeforeLast('.').toInt()
libVersion in LIB_VERSION_MIN..LIB_VERSION_MAX
}
.map { element ->
@@ -69,7 +68,7 @@ object ExtensionGithubApi {
.build()
}
private fun getRepo(): com.google.gson.JsonArray {
private fun getRepo(): JsonArray {
val request = Request.Builder()
.url("$REPO_URL_PREFIX/index.json.gz")
.build()
@@ -0,0 +1,19 @@
package suwayomi.tachidesk.anime.impl.extension.github
/*
* 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/. */
data class OnlineExtension(
val name: String,
val pkgName: String,
val versionName: String,
val versionCode: Int,
val lang: String,
val isNsfw: Boolean,
val apkName: String,
val iconUrl: String
)
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.util
package suwayomi.tachidesk.anime.impl.util
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -7,18 +7,18 @@ package suwayomi.anime.impl.util
* 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.source.AnimeSource
import eu.kanade.tachiyomi.source.AnimeSourceFactory
import eu.kanade.tachiyomi.source.online.AnimeHttpSource
import eu.kanade.tachiyomi.animesource.AnimeSource
import eu.kanade.tachiyomi.animesource.AnimeSourceFactory
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.anime.impl.util.PackageTools.loadExtensionSources
import suwayomi.anime.model.table.AnimeExtensionTable
import suwayomi.anime.model.table.AnimeSourceTable
import suwayomi.server.ApplicationDirs
import suwayomi.tachidesk.anime.impl.util.PackageTools.loadExtensionSources
import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import suwayomi.tachidesk.anime.model.table.AnimeSourceTable
import suwayomi.tachidesk.server.ApplicationDirs
import java.util.concurrent.ConcurrentHashMap
object GetAnimeHttpSource {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.util
package suwayomi.tachidesk.anime.impl.util
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -22,7 +22,7 @@ import org.kodein.di.conf.global
import org.kodein.di.instance
import org.w3c.dom.Element
import org.w3c.dom.Node
import suwayomi.server.ApplicationDirs
import suwayomi.tachidesk.server.ApplicationDirs
import xyz.nulldev.androidcompat.pm.InstalledPackage.Companion.toList
import xyz.nulldev.androidcompat.pm.toPackageInfo
import java.io.File
@@ -40,8 +40,8 @@ object PackageTools {
const val METADATA_SOURCE_CLASS = "tachiyomi.animeextension.class"
const val METADATA_SOURCE_FACTORY = "tachiyomi.animeextension.factory"
const val METADATA_NSFW = "tachiyomi.animeextension.nsfw"
const val LIB_VERSION_MIN = 1.3
const val LIB_VERSION_MAX = 1.3
const val LIB_VERSION_MIN = 10
const val LIB_VERSION_MAX = 10
private const val officialSignature = "50ab1d1e3a20d204d0ad6d334c7691c632e41b98dfa132bf385695fdfa63839c" // jmir1's key
var trustedSignatures = mutableSetOf<String>() + officialSignature
@@ -1,4 +1,4 @@
package suwayomi.anime.model.dataclass
package suwayomi.tachidesk.anime.model.dataclass
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -7,7 +7,7 @@ package suwayomi.anime.model.dataclass
* 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 suwayomi.anime.model.table.AnimeStatus
import suwayomi.tachidesk.anime.model.table.AnimeStatus
data class AnimeDataClass(
val id: Int,
@@ -1,4 +1,4 @@
package suwayomi.anime.model.dataclass
package suwayomi.tachidesk.anime.model.dataclass
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.model.dataclass
package suwayomi.tachidesk.anime.model.dataclass
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.model.dataclass
package suwayomi.tachidesk.anime.model.dataclass
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.model.table
package suwayomi.tachidesk.anime.model.table
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.model.table
package suwayomi.tachidesk.anime.model.table
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.anime.model.table
package suwayomi.tachidesk.anime.model.table
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -7,12 +7,12 @@ package suwayomi.anime.model.table
* 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.source.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SAnime
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.MangaStatus.Companion
import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.manga.model.table.MangaStatus.Companion
object AnimeTable : IntIdTable() {
val url = varchar("url", 2048)
@@ -1,4 +1,4 @@
package suwayomi.anime.model.table
package suwayomi.tachidesk.anime.model.table
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -11,7 +11,7 @@ import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.anime.model.dataclass.EpisodeDataClass
import suwayomi.tachidesk.anime.model.dataclass.EpisodeDataClass
object EpisodeTable : IntIdTable() {
val url = varchar("url", 2048)
@@ -1,3 +0,0 @@
package suwayomi.tachidesk.impl.backup.legacy.models
data class DHistory(val url: String, val lastRead: Long)
@@ -1,28 +0,0 @@
package suwayomi.tachidesk.impl.download
import org.jetbrains.exposed.sql.ResultRow
import java.util.concurrent.LinkedBlockingQueue
/*
* 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/. */
data class Download(
val chapter: ResultRow,
)
private val downloadQueue = LinkedBlockingQueue<Download>()
class Downloader {
fun start() {
TODO()
}
fun stop() {
TODO()
}
}
@@ -1,12 +0,0 @@
package suwayomi.tachidesk.impl.extension.github
data class OnlineExtension(
val name: String,
val pkgName: String,
val versionName: String,
val versionCode: Int,
val lang: String,
val isNsfw: Boolean,
val apkName: String,
val iconUrl: String
)
@@ -1,4 +1,4 @@
package suwayomi.tachidesk
package suwayomi.tachidesk.manga
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -8,36 +8,39 @@ package suwayomi.tachidesk
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import io.javalin.Javalin
import suwayomi.server.JavalinSetup.future
import suwayomi.server.impl.About
import suwayomi.tachidesk.impl.Category
import suwayomi.tachidesk.impl.CategoryManga.addMangaToCategory
import suwayomi.tachidesk.impl.CategoryManga.getCategoryMangaList
import suwayomi.tachidesk.impl.CategoryManga.getMangaCategories
import suwayomi.tachidesk.impl.CategoryManga.removeMangaFromCategory
import suwayomi.tachidesk.impl.Chapter.getChapter
import suwayomi.tachidesk.impl.Chapter.getChapterList
import suwayomi.tachidesk.impl.Chapter.modifyChapter
import suwayomi.tachidesk.impl.Library.addMangaToLibrary
import suwayomi.tachidesk.impl.Library.getLibraryMangas
import suwayomi.tachidesk.impl.Library.removeMangaFromLibrary
import suwayomi.tachidesk.impl.Manga.getManga
import suwayomi.tachidesk.impl.Manga.getMangaThumbnail
import suwayomi.tachidesk.impl.MangaList.getMangaList
import suwayomi.tachidesk.impl.Page.getPageImage
import suwayomi.tachidesk.impl.Search.sourceFilters
import suwayomi.tachidesk.impl.Search.sourceGlobalSearch
import suwayomi.tachidesk.impl.Search.sourceSearch
import suwayomi.tachidesk.impl.Source.getSource
import suwayomi.tachidesk.impl.Source.getSourceList
import suwayomi.tachidesk.impl.backup.BackupFlags
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupExport.createLegacyBackup
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupImport.restoreLegacyBackup
import suwayomi.tachidesk.impl.extension.Extension.getExtensionIcon
import suwayomi.tachidesk.impl.extension.Extension.installExtension
import suwayomi.tachidesk.impl.extension.Extension.uninstallExtension
import suwayomi.tachidesk.impl.extension.Extension.updateExtension
import suwayomi.tachidesk.impl.extension.ExtensionsList.getExtensionList
import suwayomi.tachidesk.manga.impl.Category
import suwayomi.tachidesk.manga.impl.CategoryManga.addMangaToCategory
import suwayomi.tachidesk.manga.impl.CategoryManga.getCategoryMangaList
import suwayomi.tachidesk.manga.impl.CategoryManga.getMangaCategories
import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory
import suwayomi.tachidesk.manga.impl.Chapter.getChapter
import suwayomi.tachidesk.manga.impl.Chapter.getChapterList
import suwayomi.tachidesk.manga.impl.Chapter.modifyChapter
import suwayomi.tachidesk.manga.impl.Chapter.modifyChapterMeta
import suwayomi.tachidesk.manga.impl.Library.addMangaToLibrary
import suwayomi.tachidesk.manga.impl.Library.getLibraryMangas
import suwayomi.tachidesk.manga.impl.Library.removeMangaFromLibrary
import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.manga.impl.Manga.getMangaThumbnail
import suwayomi.tachidesk.manga.impl.Manga.modifyMangaMeta
import suwayomi.tachidesk.manga.impl.MangaList.getMangaList
import suwayomi.tachidesk.manga.impl.Page.getPageImage
import suwayomi.tachidesk.manga.impl.Search.sourceFilters
import suwayomi.tachidesk.manga.impl.Search.sourceGlobalSearch
import suwayomi.tachidesk.manga.impl.Search.sourceSearch
import suwayomi.tachidesk.manga.impl.Source.getSource
import suwayomi.tachidesk.manga.impl.Source.getSourceList
import suwayomi.tachidesk.manga.impl.backup.BackupFlags
import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupExport.createLegacyBackup
import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupImport.restoreLegacyBackup
import suwayomi.tachidesk.manga.impl.download.DownloadManager
import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIcon
import suwayomi.tachidesk.manga.impl.extension.Extension.installExtension
import suwayomi.tachidesk.manga.impl.extension.Extension.uninstallExtension
import suwayomi.tachidesk.manga.impl.extension.Extension.updateExtension
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.getExtensionList
import suwayomi.tachidesk.server.JavalinSetup.future
import suwayomi.tachidesk.server.impl.About
import java.text.SimpleDateFormat
import java.util.Date
@@ -184,6 +187,18 @@ object TachideskAPI {
ctx.json(future { getChapterList(mangaId, onlineFetch) })
}
// used to modify a manga's meta paramaters
app.patch("/api/v1/manga/:mangaId/meta") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt()
val key = ctx.formParam("key")!!
val value = ctx.formParam("value")!!
modifyMangaMeta(mangaId, key, value)
ctx.status(200)
}
// used to display a chapter, get a chapter in order to show it's pages
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex") { ctx ->
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
@@ -206,6 +221,19 @@ object TachideskAPI {
ctx.status(200)
}
// used to modify a chapter's meta paramaters
app.patch("/api/v1/manga/:mangaId/chapter/:chapterIndex/meta") { ctx ->
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
val mangaId = ctx.pathParam("mangaId").toInt()
val key = ctx.formParam("key")!!
val value = ctx.formParam("value")!!
modifyChapterMeta(mangaId, chapterIndex, key, value)
ctx.status(200)
}
// get page at index "index"
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex/page/:index") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt()
@@ -383,15 +411,56 @@ object TachideskAPI {
// Download queue stats
app.ws("/api/v1/downloads") { ws ->
ws.onConnect { ctx ->
// TODO: send current stat
// TODO: add to downlad subscribers
DownloadManager.addClient(ctx)
DownloadManager.notifyClient(ctx)
}
ws.onMessage {
// TODO: send current stat
ws.onMessage { ctx ->
DownloadManager.handleRequest(ctx)
}
ws.onClose { ctx ->
// TODO: remove from subscribers
DownloadManager.removeClient(ctx)
}
}
// Start the downloader
app.get("/api/v1/downloads/start") { ctx ->
DownloadManager.start()
ctx.status(200)
}
// Stop the downloader
app.get("/api/v1/downloads/stop") { ctx ->
DownloadManager.stop()
ctx.status(200)
}
// clear download queue
app.get("/api/v1/downloads/clear") { ctx ->
DownloadManager.clear()
ctx.status(200)
}
// Queue chapter for download
app.get("/api/v1/download/:mangaId/chapter/:chapterIndex") { ctx ->
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
val mangaId = ctx.pathParam("mangaId").toInt()
DownloadManager.enqueue(chapterIndex, mangaId)
ctx.status(200)
}
// delete chapter from download queue
app.delete("/api/v1/download/:mangaId/chapter/:chapterIndex") { ctx ->
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
val mangaId = ctx.pathParam("mangaId").toInt()
DownloadManager.unqueue(chapterIndex, mangaId)
ctx.status(200)
}
}
}
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl
package suwayomi.tachidesk.manga.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -14,11 +14,11 @@ import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.CategoryManga.removeMangaFromCategory
import suwayomi.tachidesk.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.model.table.CategoryMangaTable
import suwayomi.tachidesk.model.table.CategoryTable
import suwayomi.tachidesk.model.table.toDataClass
import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.manga.model.table.toDataClass
object Category {
/**
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl
package suwayomi.tachidesk.manga.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -14,12 +14,12 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.CategoryMangaTable
import suwayomi.tachidesk.model.table.CategoryTable
import suwayomi.tachidesk.model.table.MangaTable
import suwayomi.tachidesk.model.table.toDataClass
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.manga.model.table.toDataClass
object CategoryManga {
fun addMangaToCategory(mangaId: Int, categoryId: Int) {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl
package suwayomi.tachidesk.manga.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -9,6 +9,7 @@ package suwayomi.tachidesk.impl
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.SortOrder.DESC
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere
@@ -16,14 +17,16 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.Manga.getManga
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.dataclass.ChapterDataClass
import suwayomi.tachidesk.model.table.ChapterTable
import suwayomi.tachidesk.model.table.MangaTable
import suwayomi.tachidesk.model.table.PageTable
import suwayomi.tachidesk.model.table.toDataClass
import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.manga.model.table.PageTable
import suwayomi.tachidesk.manga.model.table.toDataClass
import java.time.Instant
object Chapter {
/** get chapter list when showing a manga */
@@ -88,7 +91,7 @@ object Chapter {
// clear any orphaned chapters that are in the db but not in `chapterList`
val dbChapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() }
if (dbChapterCount > chapterCount) { // we got some clean up due
val dbChapterList = transaction { ChapterTable.select { ChapterTable.manga eq mangaId } }
val dbChapterList = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.toList() }
dbChapterList.forEach {
if (it[ChapterTable.chapterIndex] >= chapterList.size ||
@@ -122,9 +125,15 @@ object Chapter {
dbChapter[ChapterTable.isRead],
dbChapter[ChapterTable.isBookmarked],
dbChapter[ChapterTable.lastPageRead],
dbChapter[ChapterTable.lastReadAt],
chapterCount - index,
chapterList.size
dbChapter[ChapterTable.isDownloaded],
dbChapter[ChapterTable.pageCount],
chapterList.size,
meta = getChapterMetaMap(dbChapter[ChapterTable.id])
)
}
}
@@ -136,54 +145,69 @@ object Chapter {
(ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId)
}.first()
}
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
val pageList = source.fetchPageList(
SChapter.create().apply {
url = chapterEntry[ChapterTable.url]
name = chapterEntry[ChapterTable.name]
}
).awaitSingle()
return if (!chapterEntry[ChapterTable.isDownloaded]) {
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
val chapterId = chapterEntry[ChapterTable.id].value
val chapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() }
val pageList = source.fetchPageList(
SChapter.create().apply {
url = chapterEntry[ChapterTable.url]
name = chapterEntry[ChapterTable.name]
}
).awaitSingle()
// update page list for this chapter
transaction {
pageList.forEach { page ->
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }.firstOrNull() }
if (pageEntry == null) {
PageTable.insert {
it[index] = page.index
it[url] = page.url
it[imageUrl] = page.imageUrl
it[chapter] = chapterId
}
} else {
PageTable.update({ (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }) {
it[url] = page.url
it[imageUrl] = page.imageUrl
val chapterId = chapterEntry[ChapterTable.id].value
val chapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() }
// update page list for this chapter
transaction {
pageList.forEach { page ->
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }.firstOrNull() }
if (pageEntry == null) {
PageTable.insert {
it[index] = page.index
it[url] = page.url
it[imageUrl] = page.imageUrl
it[chapter] = chapterId
}
} else {
PageTable.update({ (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }) {
it[url] = page.url
it[imageUrl] = page.imageUrl
}
}
}
}
val pageCount = pageList.count()
transaction {
ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex eq chapterIndex) }) {
it[ChapterTable.pageCount] = pageCount
}
}
return ChapterDataClass(
chapterEntry[ChapterTable.url],
chapterEntry[ChapterTable.name],
chapterEntry[ChapterTable.date_upload],
chapterEntry[ChapterTable.chapter_number],
chapterEntry[ChapterTable.scanlator],
mangaId,
chapterEntry[ChapterTable.isRead],
chapterEntry[ChapterTable.isBookmarked],
chapterEntry[ChapterTable.lastPageRead],
chapterEntry[ChapterTable.lastReadAt],
chapterEntry[ChapterTable.chapterIndex],
chapterEntry[ChapterTable.isDownloaded],
pageCount,
chapterCount.toInt(),
getChapterMetaMap(chapterEntry[ChapterTable.id])
)
} else {
ChapterTable.toDataClass(chapterEntry)
}
return ChapterDataClass(
chapterEntry[ChapterTable.url],
chapterEntry[ChapterTable.name],
chapterEntry[ChapterTable.date_upload],
chapterEntry[ChapterTable.chapter_number],
chapterEntry[ChapterTable.scanlator],
mangaId,
chapterEntry[ChapterTable.isRead],
chapterEntry[ChapterTable.isBookmarked],
chapterEntry[ChapterTable.lastPageRead],
chapterEntry[ChapterTable.chapterIndex],
chapterCount.toInt(),
pageList.count()
)
}
fun modifyChapter(mangaId: Int, chapterIndex: Int, isRead: Boolean?, isBookmarked: Boolean?, markPrevRead: Boolean?, lastPageRead: Int?) {
@@ -198,6 +222,7 @@ object Chapter {
}
lastPageRead?.also {
update[ChapterTable.lastPageRead] = it
update[ChapterTable.lastReadAt] = Instant.now().epochSecond
}
}
}
@@ -209,4 +234,30 @@ object Chapter {
}
}
}
fun getChapterMetaMap(chapter: EntityID<Int>): Map<String, String> {
return transaction {
ChapterMetaTable.select { ChapterMetaTable.ref eq chapter }
.associate { it[ChapterMetaTable.key] to it[ChapterMetaTable.value] }
}
}
fun modifyChapterMeta(mangaId: Int, chapterIndex: Int, key: String, value: String) {
transaction {
val chapter = ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex eq chapterIndex) }
.first()[ChapterTable.id]
val meta = transaction { ChapterMetaTable.select { (ChapterMetaTable.ref eq chapter) and (ChapterMetaTable.key eq key) } }.firstOrNull()
if (meta == null) {
ChapterMetaTable.insert {
it[ChapterMetaTable.key] = key
it[ChapterMetaTable.value] = value
it[ChapterMetaTable.ref] = chapter
}
} else {
ChapterMetaTable.update {
it[ChapterMetaTable.value] = value
}
}
}
}
}
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl
package suwayomi.tachidesk.manga.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -13,12 +13,12 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.Manga.getManga
import suwayomi.tachidesk.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.CategoryMangaTable
import suwayomi.tachidesk.model.table.CategoryTable
import suwayomi.tachidesk.model.table.MangaTable
import suwayomi.tachidesk.model.table.toDataClass
import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.manga.model.table.toDataClass
object Library {
// TODO: `Category.isLanding` is to handle the default categories a new library manga gets,
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl
package suwayomi.tachidesk.manga.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -9,23 +9,27 @@ package suwayomi.tachidesk.impl
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.SManga
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.server.ApplicationDirs
import suwayomi.tachidesk.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.impl.Source.getSource
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle
import suwayomi.tachidesk.impl.util.network.await
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.clearCachedImage
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.MangaStatus
import suwayomi.tachidesk.model.table.MangaTable
import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.manga.impl.Source.getSource
import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.clearCachedImage
import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.manga.model.table.MangaMetaTable
import suwayomi.tachidesk.manga.model.table.MangaStatus
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.server.ApplicationDirs
import java.io.InputStream
object Manga {
@@ -57,6 +61,7 @@ object Manga {
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
mangaEntry[MangaTable.inLibrary],
getSource(mangaEntry[MangaTable.sourceReference]),
getMangaMetaMap(mangaEntry[MangaTable.id]),
false
)
} else { // initialize manga
@@ -102,13 +107,40 @@ object Manga {
fetchedManga.description,
fetchedManga.genre,
MangaStatus.valueOf(fetchedManga.status).name,
false,
mangaEntry[MangaTable.inLibrary],
getSource(mangaEntry[MangaTable.sourceReference]),
getMangaMetaMap(mangaEntry[MangaTable.id]),
true
)
}
}
fun getMangaMetaMap(manga: EntityID<Int>): Map<String, String> {
return transaction {
MangaMetaTable.select { MangaMetaTable.ref eq manga }
.associate { it[MangaMetaTable.key] to it[MangaMetaTable.value] }
}
}
fun modifyMangaMeta(mangaId: Int, key: String, value: String) {
transaction {
val manga = MangaMetaTable.select { (MangaTable.id eq mangaId) }
.first()[MangaTable.id]
val meta = transaction { MangaMetaTable.select { (MangaMetaTable.ref eq manga) and (MangaMetaTable.key eq key) } }.firstOrNull()
if (meta == null) {
MangaMetaTable.insert {
it[MangaMetaTable.key] = key
it[MangaMetaTable.value] = value
it[MangaMetaTable.ref] = manga
}
} else {
MangaMetaTable.update {
it[MangaMetaTable.value] = value
}
}
}
}
private val applicationDirs by DI.global.instance<ApplicationDirs>()
suspend fun getMangaThumbnail(mangaId: Int): Pair<InputStream, String> {
val saveDir = applicationDirs.mangaThumbnailsRoot
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl
package suwayomi.tachidesk.manga.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -11,12 +11,13 @@ import eu.kanade.tachiyomi.source.model.MangasPage
import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.dataclass.PagedMangaListDataClass
import suwayomi.tachidesk.model.table.MangaStatus
import suwayomi.tachidesk.model.table.MangaTable
import suwayomi.tachidesk.manga.impl.Manga.getMangaMetaMap
import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass
import suwayomi.tachidesk.manga.model.table.MangaStatus
import suwayomi.tachidesk.manga.model.table.MangaTable
object MangaList {
fun proxyThumbnailUrl(mangaId: Int): String {
@@ -89,7 +90,8 @@ object MangaList {
mangaEntry[MangaTable.description],
mangaEntry[MangaTable.genre],
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
mangaEntry[MangaTable.inLibrary]
mangaEntry[MangaTable.inLibrary],
meta = getMangaMetaMap(mangaEntry[MangaTable.id])
)
}
}
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl
package suwayomi.tachidesk.manga.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -16,14 +16,14 @@ import org.jetbrains.exposed.sql.update
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.server.ApplicationDirs
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.impl.util.storage.SafePath
import suwayomi.tachidesk.model.table.ChapterTable
import suwayomi.tachidesk.model.table.MangaTable
import suwayomi.tachidesk.model.table.PageTable
import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.manga.impl.util.storage.SafePath
import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.manga.model.table.PageTable
import suwayomi.tachidesk.server.ApplicationDirs
import java.io.File
import java.io.InputStream
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl
package suwayomi.tachidesk.manga.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -7,10 +7,10 @@ package suwayomi.tachidesk.impl
* 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 suwayomi.tachidesk.impl.MangaList.processEntries
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.dataclass.PagedMangaListDataClass
import suwayomi.tachidesk.manga.impl.MangaList.processEntries
import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass
object Search {
// TODO
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl
package suwayomi.tachidesk.manga.impl
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -11,11 +11,11 @@ import mu.KotlinLogging
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.model.dataclass.SourceDataClass
import suwayomi.tachidesk.model.table.ExtensionTable
import suwayomi.tachidesk.model.table.SourceTable
import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
import suwayomi.tachidesk.manga.model.table.ExtensionTable
import suwayomi.tachidesk.manga.model.table.SourceTable
object Source {
private val logger = KotlinLogging.logger {}
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup
package suwayomi.tachidesk.manga.impl.backup
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.legacy
package suwayomi.tachidesk.manga.impl.backup.legacy
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -11,16 +11,16 @@ import com.github.salomonbrys.kotson.registerTypeAdapter
import com.github.salomonbrys.kotson.registerTypeHierarchyAdapter
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import suwayomi.tachidesk.impl.backup.legacy.models.DHistory
import suwayomi.tachidesk.impl.backup.legacy.serializer.CategoryTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.ChapterTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.HistoryTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.MangaTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.TrackTypeAdapter
import suwayomi.tachidesk.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.impl.backup.models.MangaImpl
import suwayomi.tachidesk.impl.backup.models.TrackImpl
import suwayomi.tachidesk.manga.impl.backup.legacy.models.DHistory
import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.CategoryTypeAdapter
import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.ChapterTypeAdapter
import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.HistoryTypeAdapter
import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.MangaTypeAdapter
import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.TrackTypeAdapter
import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.manga.impl.backup.models.MangaImpl
import suwayomi.tachidesk.manga.impl.backup.models.TrackImpl
import java.util.Date
open class LegacyBackupBase {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.legacy
package suwayomi.tachidesk.manga.impl.backup.legacy
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -14,18 +14,18 @@ import com.google.gson.JsonObject
import eu.kanade.tachiyomi.source.LocalSource
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.Category.getCategoryList
import suwayomi.tachidesk.impl.CategoryManga.getMangaCategories
import suwayomi.tachidesk.impl.backup.BackupFlags
import suwayomi.tachidesk.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.impl.backup.legacy.models.Backup.CURRENT_VERSION
import suwayomi.tachidesk.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.impl.backup.models.Manga
import suwayomi.tachidesk.impl.backup.models.MangaImpl
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.model.table.ChapterTable
import suwayomi.tachidesk.model.table.MangaTable
import suwayomi.tachidesk.manga.impl.Category.getCategoryList
import suwayomi.tachidesk.manga.impl.CategoryManga.getMangaCategories
import suwayomi.tachidesk.manga.impl.backup.BackupFlags
import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup.CURRENT_VERSION
import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.manga.impl.backup.models.Manga
import suwayomi.tachidesk.manga.impl.backup.models.MangaImpl
import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.manga.model.table.MangaTable
object LegacyBackupExport : LegacyBackupBase() {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.legacy
package suwayomi.tachidesk.manga.impl.backup.legacy
import com.github.salomonbrys.kotson.fromJson
import com.google.gson.JsonArray
@@ -13,22 +13,22 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.Category.createCategory
import suwayomi.tachidesk.impl.Category.getCategoryList
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupValidator.ValidationResult
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupValidator.validate
import suwayomi.tachidesk.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.impl.backup.legacy.models.DHistory
import suwayomi.tachidesk.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.impl.backup.models.Chapter
import suwayomi.tachidesk.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.impl.backup.models.Manga
import suwayomi.tachidesk.impl.backup.models.MangaImpl
import suwayomi.tachidesk.impl.backup.models.Track
import suwayomi.tachidesk.impl.backup.models.TrackImpl
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.table.MangaTable
import suwayomi.tachidesk.manga.impl.Category.createCategory
import suwayomi.tachidesk.manga.impl.Category.getCategoryList
import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupValidator.ValidationResult
import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupValidator.validate
import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.manga.impl.backup.legacy.models.DHistory
import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.manga.impl.backup.models.Chapter
import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.manga.impl.backup.models.Manga
import suwayomi.tachidesk.manga.impl.backup.models.MangaImpl
import suwayomi.tachidesk.manga.impl.backup.models.Track
import suwayomi.tachidesk.manga.impl.backup.models.TrackImpl
import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.model.table.MangaTable
import java.io.InputStream
import java.util.Date
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.legacy
package suwayomi.tachidesk.manga.impl.backup.legacy
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -10,8 +10,8 @@ package suwayomi.tachidesk.impl.backup.legacy
import com.google.gson.JsonObject
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.model.table.SourceTable
import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.manga.model.table.SourceTable
object LegacyBackupValidator {
data class ValidationResult(val missingSources: List<String>, val missingTrackers: List<String>)
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.legacy.models
package suwayomi.tachidesk.manga.impl.backup.legacy.models
import java.text.SimpleDateFormat
import java.util.Date
@@ -0,0 +1,3 @@
package suwayomi.tachidesk.manga.impl.backup.legacy.models
data class DHistory(val url: String, val lastRead: Long)
@@ -1,8 +1,8 @@
package suwayomi.tachidesk.impl.backup.legacy.serializer
package suwayomi.tachidesk.manga.impl.backup.legacy.serializer
import com.github.salomonbrys.kotson.typeAdapter
import com.google.gson.TypeAdapter
import suwayomi.tachidesk.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
/**
* JSON Serializer used to write / read [CategoryImpl] to / from json
@@ -1,9 +1,9 @@
package suwayomi.tachidesk.impl.backup.legacy.serializer
package suwayomi.tachidesk.manga.impl.backup.legacy.serializer
import com.github.salomonbrys.kotson.typeAdapter
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonToken
import suwayomi.tachidesk.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
/**
* JSON Serializer used to write / read [ChapterImpl] to / from json
@@ -1,8 +1,8 @@
package suwayomi.tachidesk.impl.backup.legacy.serializer
package suwayomi.tachidesk.manga.impl.backup.legacy.serializer
import com.github.salomonbrys.kotson.typeAdapter
import com.google.gson.TypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.models.DHistory
import suwayomi.tachidesk.manga.impl.backup.legacy.models.DHistory
/**
* JSON Serializer used to write / read [DHistory] to / from json
@@ -1,8 +1,8 @@
package suwayomi.tachidesk.impl.backup.legacy.serializer
package suwayomi.tachidesk.manga.impl.backup.legacy.serializer
import com.github.salomonbrys.kotson.typeAdapter
import com.google.gson.TypeAdapter
import suwayomi.tachidesk.impl.backup.models.MangaImpl
import suwayomi.tachidesk.manga.impl.backup.models.MangaImpl
/**
* JSON Serializer used to write / read [MangaImpl] to / from json
@@ -1,9 +1,9 @@
package suwayomi.tachidesk.impl.backup.legacy.serializer
package suwayomi.tachidesk.manga.impl.backup.legacy.serializer
import com.github.salomonbrys.kotson.typeAdapter
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonToken
import suwayomi.tachidesk.impl.backup.models.TrackImpl
import suwayomi.tachidesk.manga.impl.backup.models.TrackImpl
/**
* JSON Serializer used to write / read [TrackImpl] to / from json
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
import java.io.Serializable
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
class CategoryImpl : Category {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
import eu.kanade.tachiyomi.source.model.SChapter
import java.io.Serializable
@@ -1,7 +1,7 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
import org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.model.table.ChapterTable
import suwayomi.tachidesk.manga.model.table.ChapterTable
class ChapterImpl : Chapter {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
import java.io.Serializable
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
/**
* Object containing the history statistics of a chapter
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
class LibraryManga : MangaImpl() {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
import eu.kanade.tachiyomi.source.model.SManga
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
class MangaCategory {
@@ -1,3 +1,3 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
class MangaChapter(val manga: Manga, val chapter: Chapter)
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
/**
* Object containing manga, chapter and history
@@ -1,7 +1,7 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
import org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.model.table.MangaTable
import suwayomi.tachidesk.manga.model.table.MangaTable
open class MangaImpl : Manga {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
import java.io.Serializable
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models
package suwayomi.tachidesk.manga.impl.backup.models
class TrackImpl : Track {
@@ -0,0 +1,129 @@
package suwayomi.tachidesk.manga.impl.download
/*
* 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 io.javalin.websocket.WsContext
import io.javalin.websocket.WsMessageContext
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter
import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Downloading
import suwayomi.tachidesk.manga.impl.download.model.DownloadStatus
import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.manga.model.table.toDataClass
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
object DownloadManager {
private val clients = ConcurrentHashMap<String, WsContext>()
private val downloadQueue = CopyOnWriteArrayList<DownloadChapter>()
private var downloader: Downloader? = null
fun addClient(ctx: WsContext) {
clients[ctx.sessionId] = ctx
}
fun removeClient(ctx: WsContext) {
clients.remove(ctx.sessionId)
}
fun notifyClient(ctx: WsContext) {
ctx.send(
getStatus()
)
}
fun handleRequest(ctx: WsMessageContext) {
when (ctx.message()) {
"STATUS" -> notifyClient(ctx)
else -> ctx.send(
"""
|Invalid command.
|Supported commands are:
| - STATUS
| sends the current download status
|""".trimMargin()
)
}
}
private fun notifyAllClients() {
val status = getStatus()
clients.forEach {
it.value.send(status)
}
}
private fun getStatus(): DownloadStatus {
return DownloadStatus(
if (downloader == null ||
downloadQueue.none { it.state == Downloading }
) "Stopped" else "Started",
downloadQueue
)
}
fun enqueue(chapterIndex: Int, mangaId: Int) {
if (downloadQueue.none { it.mangaId == mangaId && it.chapterIndex == chapterIndex }) {
downloadQueue.add(
DownloadChapter(
chapterIndex,
mangaId,
chapter = ChapterTable.toDataClass(
transaction {
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex eq chapterIndex) }
.first()
}
)
)
)
start()
}
notifyAllClients()
}
fun unqueue(chapterIndex: Int, mangaId: Int) {
downloadQueue.removeIf { it.mangaId == mangaId && it.chapterIndex == chapterIndex }
notifyAllClients()
}
fun start() {
if (downloader != null && !downloader?.isAlive!!) // doesn't exist or is dead
downloader = null
if (downloader == null) {
downloader = Downloader(downloadQueue) { notifyAllClients() }
downloader!!.start()
}
notifyAllClients()
}
fun stop() {
downloader?.let {
synchronized(it.shouldStop) {
it.shouldStop = true
}
}
downloader = null
notifyAllClients()
}
fun clear() {
stop()
downloadQueue.clear()
notifyAllClients()
}
}
enum class DownloaderState(val state: Int) {
Stopped(0),
Running(1),
Paused(2),
}
@@ -0,0 +1,81 @@
package suwayomi.tachidesk.manga.impl.download
/*
* 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 kotlinx.coroutines.runBlocking
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.manga.impl.Chapter.getChapter
import suwayomi.tachidesk.manga.impl.Page.getPageImage
import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter
import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Downloading
import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Error
import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Finished
import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Queued
import suwayomi.tachidesk.manga.model.table.ChapterTable
import java.util.concurrent.CopyOnWriteArrayList
class Downloader(private val downloadQueue: CopyOnWriteArrayList<DownloadChapter>, val notifier: () -> Unit) : Thread() {
var shouldStop: Boolean = false
class DownloadShouldStopException : Exception()
fun step() {
notifier()
synchronized(shouldStop) {
if (shouldStop) throw DownloadShouldStopException()
}
}
override fun run() {
do {
val download = downloadQueue.firstOrNull {
it.state == Queued ||
(it.state == Error && it.tries < 3) // 3 re-tries per download
} ?: break
try {
download.state = Downloading
step()
download.chapter = runBlocking { getChapter(download.chapterIndex, download.mangaId) }
step()
val pageCount = download.chapter!!.pageCount
for (pageNum in 0 until pageCount) {
runBlocking { getPageImage(download.mangaId, download.chapterIndex, pageNum) }
// TODO: retry on error with 2,4,8 seconds of wait
// TODO: download multiple pages at once, possible solution: rx observer's strategy is used in Tachiyomi
// TODO: fine grained download percentage
download.progress = (pageNum + 1).toFloat() / pageCount
step()
}
download.state = Finished
transaction {
ChapterTable.update({ (ChapterTable.manga eq download.mangaId) and (ChapterTable.chapterIndex eq download.chapterIndex) }) {
it[isDownloaded] = true
}
}
step()
downloadQueue.removeIf { it.mangaId == download.mangaId && it.chapterIndex == download.chapterIndex }
step()
} catch (e: DownloadShouldStopException) {
println("Downloader was stopped")
downloadQueue.filter { it.state == Downloading }.forEach { it.state = Queued }
} catch (e: Exception) {
println("Downloader faced an exception")
downloadQueue.filter { it.state == Downloading }.forEach { it.state = Error; it.tries++ }
e.printStackTrace()
} finally {
notifier()
}
} while (!shouldStop)
}
}
@@ -0,0 +1,20 @@
package suwayomi.tachidesk.manga.impl.download.model
/*
* 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 suwayomi.tachidesk.manga.impl.download.model.DownloadState.Queued
import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass
class DownloadChapter(
val chapterIndex: Int,
val mangaId: Int,
var state: DownloadState = Queued,
var progress: Float = 0f,
var tries: Int = 0,
var chapter: ChapterDataClass? = null,
)
@@ -0,0 +1,15 @@
package suwayomi.tachidesk.manga.impl.download.model
/*
* 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/. */
enum class DownloadState(val state: Int) {
Queued(0),
Downloading(1),
Finished(2),
Error(3),
}
@@ -0,0 +1,13 @@
package suwayomi.tachidesk.manga.impl.download.model
/*
* 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/. */
data class DownloadStatus(
val status: String,
val queue: List<DownloadChapter>,
)
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.extension
package suwayomi.tachidesk.manga.impl.extension
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -25,23 +25,23 @@ import org.jetbrains.exposed.sql.update
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.server.ApplicationDirs
import suwayomi.tachidesk.impl.extension.ExtensionsList.extensionTableAsDataClass
import suwayomi.tachidesk.impl.extension.github.ExtensionGithubApi
import suwayomi.tachidesk.impl.util.PackageTools.EXTENSION_FEATURE
import suwayomi.tachidesk.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.tachidesk.impl.util.PackageTools.LIB_VERSION_MIN
import suwayomi.tachidesk.impl.util.PackageTools.METADATA_NSFW
import suwayomi.tachidesk.impl.util.PackageTools.METADATA_SOURCE_CLASS
import suwayomi.tachidesk.impl.util.PackageTools.dex2jar
import suwayomi.tachidesk.impl.util.PackageTools.getPackageInfo
import suwayomi.tachidesk.impl.util.PackageTools.getSignatureHash
import suwayomi.tachidesk.impl.util.PackageTools.loadExtensionSources
import suwayomi.tachidesk.impl.util.PackageTools.trustedSignatures
import suwayomi.tachidesk.impl.util.network.await
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.model.table.ExtensionTable
import suwayomi.tachidesk.model.table.SourceTable
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.extensionTableAsDataClass
import suwayomi.tachidesk.manga.impl.extension.github.ExtensionGithubApi
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_NSFW
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.getSignatureHash
import suwayomi.tachidesk.manga.impl.util.PackageTools.loadExtensionSources
import suwayomi.tachidesk.manga.impl.util.PackageTools.trustedSignatures
import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.manga.model.table.ExtensionTable
import suwayomi.tachidesk.manga.model.table.SourceTable
import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.InputStream
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.extension
package suwayomi.tachidesk.manga.impl.extension
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -14,11 +14,11 @@ import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.impl.extension.github.ExtensionGithubApi
import suwayomi.tachidesk.impl.extension.github.OnlineExtension
import suwayomi.tachidesk.model.dataclass.ExtensionDataClass
import suwayomi.tachidesk.model.table.ExtensionTable
import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.manga.impl.extension.github.ExtensionGithubApi
import suwayomi.tachidesk.manga.impl.extension.github.OnlineExtension
import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass
import suwayomi.tachidesk.manga.model.table.ExtensionTable
import java.util.concurrent.ConcurrentHashMap
object ExtensionsList {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.extension.github
package suwayomi.tachidesk.manga.impl.extension.github
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -13,16 +13,14 @@ import com.google.gson.JsonArray
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.NetworkHelper
import okhttp3.Request
import suwayomi.tachidesk.impl.util.network.UnzippingInterceptor
import suwayomi.tachidesk.model.dataclass.ExtensionDataClass
import suwayomi.tachidesk.manga.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.tachidesk.manga.impl.util.PackageTools.LIB_VERSION_MIN
import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass
import uy.kohesive.injekt.injectLazy
object ExtensionGithubApi {
const val BASE_URL = "https://raw.githubusercontent.com"
const val REPO_URL_PREFIX = "$BASE_URL/tachiyomiorg/tachiyomi-extensions/repo"
private const val LIB_VERSION_MIN = 1.2
private const val LIB_VERSION_MAX = 1.2
private const val BASE_URL = "https://raw.githubusercontent.com"
private const val REPO_URL_PREFIX = "$BASE_URL/tachiyomiorg/tachiyomi-extensions/repo"
private fun parseResponse(json: JsonArray): List<OnlineExtension> {
return json
@@ -61,17 +59,15 @@ object ExtensionGithubApi {
.addNetworkInterceptor { chain ->
val originalResponse = chain.proceed(chain.request())
originalResponse.newBuilder()
.header("Content-Encoding", "gzip")
.header("Content-Type", "application/json")
.build()
}
.addInterceptor(UnzippingInterceptor())
.build()
}
private fun getRepo(): com.google.gson.JsonArray {
private fun getRepo(): JsonArray {
val request = Request.Builder()
.url("$REPO_URL_PREFIX/index.json.gz")
.url("$REPO_URL_PREFIX/index.min.json")
.build()
val response = client.newCall(request).execute().use { response -> response.body!!.string() }
@@ -0,0 +1,19 @@
package suwayomi.tachidesk.manga.impl.extension.github
/*
* 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/. */
data class OnlineExtension(
val name: String,
val pkgName: String,
val versionName: String,
val versionCode: Int,
val lang: String,
val isNsfw: Boolean,
val apkName: String,
val iconUrl: String
)

Some files were not shown because too many files have changed in this diff Show More