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: steps:
- name: Cancel previous runs - name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0 uses: styfle/cancel-workflow-action@0.9.0
with: with:
access_token: ${{ github.token }} access_token: ${{ github.token }}
@@ -48,14 +48,14 @@ jobs:
- name: Download android.jar - name: Download android.jar
run: | run: |
cd master 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 - name: Cache node_modules
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: | path: |
**/react/node_modules **/webUI/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }} key: ${{ runner.os }}-${{ hashFiles('**/webUI/yarn.lock') }}
- name: Build and copy webUI, Build Jar - name: Build and copy webUI, Build Jar
uses: eskatos/gradle-command-action@v1 uses: eskatos/gradle-command-action@v1
+9 -5
View File
@@ -25,7 +25,7 @@ jobs:
steps: steps:
- name: Cancel previous runs - name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0 uses: styfle/cancel-workflow-action@0.9.0
with: with:
access_token: ${{ github.token }} access_token: ${{ github.token }}
@@ -50,19 +50,19 @@ jobs:
- name: Download android.jar - name: Download android.jar
run: | run: |
cd master 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 - name: Cache node_modules
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: | path: |
**/react/node_modules **/webUI/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }} key: ${{ runner.os }}-${{ hashFiles('**/webUI/yarn.lock') }}
- name: Build and copy webUI, Build Jar - name: Build and copy webUI, Build Jar
uses: eskatos/gradle-command-action@v1 uses: eskatos/gradle-command-action@v1
env: env:
TachideskBuildType: "Preview" ProductBuildType: "Preview"
with: with:
build-root-directory: master build-root-directory: master
wrapper-directory: master wrapper-directory: master
@@ -127,3 +127,7 @@ jobs:
owner: "Suwayomi" owner: "Suwayomi"
repo: "Tachidesk-preview" repo: "Tachidesk-preview"
tag: ${{ steps.GenTagName.outputs.value }} 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: steps:
- name: Cancel previous runs - name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.5.0 uses: styfle/cancel-workflow-action@0.9.0
with: with:
access_token: ${{ github.token }} access_token: ${{ github.token }}
@@ -49,19 +49,19 @@ jobs:
- name: Download android.jar - name: Download android.jar
run: | run: |
cd master 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 - name: Cache node_modules
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: | path: |
**/react/node_modules **/webUI/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }} key: ${{ runner.os }}-${{ hashFiles('**/webUI/yarn.lock') }}
- name: Build and copy webUI, Build Jar - name: Build and copy webUI, Build Jar
uses: eskatos/gradle-command-action@v1 uses: eskatos/gradle-command-action@v1
env: env:
TachideskBuildType: "Stable" ProductBuildType: "Stable"
with: with:
build-root-directory: master build-root-directory: master
wrapper-directory: master wrapper-directory: master
@@ -85,3 +85,8 @@ jobs:
tags: true tags: true
draft: true draft: true
verbose: 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 # Ignore Gradle build output directory
build build
server/src/main/resources/react server/src/main/resources/webUI
server/tmp/ server/tmp/
server/tachiserver-data/ server/tachiserver-data/
@@ -12,7 +12,7 @@ import net.harawata.appdirs.AppDirsFactory
val ApplicationRootDir: String val ApplicationRootDir: String
get(): String { get(): String {
return System.getProperty( return System.getProperty(
"suwayomi.server.rootDir", "suwayomi.tachidesk.server.rootDir",
AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null) AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)
) )
} }
@@ -17,4 +17,4 @@ fun setLogLevel(level: Level) {
} }
fun debugLogsEnabled(config: Config) 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 { plugins {
application application
kotlin("plugin.serialization")
} }
@@ -46,6 +47,17 @@ dependencies {
implementation("org.mozilla:rhino-runtime:1.7.13") implementation("org.mozilla:rhino-runtime:1.7.13")
// 'org.mozilla:rhino-engine' provides the same interface as 'javax.script' a.k.a Nashorn // 'org.mozilla:rhino-engine' provides the same interface as 'javax.script' a.k.a Nashorn
implementation("org.mozilla:rhino-engine:1.7.13") 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') //def fatJarTask = tasks.getByPath(':AndroidCompat:JVMPatch:fatJar')
@@ -38,7 +38,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl; import xyz.nulldev.androidcompat.info.ApplicationInfoImpl;
import xyz.nulldev.androidcompat.io.AndroidFiles; 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.service.ServiceSupport;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper; import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
@@ -165,23 +165,22 @@ public class CustomContext extends Context implements DIAware {
/** Fake shared prefs! **/ /** Fake shared prefs! **/
private Map<String, SharedPreferences> prefs = new HashMap<>(); //Cache private Map<String, SharedPreferences> prefs = new HashMap<>(); //Cache
private File sharedPrefsFileFromString(String s) {
return new File(androidFiles.getPrefsDir(), s + ".json");
}
@Override @Override
public synchronized SharedPreferences getSharedPreferences(String s, int i) { public synchronized SharedPreferences getSharedPreferences(String s, int i) {
SharedPreferences preferences = prefs.get(s); SharedPreferences preferences = prefs.get(s);
//Create new shared preferences if one does not exist //Create new shared preferences if one does not exist
if(preferences == null) { if(preferences == null) {
preferences = getSharedPreferences(sharedPrefsFileFromString(s), i); preferences = new JavaSharedPreferences(s);
prefs.put(s, preferences); prefs.put(s, preferences);
} }
return preferences; return preferences;
} }
public SharedPreferences getSharedPreferences(File file, int mode) { @Override
return new JsonSharedPreferences(file); public SharedPreferences getSharedPreferences(@NotNull File file, int mode) {
String path = file.getAbsolutePath().replace('\\', '/');
int firstSlash = path.indexOf("/");
return new JavaSharedPreferences(path.substring(firstSlash));
} }
@Override @Override
@@ -191,8 +190,8 @@ public class CustomContext extends Context implements DIAware {
@Override @Override
public boolean deleteSharedPreferences(String name) { public boolean deleteSharedPreferences(String name) {
prefs.remove(name); JavaSharedPreferences item = (JavaSharedPreferences) prefs.remove(name);
return sharedPrefsFileFromString(name).delete(); return item.deleteAll();
} }
@Override @Override
@@ -26,8 +26,6 @@ class AndroidFiles(val configManager: ConfigManager = GlobalConfigManager) {
val downloadCacheDir: File get() = registerFile(filesConfig.downloadCacheDir) val downloadCacheDir: File get() = registerFile(filesConfig.downloadCacheDir)
val databasesDir: File get() = registerFile(filesConfig.databasesDir) val databasesDir: File get() = registerFile(filesConfig.databasesDir)
val prefsDir: File get() = registerFile(filesConfig.prefsDir)
val packagesDir: File get() = registerFile(filesConfig.packageDir) val packagesDir: File get() = registerFile(filesConfig.packageDir)
fun registerFile(file: String): File { 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. - A library to save your mangas and categories to put them into.
- Searching and browsing installed sources. - Searching and browsing installed sources.
- A decent chapter reader. - 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 - 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. **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 { plugins {
kotlin("jvm") version "1.4.32" kotlin("jvm") version "1.4.32"
kotlin("plugin.serialization") version "1.4.32" apply false
} }
allprojects { allprojects {
@@ -50,6 +51,10 @@ configure(projects) {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$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 // Dependency Injection
implementation("org.kodein.di:kodein-di-conf-jvm:7.5.0") 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" echo "creating windows bundle"
jar=$(ls ../server/build/Tachidesk-*.jar) jar=$(ls ../server/build/*.jar | tail -n1)
jar_name=$(echo $jar | cut -d'/' -f4) 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 # make release dir
+4 -5
View File
@@ -73,7 +73,7 @@ dependencies {
testImplementation(kotlin("test-junit5")) testImplementation(kotlin("test-junit5"))
} }
val MainClass = "suwayomi.MainKt" val MainClass = "suwayomi.tachidesk.MainKt"
application { application {
mainClass.set(MainClass) mainClass.set(MainClass)
@@ -93,11 +93,11 @@ sourceSets {
} }
// should be bumped with each stable release // 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 // counts commit count on master
val tachideskRevision = runCatching { val tachideskRevision = runCatching {
Runtime System.getenv("ProductRevision") ?: Runtime
.getRuntime() .getRuntime()
.exec("git rev-list HEAD --count") .exec("git rev-list HEAD --count")
.let { process -> .let { process ->
@@ -107,7 +107,6 @@ val tachideskRevision = runCatching {
} }
process.destroy() process.destroy()
"r" + output.trim() "r" + output.trim()
} }
}.getOrDefault("r0") }.getOrDefault("r0")
@@ -119,7 +118,7 @@ buildConfig {
buildConfigField("String", "NAME", rootProject.name) buildConfigField("String", "NAME", rootProject.name)
buildConfigField("String", "VERSION", tachideskVersion) buildConfigField("String", "VERSION", tachideskVersion)
buildConfigField("String", "REVISION", tachideskRevision) 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("long", "BUILD_TIME", Instant.now().epochSecond.toString())
buildConfigField("String", "GITHUB", "https://github.com/Suwayomi/Tachidesk") 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.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.animesource.model.AnimesPage
import rx.Observable import rx.Observable
interface AnimeCatalogueSource : AnimeSource { interface AnimeCatalogueSource : AnimeSource {
@@ -30,7 +30,7 @@ interface AnimeCatalogueSource : AnimeSource {
* @param query the search query. * @param query the search query.
* @param filters the list of filters to apply. * @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. * 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. * 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.animesource.model.SAnime
import eu.kanade.tachiyomi.source.model.SEpisode import eu.kanade.tachiyomi.animesource.model.SEpisode
import rx.Observable import rx.Observable
/** /**
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.source package eu.kanade.tachiyomi.animesource
/** /**
* A factory for creating sources at runtime. * 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 android.content.Context
import eu.kanade.tachiyomi.source.model.SAnime import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.source.model.SEpisode import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.source.online.AnimeHttpSource import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import rx.Observable import rx.Observable
open class AnimeSourceManager(private val context: Context) { 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 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) 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 import java.io.Serializable
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.source.model package eu.kanade.tachiyomi.animesource.model
class SAnimeImpl : SAnime { class SAnimeImpl : SAnime {
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.source.model package eu.kanade.tachiyomi.animesource.model
import java.io.Serializable import java.io.Serializable
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.source.model package eu.kanade.tachiyomi.animesource.model
class SEpisodeImpl : SEpisode { 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.GET
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.newCallWithProgress 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.Page
import eu.kanade.tachiyomi.source.model.SAnime
import eu.kanade.tachiyomi.source.model.SEpisode
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@@ -118,7 +118,7 @@ abstract class AnimeHttpSource : AnimeCatalogueSource {
* @param query the search query. * @param query the search query.
* @param filters the list of filters to apply. * @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)) return client.newCall(searchAnimeRequest(page, query, filters))
.asObservableSuccess() .asObservableSuccess()
.map { response -> .map { response ->
@@ -133,7 +133,7 @@ abstract class AnimeHttpSource : AnimeCatalogueSource {
* @param query the search query. * @param query the search query.
* @param filters the list of filters to apply. * @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. * 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. * Returns the list of filters for the source.
*/ */
override fun getFilterList() = FilterList() override fun getFilterList() = AnimeFilterList()
companion object { 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" 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 package eu.kanade.tachiyomi.source.online
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import rx.Observable 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.Page
import eu.kanade.tachiyomi.source.model.SAnime
import eu.kanade.tachiyomi.source.model.SEpisode
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document 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 * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import suwayomi.server.JavalinSetup.javalinSetup import suwayomi.tachidesk.server.JavalinSetup.javalinSetup
import suwayomi.server.applicationSetup import suwayomi.tachidesk.server.applicationSetup
fun main() { fun main() {
applicationSetup() applicationSetup()
@@ -1,4 +1,4 @@
package suwayomi.anime package suwayomi.tachidesk.anime
/* /*
* Copyright (C) Contributors to the Suwayomi project * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import io.javalin.Javalin import io.javalin.Javalin
import suwayomi.anime.impl.Anime.getAnime import suwayomi.tachidesk.anime.impl.Anime.getAnime
import suwayomi.anime.impl.Anime.getAnimeThumbnail import suwayomi.tachidesk.anime.impl.Anime.getAnimeThumbnail
import suwayomi.anime.impl.AnimeList.getAnimeList import suwayomi.tachidesk.anime.impl.AnimeList.getAnimeList
import suwayomi.anime.impl.Episode.getEpisode import suwayomi.tachidesk.anime.impl.Episode.getEpisode
import suwayomi.anime.impl.Episode.getEpisodeList import suwayomi.tachidesk.anime.impl.Episode.getEpisodeList
import suwayomi.anime.impl.Episode.modifyEpisode import suwayomi.tachidesk.anime.impl.Episode.modifyEpisode
import suwayomi.anime.impl.Source.getAnimeSource import suwayomi.tachidesk.anime.impl.Source.getAnimeSource
import suwayomi.anime.impl.Source.getSourceList import suwayomi.tachidesk.anime.impl.Source.getSourceList
import suwayomi.anime.impl.extension.Extension.getExtensionIcon import suwayomi.tachidesk.anime.impl.extension.Extension.getExtensionIcon
import suwayomi.anime.impl.extension.Extension.installExtension import suwayomi.tachidesk.anime.impl.extension.Extension.installExtension
import suwayomi.anime.impl.extension.Extension.uninstallExtension import suwayomi.tachidesk.anime.impl.extension.Extension.uninstallExtension
import suwayomi.anime.impl.extension.Extension.updateExtension import suwayomi.tachidesk.anime.impl.extension.Extension.updateExtension
import suwayomi.anime.impl.extension.ExtensionsList.getExtensionList import suwayomi.tachidesk.anime.impl.extension.ExtensionsList.getExtensionList
import suwayomi.server.JavalinSetup.future import suwayomi.tachidesk.server.JavalinSetup.future
object AnimeAPI { object AnimeAPI {
fun defineEndpoints(app: Javalin) { fun defineEndpoints(app: Javalin) {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl package suwayomi.tachidesk.anime.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.SAnime
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.anime.impl.AnimeList.proxyThumbnailUrl import suwayomi.tachidesk.anime.impl.AnimeList.proxyThumbnailUrl
import suwayomi.anime.impl.Source.getAnimeSource import suwayomi.tachidesk.anime.impl.Source.getAnimeSource
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.AnimeDataClass import suwayomi.tachidesk.anime.model.dataclass.AnimeDataClass
import suwayomi.anime.model.table.AnimeStatus import suwayomi.tachidesk.anime.model.table.AnimeStatus
import suwayomi.anime.model.table.AnimeTable import suwayomi.tachidesk.anime.model.table.AnimeTable
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.impl.util.network.await import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.clearCachedImage
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.clearCachedImage import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse import suwayomi.tachidesk.server.ApplicationDirs
import java.io.InputStream import java.io.InputStream
object Anime { object Anime {
@@ -102,7 +102,7 @@ object Anime {
fetchedAnime.description, fetchedAnime.description,
fetchedAnime.genre, fetchedAnime.genre,
AnimeStatus.valueOf(fetchedAnime.status).name, AnimeStatus.valueOf(fetchedAnime.status).name,
false, animeEntry[AnimeTable.inLibrary],
getAnimeSource(animeEntry[AnimeTable.sourceReference]), getAnimeSource(animeEntry[AnimeTable.sourceReference]),
true true
) )
@@ -1,4 +1,4 @@
package suwayomi.anime.impl package suwayomi.tachidesk.anime.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import eu.kanade.tachiyomi.source.model.AnimesPage import eu.kanade.tachiyomi.animesource.model.AnimesPage
import org.jetbrains.exposed.sql.insertAndGetId import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.AnimeDataClass import suwayomi.tachidesk.anime.model.dataclass.AnimeDataClass
import suwayomi.anime.model.dataclass.PagedAnimeListDataClass import suwayomi.tachidesk.anime.model.dataclass.PagedAnimeListDataClass
import suwayomi.anime.model.table.AnimeStatus import suwayomi.tachidesk.anime.model.table.AnimeStatus
import suwayomi.anime.model.table.AnimeTable import suwayomi.tachidesk.anime.model.table.AnimeTable
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
object AnimeList { object AnimeList {
fun proxyThumbnailUrl(animeId: Int): String { 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 * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import eu.kanade.tachiyomi.source.model.SAnime import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.source.model.SEpisode import eu.kanade.tachiyomi.animesource.model.SEpisode
import org.jetbrains.exposed.sql.SortOrder.DESC import org.jetbrains.exposed.sql.SortOrder.DESC
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere 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.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.anime.impl.Anime.getAnime import suwayomi.tachidesk.anime.impl.Anime.getAnime
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.EpisodeDataClass import suwayomi.tachidesk.anime.model.dataclass.EpisodeDataClass
import suwayomi.anime.model.table.AnimeTable import suwayomi.tachidesk.anime.model.table.AnimeTable
import suwayomi.anime.model.table.EpisodeTable import suwayomi.tachidesk.anime.model.table.EpisodeTable
import suwayomi.anime.model.table.toDataClass import suwayomi.tachidesk.anime.model.table.toDataClass
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
object Episode { object Episode {
/** get episode list when showing an anime */ /** 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 * 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.select
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.anime.impl.extension.Extension.getExtensionIconUrl import suwayomi.tachidesk.anime.impl.extension.Extension.getExtensionIconUrl
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource import suwayomi.tachidesk.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
import suwayomi.anime.model.dataclass.AnimeSourceDataClass import suwayomi.tachidesk.anime.model.dataclass.AnimeSourceDataClass
import suwayomi.anime.model.table.AnimeExtensionTable import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import suwayomi.anime.model.table.AnimeSourceTable import suwayomi.tachidesk.anime.model.table.AnimeSourceTable
object Source { object Source {
private val logger = KotlinLogging.logger {} 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 * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.net.Uri 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.GET
import eu.kanade.tachiyomi.network.NetworkHelper 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 mu.KotlinLogging
import okhttp3.Request import okhttp3.Request
import okio.buffer import okio.buffer
@@ -25,23 +25,23 @@ import org.jetbrains.exposed.sql.update
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.anime.impl.extension.ExtensionsList.extensionTableAsDataClass import suwayomi.tachidesk.anime.impl.extension.ExtensionsList.extensionTableAsDataClass
import suwayomi.anime.impl.extension.github.ExtensionGithubApi import suwayomi.tachidesk.anime.impl.extension.github.ExtensionGithubApi
import suwayomi.anime.impl.util.PackageTools.EXTENSION_FEATURE import suwayomi.tachidesk.anime.impl.util.PackageTools.EXTENSION_FEATURE
import suwayomi.anime.impl.util.PackageTools.LIB_VERSION_MAX import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.anime.impl.util.PackageTools.LIB_VERSION_MIN import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MIN
import suwayomi.anime.impl.util.PackageTools.METADATA_NSFW import suwayomi.tachidesk.anime.impl.util.PackageTools.METADATA_NSFW
import suwayomi.anime.impl.util.PackageTools.METADATA_SOURCE_CLASS import suwayomi.tachidesk.anime.impl.util.PackageTools.METADATA_SOURCE_CLASS
import suwayomi.anime.impl.util.PackageTools.dex2jar import suwayomi.tachidesk.anime.impl.util.PackageTools.dex2jar
import suwayomi.anime.impl.util.PackageTools.getPackageInfo import suwayomi.tachidesk.anime.impl.util.PackageTools.getPackageInfo
import suwayomi.anime.impl.util.PackageTools.getSignatureHash import suwayomi.tachidesk.anime.impl.util.PackageTools.getSignatureHash
import suwayomi.anime.impl.util.PackageTools.loadExtensionSources import suwayomi.tachidesk.anime.impl.util.PackageTools.loadExtensionSources
import suwayomi.anime.impl.util.PackageTools.trustedSignatures import suwayomi.tachidesk.anime.impl.util.PackageTools.trustedSignatures
import suwayomi.anime.model.table.AnimeExtensionTable import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import suwayomi.anime.model.table.AnimeSourceTable import suwayomi.tachidesk.anime.model.table.AnimeSourceTable
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.impl.util.network.await import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.io.InputStream 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 * 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.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.anime.impl.extension.Extension.getExtensionIconUrl import suwayomi.tachidesk.anime.impl.extension.Extension.getExtensionIconUrl
import suwayomi.anime.impl.extension.github.ExtensionGithubApi import suwayomi.tachidesk.anime.impl.extension.github.ExtensionGithubApi
import suwayomi.anime.impl.extension.github.OnlineExtension import suwayomi.tachidesk.anime.impl.extension.github.OnlineExtension
import suwayomi.anime.model.dataclass.AnimeExtensionDataClass import suwayomi.tachidesk.anime.model.dataclass.AnimeExtensionDataClass
import suwayomi.anime.model.table.AnimeExtensionTable import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
object ExtensionsList { 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 * Copyright (C) Contributors to the Suwayomi project
@@ -13,23 +13,22 @@ import com.google.gson.JsonArray
import com.google.gson.JsonParser import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import okhttp3.Request import okhttp3.Request
import suwayomi.anime.model.dataclass.AnimeExtensionDataClass import suwayomi.tachidesk.anime.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.tachidesk.impl.util.network.UnzippingInterceptor 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 import uy.kohesive.injekt.injectLazy
object ExtensionGithubApi { object ExtensionGithubApi {
const val BASE_URL = "https://raw.githubusercontent.com" private const val BASE_URL = "https://raw.githubusercontent.com"
const val REPO_URL_PREFIX = "$BASE_URL/jmir1/tachiyomi-extensions/repo" private 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 fun parseResponse(json: JsonArray): List<OnlineExtension> { private fun parseResponse(json: JsonArray): List<OnlineExtension> {
return json return json
.map { it.asJsonObject } .map { it.asJsonObject }
.filter { element -> .filter { element ->
val versionName = element["version"].string val versionName = element["version"].string
val libVersion = versionName.substringBeforeLast('.').toDouble() val libVersion = versionName.substringBeforeLast('.').toInt()
libVersion in LIB_VERSION_MIN..LIB_VERSION_MAX libVersion in LIB_VERSION_MIN..LIB_VERSION_MAX
} }
.map { element -> .map { element ->
@@ -69,7 +68,7 @@ object ExtensionGithubApi {
.build() .build()
} }
private fun getRepo(): com.google.gson.JsonArray { private fun getRepo(): JsonArray {
val request = Request.Builder() val request = Request.Builder()
.url("$REPO_URL_PREFIX/index.json.gz") .url("$REPO_URL_PREFIX/index.json.gz")
.build() .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 * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import eu.kanade.tachiyomi.source.AnimeSource import eu.kanade.tachiyomi.animesource.AnimeSource
import eu.kanade.tachiyomi.source.AnimeSourceFactory import eu.kanade.tachiyomi.animesource.AnimeSourceFactory
import eu.kanade.tachiyomi.source.online.AnimeHttpSource import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.anime.impl.util.PackageTools.loadExtensionSources import suwayomi.tachidesk.anime.impl.util.PackageTools.loadExtensionSources
import suwayomi.anime.model.table.AnimeExtensionTable import suwayomi.tachidesk.anime.model.table.AnimeExtensionTable
import suwayomi.anime.model.table.AnimeSourceTable import suwayomi.tachidesk.anime.model.table.AnimeSourceTable
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.server.ApplicationDirs
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
object GetAnimeHttpSource { object GetAnimeHttpSource {
@@ -1,4 +1,4 @@
package suwayomi.anime.impl.util package suwayomi.tachidesk.anime.impl.util
/* /*
* Copyright (C) Contributors to the Suwayomi project * Copyright (C) Contributors to the Suwayomi project
@@ -22,7 +22,7 @@ import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.Node 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.InstalledPackage.Companion.toList
import xyz.nulldev.androidcompat.pm.toPackageInfo import xyz.nulldev.androidcompat.pm.toPackageInfo
import java.io.File import java.io.File
@@ -40,8 +40,8 @@ object PackageTools {
const val METADATA_SOURCE_CLASS = "tachiyomi.animeextension.class" const val METADATA_SOURCE_CLASS = "tachiyomi.animeextension.class"
const val METADATA_SOURCE_FACTORY = "tachiyomi.animeextension.factory" const val METADATA_SOURCE_FACTORY = "tachiyomi.animeextension.factory"
const val METADATA_NSFW = "tachiyomi.animeextension.nsfw" const val METADATA_NSFW = "tachiyomi.animeextension.nsfw"
const val LIB_VERSION_MIN = 1.3 const val LIB_VERSION_MIN = 10
const val LIB_VERSION_MAX = 1.3 const val LIB_VERSION_MAX = 10
private const val officialSignature = "50ab1d1e3a20d204d0ad6d334c7691c632e41b98dfa132bf385695fdfa63839c" // jmir1's key private const val officialSignature = "50ab1d1e3a20d204d0ad6d334c7691c632e41b98dfa132bf385695fdfa63839c" // jmir1's key
var trustedSignatures = mutableSetOf<String>() + officialSignature 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 * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import suwayomi.anime.model.table.AnimeStatus import suwayomi.tachidesk.anime.model.table.AnimeStatus
data class AnimeDataClass( data class AnimeDataClass(
val id: Int, 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 * 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 * 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 * 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 * 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 * 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 * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import eu.kanade.tachiyomi.source.model.SAnime import eu.kanade.tachiyomi.animesource.model.SAnime
import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.impl.MangaList.proxyThumbnailUrl import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.model.dataclass.MangaDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.MangaStatus.Companion import suwayomi.tachidesk.manga.model.table.MangaStatus.Companion
object AnimeTable : IntIdTable() { object AnimeTable : IntIdTable() {
val url = varchar("url", 2048) 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 * 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.ResultRow
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.anime.model.dataclass.EpisodeDataClass import suwayomi.tachidesk.anime.model.dataclass.EpisodeDataClass
object EpisodeTable : IntIdTable() { object EpisodeTable : IntIdTable() {
val url = varchar("url", 2048) 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 * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import io.javalin.Javalin import io.javalin.Javalin
import suwayomi.server.JavalinSetup.future import suwayomi.tachidesk.manga.impl.Category
import suwayomi.server.impl.About import suwayomi.tachidesk.manga.impl.CategoryManga.addMangaToCategory
import suwayomi.tachidesk.impl.Category import suwayomi.tachidesk.manga.impl.CategoryManga.getCategoryMangaList
import suwayomi.tachidesk.impl.CategoryManga.addMangaToCategory import suwayomi.tachidesk.manga.impl.CategoryManga.getMangaCategories
import suwayomi.tachidesk.impl.CategoryManga.getCategoryMangaList import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory
import suwayomi.tachidesk.impl.CategoryManga.getMangaCategories import suwayomi.tachidesk.manga.impl.Chapter.getChapter
import suwayomi.tachidesk.impl.CategoryManga.removeMangaFromCategory import suwayomi.tachidesk.manga.impl.Chapter.getChapterList
import suwayomi.tachidesk.impl.Chapter.getChapter import suwayomi.tachidesk.manga.impl.Chapter.modifyChapter
import suwayomi.tachidesk.impl.Chapter.getChapterList import suwayomi.tachidesk.manga.impl.Chapter.modifyChapterMeta
import suwayomi.tachidesk.impl.Chapter.modifyChapter import suwayomi.tachidesk.manga.impl.Library.addMangaToLibrary
import suwayomi.tachidesk.impl.Library.addMangaToLibrary import suwayomi.tachidesk.manga.impl.Library.getLibraryMangas
import suwayomi.tachidesk.impl.Library.getLibraryMangas import suwayomi.tachidesk.manga.impl.Library.removeMangaFromLibrary
import suwayomi.tachidesk.impl.Library.removeMangaFromLibrary import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.impl.Manga.getManga import suwayomi.tachidesk.manga.impl.Manga.getMangaThumbnail
import suwayomi.tachidesk.impl.Manga.getMangaThumbnail import suwayomi.tachidesk.manga.impl.Manga.modifyMangaMeta
import suwayomi.tachidesk.impl.MangaList.getMangaList import suwayomi.tachidesk.manga.impl.MangaList.getMangaList
import suwayomi.tachidesk.impl.Page.getPageImage import suwayomi.tachidesk.manga.impl.Page.getPageImage
import suwayomi.tachidesk.impl.Search.sourceFilters import suwayomi.tachidesk.manga.impl.Search.sourceFilters
import suwayomi.tachidesk.impl.Search.sourceGlobalSearch import suwayomi.tachidesk.manga.impl.Search.sourceGlobalSearch
import suwayomi.tachidesk.impl.Search.sourceSearch import suwayomi.tachidesk.manga.impl.Search.sourceSearch
import suwayomi.tachidesk.impl.Source.getSource import suwayomi.tachidesk.manga.impl.Source.getSource
import suwayomi.tachidesk.impl.Source.getSourceList import suwayomi.tachidesk.manga.impl.Source.getSourceList
import suwayomi.tachidesk.impl.backup.BackupFlags import suwayomi.tachidesk.manga.impl.backup.BackupFlags
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupExport.createLegacyBackup import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupExport.createLegacyBackup
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupImport.restoreLegacyBackup import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupImport.restoreLegacyBackup
import suwayomi.tachidesk.impl.extension.Extension.getExtensionIcon import suwayomi.tachidesk.manga.impl.download.DownloadManager
import suwayomi.tachidesk.impl.extension.Extension.installExtension import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIcon
import suwayomi.tachidesk.impl.extension.Extension.uninstallExtension import suwayomi.tachidesk.manga.impl.extension.Extension.installExtension
import suwayomi.tachidesk.impl.extension.Extension.updateExtension import suwayomi.tachidesk.manga.impl.extension.Extension.uninstallExtension
import suwayomi.tachidesk.impl.extension.ExtensionsList.getExtensionList 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.text.SimpleDateFormat
import java.util.Date import java.util.Date
@@ -184,6 +187,18 @@ object TachideskAPI {
ctx.json(future { getChapterList(mangaId, onlineFetch) }) 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 // used to display a chapter, get a chapter in order to show it's pages
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex") { ctx -> app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex") { ctx ->
val chapterIndex = ctx.pathParam("chapterIndex").toInt() val chapterIndex = ctx.pathParam("chapterIndex").toInt()
@@ -206,6 +221,19 @@ object TachideskAPI {
ctx.status(200) 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" // get page at index "index"
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex/page/:index") { ctx -> app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex/page/:index") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt() val mangaId = ctx.pathParam("mangaId").toInt()
@@ -383,15 +411,56 @@ object TachideskAPI {
// Download queue stats // Download queue stats
app.ws("/api/v1/downloads") { ws -> app.ws("/api/v1/downloads") { ws ->
ws.onConnect { ctx -> ws.onConnect { ctx ->
// TODO: send current stat DownloadManager.addClient(ctx)
// TODO: add to downlad subscribers DownloadManager.notifyClient(ctx)
} }
ws.onMessage { ws.onMessage { ctx ->
// TODO: send current stat DownloadManager.handleRequest(ctx)
} }
ws.onClose { 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 * 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.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.CategoryManga.removeMangaFromCategory import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory
import suwayomi.tachidesk.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.model.table.toDataClass import suwayomi.tachidesk.manga.model.table.toDataClass
object Category { object Category {
/** /**
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * 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.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.model.dataclass.MangaDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.model.table.toDataClass import suwayomi.tachidesk.manga.model.table.toDataClass
object CategoryManga { object CategoryManga {
fun addMangaToCategory(mangaId: Int, categoryId: Int) { 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 * 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.SChapter
import eu.kanade.tachiyomi.source.model.SManga 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.SortOrder.DESC
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere 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.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.Manga.getManga import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.dataclass.ChapterDataClass import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass
import suwayomi.tachidesk.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.model.table.PageTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.model.table.toDataClass import suwayomi.tachidesk.manga.model.table.PageTable
import suwayomi.tachidesk.manga.model.table.toDataClass
import java.time.Instant
object Chapter { object Chapter {
/** get chapter list when showing a manga */ /** 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` // clear any orphaned chapters that are in the db but not in `chapterList`
val dbChapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() } val dbChapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() }
if (dbChapterCount > chapterCount) { // we got some clean up due 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 { dbChapterList.forEach {
if (it[ChapterTable.chapterIndex] >= chapterList.size || if (it[ChapterTable.chapterIndex] >= chapterList.size ||
@@ -122,9 +125,15 @@ object Chapter {
dbChapter[ChapterTable.isRead], dbChapter[ChapterTable.isRead],
dbChapter[ChapterTable.isBookmarked], dbChapter[ChapterTable.isBookmarked],
dbChapter[ChapterTable.lastPageRead], dbChapter[ChapterTable.lastPageRead],
dbChapter[ChapterTable.lastReadAt],
chapterCount - index, 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) (ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId)
}.first() }.first()
} }
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
val pageList = source.fetchPageList( return if (!chapterEntry[ChapterTable.isDownloaded]) {
SChapter.create().apply { val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
url = chapterEntry[ChapterTable.url] val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
name = chapterEntry[ChapterTable.name]
}
).awaitSingle()
val chapterId = chapterEntry[ChapterTable.id].value val pageList = source.fetchPageList(
val chapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() } SChapter.create().apply {
url = chapterEntry[ChapterTable.url]
name = chapterEntry[ChapterTable.name]
}
).awaitSingle()
// update page list for this chapter val chapterId = chapterEntry[ChapterTable.id].value
transaction { val chapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() }
pageList.forEach { page ->
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }.firstOrNull() } // update page list for this chapter
if (pageEntry == null) { transaction {
PageTable.insert { pageList.forEach { page ->
it[index] = page.index val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }.firstOrNull() }
it[url] = page.url if (pageEntry == null) {
it[imageUrl] = page.imageUrl PageTable.insert {
it[chapter] = chapterId it[index] = page.index
} it[url] = page.url
} else { it[imageUrl] = page.imageUrl
PageTable.update({ (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }) { it[chapter] = chapterId
it[url] = page.url }
it[imageUrl] = page.imageUrl } 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?) { fun modifyChapter(mangaId: Int, chapterIndex: Int, isRead: Boolean?, isBookmarked: Boolean?, markPrevRead: Boolean?, lastPageRead: Int?) {
@@ -198,6 +222,7 @@ object Chapter {
} }
lastPageRead?.also { lastPageRead?.also {
update[ChapterTable.lastPageRead] = it 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 * 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.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.Manga.getManga import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.model.dataclass.MangaDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.model.table.toDataClass import suwayomi.tachidesk.manga.model.table.toDataClass
object Library { object Library {
// TODO: `Category.isLanding` is to handle the default categories a new library manga gets, // 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 * 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.network.GET
import eu.kanade.tachiyomi.source.model.SManga 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.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.impl.MangaList.proxyThumbnailUrl import suwayomi.tachidesk.manga.impl.Source.getSource
import suwayomi.tachidesk.impl.Source.getSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.impl.util.network.await import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.clearCachedImage
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.clearCachedImage import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.dataclass.MangaDataClass import suwayomi.tachidesk.manga.model.table.MangaMetaTable
import suwayomi.tachidesk.model.table.MangaStatus import suwayomi.tachidesk.manga.model.table.MangaStatus
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.server.ApplicationDirs
import java.io.InputStream import java.io.InputStream
object Manga { object Manga {
@@ -57,6 +61,7 @@ object Manga {
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name, MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
mangaEntry[MangaTable.inLibrary], mangaEntry[MangaTable.inLibrary],
getSource(mangaEntry[MangaTable.sourceReference]), getSource(mangaEntry[MangaTable.sourceReference]),
getMangaMetaMap(mangaEntry[MangaTable.id]),
false false
) )
} else { // initialize manga } else { // initialize manga
@@ -102,13 +107,40 @@ object Manga {
fetchedManga.description, fetchedManga.description,
fetchedManga.genre, fetchedManga.genre,
MangaStatus.valueOf(fetchedManga.status).name, MangaStatus.valueOf(fetchedManga.status).name,
false, mangaEntry[MangaTable.inLibrary],
getSource(mangaEntry[MangaTable.sourceReference]), getSource(mangaEntry[MangaTable.sourceReference]),
getMangaMetaMap(mangaEntry[MangaTable.id]),
true 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>() private val applicationDirs by DI.global.instance<ApplicationDirs>()
suspend fun getMangaThumbnail(mangaId: Int): Pair<InputStream, String> { suspend fun getMangaThumbnail(mangaId: Int): Pair<InputStream, String> {
val saveDir = applicationDirs.mangaThumbnailsRoot val saveDir = applicationDirs.mangaThumbnailsRoot
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * 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.insertAndGetId
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.Manga.getMangaMetaMap
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.model.dataclass.MangaDataClass import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.dataclass.PagedMangaListDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.model.table.MangaStatus import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaStatus
import suwayomi.tachidesk.manga.model.table.MangaTable
object MangaList { object MangaList {
fun proxyThumbnailUrl(mangaId: Int): String { fun proxyThumbnailUrl(mangaId: Int): String {
@@ -89,7 +90,8 @@ object MangaList {
mangaEntry[MangaTable.description], mangaEntry[MangaTable.description],
mangaEntry[MangaTable.genre], mangaEntry[MangaTable.genre],
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name, 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 * 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.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse import suwayomi.tachidesk.manga.impl.util.storage.SafePath
import suwayomi.tachidesk.impl.util.storage.SafePath import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.PageTable
import suwayomi.tachidesk.model.table.PageTable import suwayomi.tachidesk.server.ApplicationDirs
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import suwayomi.tachidesk.impl.MangaList.processEntries import suwayomi.tachidesk.manga.impl.MangaList.processEntries
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.dataclass.PagedMangaListDataClass import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass
object Search { object Search {
// TODO // TODO
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl package suwayomi.tachidesk.manga.impl
/* /*
* Copyright (C) Contributors to the Suwayomi project * 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.select
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.extension.Extension.getExtensionIconUrl import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.model.dataclass.SourceDataClass import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
import suwayomi.tachidesk.model.table.ExtensionTable import suwayomi.tachidesk.manga.model.table.ExtensionTable
import suwayomi.tachidesk.model.table.SourceTable import suwayomi.tachidesk.manga.model.table.SourceTable
object Source { object Source {
private val logger = KotlinLogging.logger {} 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 * 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 * 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.github.salomonbrys.kotson.registerTypeHierarchyAdapter
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import suwayomi.tachidesk.impl.backup.legacy.models.DHistory import suwayomi.tachidesk.manga.impl.backup.legacy.models.DHistory
import suwayomi.tachidesk.impl.backup.legacy.serializer.CategoryTypeAdapter import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.CategoryTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.ChapterTypeAdapter import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.ChapterTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.HistoryTypeAdapter import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.HistoryTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.MangaTypeAdapter import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.MangaTypeAdapter
import suwayomi.tachidesk.impl.backup.legacy.serializer.TrackTypeAdapter import suwayomi.tachidesk.manga.impl.backup.legacy.serializer.TrackTypeAdapter
import suwayomi.tachidesk.impl.backup.models.CategoryImpl import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.impl.backup.models.ChapterImpl import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.impl.backup.models.MangaImpl import suwayomi.tachidesk.manga.impl.backup.models.MangaImpl
import suwayomi.tachidesk.impl.backup.models.TrackImpl import suwayomi.tachidesk.manga.impl.backup.models.TrackImpl
import java.util.Date import java.util.Date
open class LegacyBackupBase { 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 * Copyright (C) Contributors to the Suwayomi project
@@ -14,18 +14,18 @@ import com.google.gson.JsonObject
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.Category.getCategoryList import suwayomi.tachidesk.manga.impl.Category.getCategoryList
import suwayomi.tachidesk.impl.CategoryManga.getMangaCategories import suwayomi.tachidesk.manga.impl.CategoryManga.getMangaCategories
import suwayomi.tachidesk.impl.backup.BackupFlags import suwayomi.tachidesk.manga.impl.backup.BackupFlags
import suwayomi.tachidesk.impl.backup.legacy.models.Backup import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.impl.backup.legacy.models.Backup.CURRENT_VERSION import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup.CURRENT_VERSION
import suwayomi.tachidesk.impl.backup.models.CategoryImpl import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.impl.backup.models.ChapterImpl import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.impl.backup.models.Manga import suwayomi.tachidesk.manga.impl.backup.models.Manga
import suwayomi.tachidesk.impl.backup.models.MangaImpl import suwayomi.tachidesk.manga.impl.backup.models.MangaImpl
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
object LegacyBackupExport : LegacyBackupBase() { 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.github.salomonbrys.kotson.fromJson
import com.google.gson.JsonArray 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.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.Category.createCategory import suwayomi.tachidesk.manga.impl.Category.createCategory
import suwayomi.tachidesk.impl.Category.getCategoryList import suwayomi.tachidesk.manga.impl.Category.getCategoryList
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupValidator.ValidationResult import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupValidator.ValidationResult
import suwayomi.tachidesk.impl.backup.legacy.LegacyBackupValidator.validate import suwayomi.tachidesk.manga.impl.backup.legacy.LegacyBackupValidator.validate
import suwayomi.tachidesk.impl.backup.legacy.models.Backup import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.impl.backup.legacy.models.DHistory import suwayomi.tachidesk.manga.impl.backup.legacy.models.DHistory
import suwayomi.tachidesk.impl.backup.models.CategoryImpl import suwayomi.tachidesk.manga.impl.backup.models.CategoryImpl
import suwayomi.tachidesk.impl.backup.models.Chapter import suwayomi.tachidesk.manga.impl.backup.models.Chapter
import suwayomi.tachidesk.impl.backup.models.ChapterImpl import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
import suwayomi.tachidesk.impl.backup.models.Manga import suwayomi.tachidesk.manga.impl.backup.models.Manga
import suwayomi.tachidesk.impl.backup.models.MangaImpl import suwayomi.tachidesk.manga.impl.backup.models.MangaImpl
import suwayomi.tachidesk.impl.backup.models.Track import suwayomi.tachidesk.manga.impl.backup.models.Track
import suwayomi.tachidesk.impl.backup.models.TrackImpl import suwayomi.tachidesk.manga.impl.backup.models.TrackImpl
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource import suwayomi.tachidesk.manga.impl.util.GetHttpSource.getHttpSource
import suwayomi.tachidesk.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import java.io.InputStream import java.io.InputStream
import java.util.Date 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 * Copyright (C) Contributors to the Suwayomi project
@@ -10,8 +10,8 @@ package suwayomi.tachidesk.impl.backup.legacy
import com.google.gson.JsonObject import com.google.gson.JsonObject
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.impl.backup.legacy.models.Backup import suwayomi.tachidesk.manga.impl.backup.legacy.models.Backup
import suwayomi.tachidesk.model.table.SourceTable import suwayomi.tachidesk.manga.model.table.SourceTable
object LegacyBackupValidator { object LegacyBackupValidator {
data class ValidationResult(val missingSources: List<String>, val missingTrackers: List<String>) 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.text.SimpleDateFormat
import java.util.Date 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.github.salomonbrys.kotson.typeAdapter
import com.google.gson.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 * 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.github.salomonbrys.kotson.typeAdapter
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonToken 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 * 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.github.salomonbrys.kotson.typeAdapter
import com.google.gson.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 * 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.github.salomonbrys.kotson.typeAdapter
import com.google.gson.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 * 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.github.salomonbrys.kotson.typeAdapter
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonToken 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 * 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 import java.io.Serializable
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models package suwayomi.tachidesk.manga.impl.backup.models
class CategoryImpl : Category { 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 eu.kanade.tachiyomi.source.model.SChapter
import java.io.Serializable 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 org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.ChapterTable
class ChapterImpl : Chapter { class ChapterImpl : Chapter {
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models package suwayomi.tachidesk.manga.impl.backup.models
import java.io.Serializable 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 * 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() { 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 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 { 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) 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 * 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 org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
open class MangaImpl : Manga { 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 import java.io.Serializable
@@ -1,4 +1,4 @@
package suwayomi.tachidesk.impl.backup.models package suwayomi.tachidesk.manga.impl.backup.models
class TrackImpl : Track { 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 * 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.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.server.ApplicationDirs import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.extensionTableAsDataClass
import suwayomi.tachidesk.impl.extension.ExtensionsList.extensionTableAsDataClass import suwayomi.tachidesk.manga.impl.extension.github.ExtensionGithubApi
import suwayomi.tachidesk.impl.extension.github.ExtensionGithubApi import suwayomi.tachidesk.manga.impl.util.PackageTools.EXTENSION_FEATURE
import suwayomi.tachidesk.impl.util.PackageTools.EXTENSION_FEATURE import suwayomi.tachidesk.manga.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.tachidesk.impl.util.PackageTools.LIB_VERSION_MAX import suwayomi.tachidesk.manga.impl.util.PackageTools.LIB_VERSION_MIN
import suwayomi.tachidesk.impl.util.PackageTools.LIB_VERSION_MIN import suwayomi.tachidesk.manga.impl.util.PackageTools.METADATA_NSFW
import suwayomi.tachidesk.impl.util.PackageTools.METADATA_NSFW import suwayomi.tachidesk.manga.impl.util.PackageTools.METADATA_SOURCE_CLASS
import suwayomi.tachidesk.impl.util.PackageTools.METADATA_SOURCE_CLASS import suwayomi.tachidesk.manga.impl.util.PackageTools.dex2jar
import suwayomi.tachidesk.impl.util.PackageTools.dex2jar import suwayomi.tachidesk.manga.impl.util.PackageTools.getPackageInfo
import suwayomi.tachidesk.impl.util.PackageTools.getPackageInfo import suwayomi.tachidesk.manga.impl.util.PackageTools.getSignatureHash
import suwayomi.tachidesk.impl.util.PackageTools.getSignatureHash import suwayomi.tachidesk.manga.impl.util.PackageTools.loadExtensionSources
import suwayomi.tachidesk.impl.util.PackageTools.loadExtensionSources import suwayomi.tachidesk.manga.impl.util.PackageTools.trustedSignatures
import suwayomi.tachidesk.impl.util.PackageTools.trustedSignatures import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.impl.util.network.await import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse import suwayomi.tachidesk.manga.model.table.ExtensionTable
import suwayomi.tachidesk.model.table.ExtensionTable import suwayomi.tachidesk.manga.model.table.SourceTable
import suwayomi.tachidesk.model.table.SourceTable import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.io.InputStream 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 * 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.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.impl.extension.Extension.getExtensionIconUrl import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.impl.extension.github.ExtensionGithubApi import suwayomi.tachidesk.manga.impl.extension.github.ExtensionGithubApi
import suwayomi.tachidesk.impl.extension.github.OnlineExtension import suwayomi.tachidesk.manga.impl.extension.github.OnlineExtension
import suwayomi.tachidesk.model.dataclass.ExtensionDataClass import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass
import suwayomi.tachidesk.model.table.ExtensionTable import suwayomi.tachidesk.manga.model.table.ExtensionTable
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
object ExtensionsList { 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 * Copyright (C) Contributors to the Suwayomi project
@@ -13,16 +13,14 @@ import com.google.gson.JsonArray
import com.google.gson.JsonParser import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import okhttp3.Request import okhttp3.Request
import suwayomi.tachidesk.impl.util.network.UnzippingInterceptor import suwayomi.tachidesk.manga.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.tachidesk.model.dataclass.ExtensionDataClass import suwayomi.tachidesk.manga.impl.util.PackageTools.LIB_VERSION_MIN
import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
object ExtensionGithubApi { object ExtensionGithubApi {
const val BASE_URL = "https://raw.githubusercontent.com" private const val BASE_URL = "https://raw.githubusercontent.com"
const val REPO_URL_PREFIX = "$BASE_URL/tachiyomiorg/tachiyomi-extensions/repo" private 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 fun parseResponse(json: JsonArray): List<OnlineExtension> { private fun parseResponse(json: JsonArray): List<OnlineExtension> {
return json return json
@@ -61,17 +59,15 @@ object ExtensionGithubApi {
.addNetworkInterceptor { chain -> .addNetworkInterceptor { chain ->
val originalResponse = chain.proceed(chain.request()) val originalResponse = chain.proceed(chain.request())
originalResponse.newBuilder() originalResponse.newBuilder()
.header("Content-Encoding", "gzip")
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.build() .build()
} }
.addInterceptor(UnzippingInterceptor())
.build() .build()
} }
private fun getRepo(): com.google.gson.JsonArray { private fun getRepo(): JsonArray {
val request = Request.Builder() val request = Request.Builder()
.url("$REPO_URL_PREFIX/index.json.gz") .url("$REPO_URL_PREFIX/index.min.json")
.build() .build()
val response = client.newCall(request).execute().use { response -> response.body!!.string() } 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