Compare commits
61 Commits
v0.3.0-rc1
...
v0.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 63a078cf7d | |||
| 5304917e53 | |||
| 831b74d2ec | |||
| 1bad9dcd69 | |||
| dd43716851 | |||
| f2e55e95a2 | |||
| 14658a0c4d | |||
| 4195e7056b | |||
| 1d29e8b248 | |||
| b718c718df | |||
| a3601cf1b5 | |||
| 0236a9639b | |||
| 5f4c7454ee | |||
| 773120c96a | |||
| 4b273c6bf9 | |||
| b626aa66ba | |||
| 1dd029559e | |||
| 59cbe5d5bc | |||
| 40d1173653 | |||
| bf6a0aba5d | |||
| 34d8feacdd | |||
| 1ea821584c | |||
| 3d2fee19bb | |||
| 449d12779a | |||
| 6fb6a251e7 | |||
| 4d6220f894 | |||
| fe747bfc52 | |||
| 0c2d038870 | |||
| 4e3f73af75 | |||
| 63e5e1b45f | |||
| 2e1558bd96 | |||
| 0671dee8b2 | |||
| 8f91b8089a | |||
| 009b45f676 | |||
| 8f7d5eb311 | |||
| f3de835ef3 | |||
| fd6662f428 | |||
| fde137b3ed | |||
| a1349aa0e3 | |||
| c9ef5f9b9d | |||
| 8fbf564177 | |||
| ae0b1a818c | |||
| c04cc780b7 | |||
| 71ad1bb6e3 | |||
| c1be77ee9b | |||
| d1fa857ffb | |||
| 93fd81b38b | |||
| 2f116b40b2 | |||
| b884d34bdf | |||
| 309803368b | |||
| 19fc5be8f3 | |||
| c28fac14c0 | |||
| 66e38de29f | |||
| 282cb1d3be | |||
| b741ded595 | |||
| 6b290695fc | |||
| 4e43c554c0 | |||
| 090a72b35f | |||
| 3fcc269df3 | |||
| 9958e0eb34 | |||
| c5269002a2 |
@@ -1,24 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
git lfs install
|
||||
#git lfs track "*.zip"
|
||||
cp master/server/build/Tachidesk-*.jar preview
|
||||
cd preview
|
||||
|
||||
cp ../master/repo/* .
|
||||
new_jar_build=$(ls *.jar| tail -1)
|
||||
echo "last jar build file name: $new_jar_build"
|
||||
|
||||
new_win32_build=$(ls *.zip| tail -1)
|
||||
echo "last win32 build file name: $new_win32_build"
|
||||
|
||||
cp -f $new_jar_build Tachidesk-latest.jar
|
||||
cp -f $new_win32_build Tachidesk-latest-win32.zip
|
||||
|
||||
rm -rf latest_pointer/*
|
||||
cp $new_jar_build latest_pointer
|
||||
|
||||
latest=$(ls *.jar | tail -n1 | cut -d"-" -f3 | cut -d"." -f1)
|
||||
echo "{ \"latest\": \"$latest\" }" > index.json
|
||||
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git status
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
git add .
|
||||
git commit -m "Update repo"
|
||||
git commit -m "Update preview repository"
|
||||
git push
|
||||
else
|
||||
echo "No changes to commit"
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get last commit message
|
||||
#last_commit_log=$(git log -1 --pretty=format:"%s")
|
||||
#echo "last commit log: $last_commit_log"
|
||||
#
|
||||
#filter_count=$(echo "$last_commit_log" | grep -e '\[RELEASE CI\]' -e '\[CI RELEASE\]' | wc -c)
|
||||
#echo "count is: $filter_count"
|
||||
|
||||
mkdir -p repo/
|
||||
cp server/build/Tachidesk-*.jar repo/
|
||||
cp server/build/Tachidesk-*.zip repo/
|
||||
|
||||
ls repo
|
||||
pwd
|
||||
|
||||
#if [ "$filter_count" -gt 0 ]; then
|
||||
# cp server/build/Tachidesk-*.jar repo/
|
||||
# cp server/build/Tachidesk-*.zip repo/
|
||||
#fi
|
||||
@@ -1,9 +1,6 @@
|
||||
name: CI
|
||||
name: CI Pull Request
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -33,7 +30,7 @@ jobs:
|
||||
- name: Checkout master branch
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: master
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
path: master
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
name: CI build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
check_wrapper:
|
||||
name: Validate Gradle Wrapper
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
build:
|
||||
name: Build FatJar
|
||||
needs: check_wrapper
|
||||
if: "!startsWith(github.event.head_commit.message, '[SKIP CI]')"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Cancel previous runs
|
||||
uses: styfle/cancel-workflow-action@0.5.0
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
- name: Checkout master branch
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: master
|
||||
path: master
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
|
||||
- name: Copy CI gradle.properties
|
||||
run: |
|
||||
cd master
|
||||
mkdir -p ~/.gradle
|
||||
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
|
||||
|
||||
- name: Download android.jar
|
||||
run: |
|
||||
cd master
|
||||
curl https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar -o AndroidCompat/lib/android.jar
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
**/react/node_modules
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/react/yarn.lock') }}
|
||||
|
||||
- name: Build and copy webUI, Build Jar and launch4j
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
build-root-directory: master
|
||||
wrapper-directory: master
|
||||
arguments: :webUI:copyBuild :server:windowsPackage --stacktrace
|
||||
wrapper-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
|
||||
- name: Checkout preview branch
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: preview
|
||||
path: preview
|
||||
|
||||
- name: Deploy preview
|
||||
run: |
|
||||
./master/.github/scripts/commit-repo.sh
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Publish
|
||||
name: CI Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -68,18 +68,12 @@ jobs:
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
|
||||
|
||||
- name: Create repo artifacts
|
||||
run: |
|
||||
cd master
|
||||
./.github/scripts/create-repo.sh
|
||||
|
||||
- name: Upload Release
|
||||
uses: xresloader/upload-to-github-release@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
file: "master/repo/*"
|
||||
file: "master/server/build/*.jar;master/server/build/*-win32.zip"
|
||||
tags: true
|
||||
draft: true
|
||||
verbose: true
|
||||
|
||||
@@ -6,7 +6,6 @@ plugins {
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven {
|
||||
url = uri("https://jitpack.io")
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ Write-Output "Getting required Android.jar..."
|
||||
Remove-Item -Recurse -Force "tmp" -ErrorAction SilentlyContinue | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path "tmp" | Out-Null
|
||||
|
||||
$androidEncoded = (Invoke-WebRequest -Uri "https://android.googlesource.com/platform/prebuilts/sdk/+/3b8a524d25fa6c3d795afb1eece3f24870c60988/27/public/android.jar?format=TEXT").content
|
||||
$androidEncoded = (Invoke-WebRequest -Uri "https://android.googlesource.com/platform/prebuilts/sdk/+/3b8a524d25fa6c3d795afb1eece3f24870c60988/27/public/android.jar?format=TEXT" -UseBasicParsing).content
|
||||
|
||||
$android_jar = (Get-Location).Path + "\tmp\android.jar"
|
||||
|
||||
|
||||
@@ -8,6 +8,17 @@
|
||||
|
||||
# This is a bash script to create android.jar stubs
|
||||
|
||||
for dep in "curl" "base64" "zip"
|
||||
do
|
||||
which $dep >/dev/null 2>&1 || { echo >&2 "Error: This script needs $dep installed."; abort=yes; }
|
||||
done
|
||||
|
||||
if [ $abort = yes ]; then
|
||||
echo "Some of the dependencies didn't exist. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# foolproof against running from AndroidCompat dir instead of running from project root
|
||||
if [ "$(basename $(pwd))" = "AndroidCompat" ]; then
|
||||
cd ..
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
package com.squareup.duktape;
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
* Copyright (C) 2015 Square, Inc.
|
||||
*
|
||||
* 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/. */
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.squareup.duktape;
|
||||
|
||||
import kotlin.NotImplementedError;
|
||||
|
||||
@@ -64,18 +73,18 @@ public final class Duktape implements Closeable, AutoCloseable {
|
||||
throw new NotImplementedError("Not implemented!");
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Attaches to a global JavaScript object called {@code name} that implements {@code type}.
|
||||
// * {@code type} defines the interface implemented in JavaScript that will be accessible to Java.
|
||||
// * {@code type} must be an interface that does not extend any other interfaces, and cannot define
|
||||
// * any overloaded methods.
|
||||
// * <p>Methods of the interface may return {@code void} or any of the following supported argument
|
||||
// * types: {@code boolean}, {@link Boolean}, {@code int}, {@link Integer}, {@code double},
|
||||
// * {@link Double}, {@link String}.
|
||||
// */
|
||||
// public synchronized <T> T get(final String name, final Class<T> type) {
|
||||
// throw new NotImplementedError("Not implemented!");
|
||||
// }
|
||||
/**
|
||||
* Attaches to a global JavaScript object called {@code name} that implements {@code type}.
|
||||
* {@code type} defines the interface implemented in JavaScript that will be accessible to Java.
|
||||
* {@code type} must be an interface that does not extend any other interfaces, and cannot define
|
||||
* any overloaded methods.
|
||||
* <p>Methods of the interface may return {@code void} or any of the following supported argument
|
||||
* types: {@code boolean}, {@link Boolean}, {@code int}, {@link Integer}, {@code double},
|
||||
* {@link Double}, {@link String}.
|
||||
*/
|
||||
public synchronized <T> T get(final String name, final Class<T> type) {
|
||||
throw new NotImplementedError("Not implemented!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the native resources associated with this object. You <strong>must</strong> call this
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
package com.squareup.duktape;
|
||||
|
||||
/*
|
||||
* 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/. */
|
||||
// part of tachiyomi-extensions which was originally licensed under Apache License Version 2.0
|
||||
|
||||
/* part of tachiyomi-extensions which is licensed under Apache License Version 2.0 */
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
|
||||

|
||||
| Build | Stable | Preview | Support Server |
|
||||
|-------|----------|---------|---------|
|
||||
|  | [](https://github.com/Suwayomi/Tachidesk/releases) | [](https://github.com/Suwayomi/Tachidesk/tree/preview/latest_pointer) | [](https://discord.gg/DDZdqZWaHA) |
|
||||
|
||||
# Tachidesk
|
||||
<img src="https://github.com/Suwayomi/Tachidesk/raw/master/server/src/main/resources/icon/faviconlogo.png" alt="drawing" width="200"/>
|
||||
|
||||
A free and open source manga reader that runs extensions built for [Tachiyomi](https://tachiyomi.org/).
|
||||
|
||||
Tachidesk is as multi-platform as you can get. Any platform that runs java and/or has a modern browser can run it.
|
||||
Tachidesk is an independent Tachiyomi compatible software made by [@AriaMoradi AKA ArMor](https://github.com/AriaMoradi) and contributors and is **not a Fork of** Tachiyomi.
|
||||
|
||||
Tachidesk is as multi-platform as you can get. Any platform that runs java and/or has a modern browser can run it.
|
||||
|
||||
Ability to read and write Tachiyomi compatible backups and syncing is a planned feature.
|
||||
|
||||
@@ -15,6 +22,7 @@ Here is a list of current features:
|
||||
- Searching and browsing installed sources.
|
||||
- A decent chapter reader.
|
||||
- Ability to download Mangas for offline read(This partially works)
|
||||
- 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, so you may have to delete your data to fix it. See [General troubleshooting](#general-troubleshooting) and [Support and help](#support-and-help) if it happens.
|
||||
|
||||
@@ -24,9 +32,9 @@ Anyways, for more info checkout [finished milestone #1](https://github.com/Suway
|
||||
### All Operating Systems
|
||||
You should have The Java Runtime Environment(JRE) 8 or newer and a modern browser installed. Also an internet connection is required as almost everything this app does is downloading stuff.
|
||||
|
||||
Download the latest jar release from [the releases section](https://github.com/Suwayomi/Tachidesk/releases).
|
||||
Download the latest "Stable" jar release from [the releases section](https://github.com/Suwayomi/Tachidesk/releases) or a preview jar build from [the preview branch](https://github.com/Suwayomi/Tachidesk/tree/preview).
|
||||
|
||||
Double click on the jar file or run `java -jar Tachidesk-vX.Y.Z-rxxx.jar` from a Terminal/Command Prompt window to run the app which will open a new browser window automatically. Also the System Tray Icon is your friend if you need to open the browser window again or close Tachidesk.
|
||||
Double click on the jar file or run `java -jar Tachidesk-vX.Y.Z-rxxx.jar` (or `java -jar Tachidesk-latest.jar` if you have the latest preview) from a Terminal/Command Prompt window to run the app which will open a new browser window automatically. Also the System Tray Icon is your friend if you need to open the browser window again or close Tachidesk.
|
||||
|
||||
### Windows
|
||||
Download the latest win32 release from [the releases section](https://github.com/Suwayomi/Tachidesk/releases).
|
||||
@@ -43,7 +51,7 @@ yay -S tachidesk
|
||||
Check [arbuilder's repo](https://github.com/arbuilder/Tachidesk-docker) out for more details and the dockerfile.
|
||||
|
||||
## General troubleshooting
|
||||
If the app breaks try deleting the directory below and re-running the app (**This will delete all your data!**) and if the problem persists open an issue.
|
||||
If the app breaks, make sure that it's not running(right click on tray icon and quit or kill it through the way your Operating System provides), delete the directory below and re-run the app (**This procedure will delete all your data!**) and if the problem persists open an issue or ask for help on discord.
|
||||
|
||||
On Mac OS X : `/Users/<Account>/Library/Application Support/Tachidesk`
|
||||
|
||||
@@ -54,7 +62,7 @@ On Windows 7 and later : `C:\Users\<Account>\AppData\Local\Tachidesk`
|
||||
On Unix/Linux : `/home/<account>/.local/share/Tachidesk`
|
||||
|
||||
## Support and help
|
||||
Join Tachidesk's [discord server](https://discord.gg/wgPyb7hE5d) to hang out with the community and receive support and help.
|
||||
Join Tachidesk's [discord server](https://discord.gg/DDZdqZWaHA) to hang out with the community and to receive support and help.
|
||||
|
||||
## How does it work?
|
||||
This project has two components:
|
||||
@@ -65,13 +73,14 @@ This project has two components:
|
||||
### Prerequisite: Get Android stubs jar
|
||||
#### Manual download
|
||||
Download [android.jar](https://raw.githubusercontent.com/Suwayomi/Tachidesk/android-jar/android.jar) and put it under `AndroidCompat/lib`.
|
||||
#### Automated download(needs `bash`, `curl`, `base64`, `zip` to work)
|
||||
#### Automated download
|
||||
Run `AndroidCompat/getAndroid.sh`(MacOS/Linux) or `AndroidCompat/getAndroid.ps1`(Windows) from project's root directory to download and rebuild the jar file from Google's repository.
|
||||
### Prerequisite: Software dependencies
|
||||
You need this software packages installed in order to build this project:
|
||||
- Java Development Kit and Java Runtime Environment version 8 or newer(both Oracle JDK and OpenJDK works)
|
||||
- Nodejs LTS or latest
|
||||
- Yarn
|
||||
- Git
|
||||
### building the full-blown jar
|
||||
Run `./gradlew :webUI:copyBuild server:shadowJar`, the resulting built jar file will be `server/build/Tachidesk-vX.Y.Z-rxxx.jar`.
|
||||
### building without `webUI` bundled(server only)
|
||||
|
||||
+12
-17
@@ -1,8 +1,7 @@
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.jvm") version "1.4.21" apply false // Also in buildSrc Config.kt
|
||||
id("java")
|
||||
kotlin("jvm") version "1.4.32"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -11,12 +10,10 @@ allprojects {
|
||||
version = "1.0"
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven("https://maven.google.com/")
|
||||
maven("https://jitpack.io")
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||
maven("https://dl.bintray.com/inorichi/maven")
|
||||
maven("https://dl.google.com/dl/android/maven2/")
|
||||
}
|
||||
}
|
||||
@@ -28,7 +25,6 @@ val projects = listOf(
|
||||
)
|
||||
|
||||
configure(projects) {
|
||||
apply(plugin = "java")
|
||||
apply(plugin = "org.jetbrains.kotlin.jvm")
|
||||
|
||||
java {
|
||||
@@ -36,33 +32,32 @@ configure(projects) {
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Kotlin
|
||||
implementation(kotlin("stdlib", KotlinCompilerVersion.VERSION))
|
||||
implementation(kotlin("stdlib", KotlinCompilerVersion.VERSION))
|
||||
implementation(kotlin("reflect", version = "1.4.21"))
|
||||
testImplementation(kotlin("test", version = "1.4.21"))
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
implementation(kotlin("reflect"))
|
||||
testImplementation(kotlin("test"))
|
||||
|
||||
// coroutines
|
||||
val coroutinesVersion = "1.4.2"
|
||||
val coroutinesVersion = "1.4.3"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutinesVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion")
|
||||
|
||||
|
||||
// Dependency Injection
|
||||
implementation("org.kodein.di:kodein-di-conf-jvm:7.1.0")
|
||||
implementation("org.kodein.di:kodein-di-conf-jvm:7.5.0")
|
||||
|
||||
// Logging
|
||||
implementation("org.slf4j:slf4j-api:1.7.30")
|
||||
implementation("ch.qos.logback:logback-classic:1.2.3")
|
||||
implementation("io.github.microutils:kotlin-logging:2.0.3")
|
||||
implementation("io.github.microutils:kotlin-logging:2.0.6")
|
||||
|
||||
// RxJava
|
||||
implementation("io.reactivex:rxjava:1.3.8")
|
||||
@@ -73,11 +68,11 @@ configure(projects) {
|
||||
|
||||
|
||||
// dependency of :AndroidCompat:Config
|
||||
implementation("com.typesafe:config:1.4.0")
|
||||
implementation("com.typesafe:config:1.4.1")
|
||||
implementation("io.github.config4k:config4k:0.4.2")
|
||||
|
||||
// to get application content root
|
||||
implementation("net.harawata:appdirs:1.2.0")
|
||||
implementation("net.harawata:appdirs:1.2.1")
|
||||
|
||||
// dex2jar: https://github.com/DexPatcher/dex2jar/releases/tag/v2.1-20190905-lanchon
|
||||
implementation("com.github.DexPatcher.dex2jar:dex-tools:v2.1-20190905-lanchon")
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
+135
-105
@@ -1,29 +1,25 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jmailen.gradle.kotlinter.tasks.FormatTask
|
||||
import org.jmailen.gradle.kotlinter.tasks.LintTask
|
||||
import java.io.BufferedReader
|
||||
|
||||
plugins {
|
||||
// id("org.jetbrains.kotlin.jvm") version "1.4.21"
|
||||
application
|
||||
id("com.github.johnrengelman.shadow") version "6.1.0"
|
||||
id("org.jmailen.kotlinter") version "3.3.0"
|
||||
id("edu.sc.seis.launch4j") version "2.4.9"
|
||||
id("com.github.johnrengelman.shadow") version "7.0.0"
|
||||
id("org.jmailen.kotlinter") version "3.4.3"
|
||||
id("edu.sc.seis.launch4j") version "2.5.0"
|
||||
id("de.fuerstenau.buildconfig") version "1.1.8"
|
||||
}
|
||||
|
||||
val TachideskVersion = "v0.2.7"
|
||||
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven {
|
||||
url = uri("https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
|
||||
// Source models and interfaces from Tachiyomi 1.x
|
||||
// using source class from tachiyomi commit 9493577de27c40ce8b2b6122cc447d025e34c477 to not depend on tachiyomi.sourceapi
|
||||
// implementation("tachiyomi.sourceapi:source-api:1.1")
|
||||
@@ -34,10 +30,10 @@ dependencies {
|
||||
implementation("com.squareup.okhttp3:okhttp:$okhttpVersion")
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:$okhttpVersion")
|
||||
implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:$okhttpVersion")
|
||||
implementation("com.squareup.okio:okio:2.9.0")
|
||||
implementation("com.squareup.okio:okio:2.10.0")
|
||||
|
||||
|
||||
// retrofit
|
||||
// Retrofit
|
||||
val retrofitVersion = "2.9.0"
|
||||
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
|
||||
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0")
|
||||
@@ -45,13 +41,10 @@ dependencies {
|
||||
implementation("com.squareup.retrofit2:adapter-rxjava:$retrofitVersion")
|
||||
|
||||
|
||||
// reactivex
|
||||
// Reactivex
|
||||
implementation("io.reactivex:rxjava:1.3.8")
|
||||
// implementation("io.reactivex:rxandroid:1.2.1")
|
||||
// implementation("com.jakewharton.rxrelay:rxrelay:1.2.0")
|
||||
// implementation("com.github.pwittchen:reactivenetwork:0.13.0")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
||||
implementation("com.google.code.gson:gson:2.8.6")
|
||||
implementation("com.github.salomonbrys.kotson:kotson:2.5.0")
|
||||
|
||||
@@ -60,19 +53,24 @@ dependencies {
|
||||
|
||||
|
||||
// api
|
||||
implementation("io.javalin:javalin:3.12.0")
|
||||
implementation("com.fasterxml.jackson.core:jackson-databind:2.10.3")
|
||||
implementation("io.javalin:javalin:3.13.6")
|
||||
implementation("com.fasterxml.jackson.core:jackson-databind:2.12.3")
|
||||
|
||||
// Exposed ORM
|
||||
val exposedVersion = "0.28.1"
|
||||
val exposedVersion = "0.31.1"
|
||||
implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
|
||||
implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")
|
||||
implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
|
||||
implementation("com.h2database:h2:1.4.199")
|
||||
implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion")
|
||||
|
||||
// current database driver
|
||||
implementation("com.h2database:h2:1.4.200")
|
||||
|
||||
// tray icon
|
||||
implementation("com.dorkbox:SystemTray:3.17")
|
||||
implementation("com.dorkbox:SystemTray:4.1")
|
||||
implementation("com.dorkbox:Utilities:1.9")
|
||||
|
||||
implementation("com.google.guava:guava:30.1.1-jre")
|
||||
|
||||
// AndroidCompat
|
||||
implementation(project(":AndroidCompat"))
|
||||
@@ -85,12 +83,9 @@ dependencies {
|
||||
testImplementation(kotlin("test-junit5"))
|
||||
}
|
||||
|
||||
val name = "ir.armor.tachidesk.Main"
|
||||
val MainClass = "ir.armor.tachidesk.Main"
|
||||
application {
|
||||
mainClass.set(name)
|
||||
|
||||
// Required by ShadowJar.
|
||||
mainClassName = name
|
||||
mainClass.set(MainClass)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -101,7 +96,11 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
val TachideskRevision = Runtime
|
||||
// should be bumped with each stable release
|
||||
val tachideskVersion = "v0.3.0"
|
||||
|
||||
// counts commit count on master
|
||||
val tachideskRevision = Runtime
|
||||
.getRuntime()
|
||||
.exec("git rev-list master --count")
|
||||
.let { process ->
|
||||
@@ -114,102 +113,133 @@ val TachideskRevision = Runtime
|
||||
|
||||
}
|
||||
|
||||
buildConfig {
|
||||
appName = rootProject.name
|
||||
clsName = "BuildConfig"
|
||||
packageName = "ir.armor.tachidesk.server"
|
||||
version = tachideskVersion
|
||||
|
||||
|
||||
buildConfigField("String", "name", rootProject.name) // alias for BuildConfig.NAME
|
||||
buildConfigField("String", "version", tachideskVersion) // alias for BuildConfig.VERSION
|
||||
buildConfigField("String", "revision", tachideskRevision)
|
||||
buildConfigField("boolean", "debug", project.hasProperty("debugApp").toString())
|
||||
}
|
||||
|
||||
launch4j { //used for windows
|
||||
mainClassName = MainClass
|
||||
bundledJrePath = "jre"
|
||||
bundledJre64Bit = true
|
||||
jreMinVersion = "8"
|
||||
outputDir = "${rootProject.name}-$tachideskVersion-$tachideskRevision-win32"
|
||||
icon = "${projectDir}/src/main/resources/icon/faviconlogo.ico"
|
||||
jar = "${projectDir}/build/${rootProject.name}-$tachideskVersion-$tachideskRevision.jar"
|
||||
}
|
||||
|
||||
tasks {
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
mapOf(
|
||||
"Main-Class" to "com.example.MainKt", //will make your jar (produced by jar task) runnable
|
||||
"ImplementationTitle" to project.name,
|
||||
"Implementation-Version" to project.version)
|
||||
"Main-Class" to MainClass,
|
||||
"Implementation-Title" to rootProject.name,
|
||||
"Implementation-Vendor" to "The Suwayomi Project",
|
||||
"Specification-Version" to tachideskVersion,
|
||||
"Implementation-Version" to tachideskRevision
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
shadowJar {
|
||||
manifest.inheritFrom(jar.get().manifest) //will make your shadowJar (produced by jar task) runnable
|
||||
archiveBaseName.set("Tachidesk")
|
||||
archiveVersion.set(TachideskVersion)
|
||||
archiveClassifier.set(TachideskRevision)
|
||||
archiveBaseName.set(rootProject.name)
|
||||
archiveVersion.set(tachideskVersion)
|
||||
archiveClassifier.set(tachideskRevision)
|
||||
}
|
||||
withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
withType<KotlinCompile> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xopt-in=kotlin.RequiresOptIn",
|
||||
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-Xopt-in=kotlinx.coroutines.InternalCoroutinesApi"
|
||||
"-Xopt-in=kotlin.RequiresOptIn",
|
||||
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-Xopt-in=kotlinx.coroutines.InternalCoroutinesApi"
|
||||
)
|
||||
}
|
||||
}
|
||||
test {
|
||||
useJUnit()
|
||||
}
|
||||
}
|
||||
|
||||
launch4j { //used for windows
|
||||
mainClassName = name
|
||||
bundledJrePath = "jre"
|
||||
bundledJre64Bit = true
|
||||
jreMinVersion = "8"
|
||||
outputDir = "Tachidesk-$TachideskVersion-$TachideskRevision-win32"
|
||||
icon = "${projectDir}/src/main/resources/icon/faviconlogo.ico"
|
||||
jar = "${projectDir}/build/Tachidesk-$TachideskVersion-$TachideskRevision.jar"
|
||||
}
|
||||
|
||||
tasks.register<Zip>("windowsPackage") {
|
||||
from(fileTree("$buildDir/Tachidesk-$TachideskVersion-$TachideskRevision-win32"))
|
||||
destinationDirectory.set(File("$buildDir"))
|
||||
archiveFileName.set("Tachidesk-$TachideskVersion-$TachideskRevision-win32.zip")
|
||||
dependsOn("windowsPackageWorkaround2")
|
||||
}
|
||||
|
||||
tasks.register<Delete>("windowsPackageWorkaround2") {
|
||||
delete(
|
||||
"$buildDir/Tachidesk-$TachideskVersion-$TachideskRevision-win32/jre",
|
||||
"$buildDir/Tachidesk-$TachideskVersion-$TachideskRevision-win32/lib",
|
||||
"$buildDir/Tachidesk-$TachideskVersion-$TachideskRevision-win32/server.exe",
|
||||
"$buildDir/Tachidesk-$TachideskVersion-$TachideskRevision-win32/Tachidesk-$TachideskVersion-$TachideskRevision-win32/Tachidesk-$TachideskVersion-$TachideskRevision-win32"
|
||||
)
|
||||
dependsOn("windowsPackageWorkaround")
|
||||
}
|
||||
|
||||
tasks.register<Copy>("windowsPackageWorkaround") {
|
||||
from("$buildDir/Tachidesk-$TachideskVersion-$TachideskRevision-win32")
|
||||
into("$buildDir/Tachidesk-$TachideskVersion-$TachideskRevision-win32/Tachidesk-$TachideskVersion-$TachideskRevision-win32")
|
||||
dependsOn("deleteUnwantedJreDir")
|
||||
}
|
||||
|
||||
tasks.register<Delete>("deleteUnwantedJreDir") {
|
||||
delete(
|
||||
"$buildDir/Tachidesk-$TachideskVersion-$TachideskRevision-win32/jdk8u282-b08-jre"
|
||||
)
|
||||
dependsOn("addJreToDistributable")
|
||||
}
|
||||
|
||||
tasks.register<Copy>("addJreToDistributable") {
|
||||
from(zipTree("$buildDir/OpenJDK8U-jre_x86-32_windows_hotspot_8u282b08.zip"))
|
||||
into("$buildDir/Tachidesk-$TachideskVersion-$TachideskRevision-win32")
|
||||
eachFile {
|
||||
path = path.replace(".*-jre".toRegex(),"jre")
|
||||
register<Zip>("windowsPackage") {
|
||||
from(fileTree("$buildDir/${rootProject.name}-$tachideskVersion-$tachideskRevision-win32"))
|
||||
destinationDirectory.set(File("$buildDir"))
|
||||
archiveFileName.set("${rootProject.name}-$tachideskVersion-$tachideskRevision-win32.zip")
|
||||
dependsOn("windowsPackageWorkaround2")
|
||||
}
|
||||
|
||||
register<Delete>("windowsPackageWorkaround2") {
|
||||
delete(
|
||||
"$buildDir/${rootProject.name}-$tachideskVersion-$tachideskRevision-win32/jre",
|
||||
"$buildDir/${rootProject.name}-$tachideskVersion-$tachideskRevision-win32/lib",
|
||||
"$buildDir/${rootProject.name}-$tachideskVersion-$tachideskRevision-win32/server.exe",
|
||||
"$buildDir/${rootProject.name}-$tachideskVersion-$tachideskRevision-win32/Tachidesk-$tachideskVersion-$tachideskRevision-win32/Tachidesk-$tachideskVersion-$tachideskRevision-win32"
|
||||
)
|
||||
dependsOn("windowsPackageWorkaround")
|
||||
}
|
||||
|
||||
register<Copy>("windowsPackageWorkaround") {
|
||||
from("$buildDir/${rootProject.name}-$tachideskVersion-$tachideskRevision-win32")
|
||||
into("$buildDir/${rootProject.name}-$tachideskVersion-$tachideskRevision-win32/${rootProject.name}-$tachideskVersion-$tachideskRevision-win32")
|
||||
dependsOn("deleteUnwantedJreDir")
|
||||
}
|
||||
|
||||
register<Delete>("deleteUnwantedJreDir") {
|
||||
delete(
|
||||
"$buildDir/${rootProject.name}-$tachideskVersion-$tachideskRevision-win32/jdk8u282-b08-jre"
|
||||
)
|
||||
dependsOn("addJreToDistributable")
|
||||
}
|
||||
|
||||
register<Copy>("addJreToDistributable") {
|
||||
from(zipTree("$buildDir/OpenJDK8U-jre_x86-32_windows_hotspot_8u282b08.zip"))
|
||||
into("$buildDir/${rootProject.name}-$tachideskVersion-$tachideskRevision-win32")
|
||||
eachFile {
|
||||
path = path.replace(".*-jre".toRegex(), "jre")
|
||||
}
|
||||
dependsOn("downloadJre")
|
||||
dependsOn("createExe")
|
||||
}
|
||||
|
||||
named("createExe") {
|
||||
dependsOn("shadowJar")
|
||||
}
|
||||
|
||||
register<de.undercouch.gradle.tasks.download.Download>("downloadJre") {
|
||||
src("https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u282-b08/OpenJDK8U-jre_x86-32_windows_hotspot_8u282b08.zip")
|
||||
dest("$buildDir/OpenJDK8U-jre_x86-32_windows_hotspot_8u282b08.zip")
|
||||
overwrite(false)
|
||||
onlyIfModified(true)
|
||||
}
|
||||
|
||||
withType<ShadowJar> {
|
||||
destinationDirectory.set(File("$rootDir/server/build"))
|
||||
dependsOn("formatKotlin", "lintKotlin")
|
||||
}
|
||||
|
||||
named("run") {
|
||||
dependsOn("formatKotlin", "lintKotlin")
|
||||
}
|
||||
|
||||
named<Copy>("processResources") {
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
mustRunAfter(":webUI:copyBuild")
|
||||
}
|
||||
|
||||
withType<LintTask> {
|
||||
source(files("src"))
|
||||
}
|
||||
|
||||
withType<FormatTask> {
|
||||
source(files("src"))
|
||||
}
|
||||
dependsOn("downloadJre")
|
||||
dependsOn("createExe")
|
||||
}
|
||||
|
||||
tasks.register<de.undercouch.gradle.tasks.download.Download>("downloadJre") {
|
||||
src("https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u282-b08/OpenJDK8U-jre_x86-32_windows_hotspot_8u282b08.zip")
|
||||
dest(buildDir)
|
||||
overwrite(false)
|
||||
onlyIfModified(true)
|
||||
}
|
||||
|
||||
tasks.withType<ShadowJar> {
|
||||
destinationDirectory.set(File("$rootDir/server/build"))
|
||||
dependsOn("formatKotlin", "lintKotlin")
|
||||
}
|
||||
|
||||
tasks.named("run") {
|
||||
dependsOn("formatKotlin", "lintKotlin")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ package ir.armor.tachidesk.impl
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import ir.armor.tachidesk.impl.CategoryManga.removeMangaFromCategory
|
||||
import ir.armor.tachidesk.model.database.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.CategoryTable
|
||||
import ir.armor.tachidesk.model.database.toDataClass
|
||||
import ir.armor.tachidesk.model.database.table.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.table.CategoryTable
|
||||
import ir.armor.tachidesk.model.database.table.toDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.CategoryDataClass
|
||||
import org.jetbrains.exposed.sql.SortOrder
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
|
||||
@@ -7,10 +7,10 @@ package ir.armor.tachidesk.impl
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import ir.armor.tachidesk.model.database.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.CategoryTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.toDataClass
|
||||
import ir.armor.tachidesk.model.database.table.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.table.CategoryTable
|
||||
import ir.armor.tachidesk.model.database.table.MangaTable
|
||||
import ir.armor.tachidesk.model.database.table.toDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.CategoryDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.MangaDataClass
|
||||
import org.jetbrains.exposed.sql.SortOrder
|
||||
|
||||
@@ -12,9 +12,9 @@ import eu.kanade.tachiyomi.source.model.SManga
|
||||
import ir.armor.tachidesk.impl.Manga.getManga
|
||||
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||
import ir.armor.tachidesk.model.database.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.PageTable
|
||||
import ir.armor.tachidesk.model.database.table.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.table.MangaTable
|
||||
import ir.armor.tachidesk.model.database.table.PageTable
|
||||
import ir.armor.tachidesk.model.dataclass.ChapterDataClass
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
@@ -71,7 +71,7 @@ object Chapter {
|
||||
// TODO: delete orphan chapters
|
||||
}
|
||||
|
||||
chapterList.map { it ->
|
||||
chapterList.mapIndexed { index, it ->
|
||||
ChapterDataClass(
|
||||
it.url,
|
||||
it.name,
|
||||
@@ -79,6 +79,7 @@ object Chapter {
|
||||
it.chapter_number,
|
||||
it.scanlator,
|
||||
mangaId,
|
||||
chapterCount - index,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ import ir.armor.tachidesk.impl.util.PackageTools.getSignatureHash
|
||||
import ir.armor.tachidesk.impl.util.PackageTools.loadExtensionSources
|
||||
import ir.armor.tachidesk.impl.util.PackageTools.trustedSignatures
|
||||
import ir.armor.tachidesk.impl.util.await
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.model.database.table.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.table.SourceTable
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import mu.KotlinLogging
|
||||
import okhttp3.Request
|
||||
|
||||
@@ -10,7 +10,7 @@ package ir.armor.tachidesk.impl
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import ir.armor.tachidesk.impl.Extension.getExtensionIconUrl
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.table.ExtensionTable
|
||||
import ir.armor.tachidesk.model.dataclass.ExtensionDataClass
|
||||
import mu.KotlinLogging
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
|
||||
@@ -8,9 +8,9 @@ package ir.armor.tachidesk.impl
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import ir.armor.tachidesk.impl.Manga.getManga
|
||||
import ir.armor.tachidesk.model.database.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.toDataClass
|
||||
import ir.armor.tachidesk.model.database.table.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.table.MangaTable
|
||||
import ir.armor.tachidesk.model.database.table.toDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.MangaDataClass
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
|
||||
@@ -15,8 +15,8 @@ import ir.armor.tachidesk.impl.util.CachedImageResponse.getCachedImageResponse
|
||||
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||
import ir.armor.tachidesk.impl.util.await
|
||||
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||
import ir.armor.tachidesk.model.database.MangaStatus
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.table.MangaStatus
|
||||
import ir.armor.tachidesk.model.database.table.MangaTable
|
||||
import ir.armor.tachidesk.model.dataclass.MangaDataClass
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import org.jetbrains.exposed.sql.select
|
||||
@@ -28,6 +28,13 @@ import org.kodein.di.instance
|
||||
import java.io.InputStream
|
||||
|
||||
object Manga {
|
||||
private fun truncate(text: String?, maxLength: Int): String? {
|
||||
return if (text?.length ?: 0 > maxLength)
|
||||
text?.take(maxLength - 3) + "..."
|
||||
else
|
||||
text
|
||||
}
|
||||
|
||||
suspend fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
||||
var mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
|
||||
|
||||
@@ -66,7 +73,7 @@ object Manga {
|
||||
|
||||
it[MangaTable.artist] = fetchedManga.artist
|
||||
it[MangaTable.author] = fetchedManga.author
|
||||
it[MangaTable.description] = fetchedManga.description
|
||||
it[MangaTable.description] = truncate(fetchedManga.description, 4096)
|
||||
it[MangaTable.genre] = fetchedManga.genre
|
||||
it[MangaTable.status] = fetchedManga.status
|
||||
if (fetchedManga.thumbnail_url != null && fetchedManga.thumbnail_url!!.isNotEmpty())
|
||||
|
||||
@@ -10,8 +10,8 @@ package ir.armor.tachidesk.impl
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||
import ir.armor.tachidesk.model.database.MangaStatus
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.table.MangaStatus
|
||||
import ir.armor.tachidesk.model.database.table.MangaTable
|
||||
import ir.armor.tachidesk.model.dataclass.MangaDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass
|
||||
import org.jetbrains.exposed.sql.insertAndGetId
|
||||
|
||||
@@ -12,10 +12,10 @@ import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import ir.armor.tachidesk.impl.util.CachedImageResponse.getCachedImageResponse
|
||||
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||
import ir.armor.tachidesk.model.database.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.PageTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.model.database.table.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.table.MangaTable
|
||||
import ir.armor.tachidesk.model.database.table.PageTable
|
||||
import ir.armor.tachidesk.model.database.table.SourceTable
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.select
|
||||
|
||||
@@ -9,8 +9,8 @@ package ir.armor.tachidesk.impl
|
||||
|
||||
import ir.armor.tachidesk.impl.Extension.getExtensionIconUrl
|
||||
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.model.database.table.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.table.SourceTable
|
||||
import ir.armor.tachidesk.model.dataclass.SourceDataClass
|
||||
import mu.KotlinLogging
|
||||
import org.jetbrains.exposed.sql.select
|
||||
|
||||
@@ -12,15 +12,18 @@ import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import ir.armor.tachidesk.impl.Category.getCategoryList
|
||||
import ir.armor.tachidesk.impl.CategoryManga.getMangaCategories
|
||||
import ir.armor.tachidesk.impl.backup.BackupFlags
|
||||
import ir.armor.tachidesk.impl.backup.legacy.models.Backup
|
||||
import ir.armor.tachidesk.impl.backup.legacy.models.Backup.CURRENT_VERSION
|
||||
import ir.armor.tachidesk.impl.backup.models.CategoryImpl
|
||||
import ir.armor.tachidesk.impl.backup.models.ChapterImpl
|
||||
import ir.armor.tachidesk.impl.backup.models.Manga
|
||||
import ir.armor.tachidesk.impl.backup.models.MangaImpl
|
||||
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||
import ir.armor.tachidesk.model.database.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.table.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.table.MangaTable
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
@@ -83,11 +86,11 @@ object LegacyBackupExport : LegacyBackupBase() {
|
||||
|
||||
// Backup manga fields
|
||||
entry[Backup.MANGA] = parser.toJsonTree(manga)
|
||||
val mangaId = manga.id!!.toInt()
|
||||
|
||||
// Check if user wants chapter information in backup
|
||||
if (options.includeChapters && false) { // TODO
|
||||
if (options.includeChapters) {
|
||||
// Backup all the chapters
|
||||
val mangaId = manga.id!!.toInt()
|
||||
val chapters = ChapterTable.select { ChapterTable.manga eq mangaId }.map { ChapterImpl.fromQuery(it) }
|
||||
if (chapters.count() > 0) {
|
||||
val chaptersJson = parser.toJsonTree(chapters)
|
||||
@@ -97,14 +100,50 @@ object LegacyBackupExport : LegacyBackupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO the rest
|
||||
// Check if user wants category information in backup
|
||||
if (options.includeCategories) {
|
||||
// Backup categories for this manga
|
||||
val categoriesForManga = getMangaCategories(mangaId)
|
||||
if (categoriesForManga.isNotEmpty()) {
|
||||
val categoriesNames = categoriesForManga.map { it.name }
|
||||
entry[Backup.CATEGORIES] = parser.toJsonTree(categoriesNames)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user wants track information in backup
|
||||
if (options.includeTracking) { // TODO
|
||||
// val tracks = databaseHelper.getTracks(manga).executeAsBlocking()
|
||||
// if (tracks.isNotEmpty()) {
|
||||
// entry[TRACK] = parser.toJsonTree(tracks)
|
||||
// }
|
||||
}
|
||||
//
|
||||
// // Check if user wants history information in backup
|
||||
if (options.includeHistory) { // TODO
|
||||
// val historyForManga = databaseHelper.getHistoryByMangaId(manga.id!!).executeAsBlocking()
|
||||
// if (historyForManga.isNotEmpty()) {
|
||||
// val historyData = historyForManga.mapNotNull { history ->
|
||||
// val url = databaseHelper.getChapter(history.chapter_id).executeAsBlocking()?.url
|
||||
// url?.let { DHistory(url, history.last_read) }
|
||||
// }
|
||||
// val historyJson = parser.toJsonTree(historyData)
|
||||
// if (historyJson.asJsonArray.size() > 0) {
|
||||
// entry[HISTORY] = historyJson
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
private fun backupCategories(root: JsonArray) { // TODO
|
||||
// val categories = databaseHelper.getCategories().executeAsBlocking()
|
||||
// categories.forEach { root.add(parser.toJsonTree(it)) }
|
||||
private fun backupCategories(root: JsonArray) {
|
||||
val categories = getCategoryList().map {
|
||||
CategoryImpl().apply {
|
||||
name = it.name
|
||||
order = it.order
|
||||
}
|
||||
}
|
||||
categories.forEach { root.add(parser.toJsonTree(it)) }
|
||||
}
|
||||
|
||||
private fun backupExtensionInfo(root: JsonArray, extensions: Set<String>) {
|
||||
|
||||
+26
-14
@@ -7,10 +7,13 @@ import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import ir.armor.tachidesk.impl.backup.legacy.LegacyBackupRestoreValidator.ValidationResult
|
||||
import ir.armor.tachidesk.impl.backup.legacy.LegacyBackupRestoreValidator.validate
|
||||
import ir.armor.tachidesk.impl.Category.createCategory
|
||||
import ir.armor.tachidesk.impl.Category.getCategoryList
|
||||
import ir.armor.tachidesk.impl.backup.legacy.LegacyBackupValidator.ValidationResult
|
||||
import ir.armor.tachidesk.impl.backup.legacy.LegacyBackupValidator.validate
|
||||
import ir.armor.tachidesk.impl.backup.legacy.models.Backup
|
||||
import ir.armor.tachidesk.impl.backup.legacy.models.DHistory
|
||||
import ir.armor.tachidesk.impl.backup.models.CategoryImpl
|
||||
import ir.armor.tachidesk.impl.backup.models.Chapter
|
||||
import ir.armor.tachidesk.impl.backup.models.ChapterImpl
|
||||
import ir.armor.tachidesk.impl.backup.models.Manga
|
||||
@@ -19,7 +22,7 @@ import ir.armor.tachidesk.impl.backup.models.Track
|
||||
import ir.armor.tachidesk.impl.backup.models.TrackImpl
|
||||
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.table.MangaTable
|
||||
import mu.KotlinLogging
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
@@ -51,7 +54,7 @@ object LegacyBackupImport : LegacyBackupBase() {
|
||||
json.get(Backup.CATEGORIES)?.let { restoreCategories(it) }
|
||||
|
||||
// Store source mapping for error messages
|
||||
sourceMapping = LegacyBackupRestoreValidator.getSourceMapping(json)
|
||||
sourceMapping = LegacyBackupValidator.getSourceMapping(json)
|
||||
|
||||
// Restore individual manga
|
||||
mangasJson.forEach {
|
||||
@@ -77,13 +80,16 @@ object LegacyBackupImport : LegacyBackupBase() {
|
||||
return validationResult
|
||||
}
|
||||
|
||||
private fun restoreCategories(categoriesJson: JsonElement) { // TODO
|
||||
// db.inTransaction {
|
||||
// backupManager.restoreCategories(categoriesJson.asJsonArray)
|
||||
// }
|
||||
//
|
||||
// restoreProgress += 1
|
||||
// showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories))
|
||||
private fun restoreCategories(jsonCategories: JsonElement) { // TODO
|
||||
val backupCategories = parser.fromJson<List<CategoryImpl>>(jsonCategories)
|
||||
val dbCategories = getCategoryList()
|
||||
|
||||
// Iterate over them
|
||||
backupCategories.forEach { category ->
|
||||
if (dbCategories.none { it.name == category.name }) {
|
||||
createCategory(category.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun restoreManga(mangaJson: JsonObject) {
|
||||
@@ -145,10 +151,11 @@ object LegacyBackupImport : LegacyBackupBase() {
|
||||
history: List<DHistory>,
|
||||
tracks: List<Track>
|
||||
) {
|
||||
fetchManga(source, manga)
|
||||
val fetchedManga = fetchManga(source, manga)
|
||||
|
||||
// updateChapters(source, fetchedManga, chapters)
|
||||
updateChapters(source, fetchedManga, chapters)
|
||||
|
||||
// TODO
|
||||
// backupManager.restoreCategoriesForManga(manga, categories)
|
||||
|
||||
// backupManager.restoreHistoryForManga(history)
|
||||
@@ -166,6 +173,7 @@ object LegacyBackupImport : LegacyBackupBase() {
|
||||
* @return Updated manga.
|
||||
*/
|
||||
private suspend fun fetchManga(source: Source, manga: Manga): SManga {
|
||||
// make sure we have the manga record in library
|
||||
transaction {
|
||||
if (MangaTable.select { (MangaTable.url eq manga.url) and (MangaTable.sourceReference eq manga.source) }.firstOrNull() == null) {
|
||||
MangaTable.insert {
|
||||
@@ -180,8 +188,8 @@ object LegacyBackupImport : LegacyBackupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
// update manga details
|
||||
val fetchedManga = source.fetchMangaDetails(manga).awaitSingle()
|
||||
|
||||
transaction {
|
||||
MangaTable.update({ (MangaTable.url eq manga.url) and (MangaTable.sourceReference eq manga.source) }) {
|
||||
|
||||
@@ -197,4 +205,8 @@ object LegacyBackupImport : LegacyBackupBase() {
|
||||
|
||||
return fetchedManga
|
||||
}
|
||||
|
||||
private fun updateChapters(source: Source, fetchedManga: SManga, chapters: List<Chapter>) {
|
||||
// TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -9,11 +9,11 @@ package ir.armor.tachidesk.impl.backup.legacy
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import ir.armor.tachidesk.impl.backup.legacy.models.Backup
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.model.database.table.SourceTable
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
object LegacyBackupRestoreValidator {
|
||||
object LegacyBackupValidator {
|
||||
data class ValidationResult(val missingSources: List<String>, val missingTrackers: List<String>)
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ir.armor.tachidesk.impl.backup.models
|
||||
|
||||
import ir.armor.tachidesk.model.database.table.ChapterTable
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
|
||||
class ChapterImpl : Chapter {
|
||||
@@ -45,7 +46,10 @@ class ChapterImpl : Chapter {
|
||||
companion object {
|
||||
fun fromQuery(chapterRecord: ResultRow): ChapterImpl {
|
||||
return ChapterImpl().apply {
|
||||
// TODO
|
||||
url = chapterRecord[ChapterTable.url]
|
||||
read = chapterRecord[ChapterTable.isRead]
|
||||
bookmark = chapterRecord[ChapterTable.isBookmarked]
|
||||
last_page_read = chapterRecord[ChapterTable.lastPageRead]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package ir.armor.tachidesk.impl.backup.models
|
||||
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.table.MangaTable
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
|
||||
open class MangaImpl : Manga {
|
||||
|
||||
@@ -11,8 +11,8 @@ import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import ir.armor.tachidesk.impl.util.PackageTools.loadExtensionSources
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.model.database.table.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.table.SourceTable
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package ir.armor.tachidesk.model.database
|
||||
|
||||
/*
|
||||
* 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 ir.armor.tachidesk.model.database.migration.lib.loadMigrationsFrom
|
||||
import ir.armor.tachidesk.model.database.migration.lib.runMigrations
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
|
||||
object DBMangaer {
|
||||
val db by lazy {
|
||||
val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||
Database.connect("jdbc:h2:${applicationDirs.dataRoot}/database", "org.h2.Driver")
|
||||
}
|
||||
}
|
||||
|
||||
fun databaseUp() {
|
||||
// must mention db object so the lazy block executes
|
||||
val db = DBMangaer.db
|
||||
db.useNestedTransactions = true
|
||||
|
||||
val migrations = loadMigrationsFrom("ir.armor.tachidesk.model.database.migration")
|
||||
runMigrations(migrations)
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package ir.armor.tachidesk.model.database.migration
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import ir.armor.tachidesk.model.database.migration.lib.Migration
|
||||
import org.jetbrains.exposed.dao.id.IdTable
|
||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
/*
|
||||
* 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 M0001_Initial : Migration() {
|
||||
private object ExtensionTable : IntIdTable() {
|
||||
val apkName = varchar("apk_name", 1024)
|
||||
|
||||
// default is the local source icon from tachiyomi
|
||||
val iconUrl = varchar("icon_url", 2048)
|
||||
.default("https://raw.githubusercontent.com/tachiyomiorg/tachiyomi/64ba127e7d43b1d7e6d58a6f5c9b2bd5fe0543f7/app/src/main/res/mipmap-xxxhdpi/ic_local_source.webp")
|
||||
|
||||
val name = varchar("name", 128)
|
||||
val pkgName = varchar("pkg_name", 128)
|
||||
val versionName = varchar("version_name", 16)
|
||||
val versionCode = integer("version_code")
|
||||
val lang = varchar("lang", 10)
|
||||
val isNsfw = bool("is_nsfw")
|
||||
|
||||
val isInstalled = bool("is_installed").default(false)
|
||||
val hasUpdate = bool("has_update").default(false)
|
||||
val isObsolete = bool("is_obsolete").default(false)
|
||||
|
||||
val classFQName = varchar("class_name", 1024).default("") // fully qualified name
|
||||
}
|
||||
|
||||
private object SourceTable : IdTable<Long>() {
|
||||
override val id = long("id").entityId()
|
||||
val name = varchar("name", 128)
|
||||
val lang = varchar("lang", 10)
|
||||
val extension = reference("extension", ExtensionTable)
|
||||
val partOfFactorySource = bool("part_of_factory_source").default(false)
|
||||
}
|
||||
|
||||
private object MangaTable : IntIdTable() {
|
||||
val url = varchar("url", 2048)
|
||||
val title = varchar("title", 512)
|
||||
val initialized = bool("initialized").default(false)
|
||||
|
||||
val artist = varchar("artist", 64).nullable()
|
||||
val author = varchar("author", 64).nullable()
|
||||
val description = varchar("description", 4096).nullable()
|
||||
val genre = varchar("genre", 1024).nullable()
|
||||
|
||||
// val status = enumeration("status", MangaStatus::class).default(MangaStatus.UNKNOWN)
|
||||
val status = integer("status").default(SManga.UNKNOWN)
|
||||
val thumbnail_url = varchar("thumbnail_url", 2048).nullable()
|
||||
|
||||
val inLibrary = bool("in_library").default(false)
|
||||
val defaultCategory = bool("default_category").default(true)
|
||||
|
||||
// source is used by some ancestor of IntIdTable
|
||||
val sourceReference = long("source")
|
||||
}
|
||||
|
||||
private object ChapterTable : IntIdTable() {
|
||||
val url = varchar("url", 2048)
|
||||
val name = varchar("name", 512)
|
||||
val date_upload = long("date_upload").default(0)
|
||||
val chapter_number = float("chapter_number").default(-1f)
|
||||
val scanlator = varchar("scanlator", 128).nullable()
|
||||
|
||||
val isRead = bool("read").default(false)
|
||||
val isBookmarked = bool("bookmark").default(false)
|
||||
val lastPageRead = integer("last_page_read").default(0)
|
||||
|
||||
val chapterIndex = integer("number_in_list")
|
||||
|
||||
val manga = reference("manga", MangaTable)
|
||||
}
|
||||
|
||||
private object PageTable : IntIdTable() {
|
||||
val index = integer("index")
|
||||
val url = varchar("url", 2048)
|
||||
val imageUrl = varchar("imageUrl", 2048).nullable()
|
||||
|
||||
val chapter = reference("chapter", ChapterTable)
|
||||
}
|
||||
|
||||
private object CategoryTable : IntIdTable() {
|
||||
val name = varchar("name", 64)
|
||||
val isLanding = bool("is_landing").default(false)
|
||||
val order = integer("order").default(0)
|
||||
}
|
||||
|
||||
private object CategoryMangaTable : IntIdTable() {
|
||||
val category = reference("category", ir.armor.tachidesk.model.database.table.CategoryTable)
|
||||
val manga = reference("manga", ir.armor.tachidesk.model.database.table.MangaTable)
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
transaction {
|
||||
SchemaUtils.create(
|
||||
ExtensionTable,
|
||||
ExtensionTable,
|
||||
SourceTable,
|
||||
MangaTable,
|
||||
ChapterTable,
|
||||
PageTable,
|
||||
CategoryTable,
|
||||
CategoryMangaTable,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Andreas Mausch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,25 @@
|
||||
package ir.armor.tachidesk.model.database.migration.lib
|
||||
|
||||
/*
|
||||
* 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/. */
|
||||
|
||||
// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0.
|
||||
// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595
|
||||
|
||||
abstract class Migration {
|
||||
val name: String
|
||||
val version: Int
|
||||
|
||||
init {
|
||||
val groups = Regex("^M(\\d+)_(.*)$").matchEntire(this::class.simpleName!!)?.groupValues
|
||||
?: throw IllegalArgumentException("Migration class name doesn't match convention")
|
||||
version = groups[1].toInt()
|
||||
name = groups[2]
|
||||
}
|
||||
|
||||
abstract fun run()
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package ir.armor.tachidesk.model.database.migration.lib
|
||||
|
||||
/*
|
||||
* 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/. */
|
||||
|
||||
// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0.
|
||||
// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595
|
||||
|
||||
import org.jetbrains.exposed.dao.IntEntity
|
||||
import org.jetbrains.exposed.dao.IntEntityClass
|
||||
import org.jetbrains.exposed.dao.id.EntityID
|
||||
import org.jetbrains.exposed.dao.id.IdTable
|
||||
import org.jetbrains.exposed.sql.`java-time`.timestamp
|
||||
|
||||
object MigrationsTable : IdTable<Int>() {
|
||||
override val id = integer("version").entityId()
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
|
||||
val name = varchar("name", length = 400)
|
||||
val executedAt = timestamp("executed_at")
|
||||
|
||||
init {
|
||||
index(true, name)
|
||||
}
|
||||
}
|
||||
|
||||
class MigrationEntity(id: EntityID<Int>) : IntEntity(id) {
|
||||
companion object : IntEntityClass<MigrationEntity>(MigrationsTable)
|
||||
|
||||
var version by MigrationsTable.id
|
||||
var name by MigrationsTable.name
|
||||
var executedAt by MigrationsTable.executedAt
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package ir.armor.tachidesk.model.database.migration.lib
|
||||
|
||||
/*
|
||||
* 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/. */
|
||||
|
||||
// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0.
|
||||
// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595
|
||||
|
||||
import com.google.common.reflect.ClassPath
|
||||
import mu.KotlinLogging
|
||||
import org.jetbrains.exposed.dao.id.EntityID
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.SchemaUtils.create
|
||||
import org.jetbrains.exposed.sql.exists
|
||||
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import java.time.Clock
|
||||
import java.time.Instant.now
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
fun runMigrations(migrations: List<Migration>, database: Database = TransactionManager.defaultDatabase!!, clock: Clock = Clock.systemUTC()) {
|
||||
checkVersions(migrations)
|
||||
|
||||
logger.info { "Running migrations on database ${database.url}" }
|
||||
|
||||
val latestVersion = transaction(database) {
|
||||
createTableIfNotExists(database)
|
||||
MigrationEntity.all().maxByOrNull { it.version }?.version?.value
|
||||
}
|
||||
|
||||
logger.info { "Database version before migrations: $latestVersion" }
|
||||
|
||||
migrations
|
||||
.sortedBy { it.version }
|
||||
.filter { shouldRun(latestVersion, it) }
|
||||
.forEach {
|
||||
logger.info { "Running migration version ${it.version}: ${it.name}" }
|
||||
transaction(database) {
|
||||
it.run()
|
||||
|
||||
MigrationEntity.new {
|
||||
version = EntityID(it.version, MigrationsTable)
|
||||
name = it.name
|
||||
executedAt = now(clock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info { "Migrations finished successfully" }
|
||||
}
|
||||
|
||||
fun loadMigrationsFrom(classPath: String): List<Migration> {
|
||||
return ClassPath.from(Thread.currentThread().contextClassLoader)
|
||||
.getTopLevelClasses(classPath)
|
||||
.map {
|
||||
logger.debug("found Migration class ${it.name}")
|
||||
val clazz = it.load().getDeclaredConstructor().newInstance()
|
||||
if (clazz is Migration)
|
||||
clazz
|
||||
else
|
||||
throw RuntimeException("found a class that's not a Migration")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkVersions(migrations: List<Migration>) {
|
||||
val sorted = migrations.map { it.version }.sorted()
|
||||
if ((1..migrations.size).toList() != sorted) {
|
||||
throw IllegalStateException("List of migrations version is not consecutive: $sorted")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTableIfNotExists(database: Database) {
|
||||
if (MigrationsTable.exists()) {
|
||||
return
|
||||
}
|
||||
val tableNames = database.dialect.allTablesNames()
|
||||
when (tableNames.isEmpty()) {
|
||||
true -> {
|
||||
logger.info { "Empty database found, creating table for migrations" }
|
||||
create(MigrationsTable)
|
||||
}
|
||||
false -> throw IllegalStateException("Tried to run migrations against a non-empty database without a Migrations table. This is not supported.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldRun(latestVersion: Int?, migration: Migration): Boolean {
|
||||
val run = latestVersion?.let { migration.version > it } ?: true
|
||||
if (!run) {
|
||||
logger.debug { "Skipping migration version ${migration.version}: ${migration.name}" }
|
||||
}
|
||||
return run
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.model.database
|
||||
package ir.armor.tachidesk.model.database.table
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.model.database
|
||||
package ir.armor.tachidesk.model.database.table
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
+5
-1
@@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.model.database
|
||||
package ir.armor.tachidesk.model.database.table
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@@ -16,6 +16,10 @@ object ChapterTable : IntIdTable() {
|
||||
val chapter_number = float("chapter_number").default(-1f)
|
||||
val scanlator = varchar("scanlator", 128).nullable()
|
||||
|
||||
val isRead = bool("read").default(false)
|
||||
val isBookmarked = bool("bookmark").default(false)
|
||||
val lastPageRead = integer("last_page_read").default(0)
|
||||
|
||||
val chapterIndex = integer("number_in_list")
|
||||
|
||||
val manga = reference("manga", MangaTable)
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.model.database
|
||||
package ir.armor.tachidesk.model.database.table
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.model.database
|
||||
package ir.armor.tachidesk.model.database.table
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@@ -37,7 +37,7 @@ object MangaTable : IntIdTable() {
|
||||
fun MangaTable.toDataClass(mangaEntry: ResultRow) =
|
||||
MangaDataClass(
|
||||
mangaEntry[MangaTable.id].value,
|
||||
mangaEntry[sourceReference].toString(),
|
||||
mangaEntry[MangaTable.sourceReference].toString(),
|
||||
|
||||
mangaEntry[MangaTable.url],
|
||||
mangaEntry[MangaTable.title],
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.model.database
|
||||
package ir.armor.tachidesk.model.database.table
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.model.database
|
||||
package ir.armor.tachidesk.model.database.table
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@@ -1,48 +0,0 @@
|
||||
package ir.armor.tachidesk.model.dataclass
|
||||
|
||||
/*
|
||||
* 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 ir.armor.tachidesk.model.database.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.CategoryTable
|
||||
import ir.armor.tachidesk.model.database.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.PageTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
|
||||
object DBMangaer {
|
||||
val db by lazy {
|
||||
val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||
Database.connect("jdbc:h2:${applicationDirs.dataRoot}/database", "org.h2.Driver")
|
||||
}
|
||||
}
|
||||
|
||||
fun makeDataBaseTables() {
|
||||
// must mention db object so the lazy block executes
|
||||
val db = DBMangaer.db
|
||||
db.useNestedTransactions = true
|
||||
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(
|
||||
ExtensionTable,
|
||||
SourceTable,
|
||||
MangaTable,
|
||||
ChapterTable,
|
||||
PageTable,
|
||||
CategoryTable,
|
||||
CategoryMangaTable,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ package ir.armor.tachidesk.model.dataclass
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import ir.armor.tachidesk.model.database.MangaStatus
|
||||
import ir.armor.tachidesk.model.database.table.MangaStatus
|
||||
|
||||
data class MangaDataClass(
|
||||
val id: Int,
|
||||
|
||||
@@ -33,6 +33,7 @@ import ir.armor.tachidesk.impl.Source.getSourceList
|
||||
import ir.armor.tachidesk.impl.backup.BackupFlags
|
||||
import ir.armor.tachidesk.impl.backup.legacy.LegacyBackupExport.createLegacyBackup
|
||||
import ir.armor.tachidesk.impl.backup.legacy.LegacyBackupImport.restoreLegacyBackup
|
||||
import ir.armor.tachidesk.server.internal.About.getAbout
|
||||
import ir.armor.tachidesk.server.util.openInBrowser
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -43,6 +44,7 @@ import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@@ -64,7 +66,10 @@ object JavalinSetup {
|
||||
|
||||
val app = Javalin.create { config ->
|
||||
try {
|
||||
// if the bellow line throws an exception then webUI is not bundled
|
||||
Main::class.java.getResource("/react/index.html")
|
||||
|
||||
// no exception so we can tell javalin to serve webUI
|
||||
hasWebUiBundled = true
|
||||
config.addStaticFiles("/react")
|
||||
config.addSinglePageRoot("/", "/react/index.html")
|
||||
@@ -74,6 +79,14 @@ object JavalinSetup {
|
||||
}
|
||||
config.enableCorsForAllOrigins()
|
||||
}.start(serverConfig.ip, serverConfig.port)
|
||||
|
||||
// when JVM is prompted to shutdown, stop javalin gracefully
|
||||
Runtime.getRuntime().addShutdownHook(
|
||||
thread(start = false) {
|
||||
app.stop()
|
||||
}
|
||||
)
|
||||
|
||||
if (hasWebUiBundled && serverConfig.initialOpenInBrowserEnabled) {
|
||||
openInBrowser()
|
||||
}
|
||||
@@ -297,6 +310,11 @@ object JavalinSetup {
|
||||
ctx.status(200)
|
||||
}
|
||||
|
||||
// returns some static info of the current app build
|
||||
app.get("/api/v1/about/") { ctx ->
|
||||
ctx.json(getAbout())
|
||||
}
|
||||
|
||||
// category modification
|
||||
app.patch("/api/v1/category/:categoryId") { ctx ->
|
||||
val categoryId = ctx.pathParam("categoryId").toInt()
|
||||
|
||||
@@ -10,7 +10,7 @@ package ir.armor.tachidesk.server
|
||||
import ch.qos.logback.classic.Level
|
||||
import eu.kanade.tachiyomi.App
|
||||
import ir.armor.tachidesk.Main
|
||||
import ir.armor.tachidesk.model.dataclass.makeDataBaseTables
|
||||
import ir.armor.tachidesk.model.database.databaseUp
|
||||
import ir.armor.tachidesk.server.util.systemTray
|
||||
import mu.KotlinLogging
|
||||
import org.kodein.di.DI
|
||||
@@ -91,7 +91,7 @@ fun applicationSetup() {
|
||||
logger.error("Exception while creating initial server.conf:\n", e)
|
||||
}
|
||||
|
||||
makeDataBaseTables()
|
||||
databaseUp()
|
||||
|
||||
// create system tray
|
||||
if (serverConfig.systemTrayEnabled) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package ir.armor.tachidesk.server.internal
|
||||
|
||||
/*
|
||||
* 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 ir.armor.tachidesk.server.BuildConfig
|
||||
|
||||
data class AboutDataClass(
|
||||
val version: String,
|
||||
val revision: String,
|
||||
)
|
||||
|
||||
object About {
|
||||
fun getAbout(): AboutDataClass {
|
||||
return AboutDataClass(
|
||||
BuildConfig.version,
|
||||
BuildConfig.revision,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,9 @@ import dorkbox.systemTray.SystemTray.TrayType
|
||||
import dorkbox.util.CacheUtil
|
||||
import dorkbox.util.Desktop
|
||||
import ir.armor.tachidesk.Main
|
||||
import ir.armor.tachidesk.server.BuildConfig
|
||||
import ir.armor.tachidesk.server.serverConfig
|
||||
import java.awt.event.ActionListener
|
||||
import java.io.IOException
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
fun openInBrowser() {
|
||||
try {
|
||||
@@ -32,22 +32,17 @@ fun systemTray(): SystemTray? {
|
||||
if (System.getProperty("os.name").startsWith("Windows"))
|
||||
SystemTray.FORCE_TRAY_TYPE = TrayType.Swing
|
||||
|
||||
CacheUtil.clear()
|
||||
CacheUtil.clear(BuildConfig.name)
|
||||
|
||||
val systemTray = SystemTray.get() ?: return null
|
||||
val systemTray = SystemTray.get(BuildConfig.name) ?: return null
|
||||
val mainMenu = systemTray.menu
|
||||
|
||||
mainMenu.add(
|
||||
MenuItem(
|
||||
"Open Tachidesk",
|
||||
ActionListener {
|
||||
try {
|
||||
Desktop.browseURL("http://127.0.0.1:4567")
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
)
|
||||
"Open Tachidesk"
|
||||
) {
|
||||
openInBrowser()
|
||||
}
|
||||
)
|
||||
|
||||
val icon = Main::class.java.getResource("/icon/faviconlogo.png")
|
||||
@@ -56,13 +51,15 @@ fun systemTray(): SystemTray? {
|
||||
systemTray.setImage(icon)
|
||||
// systemTray.status = "No Mail"
|
||||
|
||||
systemTray.getMenu().add(
|
||||
mainMenu.add(
|
||||
MenuItem("Quit") {
|
||||
systemTray.shutdown()
|
||||
System.exit(0)
|
||||
exitProcess(0)
|
||||
}
|
||||
)
|
||||
|
||||
systemTray.installShutdownHook()
|
||||
|
||||
return systemTray
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
plugins {
|
||||
id("com.moowork.node") version "1.3.1"
|
||||
id("com.github.node-gradle.node") version "3.0.1"
|
||||
}
|
||||
|
||||
node {
|
||||
workDir = file("${project.projectDir}/react/")
|
||||
nodeModulesDir = file("${project.projectDir}/react/")
|
||||
nodeProjectDir.set(file("${project.projectDir}/react/"))
|
||||
}
|
||||
|
||||
tasks.named("yarn_build") {
|
||||
dependsOn("yarn") // install node_moduels
|
||||
dependsOn("yarn") // install node_modules
|
||||
}
|
||||
|
||||
tasks.register<Copy>("copyBuild") {
|
||||
|
||||
@@ -135,6 +135,9 @@ export default function MangaDetails(props: IProps) {
|
||||
const { setAction } = useContext(NavbarContext);
|
||||
|
||||
const { manga } = props;
|
||||
if (manga.genre == null) {
|
||||
manga.genre = '';
|
||||
}
|
||||
const [inLibrary, setInLibrary] = useState<string>(
|
||||
manga.inLibrary ? 'In Library' : 'Add To Library',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user