mirror of
https://github.com/tachiyomiorg/tachiyomi-extensions-inspector.git
synced 2025-01-13 01:09:09 +01:00
Merge branch 'master' of github.com:Suwayomi/Tachidesk
This commit is contained in:
commit
10a29cab33
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
||||||
|
@ -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.2.1"
|
||||||
|
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")
|
||||||
|
@ -14,7 +14,7 @@ import suwayomi.server.database.migration.lib.Migration
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
class M0004_AnimeTablesBatch1 : Migration() {
|
class M0004_AnimeTablesBatch1 : Migration() {
|
||||||
private object AnimeExtensionTable : IntIdTable() {
|
private class AnimeExtensionTable : IntIdTable() {
|
||||||
val apkName = varchar("apk_name", 1024)
|
val apkName = varchar("apk_name", 1024)
|
||||||
|
|
||||||
// default is the local source icon from tachiyomi
|
// default is the local source icon from tachiyomi
|
||||||
@ -35,7 +35,7 @@ class M0004_AnimeTablesBatch1 : Migration() {
|
|||||||
val classFQName = varchar("class_name", 1024).default("") // fully qualified name
|
val classFQName = varchar("class_name", 1024).default("") // fully qualified name
|
||||||
}
|
}
|
||||||
|
|
||||||
private object AnimeSourceTable : IdTable<Long>() {
|
private class AnimeSourceTable : IdTable<Long>() {
|
||||||
override val id = long("id").entityId()
|
override val id = long("id").entityId()
|
||||||
val name = varchar("name", 128)
|
val name = varchar("name", 128)
|
||||||
val lang = varchar("lang", 10)
|
val lang = varchar("lang", 10)
|
||||||
@ -46,8 +46,8 @@ class M0004_AnimeTablesBatch1 : Migration() {
|
|||||||
override fun run() {
|
override fun run() {
|
||||||
transaction {
|
transaction {
|
||||||
SchemaUtils.create(
|
SchemaUtils.create(
|
||||||
AnimeExtensionTable,
|
AnimeExtensionTable(),
|
||||||
AnimeSourceTable
|
AnimeSourceTable()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import suwayomi.server.database.migration.lib.Migration
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
class M0005_AnimeTablesBatch2 : Migration() {
|
class M0005_AnimeTablesBatch2 : Migration() {
|
||||||
private object AnimeTable : IntIdTable() {
|
private class AnimeTable : IntIdTable() {
|
||||||
val url = varchar("url", 2048)
|
val url = varchar("url", 2048)
|
||||||
val title = varchar("title", 512)
|
val title = varchar("title", 512)
|
||||||
val initialized = bool("initialized").default(false)
|
val initialized = bool("initialized").default(false)
|
||||||
@ -38,7 +38,7 @@ class M0005_AnimeTablesBatch2 : Migration() {
|
|||||||
override fun run() {
|
override fun run() {
|
||||||
transaction {
|
transaction {
|
||||||
SchemaUtils.create(
|
SchemaUtils.create(
|
||||||
AnimeTable
|
AnimeTable()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import suwayomi.server.database.migration.lib.Migration
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
class M0006_AnimeTablesBatch3 : Migration() {
|
class M0006_AnimeTablesBatch3 : Migration() {
|
||||||
private object EpisodeTable : IntIdTable() {
|
private class EpisodeTable : IntIdTable() {
|
||||||
val url = varchar("url", 2048)
|
val url = varchar("url", 2048)
|
||||||
val name = varchar("name", 512)
|
val name = varchar("name", 512)
|
||||||
val date_upload = long("date_upload").default(0)
|
val date_upload = long("date_upload").default(0)
|
||||||
@ -34,7 +34,7 @@ class M0006_AnimeTablesBatch3 : Migration() {
|
|||||||
override fun run() {
|
override fun run() {
|
||||||
transaction {
|
transaction {
|
||||||
SchemaUtils.create(
|
SchemaUtils.create(
|
||||||
EpisodeTable
|
EpisodeTable()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package suwayomi.server.database.migration
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
import org.jetbrains.exposed.sql.ReferenceOption
|
||||||
|
import org.jetbrains.exposed.sql.SchemaUtils
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import suwayomi.server.database.migration.lib.Migration
|
||||||
|
import suwayomi.tachidesk.model.table.ChapterTable
|
||||||
|
import suwayomi.tachidesk.model.table.MangaTable
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
class M0010_MangaAndChapterMeta : Migration() {
|
||||||
|
private class ChapterMetaTable : IntIdTable() {
|
||||||
|
val key = varchar("key", 256)
|
||||||
|
val value = varchar("value", 4096)
|
||||||
|
val ref = reference("chapter_ref", ChapterTable, ReferenceOption.CASCADE)
|
||||||
|
}
|
||||||
|
private class MangaMetaTable : IntIdTable() {
|
||||||
|
val key = varchar("key", 256)
|
||||||
|
val value = varchar("value", 4096)
|
||||||
|
val ref = reference("manga_ref", MangaTable, ReferenceOption.CASCADE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
transaction {
|
||||||
|
SchemaUtils.create(
|
||||||
|
ChapterMetaTable(),
|
||||||
|
MangaMetaTable()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,11 +18,13 @@ import suwayomi.tachidesk.impl.CategoryManga.removeMangaFromCategory
|
|||||||
import suwayomi.tachidesk.impl.Chapter.getChapter
|
import suwayomi.tachidesk.impl.Chapter.getChapter
|
||||||
import suwayomi.tachidesk.impl.Chapter.getChapterList
|
import suwayomi.tachidesk.impl.Chapter.getChapterList
|
||||||
import suwayomi.tachidesk.impl.Chapter.modifyChapter
|
import suwayomi.tachidesk.impl.Chapter.modifyChapter
|
||||||
|
import suwayomi.tachidesk.impl.Chapter.modifyChapterMeta
|
||||||
import suwayomi.tachidesk.impl.Library.addMangaToLibrary
|
import suwayomi.tachidesk.impl.Library.addMangaToLibrary
|
||||||
import suwayomi.tachidesk.impl.Library.getLibraryMangas
|
import suwayomi.tachidesk.impl.Library.getLibraryMangas
|
||||||
import suwayomi.tachidesk.impl.Library.removeMangaFromLibrary
|
import suwayomi.tachidesk.impl.Library.removeMangaFromLibrary
|
||||||
import suwayomi.tachidesk.impl.Manga.getManga
|
import suwayomi.tachidesk.impl.Manga.getManga
|
||||||
import suwayomi.tachidesk.impl.Manga.getMangaThumbnail
|
import suwayomi.tachidesk.impl.Manga.getMangaThumbnail
|
||||||
|
import suwayomi.tachidesk.impl.Manga.modifyMangaMeta
|
||||||
import suwayomi.tachidesk.impl.MangaList.getMangaList
|
import suwayomi.tachidesk.impl.MangaList.getMangaList
|
||||||
import suwayomi.tachidesk.impl.Page.getPageImage
|
import suwayomi.tachidesk.impl.Page.getPageImage
|
||||||
import suwayomi.tachidesk.impl.Search.sourceFilters
|
import suwayomi.tachidesk.impl.Search.sourceFilters
|
||||||
@ -185,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()
|
||||||
@ -207,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()
|
||||||
|
@ -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
|
||||||
@ -20,6 +21,7 @@ import suwayomi.tachidesk.impl.Manga.getManga
|
|||||||
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
|
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||||
import suwayomi.tachidesk.impl.util.lang.awaitSingle
|
import suwayomi.tachidesk.impl.util.lang.awaitSingle
|
||||||
import suwayomi.tachidesk.model.dataclass.ChapterDataClass
|
import suwayomi.tachidesk.model.dataclass.ChapterDataClass
|
||||||
|
import suwayomi.tachidesk.model.table.ChapterMetaTable
|
||||||
import suwayomi.tachidesk.model.table.ChapterTable
|
import suwayomi.tachidesk.model.table.ChapterTable
|
||||||
import suwayomi.tachidesk.model.table.MangaTable
|
import suwayomi.tachidesk.model.table.MangaTable
|
||||||
import suwayomi.tachidesk.model.table.PageTable
|
import suwayomi.tachidesk.model.table.PageTable
|
||||||
@ -131,6 +133,7 @@ object Chapter {
|
|||||||
dbChapter[ChapterTable.pageCount],
|
dbChapter[ChapterTable.pageCount],
|
||||||
|
|
||||||
chapterList.size,
|
chapterList.size,
|
||||||
|
meta = getChapterMetaMap(dbChapter[ChapterTable.id])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,7 +202,8 @@ object Chapter {
|
|||||||
chapterEntry[ChapterTable.chapterIndex],
|
chapterEntry[ChapterTable.chapterIndex],
|
||||||
chapterEntry[ChapterTable.isDownloaded],
|
chapterEntry[ChapterTable.isDownloaded],
|
||||||
pageCount,
|
pageCount,
|
||||||
chapterCount.toInt()
|
chapterCount.toInt(),
|
||||||
|
getChapterMetaMap(chapterEntry[ChapterTable.id])
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ChapterTable.toDataClass(chapterEntry)
|
ChapterTable.toDataClass(chapterEntry)
|
||||||
@ -230,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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,9 @@ 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
|
||||||
@ -24,6 +27,7 @@ import suwayomi.tachidesk.impl.util.network.await
|
|||||||
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.clearCachedImage
|
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.clearCachedImage
|
||||||
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse
|
import suwayomi.tachidesk.impl.util.storage.CachedImageResponse.getCachedImageResponse
|
||||||
import suwayomi.tachidesk.model.dataclass.MangaDataClass
|
import suwayomi.tachidesk.model.dataclass.MangaDataClass
|
||||||
|
import suwayomi.tachidesk.model.table.MangaMetaTable
|
||||||
import suwayomi.tachidesk.model.table.MangaStatus
|
import suwayomi.tachidesk.model.table.MangaStatus
|
||||||
import suwayomi.tachidesk.model.table.MangaTable
|
import suwayomi.tachidesk.model.table.MangaTable
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@ -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
|
||||||
@ -104,11 +109,38 @@ object Manga {
|
|||||||
MangaStatus.valueOf(fetchedManga.status).name,
|
MangaStatus.valueOf(fetchedManga.status).name,
|
||||||
mangaEntry[MangaTable.inLibrary],
|
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
|
||||||
|
@ -11,6 +11,7 @@ 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.Manga.getMangaMetaMap
|
||||||
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
|
import suwayomi.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||||
import suwayomi.tachidesk.impl.util.lang.awaitSingle
|
import suwayomi.tachidesk.impl.util.lang.awaitSingle
|
||||||
import suwayomi.tachidesk.model.dataclass.MangaDataClass
|
import suwayomi.tachidesk.model.dataclass.MangaDataClass
|
||||||
@ -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])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,4 +39,6 @@ data class ChapterDataClass(
|
|||||||
/** total chapter count, used to calculate if there's a next and prev chapter */
|
/** total chapter count, used to calculate if there's a next and prev chapter */
|
||||||
val chapterCount: Int? = null,
|
val chapterCount: Int? = null,
|
||||||
|
|
||||||
|
/** used to store client specific values */
|
||||||
|
val meta: Map<String, String> = emptyMap(),
|
||||||
)
|
)
|
||||||
|
@ -26,6 +26,7 @@ data class MangaDataClass(
|
|||||||
val status: String = MangaStatus.UNKNOWN.name,
|
val status: String = MangaStatus.UNKNOWN.name,
|
||||||
val inLibrary: Boolean = false,
|
val inLibrary: Boolean = false,
|
||||||
val source: SourceDataClass? = null,
|
val source: SourceDataClass? = null,
|
||||||
|
val meta: Map<String, String> = emptyMap(),
|
||||||
|
|
||||||
val freshData: Boolean = false
|
val freshData: Boolean = false
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package suwayomi.tachidesk.model.table
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
import org.jetbrains.exposed.sql.ReferenceOption
|
||||||
|
|
||||||
|
object ChapterMetaTable : IntIdTable() {
|
||||||
|
val key = varchar("key", 256)
|
||||||
|
val value = varchar("value", 4096)
|
||||||
|
val ref = reference("chapter_ref", ChapterTable, ReferenceOption.CASCADE)
|
||||||
|
}
|
@ -11,6 +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.tachidesk.impl.Chapter.getChapterMetaMap
|
||||||
import suwayomi.tachidesk.model.dataclass.ChapterDataClass
|
import suwayomi.tachidesk.model.dataclass.ChapterDataClass
|
||||||
|
|
||||||
object ChapterTable : IntIdTable() {
|
object ChapterTable : IntIdTable() {
|
||||||
@ -51,4 +52,5 @@ fun ChapterTable.toDataClass(chapterEntry: ResultRow) =
|
|||||||
chapterEntry[isDownloaded],
|
chapterEntry[isDownloaded],
|
||||||
chapterEntry[pageCount],
|
chapterEntry[pageCount],
|
||||||
transaction { ChapterTable.select { ChapterTable.manga eq chapterEntry[manga].value }.count().toInt() },
|
transaction { ChapterTable.select { ChapterTable.manga eq chapterEntry[manga].value }.count().toInt() },
|
||||||
|
getChapterMetaMap(chapterEntry[id]),
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package suwayomi.tachidesk.model.table
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
import org.jetbrains.exposed.sql.ReferenceOption
|
||||||
|
|
||||||
|
object MangaMetaTable : IntIdTable() {
|
||||||
|
val key = varchar("key", 256)
|
||||||
|
val value = varchar("value", 4096)
|
||||||
|
val ref = reference("manga_ref", MangaTable, ReferenceOption.CASCADE)
|
||||||
|
}
|
@ -10,6 +10,7 @@ package suwayomi.tachidesk.model.table
|
|||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
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.Manga.getMangaMetaMap
|
||||||
import suwayomi.tachidesk.impl.MangaList.proxyThumbnailUrl
|
import suwayomi.tachidesk.impl.MangaList.proxyThumbnailUrl
|
||||||
import suwayomi.tachidesk.model.dataclass.MangaDataClass
|
import suwayomi.tachidesk.model.dataclass.MangaDataClass
|
||||||
import suwayomi.tachidesk.model.table.MangaStatus.Companion
|
import suwayomi.tachidesk.model.table.MangaStatus.Companion
|
||||||
@ -50,7 +51,8 @@ fun MangaTable.toDataClass(mangaEntry: ResultRow) =
|
|||||||
mangaEntry[description],
|
mangaEntry[description],
|
||||||
mangaEntry[genre],
|
mangaEntry[genre],
|
||||||
Companion.valueOf(mangaEntry[status]).name,
|
Companion.valueOf(mangaEntry[status]).name,
|
||||||
mangaEntry[inLibrary]
|
mangaEntry[inLibrary],
|
||||||
|
meta = getMangaMetaMap(mangaEntry[id])
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class MangaStatus(val status: Int) {
|
enum class MangaStatus(val status: Int) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user