mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 11:24:35 -05:00
Compare commits
2 Commits
v0.2.7
...
v0.2.6-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6e57e2700 | ||
|
|
c5f467ce3d |
31
.gitattributes
vendored
31
.gitattributes
vendored
@@ -1,27 +1,6 @@
|
|||||||
* text=auto
|
#
|
||||||
* text eol=lf
|
# https://help.github.com/articles/dealing-with-line-endings/
|
||||||
|
#
|
||||||
|
# These are explicitly windows files and should use crlf
|
||||||
|
*.bat text eol=crlf
|
||||||
|
|
||||||
# Windows forced line-endings
|
|
||||||
/.idea/* text eol=crlf
|
|
||||||
*.bat text eol=crlf
|
|
||||||
*.ps1 text eol=crlf
|
|
||||||
|
|
||||||
# Gradle wrapper
|
|
||||||
*.jar binary
|
|
||||||
|
|
||||||
# Images
|
|
||||||
*.webp binary
|
|
||||||
*.png binary
|
|
||||||
*.jpg binary
|
|
||||||
*.jpeg binary
|
|
||||||
*.gif binary
|
|
||||||
*.ico binary
|
|
||||||
*.gz binary
|
|
||||||
*.zip binary
|
|
||||||
*.7z binary
|
|
||||||
*.ttf binary
|
|
||||||
*.eot binary
|
|
||||||
*.woff binary
|
|
||||||
*.pyc binary
|
|
||||||
*.swp binary
|
|
||||||
*.pdf binary
|
|
||||||
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -25,7 +25,6 @@ Note that the issue will be automatically closed if you do not fill out the titl
|
|||||||
## Device information
|
## Device information
|
||||||
- Tachidesk version: (Example: v0.2.3-r255-win32)
|
- Tachidesk version: (Example: v0.2.3-r255-win32)
|
||||||
- Server Operating System: (Example: Ubuntu 20.04)
|
- Server Operating System: (Example: Ubuntu 20.04)
|
||||||
- Server Desktop Environment: N/A or (Example: Gnome 40)
|
|
||||||
- Server JVM version: bundled with win32 or (Example: Java 8 Update 281 or OpenJDK 8u281)
|
- Server JVM version: bundled with win32 or (Example: Java 8 Update 281 or OpenJDK 8u281)
|
||||||
- Client Operating System: <usually the same as above Server Operating System>
|
- Client Operating System: <usually the same as above Server Operating System>
|
||||||
- Client Web Browser: (Example: Google Chrome 89.0.4389.82)
|
- Client Web Browser: (Example: Google Chrome 89.0.4389.82)
|
||||||
|
|||||||
2
.github/workflows/issue_closer.yml
vendored
2
.github/workflows/issue_closer.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "body",
|
"type": "body",
|
||||||
"regex": "(Tachidesk version|Server Operating System|Server Desktop Environment|Server JVM version|Client Operating System|Client Web Browser):.*(\\(Example:|<usually).*",
|
"regex": "(Tachidesk version|Server Operating System|Server JVM version|Client Operating System|Client Web Browser):.*(\\(Example:|<usually).*",
|
||||||
"message": "The requested information was not filled out"
|
"message": "The requested information was not filled out"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
15
.github/workflows/publish.yml
vendored
15
.github/workflows/publish.yml
vendored
@@ -58,6 +58,21 @@ jobs:
|
|||||||
**/react/node_modules
|
**/react/node_modules
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
|
||||||
|
- name: Build no-webUI Jar
|
||||||
|
uses: eskatos/gradle-command-action@v1
|
||||||
|
with:
|
||||||
|
build-root-directory: master
|
||||||
|
wrapper-directory: master
|
||||||
|
arguments: :server:shadowJar -x :webUI:copyBuild --stacktrace
|
||||||
|
wrapper-cache-enabled: true
|
||||||
|
dependencies-cache-enabled: true
|
||||||
|
configuration-cache-enabled: true
|
||||||
|
|
||||||
|
- name: Rename the no-webUI Jar
|
||||||
|
run: |
|
||||||
|
cd master/server/build
|
||||||
|
mv Tachidesk-*.jar $(ls *.jar | sed 's/\.jar/-no-webUI\.jar/g')
|
||||||
|
|
||||||
- name: Build Jar and launch4j
|
- name: Build Jar and launch4j
|
||||||
uses: eskatos/gradle-command-action@v1
|
uses: eskatos/gradle-command-action@v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
package xyz.nulldev.ts.config
|
package xyz.nulldev.ts.config
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigRenderOptions
|
import com.typesafe.config.ConfigRenderOptions
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Android stub library
|
// Android stub library
|
||||||
|
// compileOnly( fileTree(File(rootProject.rootDir, "libs/android"), include: "*.jar")
|
||||||
implementation(fileTree("lib/"))
|
implementation(fileTree("lib/"))
|
||||||
|
implementation(fileTree("${rootProject.rootDir}/server/lib/dex2jar/"))
|
||||||
|
|
||||||
|
|
||||||
// Android JAR libs
|
// Android JAR libs
|
||||||
@@ -39,10 +41,10 @@ dependencies {
|
|||||||
compileOnly( group= "xmlpull", name= "xmlpull", version= "1.1.3.1")
|
compileOnly( group= "xmlpull", name= "xmlpull", version= "1.1.3.1")
|
||||||
|
|
||||||
// Config API
|
// Config API
|
||||||
implementation(project(":AndroidCompat:Config"))
|
implementation( project(":AndroidCompat:Config"))
|
||||||
|
|
||||||
// dex2jar: https://github.com/DexPatcher/dex2jar/releases/tag/v2.1-20190905-lanchon
|
// dex2jar
|
||||||
compileOnly("com.github.DexPatcher.dex2jar:dex-tools:v2.1-20190905-lanchon")
|
// compileOnly( "dex2jar:dex-translator")
|
||||||
|
|
||||||
// APK parser
|
// APK parser
|
||||||
compileOnly("net.dongliu:apk-parser:2.6.10")
|
compileOnly("net.dongliu:apk-parser:2.6.10")
|
||||||
@@ -53,11 +55,7 @@ dependencies {
|
|||||||
// AndroidX annotations
|
// AndroidX annotations
|
||||||
compileOnly( "androidx.annotation:annotation:1.2.0-alpha01")
|
compileOnly( "androidx.annotation:annotation:1.2.0-alpha01")
|
||||||
|
|
||||||
// substitute for duktape-android
|
// compileOnly("io.reactivex:rxjava:1.3.8")
|
||||||
// 'org.mozilla:rhino' includes some code that we don't need so use 'org.mozilla:rhino-runtime' instead
|
|
||||||
implementation("org.mozilla:rhino-runtime:1.7.13")
|
|
||||||
// 'org.mozilla:rhino-engine' provides the same interface as 'javax.script' a.k.a Nashorn
|
|
||||||
implementation("org.mozilla:rhino-engine:1.7.13")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//def fatJarTask = tasks.getByPath(':AndroidCompat:JVMPatch:fatJar')
|
//def fatJarTask = tasks.getByPath(':AndroidCompat:JVMPatch:fatJar')
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
# 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/.
|
|
||||||
|
|
||||||
# This is a windows only PowerShell script to create android.jar stubs
|
|
||||||
|
|
||||||
# foolproof against running from AndroidCompat dir instead of running from project root
|
|
||||||
if ($(Split-Path -Path (Get-Location) -Leaf) -eq "AndroidCompat" ) {
|
|
||||||
Set-Location ..
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
$android_jar = (Get-Location).Path + "\tmp\android.jar"
|
|
||||||
|
|
||||||
[IO.File]::WriteAllBytes($android_jar, [Convert]::FromBase64String($androidEncoded))
|
|
||||||
|
|
||||||
# We need to remove any stub classes that we have implementations for
|
|
||||||
Write-Output "Patching JAR..."
|
|
||||||
|
|
||||||
function Remove-Files-Zip($zipfile, $path)
|
|
||||||
{
|
|
||||||
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression') | Out-Null
|
|
||||||
|
|
||||||
$stream = New-Object IO.FileStream($zipfile, [IO.FileMode]::Open)
|
|
||||||
$mode = [IO.Compression.ZipArchiveMode]::Update
|
|
||||||
$zip = New-Object IO.Compression.ZipArchive($stream, $mode)
|
|
||||||
|
|
||||||
($zip.Entries | Where-Object { $_.FullName -like $path }) | ForEach-Object { Write-Output "Deleting: $($_.FullName)"; $_.Delete() }
|
|
||||||
|
|
||||||
$zip.Dispose()
|
|
||||||
$stream.Close()
|
|
||||||
$stream.Dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output "Removing org.json..."
|
|
||||||
Remove-Files-Zip $android_jar 'org/json/*'
|
|
||||||
|
|
||||||
Write-Output "Removing org.apache..."
|
|
||||||
Remove-Files-Zip $android_jar 'org/apache/*'
|
|
||||||
|
|
||||||
Write-Output "Removing org.w3c..."
|
|
||||||
Remove-Files-Zip $android_jar 'org/w3c/*'
|
|
||||||
|
|
||||||
Write-Output "Removing org.xml..."
|
|
||||||
Remove-Files-Zip $android_jar 'org/xml/*'
|
|
||||||
|
|
||||||
Write-Output "Removing org.xmlpull..."
|
|
||||||
Remove-Files-Zip $android_jar 'org/xmlpull/*'
|
|
||||||
|
|
||||||
Write-Output "Removing junit..."
|
|
||||||
Remove-Files-Zip $android_jar 'junit/*'
|
|
||||||
|
|
||||||
Write-Output "Removing javax..."
|
|
||||||
Remove-Files-Zip $android_jar 'javax/*'
|
|
||||||
|
|
||||||
Write-Output "Removing java..."
|
|
||||||
Remove-Files-Zip $android_jar 'java/*'
|
|
||||||
|
|
||||||
Write-Output "Removing overriden classes..."
|
|
||||||
Remove-Files-Zip $android_jar 'android/app/Application.class'
|
|
||||||
Remove-Files-Zip $android_jar 'android/app/Service.class'
|
|
||||||
Remove-Files-Zip $android_jar 'android/net/Uri.class'
|
|
||||||
Remove-Files-Zip $android_jar 'android/net/Uri$Builder.class'
|
|
||||||
Remove-Files-Zip $android_jar 'android/os/Environment.class'
|
|
||||||
Remove-Files-Zip $android_jar 'android/text/format/Formatter.class'
|
|
||||||
Remove-Files-Zip $android_jar 'android/text/Html.class'
|
|
||||||
|
|
||||||
function Dedupe($path)
|
|
||||||
{
|
|
||||||
Push-Location $path
|
|
||||||
$classes = Get-ChildItem . *.* -Recurse | Where-Object { !$_.PSIsContainer }
|
|
||||||
$classes | ForEach-Object {
|
|
||||||
"Processing class: $($_.FullName)"
|
|
||||||
Remove-Files-Zip $android_jar "$($_.Name).class" | Out-Null
|
|
||||||
Remove-Files-Zip $android_jar "$($_.Name)$*.class" | Out-Null
|
|
||||||
Remove-Files-Zip $android_jar "$($_.Name)Kt.class" | Out-Null
|
|
||||||
Remove-Files-Zip $android_jar "$($_.Name)Kt$*.class" | Out-Null
|
|
||||||
}
|
|
||||||
Pop-Location
|
|
||||||
}
|
|
||||||
|
|
||||||
Dedupe "AndroidCompat/src/main/java"
|
|
||||||
Dedupe "server/src/main/java"
|
|
||||||
Dedupe "server/src/main/kotlin"
|
|
||||||
|
|
||||||
Write-Output "Copying Android.jar to library folder..."
|
|
||||||
Move-Item -Force $android_jar "AndroidCompat/lib/android.jar"
|
|
||||||
|
|
||||||
Write-Output "Cleaning up..."
|
|
||||||
Remove-Item -Recurse -Force "tmp"
|
|
||||||
|
|
||||||
Write-Output "Done!"
|
|
||||||
@@ -1,13 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# 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/.
|
|
||||||
|
|
||||||
# This is a bash script to create android.jar stubs
|
|
||||||
|
|
||||||
# foolproof against running from AndroidCompat dir instead of running from project root
|
# foolproof against running from AndroidCompat dir instead of running from project root
|
||||||
if [ "$(basename $(pwd))" = "AndroidCompat" ]; then
|
if [ "$(basename $(pwd))" = "AndroidCompat" ]; then
|
||||||
cd ..
|
cd ..
|
||||||
@@ -21,7 +13,7 @@ pushd "tmp"
|
|||||||
|
|
||||||
curl "https://android.googlesource.com/platform/prebuilts/sdk/+/3b8a524d25fa6c3d795afb1eece3f24870c60988/27/public/android.jar?format=TEXT" | base64 --decode > android.jar
|
curl "https://android.googlesource.com/platform/prebuilts/sdk/+/3b8a524d25fa6c3d795afb1eece3f24870c60988/27/public/android.jar?format=TEXT" | base64 --decode > android.jar
|
||||||
|
|
||||||
# We need to remove any stub classes that we have implementations for
|
# We need to remove any stub classes that we might use
|
||||||
echo "Patching JAR..."
|
echo "Patching JAR..."
|
||||||
|
|
||||||
echo "Removing org.json..."
|
echo "Removing org.json..."
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
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
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* you may not use this file except in compliance with the License.
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* 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;
|
import kotlin.NotImplementedError;
|
||||||
|
|
||||||
@@ -14,18 +22,11 @@ import javax.script.ScriptEngineManager;
|
|||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
|
||||||
/* Note (March 2021):
|
/** A simple EMCAScript (Javascript) interpreter. */
|
||||||
* The old implementation for duktape-android used the nashorn engine which is deprecated.
|
|
||||||
* This new implementation uses Mozilla's Rhino: https://github.com/mozilla/rhino
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple EMCAScript (Javascript) interpreter.
|
|
||||||
*/
|
|
||||||
public final class Duktape implements Closeable, AutoCloseable {
|
public final class Duktape implements Closeable, AutoCloseable {
|
||||||
|
|
||||||
private ScriptEngineManager factory = new ScriptEngineManager();
|
private ScriptEngineManager factory = new ScriptEngineManager();
|
||||||
private ScriptEngine engine = factory.getEngineByName("rhino");
|
private ScriptEngine engine = factory.getEngineByName("JavaScript");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new interpreter instance. Calls to this method <strong>must</strong> matched with
|
* Create a new interpreter instance. Calls to this method <strong>must</strong> matched with
|
||||||
@@ -37,6 +38,17 @@ public final class Duktape implements Closeable, AutoCloseable {
|
|||||||
|
|
||||||
private Duktape() {}
|
private Duktape() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluate {@code script} and return a result. {@code fileName} will be used in error
|
||||||
|
* reporting. Note that the result must be one of the supported Java types or the call will
|
||||||
|
* return null.
|
||||||
|
*
|
||||||
|
* @throws DuktapeException if there is an error evaluating the script.
|
||||||
|
*/
|
||||||
|
public synchronized Object evaluate(String script, String fileName) {
|
||||||
|
throw new NotImplementedError("Not implemented!");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate {@code script} and return a result. Note that the result must be one of the
|
* Evaluate {@code script} and return a result. Note that the result must be one of the
|
||||||
* supported Java types or the call will return null.
|
* supported Java types or the call will return null.
|
||||||
@@ -64,18 +76,18 @@ public final class Duktape implements Closeable, AutoCloseable {
|
|||||||
throw new NotImplementedError("Not implemented!");
|
throw new NotImplementedError("Not implemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Attaches to a global JavaScript object called {@code name} that implements {@code type}.
|
* 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} 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
|
* {@code type} must be an interface that does not extend any other interfaces, and cannot define
|
||||||
// * any overloaded methods.
|
* any overloaded methods.
|
||||||
// * <p>Methods of the interface may return {@code void} or any of the following supported argument
|
* <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},
|
* types: {@code boolean}, {@link Boolean}, {@code int}, {@link Integer}, {@code double},
|
||||||
// * {@link Double}, {@link String}.
|
* {@link Double}, {@link String}.
|
||||||
// */
|
*/
|
||||||
// public synchronized <T> T get(final String name, final Class<T> type) {
|
public synchronized <T> T get(final String name, final Class<T> type) {
|
||||||
// throw new NotImplementedError("Not implemented!");
|
throw new NotImplementedError("Not implemented!");
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release the native resources associated with this object. You <strong>must</strong> call this
|
* Release the native resources associated with this object. You <strong>must</strong> call this
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/** This is the reference Duktape stub that tachiyomi's extensions depend on.
|
|
||||||
* Intended to be used as a reference.
|
|
||||||
*/
|
|
||||||
public class DuktapeStub implements Closeable {
|
|
||||||
|
|
||||||
public static Duktape create() {
|
|
||||||
throw new RuntimeException("Stub!");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void close() throws IOException {
|
|
||||||
throw new RuntimeException("Stub!");
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized Object evaluate(String script) {
|
|
||||||
throw new RuntimeException("Stub!");
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized <T> void set(String name, Class<T> type, T object) {
|
|
||||||
throw new RuntimeException("Stub!");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
33
README.md
33
README.md
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||

|

|
||||||
# Tachidesk
|
# Tachidesk
|
||||||
A free and open source manga reader that runs extensions built for [Tachiyomi](https://tachiyomi.org/).
|
A free and open source manga reader that runs extensions built for [Tachiyomi](https://tachiyomi.org/).
|
||||||
|
|
||||||
@@ -13,23 +13,23 @@ Here is a list of current features:
|
|||||||
- Installing and executing Tachiyomi's Extensions, So you'll get the same sources.
|
- Installing and executing Tachiyomi's Extensions, So you'll get the same sources.
|
||||||
- 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 minimal chapter reader.
|
||||||
- Ability to download Mangas for offline read(This partially works)
|
- Ability to download Mangas for offline read(This partially works)
|
||||||
|
|
||||||
**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.
|
**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.
|
||||||
|
|
||||||
Anyways, for more info checkout [finished milestone #1](https://github.com/Suwayomi/Tachidesk/issues/2) and [milestone #2](https://github.com/Suwayomi/Tachidesk/projects/1) to see what's implemented in more detail.
|
Anyways, for more info checkout [finished milestone #1](https://github.com/AriaMoradi/Tachidesk/issues/2) and [milestone #2](https://github.com/AriaMoradi/Tachidesk/projects/1) to see what's implemented in more detail.
|
||||||
|
|
||||||
## Downloading and Running the app
|
## Downloading and Running the app
|
||||||
### All Operating Systems
|
### 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.
|
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 jar release from [the releases section](https://github.com/AriaMoradi/Tachidesk/releases).
|
||||||
|
|
||||||
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` 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
|
### Windows
|
||||||
Download the latest win32 release from [the releases section](https://github.com/Suwayomi/Tachidesk/releases).
|
Download the latest win32 release from [the releases section](https://github.com/AriaMoradi/Tachidesk/releases).
|
||||||
|
|
||||||
The Windows specific build has java bundled inside, so you don't have to install java to use it. Unzip `Tachidesk-vX.Y.Z-rxxx-win32.zip` and run `server.exe`. The rest works like the previous section.
|
The Windows specific build has java bundled inside, so you don't have to install java to use it. Unzip `Tachidesk-vX.Y.Z-rxxx-win32.zip` and run `server.exe`. The rest works like the previous section.
|
||||||
|
|
||||||
@@ -62,25 +62,18 @@ This project has two components:
|
|||||||
2. **webUI:** A react SPA project that works with the server to do the presentation.
|
2. **webUI:** A react SPA project that works with the server to do the presentation.
|
||||||
|
|
||||||
## Building from source
|
## Building from source
|
||||||
### Prerequisite: Get Android stubs jar
|
### Get Android stubs jar
|
||||||
#### Manual download
|
#### Manual download
|
||||||
Download [android.jar](https://raw.githubusercontent.com/Suwayomi/Tachidesk/android-jar/android.jar) and put it under `AndroidCompat/lib`.
|
Download [android.jar](https://raw.githubusercontent.com/AriaMoradi/Tachidesk/android-jar/android.jar) and put it under `AndroidCompat/lib`.
|
||||||
#### Automated download(needs `bash`, `curl`, `base64`, `zip` to work)
|
#### Automated download(needs `bash`, `curl`, `base64`, `zip` to work)
|
||||||
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.
|
Run `AndroidCompat/getAndroid.sh` from project's root directory to download and rebuild the jar file from Google's repository.
|
||||||
### Prerequisite: Software dependencies
|
### building the jar
|
||||||
You need this software packages installed in order to build this project:
|
Run `./gradlew shadowJar`, the resulting built jar file will be `server/build/Tachidesk-vX.Y.Z-rxxx.jar`.
|
||||||
- Java Development Kit and Java Runtime Environment version 8 or newer(both Oracle JDK and OpenJDK works)
|
|
||||||
- Nodejs LTS or latest
|
|
||||||
- Yarn
|
|
||||||
### building the full-blown jar
|
|
||||||
Run `./gradlew server:shadowJar`, the resulting built jar file will be `server/build/Tachidesk-vX.Y.Z-rxxx.jar`.
|
|
||||||
### building without `webUI` bundled
|
|
||||||
Delete the `server/src/main/resources/react` directory if exists from previous runs, then run `./gradlew server:shadowJar -x :webUI:copyBuild`, the resulting built jar file will be `server/build/Tachidesk-vX.Y.Z-rxxx.jar`.
|
|
||||||
### building the Windows package
|
### building the Windows package
|
||||||
Run `./gradlew windowsPackage`, the resulting built zip package file will be `server/build/Tachidesk-vX.Y.Z-rxxx-win32.zip`.
|
Run `./gradlew windowsPackage`, the resulting built zip package file will be `server/build/Tachidesk-vX.Y.Z-rxxx-win32.zip`.
|
||||||
## Running for development purposes
|
## Running for development purposes
|
||||||
### `server` module
|
### `server` module
|
||||||
Follow [Get Android stubs jar](#prerequisite-get-android-stubs-jar) then run `./gradlew :server:run -x :webUI:copyBuild --stacktrace` to run the server
|
Run `./gradlew :server:run -x :webUI:copyBuild --stacktrace` to run the server
|
||||||
### `webUI` module
|
### `webUI` module
|
||||||
How to do it is described in `webUI/react/README.md` but for short,
|
How to do it is described in `webUI/react/README.md` but for short,
|
||||||
first cd into `webUI/react` then run `yarn` to install the node modules(do this only once)
|
first cd into `webUI/react` then run `yarn` to install the node modules(do this only once)
|
||||||
@@ -89,8 +82,6 @@ How to do it is described in `webUI/react/README.md` but for short,
|
|||||||
and supports HMR and all the other goodies you'll need.
|
and supports HMR and all the other goodies you'll need.
|
||||||
|
|
||||||
## Credit
|
## Credit
|
||||||
This project is a spiritual successor of [TachiWeb-Server](https://github.com/Tachiweb/TachiWeb-server), Many of the ideas and the groundwork adopted in this project comes from TachiWeb.
|
|
||||||
|
|
||||||
The `AndroidCompat` module was originally developed by [@null-dev](https://github.com/null-dev) for [TachiWeb-Server](https://github.com/Tachiweb/TachiWeb-server) and is licensed under `Apache License Version 2.0`.
|
The `AndroidCompat` module was originally developed by [@null-dev](https://github.com/null-dev) for [TachiWeb-Server](https://github.com/Tachiweb/TachiWeb-server) and is licensed under `Apache License Version 2.0`.
|
||||||
|
|
||||||
Parts of [tachiyomi](https://github.com/tachiyomiorg/tachiyomi) is adopted into this codebase, also licensed under `Apache License Version 2.0`.
|
Parts of [tachiyomi](https://github.com/tachiyomiorg/tachiyomi) is adopted into this codebase, also licensed under `Apache License Version 2.0`.
|
||||||
@@ -101,7 +92,7 @@ Changes to both codebases is licensed under `MPL v. 2.0` as the rest of this pro
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright (C) Contributors to the Suwayomi project
|
Copyright (C) 2020-2021 Aria Moradi and contributors
|
||||||
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public
|
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
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ configure(listOf(
|
|||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation("org.slf4j:slf4j-api:1.7.30")
|
implementation("org.slf4j:slf4j-api:1.7.30")
|
||||||
implementation("ch.qos.logback:logback-classic:1.2.3")
|
implementation("org.slf4j:slf4j-simple:1.7.30")
|
||||||
implementation("io.github.microutils:kotlin-logging:2.0.3")
|
implementation("io.github.microutils:kotlin-logging:2.0.3")
|
||||||
|
|
||||||
// RxJava
|
// RxJava
|
||||||
@@ -76,8 +76,6 @@ configure(listOf(
|
|||||||
|
|
||||||
// dependency of :AndroidCompat:Config
|
// dependency of :AndroidCompat:Config
|
||||||
implementation("com.typesafe:config:1.4.0")
|
implementation("com.typesafe:config:1.4.0")
|
||||||
implementation("io.github.config4k:config4k:0.4.2")
|
|
||||||
|
|
||||||
|
|
||||||
// to get application content root
|
// to get application content root
|
||||||
implementation("net.harawata:appdirs:1.2.0")
|
implementation("net.harawata:appdirs:1.2.0")
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ plugins {
|
|||||||
id("edu.sc.seis.launch4j") version "2.4.9"
|
id("edu.sc.seis.launch4j") version "2.4.9"
|
||||||
}
|
}
|
||||||
|
|
||||||
val TachideskVersion = "v0.2.7"
|
val TachideskVersion = "v0.2.6"
|
||||||
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@@ -57,17 +57,19 @@ dependencies {
|
|||||||
|
|
||||||
implementation("org.jsoup:jsoup:1.13.1")
|
implementation("org.jsoup:jsoup:1.13.1")
|
||||||
implementation("com.github.salomonbrys.kotson:kotson:2.5.0")
|
implementation("com.github.salomonbrys.kotson:kotson:2.5.0")
|
||||||
|
implementation("com.squareup.duktape:duktape-android:1.3.0")
|
||||||
|
|
||||||
|
|
||||||
val coroutinesVersion = "1.3.9"
|
val coroutinesVersion = "1.3.9"
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
|
||||||
|
|
||||||
// dex2jar: https://github.com/DexPatcher/dex2jar/releases/tag/v2.1-20190905-lanchon
|
// dex2jar: https://github.com/DexPatcher/dex2jar/releases/tag/v2.1-20190905-lanchon
|
||||||
implementation("com.github.DexPatcher.dex2jar:dex-tools:v2.1-20190905-lanchon")
|
implementation(fileTree("lib/dex2jar/"))
|
||||||
|
|
||||||
|
|
||||||
// api
|
// api
|
||||||
implementation("io.javalin:javalin:3.12.0")
|
implementation("io.javalin:javalin:3.12.0")
|
||||||
|
implementation("org.slf4j:slf4j-simple:1.8.0-beta4")
|
||||||
|
implementation("org.slf4j:slf4j-api:1.8.0-beta4")
|
||||||
implementation("com.fasterxml.jackson.core:jackson-databind:2.10.3")
|
implementation("com.fasterxml.jackson.core:jackson-databind:2.10.3")
|
||||||
|
|
||||||
// Exposed ORM
|
// Exposed ORM
|
||||||
@@ -85,8 +87,9 @@ dependencies {
|
|||||||
implementation(project(":AndroidCompat"))
|
implementation(project(":AndroidCompat"))
|
||||||
implementation(project(":AndroidCompat:Config"))
|
implementation(project(":AndroidCompat:Config"))
|
||||||
|
|
||||||
// uncomment to test extensions directly
|
|
||||||
// implementation(fileTree("lib/"))
|
// testImplementation("org.jetbrains.kotlin:kotlin-test")
|
||||||
|
// testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
|
||||||
}
|
}
|
||||||
|
|
||||||
val name = "ir.armor.tachidesk.Main"
|
val name = "ir.armor.tachidesk.Main"
|
||||||
|
|||||||
BIN
server/lib/dex2jar/ST4-4.0.8.jar
Normal file
BIN
server/lib/dex2jar/ST4-4.0.8.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/antlr-3.5.2.jar
Normal file
BIN
server/lib/dex2jar/antlr-3.5.2.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/antlr-runtime-3.5.2.jar
Normal file
BIN
server/lib/dex2jar/antlr-runtime-3.5.2.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/antlr4-4.5.jar
Normal file
BIN
server/lib/dex2jar/antlr4-4.5.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/antlr4-runtime-4.5.jar
Normal file
BIN
server/lib/dex2jar/antlr4-runtime-4.5.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/asm-debug-all-5.0.3.jar
Normal file
BIN
server/lib/dex2jar/asm-debug-all-5.0.3.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/d2j-base-cmd-2.1-20190905-lanchon.jar
Normal file
BIN
server/lib/dex2jar/d2j-base-cmd-2.1-20190905-lanchon.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/d2j-jasmin-2.1-20190905-lanchon.jar
Normal file
BIN
server/lib/dex2jar/d2j-jasmin-2.1-20190905-lanchon.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/d2j-smali-2.1-20190905-lanchon.jar
Normal file
BIN
server/lib/dex2jar/d2j-smali-2.1-20190905-lanchon.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/dex-ir-2.1-20190905-lanchon.jar
Normal file
BIN
server/lib/dex2jar/dex-ir-2.1-20190905-lanchon.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/dex-reader-2.1-20190905-lanchon.jar
Normal file
BIN
server/lib/dex2jar/dex-reader-2.1-20190905-lanchon.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/dex-reader-api-2.1-20190905-lanchon.jar
Normal file
BIN
server/lib/dex2jar/dex-reader-api-2.1-20190905-lanchon.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/dex-tools-2.1-20190905-lanchon.jar
Normal file
BIN
server/lib/dex2jar/dex-tools-2.1-20190905-lanchon.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/dex-translator-2.1-20190905-lanchon.jar
Normal file
BIN
server/lib/dex2jar/dex-translator-2.1-20190905-lanchon.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/dex-writer-2.1-20190905-lanchon.jar
Normal file
BIN
server/lib/dex2jar/dex-writer-2.1-20190905-lanchon.jar
Normal file
Binary file not shown.
BIN
server/lib/dex2jar/dx-27.0.3.jar
Normal file
BIN
server/lib/dex2jar/dx-27.0.3.jar
Normal file
Binary file not shown.
67
server/lib/dex2jar/open-source-license.txt
Normal file
67
server/lib/dex2jar/open-source-license.txt
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
==== dx-*.jar
|
||||||
|
Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
|
||||||
|
==== antlr-*.jar
|
||||||
|
[The BSD License]
|
||||||
|
Copyright (c) 2003-2007, Terence Parr
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of the author nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this
|
||||||
|
software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
==== asm-*.jar
|
||||||
|
|
||||||
|
ASM: a very small and fast Java bytecode manipulation framework
|
||||||
|
Copyright (c) 2000-2005 INRIA, France Telecom
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holders nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
BIN
server/lib/dex2jar/org.abego.treelayout.core-1.0.1.jar
Normal file
BIN
server/lib/dex2jar/org.abego.treelayout.core-1.0.1.jar
Normal file
Binary file not shown.
252
server/src/main/java/ir/armor/tachidesk/APKExtractor.java
Normal file
252
server/src/main/java/ir/armor/tachidesk/APKExtractor.java
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
package ir.armor.tachidesk;
|
||||||
|
|
||||||
|
/* 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 org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.NamedNodeMap;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
public class APKExtractor {
|
||||||
|
// decompressXML -- Parse the 'compressed' binary form of Android XML docs
|
||||||
|
// such as for AndroidManifest.xml in .apk files
|
||||||
|
public static int endDocTag = 0x00100101;
|
||||||
|
public static int startTag = 0x00100102;
|
||||||
|
public static int endTag = 0x00100103;
|
||||||
|
|
||||||
|
static void prt(String str) {
|
||||||
|
//System.err.print(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decompressXML(byte[] xml) {
|
||||||
|
|
||||||
|
StringBuilder finalXML = new StringBuilder();
|
||||||
|
|
||||||
|
// Compressed XML file/bytes starts with 24x bytes of data,
|
||||||
|
// 9 32 bit words in little endian order (LSB first):
|
||||||
|
// 0th word is 03 00 08 00
|
||||||
|
// 3rd word SEEMS TO BE: Offset at then of StringTable
|
||||||
|
// 4th word is: Number of strings in string table
|
||||||
|
// WARNING: Sometime I indiscriminently display or refer to word in
|
||||||
|
// little endian storage format, or in integer format (ie MSB first).
|
||||||
|
int numbStrings = LEW(xml, 4 * 4);
|
||||||
|
|
||||||
|
// StringIndexTable starts at offset 24x, an array of 32 bit LE offsets
|
||||||
|
// of the length/string data in the StringTable.
|
||||||
|
int sitOff = 0x24; // Offset of start of StringIndexTable
|
||||||
|
|
||||||
|
// StringTable, each string is represented with a 16 bit little endian
|
||||||
|
// character count, followed by that number of 16 bit (LE) (Unicode)
|
||||||
|
// chars.
|
||||||
|
int stOff = sitOff + numbStrings * 4; // StringTable follows
|
||||||
|
// StrIndexTable
|
||||||
|
|
||||||
|
// XMLTags, The XML tag tree starts after some unknown content after the
|
||||||
|
// StringTable. There is some unknown data after the StringTable, scan
|
||||||
|
// forward from this point to the flag for the start of an XML start
|
||||||
|
// tag.
|
||||||
|
int xmlTagOff = LEW(xml, 3 * 4); // Start from the offset in the 3rd
|
||||||
|
// word.
|
||||||
|
// Scan forward until we find the bytes: 0x02011000(x00100102 in normal
|
||||||
|
// int)
|
||||||
|
for (int ii = xmlTagOff; ii < xml.length - 4; ii += 4) {
|
||||||
|
if (LEW(xml, ii) == startTag) {
|
||||||
|
xmlTagOff = ii;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // end of hack, scanning for start of first start tag
|
||||||
|
|
||||||
|
// XML tags and attributes:
|
||||||
|
// Every XML start and end tag consists of 6 32 bit words:
|
||||||
|
// 0th word: 02011000 for startTag and 03011000 for endTag
|
||||||
|
// 1st word: a flag?, like 38000000
|
||||||
|
// 2nd word: Line of where this tag appeared in the original source file
|
||||||
|
// 3rd word: FFFFFFFF ??
|
||||||
|
// 4th word: StringIndex of NameSpace name, or FFFFFFFF for default NS
|
||||||
|
// 5th word: StringIndex of Element Name
|
||||||
|
// (Note: 01011000 in 0th word means end of XML document, endDocTag)
|
||||||
|
|
||||||
|
// Start tags (not end tags) contain 3 more words:
|
||||||
|
// 6th word: 14001400 meaning??
|
||||||
|
// 7th word: Number of Attributes that follow this tag(follow word 8th)
|
||||||
|
// 8th word: 00000000 meaning??
|
||||||
|
|
||||||
|
// Attributes consist of 5 words:
|
||||||
|
// 0th word: StringIndex of Attribute Name's Namespace, or FFFFFFFF
|
||||||
|
// 1st word: StringIndex of Attribute Name
|
||||||
|
// 2nd word: StringIndex of Attribute Value, or FFFFFFF if ResourceId
|
||||||
|
// used
|
||||||
|
// 3rd word: Flags?
|
||||||
|
// 4th word: str ind of attr value again, or ResourceId of value
|
||||||
|
|
||||||
|
// TMP, dump string table to tr for debugging
|
||||||
|
// tr.addSelect("strings", null);
|
||||||
|
// for (int ii=0; ii<numbStrings; ii++) {
|
||||||
|
// // Length of string starts at StringTable plus offset in StrIndTable
|
||||||
|
// String str = compXmlString(xml, sitOff, stOff, ii);
|
||||||
|
// tr.add(String.valueOf(ii), str);
|
||||||
|
// }
|
||||||
|
// tr.parent();
|
||||||
|
|
||||||
|
// Step through the XML tree element tags and attributes
|
||||||
|
int off = xmlTagOff;
|
||||||
|
int indent = 0;
|
||||||
|
int startTagLineNo = -2;
|
||||||
|
while (off < xml.length) {
|
||||||
|
int tag0 = LEW(xml, off);
|
||||||
|
// int tag1 = LEW(xml, off+1*4);
|
||||||
|
int lineNo = LEW(xml, off + 2 * 4);
|
||||||
|
// int tag3 = LEW(xml, off+3*4);
|
||||||
|
int nameNsSi = LEW(xml, off + 4 * 4);
|
||||||
|
int nameSi = LEW(xml, off + 5 * 4);
|
||||||
|
|
||||||
|
if (tag0 == startTag) { // XML START TAG
|
||||||
|
int tag6 = LEW(xml, off + 6 * 4); // Expected to be 14001400
|
||||||
|
int numbAttrs = LEW(xml, off + 7 * 4); // Number of Attributes
|
||||||
|
// to follow
|
||||||
|
// int tag8 = LEW(xml, off+8*4); // Expected to be 00000000
|
||||||
|
off += 9 * 4; // Skip over 6+3 words of startTag data
|
||||||
|
String name = compXmlString(xml, sitOff, stOff, nameSi);
|
||||||
|
// tr.addSelect(name, null);
|
||||||
|
startTagLineNo = lineNo;
|
||||||
|
|
||||||
|
// Look for the Attributes
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
for (int ii = 0; ii < numbAttrs; ii++) {
|
||||||
|
int attrNameNsSi = LEW(xml, off); // AttrName Namespace Str
|
||||||
|
// Ind, or FFFFFFFF
|
||||||
|
int attrNameSi = LEW(xml, off + 1 * 4); // AttrName String
|
||||||
|
// Index
|
||||||
|
int attrValueSi = LEW(xml, off + 2 * 4); // AttrValue Str
|
||||||
|
// Ind, or
|
||||||
|
// FFFFFFFF
|
||||||
|
int attrFlags = LEW(xml, off + 3 * 4);
|
||||||
|
int attrResId = LEW(xml, off + 4 * 4); // AttrValue
|
||||||
|
// ResourceId or dup
|
||||||
|
// AttrValue StrInd
|
||||||
|
off += 5 * 4; // Skip over the 5 words of an attribute
|
||||||
|
|
||||||
|
String attrName = compXmlString(xml, sitOff, stOff,
|
||||||
|
attrNameSi);
|
||||||
|
String attrValue = attrValueSi != -1 ? compXmlString(xml,
|
||||||
|
sitOff, stOff, attrValueSi) : "resourceID 0x"
|
||||||
|
+ Integer.toHexString(attrResId);
|
||||||
|
sb.append(" " + attrName + "=\"" + attrValue + "\"");
|
||||||
|
// tr.add(attrName, attrValue);
|
||||||
|
}
|
||||||
|
finalXML.append("<" + name + sb + ">");
|
||||||
|
prtIndent(indent, "<" + name + sb + ">");
|
||||||
|
indent++;
|
||||||
|
|
||||||
|
} else if (tag0 == endTag) { // XML END TAG
|
||||||
|
indent--;
|
||||||
|
off += 6 * 4; // Skip over 6 words of endTag data
|
||||||
|
String name = compXmlString(xml, sitOff, stOff, nameSi);
|
||||||
|
finalXML.append("</" + name + ">");
|
||||||
|
prtIndent(indent, "</" + name + "> (line " + startTagLineNo
|
||||||
|
+ "-" + lineNo + ")");
|
||||||
|
// tr.parent(); // Step back up the NobTree
|
||||||
|
|
||||||
|
} else if (tag0 == endDocTag) { // END OF XML DOC TAG
|
||||||
|
break;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
prt(" Unrecognized tag code '" + Integer.toHexString(tag0)
|
||||||
|
+ "' at offset " + off);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // end of while loop scanning tags and attributes of XML tree
|
||||||
|
//prt(" end at offset " + off);
|
||||||
|
return finalXML.toString();
|
||||||
|
} // end of decompressXML
|
||||||
|
|
||||||
|
public static String compXmlString(byte[] xml, int sitOff, int stOff, int strInd) {
|
||||||
|
if (strInd < 0)
|
||||||
|
return null;
|
||||||
|
int strOff = stOff + LEW(xml, sitOff + strInd * 4);
|
||||||
|
return compXmlStringAt(xml, strOff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String spaces = " ";
|
||||||
|
|
||||||
|
public static void prtIndent(int indent, String str) {
|
||||||
|
prt(spaces.substring(0, Math.min(indent * 2, spaces.length())) + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// compXmlStringAt -- Return the string stored in StringTable format at
|
||||||
|
// offset strOff. This offset points to the 16 bit string length, which
|
||||||
|
// is followed by that number of 16 bit (Unicode) chars.
|
||||||
|
public static String compXmlStringAt(byte[] arr, int strOff) {
|
||||||
|
int strLen = arr[strOff + 1] << 8 & 0xff00 | arr[strOff] & 0xff;
|
||||||
|
byte[] chars = new byte[strLen];
|
||||||
|
for (int ii = 0; ii < strLen; ii++) {
|
||||||
|
chars[ii] = arr[strOff + 2 + ii * 2];
|
||||||
|
}
|
||||||
|
return new String(chars); // Hack, just use 8 byte chars
|
||||||
|
} // end of compXmlStringAt
|
||||||
|
|
||||||
|
// LEW -- Return value of a Little Endian 32 bit word from the byte array
|
||||||
|
// at offset off.
|
||||||
|
public static int LEW(byte[] arr, int off) {
|
||||||
|
return arr[off + 3] << 24 & 0xff000000 | arr[off + 2] << 16 & 0xff0000
|
||||||
|
| arr[off + 1] << 8 & 0xff00 | arr[off] & 0xFF;
|
||||||
|
} // end of LEW
|
||||||
|
|
||||||
|
public static Document loadXMLFromString(String xml) throws Exception {
|
||||||
|
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||||
|
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
|
||||||
|
return docBuilder.parse(new InputSource(new StringReader(xml)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String extract_dex_and_read_className(String filePath, String dexPath) throws IOException {
|
||||||
|
ZipFile zip = null;
|
||||||
|
|
||||||
|
zip = new ZipFile(filePath);
|
||||||
|
ZipEntry androidManifest = zip.getEntry("AndroidManifest.xml");
|
||||||
|
ZipEntry classesDex = zip.getEntry("classes.dex");
|
||||||
|
|
||||||
|
// write dex file
|
||||||
|
InputStream dexStream = zip.getInputStream(classesDex);
|
||||||
|
try (OutputStream os = Files.newOutputStream(Paths.get(dexPath))) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = dexStream.read(buffer)) > 0) {
|
||||||
|
os.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read xml file
|
||||||
|
InputStream is = zip.getInputStream(androidManifest);
|
||||||
|
byte[] buf = new byte[1024000]; // 100 kb
|
||||||
|
is.read(buf);
|
||||||
|
is.close();
|
||||||
|
zip.close();
|
||||||
|
|
||||||
|
String xml = APKExtractor.decompressXML(buf);
|
||||||
|
try {
|
||||||
|
Document xmlDoc = loadXMLFromString(xml);
|
||||||
|
String pkg = xmlDoc.getDocumentElement().getAttribute("package");
|
||||||
|
NodeList nodes = xmlDoc.getElementsByTagName("meta-data");
|
||||||
|
for (int i = 0; i < nodes.getLength(); i++) {
|
||||||
|
NamedNodeMap attributes = nodes.item(i).getAttributes();
|
||||||
|
System.out.println(attributes.getNamedItem("name").getNodeValue());
|
||||||
|
if (attributes.getNamedItem("name").getNodeValue().equals("tachiyomi.extension.class"))
|
||||||
|
return pkg + attributes.getNamedItem("value").getNodeValue();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi
|
package eu.kanade.tachiyomi
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
// import android.content.res.Configuration
|
// import android.content.res.Configuration
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi
|
package eu.kanade.tachiyomi
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 android.app.Application
|
import android.app.Application
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
// import eu.kanade.tachiyomi.data.cache.ChapterCache
|
// import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi.extension.api
|
package eu.kanade.tachiyomi.extension.api
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 android.content.Context
|
// import android.content.Context
|
||||||
// import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
// import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi.extension.util
|
package eu.kanade.tachiyomi.extension.util
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
// import android.annotation.SuppressLint
|
// import android.annotation.SuppressLint
|
||||||
// import android.content.Context
|
// import android.content.Context
|
||||||
// import android.content.pm.PackageInfo
|
// import android.content.pm.PackageInfo
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi.network
|
package eu.kanade.tachiyomi.network
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 android.annotation.SuppressLint
|
// import android.annotation.SuppressLint
|
||||||
// import android.content.Context
|
// import android.content.Context
|
||||||
// import android.os.Build
|
// import android.os.Build
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi.network
|
package eu.kanade.tachiyomi.network
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 okhttp3.Cookie
|
import okhttp3.Cookie
|
||||||
import okhttp3.CookieJar
|
import okhttp3.CookieJar
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi.network
|
package eu.kanade.tachiyomi.network
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 android.content.Context
|
// import android.content.Context
|
||||||
// import eu.kanade.tachiyomi.BuildConfig
|
// import eu.kanade.tachiyomi.BuildConfig
|
||||||
// import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
// import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
|||||||
@@ -1,22 +1,260 @@
|
|||||||
package ir.armor.tachidesk
|
package ir.armor.tachidesk
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import ir.armor.tachidesk.server.applicationSetup
|
import io.javalin.Javalin
|
||||||
import ir.armor.tachidesk.server.javalinSetup
|
import ir.armor.tachidesk.util.addMangaToCategory
|
||||||
|
import ir.armor.tachidesk.util.addMangaToLibrary
|
||||||
|
import ir.armor.tachidesk.util.createCategory
|
||||||
|
import ir.armor.tachidesk.util.getCategoryList
|
||||||
|
import ir.armor.tachidesk.util.getCategoryMangaList
|
||||||
|
import ir.armor.tachidesk.util.getChapter
|
||||||
|
import ir.armor.tachidesk.util.getChapterList
|
||||||
|
import ir.armor.tachidesk.util.getExtensionIcon
|
||||||
|
import ir.armor.tachidesk.util.getExtensionList
|
||||||
|
import ir.armor.tachidesk.util.getLibraryMangas
|
||||||
|
import ir.armor.tachidesk.util.getManga
|
||||||
|
import ir.armor.tachidesk.util.getMangaCategories
|
||||||
|
import ir.armor.tachidesk.util.getMangaList
|
||||||
|
import ir.armor.tachidesk.util.getPageImage
|
||||||
|
import ir.armor.tachidesk.util.getSource
|
||||||
|
import ir.armor.tachidesk.util.getSourceList
|
||||||
|
import ir.armor.tachidesk.util.getThumbnail
|
||||||
|
import ir.armor.tachidesk.util.installAPK
|
||||||
|
import ir.armor.tachidesk.util.openInBrowser
|
||||||
|
import ir.armor.tachidesk.util.removeCategory
|
||||||
|
import ir.armor.tachidesk.util.removeExtension
|
||||||
|
import ir.armor.tachidesk.util.removeMangaFromCategory
|
||||||
|
import ir.armor.tachidesk.util.removeMangaFromLibrary
|
||||||
|
import ir.armor.tachidesk.util.reorderCategory
|
||||||
|
import ir.armor.tachidesk.util.sourceFilters
|
||||||
|
import ir.armor.tachidesk.util.sourceGlobalSearch
|
||||||
|
import ir.armor.tachidesk.util.sourceSearch
|
||||||
|
import ir.armor.tachidesk.util.updateCategory
|
||||||
|
|
||||||
class Main {
|
class Main {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
applicationSetup()
|
serverSetup()
|
||||||
javalinSetup()
|
|
||||||
|
var hasWebUiBundled: Boolean = false
|
||||||
|
|
||||||
|
val app = Javalin.create { config ->
|
||||||
|
try {
|
||||||
|
this::class.java.classLoader.getResource("/react/index.html")
|
||||||
|
hasWebUiBundled = true
|
||||||
|
config.addStaticFiles("/react")
|
||||||
|
config.addSinglePageRoot("/", "/react/index.html")
|
||||||
|
} catch (e: RuntimeException) {
|
||||||
|
println("Warning: react build files are missing.")
|
||||||
|
hasWebUiBundled = false
|
||||||
|
}
|
||||||
|
config.enableCorsForAllOrigins()
|
||||||
|
}.start(serverConfig.ip, serverConfig.port)
|
||||||
|
if (hasWebUiBundled) {
|
||||||
|
openInBrowser()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.exception(NullPointerException::class.java) { _, ctx ->
|
||||||
|
ctx.status(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("/api/v1/extension/list") { ctx ->
|
||||||
|
ctx.json(getExtensionList())
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("/api/v1/extension/install/:apkName") { ctx ->
|
||||||
|
val apkName = ctx.pathParam("apkName")
|
||||||
|
println("installing $apkName")
|
||||||
|
|
||||||
|
ctx.status(
|
||||||
|
installAPK(apkName)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("/api/v1/extension/uninstall/:apkName") { ctx ->
|
||||||
|
val apkName = ctx.pathParam("apkName")
|
||||||
|
println("uninstalling $apkName")
|
||||||
|
removeExtension(apkName)
|
||||||
|
ctx.status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
// icon for extension named `apkName`
|
||||||
|
app.get("/api/v1/extension/icon/:apkName") { ctx ->
|
||||||
|
val apkName = ctx.pathParam("apkName")
|
||||||
|
val result = getExtensionIcon(apkName)
|
||||||
|
|
||||||
|
ctx.result(result.first)
|
||||||
|
ctx.header("content-type", result.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// list of sources
|
||||||
|
app.get("/api/v1/source/list") { ctx ->
|
||||||
|
ctx.json(getSourceList())
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch source with id `sourceId`
|
||||||
|
app.get("/api/v1/source/:sourceId") { ctx ->
|
||||||
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
|
ctx.json(getSource(sourceId))
|
||||||
|
}
|
||||||
|
|
||||||
|
// popular mangas from source with id `sourceId`
|
||||||
|
app.get("/api/v1/source/:sourceId/popular/:pageNum") { ctx ->
|
||||||
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
|
val pageNum = ctx.pathParam("pageNum").toInt()
|
||||||
|
ctx.json(getMangaList(sourceId, pageNum, popular = true))
|
||||||
|
}
|
||||||
|
|
||||||
|
// latest mangas from source with id `sourceId`
|
||||||
|
app.get("/api/v1/source/:sourceId/latest/:pageNum") { ctx ->
|
||||||
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
|
val pageNum = ctx.pathParam("pageNum").toInt()
|
||||||
|
ctx.json(getMangaList(sourceId, pageNum, popular = false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get manga info
|
||||||
|
app.get("/api/v1/manga/:mangaId/") { ctx ->
|
||||||
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
ctx.json(getManga(mangaId))
|
||||||
|
}
|
||||||
|
|
||||||
|
// manga thumbnail
|
||||||
|
app.get("api/v1/manga/:mangaId/thumbnail") { ctx ->
|
||||||
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
val result = getThumbnail(mangaId)
|
||||||
|
|
||||||
|
ctx.result(result.first)
|
||||||
|
ctx.header("content-type", result.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// adds the manga to library
|
||||||
|
app.get("api/v1/manga/:mangaId/library") { ctx ->
|
||||||
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
addMangaToLibrary(mangaId)
|
||||||
|
ctx.status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes the manga from the library
|
||||||
|
app.delete("api/v1/manga/:mangaId/library") { ctx ->
|
||||||
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
removeMangaFromLibrary(mangaId)
|
||||||
|
ctx.status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
// list manga's categories
|
||||||
|
app.get("api/v1/manga/:mangaId/category/") { ctx ->
|
||||||
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
ctx.json(getMangaCategories(mangaId))
|
||||||
|
}
|
||||||
|
|
||||||
|
// adds the manga to category
|
||||||
|
app.get("api/v1/manga/:mangaId/category/:categoryId") { ctx ->
|
||||||
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
||||||
|
addMangaToCategory(mangaId, categoryId)
|
||||||
|
ctx.status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes the manga from the category
|
||||||
|
app.delete("api/v1/manga/:mangaId/category/:categoryId") { ctx ->
|
||||||
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
||||||
|
removeMangaFromCategory(mangaId, categoryId)
|
||||||
|
ctx.status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("/api/v1/manga/:mangaId/chapters") { ctx ->
|
||||||
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
ctx.json(getChapterList(mangaId))
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex") { ctx ->
|
||||||
|
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
|
||||||
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
ctx.json(getChapter(chapterIndex, mangaId))
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("/api/v1/manga/:mangaId/chapter/:chapterId/page/:index") { ctx ->
|
||||||
|
val chapterId = ctx.pathParam("chapterId").toInt()
|
||||||
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
val index = ctx.pathParam("index").toInt()
|
||||||
|
val result = getPageImage(mangaId, chapterId, index)
|
||||||
|
|
||||||
|
ctx.result(result.first)
|
||||||
|
ctx.header("content-type", result.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// global search
|
||||||
|
app.get("/api/v1/search/:searchTerm") { ctx ->
|
||||||
|
val searchTerm = ctx.pathParam("searchTerm")
|
||||||
|
ctx.json(sourceGlobalSearch(searchTerm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// single source search
|
||||||
|
app.get("/api/v1/source/:sourceId/search/:searchTerm/:pageNum") { ctx ->
|
||||||
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
|
val searchTerm = ctx.pathParam("searchTerm")
|
||||||
|
val pageNum = ctx.pathParam("pageNum").toInt()
|
||||||
|
ctx.json(sourceSearch(sourceId, searchTerm, pageNum))
|
||||||
|
}
|
||||||
|
|
||||||
|
// source filter list
|
||||||
|
app.get("/api/v1/source/:sourceId/filters/") { ctx ->
|
||||||
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
|
ctx.json(sourceFilters(sourceId))
|
||||||
|
}
|
||||||
|
|
||||||
|
// lists mangas that have no category assigned
|
||||||
|
app.get("/api/v1/library/") { ctx ->
|
||||||
|
ctx.json(getLibraryMangas())
|
||||||
|
}
|
||||||
|
|
||||||
|
// category list
|
||||||
|
app.get("/api/v1/category/") { ctx ->
|
||||||
|
ctx.json(getCategoryList())
|
||||||
|
}
|
||||||
|
|
||||||
|
// category create
|
||||||
|
app.post("/api/v1/category/") { ctx ->
|
||||||
|
val name = ctx.formParam("name")!!
|
||||||
|
createCategory(name)
|
||||||
|
ctx.status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
// category modification
|
||||||
|
app.patch("/api/v1/category/:categoryId") { ctx ->
|
||||||
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
||||||
|
val name = ctx.formParam("name")
|
||||||
|
val isLanding = if (ctx.formParam("isLanding") != null) ctx.formParam("isLanding")?.toBoolean() else null
|
||||||
|
updateCategory(categoryId, name, isLanding)
|
||||||
|
ctx.status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
// category re-ordering
|
||||||
|
app.patch("/api/v1/category/:categoryId/reorder") { ctx ->
|
||||||
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
||||||
|
val from = ctx.formParam("from")!!.toInt()
|
||||||
|
val to = ctx.formParam("to")!!.toInt()
|
||||||
|
reorderCategory(categoryId, from, to)
|
||||||
|
ctx.status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
// category delete
|
||||||
|
app.delete("/api/v1/category/:categoryId") { ctx ->
|
||||||
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
||||||
|
removeCategory(categoryId)
|
||||||
|
ctx.status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the manga list associated with a category
|
||||||
|
app.get("/api/v1/category/:categoryId") { ctx ->
|
||||||
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
||||||
|
ctx.json(getCategoryMangaList(categoryId))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
server/src/main/kotlin/ir/armor/tachidesk/ServerConfig.kt
Normal file
25
server/src/main/kotlin/ir/armor/tachidesk/ServerConfig.kt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package ir.armor.tachidesk
|
||||||
|
|
||||||
|
import com.typesafe.config.Config
|
||||||
|
import xyz.nulldev.ts.config.ConfigModule
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class ServerConfig(config: Config) : ConfigModule(config) {
|
||||||
|
val ip = config.getString("ip")
|
||||||
|
val port = config.getInt("port")
|
||||||
|
|
||||||
|
// proxy
|
||||||
|
val socksProxy = config.getBoolean("socksProxy")
|
||||||
|
val socksProxyHost = config.getString("socksProxyHost")
|
||||||
|
val socksProxyPort = config.getString("socksProxyPort")
|
||||||
|
|
||||||
|
fun registerFile(file: String): File {
|
||||||
|
return File(file).apply {
|
||||||
|
mkdirs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun register(config: Config) = ServerConfig(config.getConfig("server"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,8 @@
|
|||||||
package ir.armor.tachidesk.server
|
package ir.armor.tachidesk
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 ch.qos.logback.classic.Level
|
|
||||||
import eu.kanade.tachiyomi.App
|
import eu.kanade.tachiyomi.App
|
||||||
import ir.armor.tachidesk.Main
|
|
||||||
import ir.armor.tachidesk.database.makeDataBaseTables
|
import ir.armor.tachidesk.database.makeDataBaseTables
|
||||||
import ir.armor.tachidesk.server.util.systemTray
|
import ir.armor.tachidesk.util.systemTray
|
||||||
import mu.KotlinLogging
|
|
||||||
import net.harawata.appdirs.AppDirsFactory
|
import net.harawata.appdirs.AppDirsFactory
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.DI
|
||||||
import org.kodein.di.conf.global
|
import org.kodein.di.conf.global
|
||||||
@@ -22,8 +12,6 @@ import xyz.nulldev.ts.config.ConfigKodeinModule
|
|||||||
import xyz.nulldev.ts.config.GlobalConfigManager
|
import xyz.nulldev.ts.config.GlobalConfigManager
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
private val logger = KotlinLogging.logger {}
|
|
||||||
|
|
||||||
object applicationDirs {
|
object applicationDirs {
|
||||||
val dataRoot = AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)!!
|
val dataRoot = AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)!!
|
||||||
val extensionsRoot = "$dataRoot/extensions"
|
val extensionsRoot = "$dataRoot/extensions"
|
||||||
@@ -37,17 +25,12 @@ val systemTray by lazy { systemTray() }
|
|||||||
|
|
||||||
val androidCompat by lazy { AndroidCompat() }
|
val androidCompat by lazy { AndroidCompat() }
|
||||||
|
|
||||||
fun applicationSetup() {
|
fun serverSetup() {
|
||||||
// register server config
|
// register server config
|
||||||
GlobalConfigManager.registerModule(
|
GlobalConfigManager.registerModule(
|
||||||
ServerConfig.register(GlobalConfigManager.config)
|
ServerConfig.register(GlobalConfigManager.config)
|
||||||
)
|
)
|
||||||
|
|
||||||
// set application wide logging level
|
|
||||||
if (serverConfig.debugLogsEnabled) {
|
|
||||||
(mu.KotlinLogging.logger(org.slf4j.Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger).level = Level.DEBUG
|
|
||||||
}
|
|
||||||
|
|
||||||
// make dirs we need
|
// make dirs we need
|
||||||
listOf(
|
listOf(
|
||||||
applicationDirs.dataRoot,
|
applicationDirs.dataRoot,
|
||||||
@@ -58,29 +41,10 @@ fun applicationSetup() {
|
|||||||
File(it).mkdirs()
|
File(it).mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
// create conf file if doesn't exist
|
|
||||||
try {
|
|
||||||
val dataConfFile = File("${applicationDirs.dataRoot}/server.conf")
|
|
||||||
if (!dataConfFile.exists()) {
|
|
||||||
Main::class.java.getResourceAsStream("/server-reference.conf").use { input ->
|
|
||||||
dataConfFile.outputStream().use { output ->
|
|
||||||
input.copyTo(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.error("Exception while creating initial server.conf:\n", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
makeDataBaseTables()
|
makeDataBaseTables()
|
||||||
|
|
||||||
// create system tray
|
// create system tray
|
||||||
if (serverConfig.systemTrayEnabled)
|
systemTray
|
||||||
try {
|
|
||||||
systemTray
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load config API
|
// Load config API
|
||||||
DI.global.addImport(ConfigKodeinModule().create())
|
DI.global.addImport(ConfigKodeinModule().create())
|
||||||
@@ -89,11 +53,6 @@ fun applicationSetup() {
|
|||||||
// start app
|
// start app
|
||||||
androidCompat.startApp(App())
|
androidCompat.startApp(App())
|
||||||
|
|
||||||
// Disable jetty's logging
|
|
||||||
System.setProperty("org.eclipse.jetty.util.log.announce", "false")
|
|
||||||
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog")
|
|
||||||
System.setProperty("org.eclipse.jetty.LEVEL", "OFF")
|
|
||||||
|
|
||||||
// socks proxy settings
|
// socks proxy settings
|
||||||
System.getProperties()["proxySet"] = serverConfig.socksProxy.toString()
|
System.getProperties()["proxySet"] = serverConfig.socksProxy.toString()
|
||||||
System.getProperties()["socksProxyHost"] = serverConfig.socksProxyHost
|
System.getProperties()["socksProxyHost"] = serverConfig.socksProxyHost
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
package ir.armor.tachidesk.database
|
package ir.armor.tachidesk.database
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import ir.armor.tachidesk.applicationDirs
|
||||||
import ir.armor.tachidesk.database.table.CategoryMangaTable
|
import ir.armor.tachidesk.database.table.CategoryMangaTable
|
||||||
import ir.armor.tachidesk.database.table.CategoryTable
|
import ir.armor.tachidesk.database.table.CategoryTable
|
||||||
import ir.armor.tachidesk.database.table.ChapterTable
|
import ir.armor.tachidesk.database.table.ChapterTable
|
||||||
@@ -14,7 +12,6 @@ import ir.armor.tachidesk.database.table.ExtensionTable
|
|||||||
import ir.armor.tachidesk.database.table.MangaTable
|
import ir.armor.tachidesk.database.table.MangaTable
|
||||||
import ir.armor.tachidesk.database.table.PageTable
|
import ir.armor.tachidesk.database.table.PageTable
|
||||||
import ir.armor.tachidesk.database.table.SourceTable
|
import ir.armor.tachidesk.database.table.SourceTable
|
||||||
import ir.armor.tachidesk.server.applicationDirs
|
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.sql.Database
|
||||||
import org.jetbrains.exposed.sql.SchemaUtils
|
import org.jetbrains.exposed.sql.SchemaUtils
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.dataclass
|
package ir.armor.tachidesk.database.dataclass
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.dataclass
|
package ir.armor.tachidesk.database.dataclass
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.dataclass
|
package ir.armor.tachidesk.database.dataclass
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.dataclass
|
package ir.armor.tachidesk.database.dataclass
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.dataclass
|
package ir.armor.tachidesk.database.dataclass
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.dataclass
|
package ir.armor.tachidesk.database.dataclass
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.entity
|
package ir.armor.tachidesk.database.entity
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.entity
|
package ir.armor.tachidesk.database.entity
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.entity
|
package ir.armor.tachidesk.database.entity
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
package ir.armor.tachidesk.database.table
|
package ir.armor.tachidesk.database.table
|
||||||
|
|
||||||
/*
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
|
||||||
|
|
||||||
object CategoryMangaTable : IntIdTable() {
|
object CategoryMangaTable : IntIdTable() {
|
||||||
val category = reference("category", CategoryTable)
|
val category = reference("category", CategoryTable)
|
||||||
val manga = reference("manga", MangaTable)
|
val manga = reference("manga", MangaTable)
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.table
|
package ir.armor.tachidesk.database.table
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.table
|
package ir.armor.tachidesk.database.table
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.table
|
package ir.armor.tachidesk.database.table
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
package ir.armor.tachidesk.database.table
|
package ir.armor.tachidesk.database.table
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||||
import ir.armor.tachidesk.impl.proxyThumbnailUrl
|
import ir.armor.tachidesk.util.proxyThumbnailUrl
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.table
|
package ir.armor.tachidesk.database.table
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.database.table
|
package ir.armor.tachidesk.database.table
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,510 +0,0 @@
|
|||||||
package ir.armor.tachidesk.impl.util
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
import org.w3c.dom.Document
|
|
||||||
import org.xml.sax.InputSource
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.StringReader
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.util.zip.ZipFile
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory
|
|
||||||
|
|
||||||
object APKExtractor {
|
|
||||||
// decompressXML -- Parse the 'compressed' binary form of Android XML docs
|
|
||||||
// such as for AndroidManifest.xml in .apk files
|
|
||||||
var endDocTag = 0x00100101
|
|
||||||
var startTag = 0x00100102
|
|
||||||
var endTag = 0x00100103
|
|
||||||
fun prt(str: String?) {
|
|
||||||
// System.err.print(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decompressXML(xml: ByteArray): String {
|
|
||||||
val finalXML = StringBuilder()
|
|
||||||
|
|
||||||
// Compressed XML file/bytes starts with 24x bytes of data,
|
|
||||||
// 9 32 bit words in little endian order (LSB first):
|
|
||||||
// 0th word is 03 00 08 00
|
|
||||||
// 3rd word SEEMS TO BE: Offset at then of StringTable
|
|
||||||
// 4th word is: Number of strings in string table
|
|
||||||
// WARNING: Sometime I indiscriminently display or refer to word in
|
|
||||||
// little endian storage format, or in integer format (ie MSB first).
|
|
||||||
val numbStrings = LEW(xml, 4 * 4)
|
|
||||||
|
|
||||||
// StringIndexTable starts at offset 24x, an array of 32 bit LE offsets
|
|
||||||
// of the length/string data in the StringTable.
|
|
||||||
val sitOff = 0x24 // Offset of start of StringIndexTable
|
|
||||||
|
|
||||||
// StringTable, each string is represented with a 16 bit little endian
|
|
||||||
// character count, followed by that number of 16 bit (LE) (Unicode)
|
|
||||||
// chars.
|
|
||||||
val stOff = sitOff + numbStrings * 4 // StringTable follows
|
|
||||||
// StrIndexTable
|
|
||||||
|
|
||||||
// XMLTags, The XML tag tree starts after some unknown content after the
|
|
||||||
// StringTable. There is some unknown data after the StringTable, scan
|
|
||||||
// forward from this point to the flag for the start of an XML start
|
|
||||||
// tag.
|
|
||||||
var xmlTagOff = LEW(xml, 3 * 4) // Start from the offset in the 3rd
|
|
||||||
// word.
|
|
||||||
// Scan forward until we find the bytes: 0x02011000(x00100102 in normal
|
|
||||||
// int)
|
|
||||||
var ii = xmlTagOff
|
|
||||||
while (ii < xml.size - 4) {
|
|
||||||
if (LEW(xml, ii) == startTag) {
|
|
||||||
xmlTagOff = ii
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ii += 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// XML tags and attributes:
|
|
||||||
// Every XML start and end tag consists of 6 32 bit words:
|
|
||||||
// 0th word: 02011000 for startTag and 03011000 for endTag
|
|
||||||
// 1st word: a flag?, like 38000000
|
|
||||||
// 2nd word: Line of where this tag appeared in the original source file
|
|
||||||
// 3rd word: FFFFFFFF ??
|
|
||||||
// 4th word: StringIndex of NameSpace name, or FFFFFFFF for default NS
|
|
||||||
// 5th word: StringIndex of Element Name
|
|
||||||
// (Note: 01011000 in 0th word means end of XML document, endDocTag)
|
|
||||||
|
|
||||||
// Start tags (not end tags) contain 3 more words:
|
|
||||||
// 6th word: 14001400 meaning??
|
|
||||||
// 7th word: Number of Attributes that follow this tag(follow word 8th)
|
|
||||||
// 8th word: 00000000 meaning??
|
|
||||||
|
|
||||||
// Attributes consist of 5 words:
|
|
||||||
// 0th word: StringIndex of Attribute Name's Namespace, or FFFFFFFF
|
|
||||||
// 1st word: StringIndex of Attribute Name
|
|
||||||
// 2nd word: StringIndex of Attribute Value, or FFFFFFF if ResourceId
|
|
||||||
// used
|
|
||||||
// 3rd word: Flags?
|
|
||||||
// 4th word: str ind of attr value again, or ResourceId of value
|
|
||||||
|
|
||||||
// TMP, dump string table to tr for debugging
|
|
||||||
// tr.addSelect("strings", null);
|
|
||||||
// for (int ii=0; ii<numbStrings; ii++) {
|
|
||||||
// // Length of string starts at StringTable plus offset in StrIndTable
|
|
||||||
// String str = compXmlString(xml, sitOff, stOff, ii);
|
|
||||||
// tr.add(String.valueOf(ii), str);
|
|
||||||
// }
|
|
||||||
// tr.parent();
|
|
||||||
|
|
||||||
// Step through the XML tree element tags and attributes
|
|
||||||
var off = xmlTagOff
|
|
||||||
var indent = 0
|
|
||||||
var startTagLineNo = -2
|
|
||||||
while (off < xml.size) {
|
|
||||||
val tag0 = LEW(xml, off)
|
|
||||||
// int tag1 = LEW(xml, off+1*4);
|
|
||||||
val lineNo = LEW(xml, off + 2 * 4)
|
|
||||||
// int tag3 = LEW(xml, off+3*4);
|
|
||||||
val nameNsSi = LEW(xml, off + 4 * 4)
|
|
||||||
val nameSi = LEW(xml, off + 5 * 4)
|
|
||||||
if (tag0 == startTag) { // XML START TAG
|
|
||||||
val tag6 = LEW(xml, off + 6 * 4) // Expected to be 14001400
|
|
||||||
val numbAttrs = LEW(xml, off + 7 * 4) // Number of Attributes
|
|
||||||
// to follow
|
|
||||||
// int tag8 = LEW(xml, off+8*4); // Expected to be 00000000
|
|
||||||
off += 9 * 4 // Skip over 6+3 words of startTag data
|
|
||||||
val name = compXmlString(xml, sitOff, stOff, nameSi)
|
|
||||||
// tr.addSelect(name, null);
|
|
||||||
startTagLineNo = lineNo
|
|
||||||
|
|
||||||
// Look for the Attributes
|
|
||||||
val sb = StringBuffer()
|
|
||||||
for (ii in 0 until numbAttrs) {
|
|
||||||
val attrNameNsSi = LEW(xml, off) // AttrName Namespace Str
|
|
||||||
// Ind, or FFFFFFFF
|
|
||||||
val attrNameSi = LEW(xml, off + 1 * 4) // AttrName String
|
|
||||||
// Index
|
|
||||||
val attrValueSi = LEW(xml, off + 2 * 4) // AttrValue Str
|
|
||||||
// Ind, or
|
|
||||||
// FFFFFFFF
|
|
||||||
val attrFlags = LEW(xml, off + 3 * 4)
|
|
||||||
val attrResId = LEW(xml, off + 4 * 4) // AttrValue
|
|
||||||
// ResourceId or dup
|
|
||||||
// AttrValue StrInd
|
|
||||||
off += 5 * 4 // Skip over the 5 words of an attribute
|
|
||||||
val attrName = compXmlString(
|
|
||||||
xml, sitOff, stOff,
|
|
||||||
attrNameSi
|
|
||||||
)
|
|
||||||
val attrValue = if (attrValueSi != -1) compXmlString(xml, sitOff, stOff, attrValueSi)
|
|
||||||
else "resourceID 0x ${Integer.toHexString(attrResId)}"
|
|
||||||
sb.append(" $attrName=\"$attrValue\"")
|
|
||||||
// tr.add(attrName, attrValue);
|
|
||||||
}
|
|
||||||
finalXML.append("<$name$sb>")
|
|
||||||
prtIndent(indent, "<$name$sb>")
|
|
||||||
indent++
|
|
||||||
} else if (tag0 == endTag) { // XML END TAG
|
|
||||||
indent--
|
|
||||||
off += 6 * 4 // Skip over 6 words of endTag data
|
|
||||||
val name = compXmlString(xml, sitOff, stOff, nameSi)
|
|
||||||
finalXML.append("</$name>")
|
|
||||||
prtIndent(
|
|
||||||
indent,
|
|
||||||
"</" + name + "> (line " + startTagLineNo +
|
|
||||||
"-" + lineNo + ")"
|
|
||||||
)
|
|
||||||
// tr.parent(); // Step back up the NobTree
|
|
||||||
} else if (tag0 == endDocTag) { // END OF XML DOC TAG
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
prt(
|
|
||||||
" Unrecognized tag code '" + Integer.toHexString(tag0) +
|
|
||||||
"' at offset " + off
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} // end of while loop scanning tags and attributes of XML tree
|
|
||||||
// prt(" end at offset " + off);
|
|
||||||
return finalXML.toString()
|
|
||||||
} // end of decompressXML
|
|
||||||
|
|
||||||
fun compXmlString(xml: ByteArray, sitOff: Int, stOff: Int, strInd: Int): String? {
|
|
||||||
if (strInd < 0) return null
|
|
||||||
val strOff = stOff + LEW(xml, sitOff + strInd * 4)
|
|
||||||
return compXmlStringAt(xml, strOff)
|
|
||||||
}
|
|
||||||
|
|
||||||
var spaces = " "
|
|
||||||
fun prtIndent(indent: Int, str: String) {
|
|
||||||
prt(spaces.substring(0, Math.min(indent * 2, spaces.length)) + str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// compXmlStringAt -- Return the string stored in StringTable format at
|
|
||||||
// offset strOff. This offset points to the 16 bit string length, which
|
|
||||||
// is followed by that number of 16 bit (Unicode) chars.
|
|
||||||
fun compXmlStringAt(arr: ByteArray, strOff: Int): String {
|
|
||||||
val strLen: Int = arr[strOff + 1].toInt() shl 8 and 0xff00 or arr[strOff].toInt() and 0xff
|
|
||||||
val chars = ByteArray(strLen)
|
|
||||||
for (ii in 0 until strLen) {
|
|
||||||
chars[ii] = arr[strOff + 2 + ii * 2]
|
|
||||||
}
|
|
||||||
return String(chars) // Hack, just use 8 byte chars
|
|
||||||
} // end of compXmlStringAt
|
|
||||||
|
|
||||||
// LEW -- Return value of a Little Endian 32 bit word from the byte array
|
|
||||||
// at offset off.
|
|
||||||
fun LEW(arr: ByteArray, off: Int): Int {
|
|
||||||
|
|
||||||
return (arr[off + 3].toInt() shl 24) and -0x1000000 or
|
|
||||||
(arr[off + 2].toInt() shl 16 and 0xff0000) or
|
|
||||||
(arr[off + 1].toInt() shl 8 and 0xff00) or
|
|
||||||
(arr[off].toInt() and 0xFF)
|
|
||||||
} // end of LEW
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun loadXMLFromString(xml: String?): Document {
|
|
||||||
val docBuilderFactory = DocumentBuilderFactory.newInstance()
|
|
||||||
val docBuilder = docBuilderFactory.newDocumentBuilder()
|
|
||||||
return docBuilder.parse(InputSource(StringReader(xml)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun extract_dex_and_read_className(filePath: String?, dexPath: String?): String {
|
|
||||||
var zip: ZipFile? = null
|
|
||||||
zip = ZipFile(filePath)
|
|
||||||
val androidManifest = zip.getEntry("AndroidManifest.xml")
|
|
||||||
val classesDex = zip.getEntry("classes.dex")
|
|
||||||
|
|
||||||
// write dex file
|
|
||||||
val dexStream = zip.getInputStream(classesDex)
|
|
||||||
Files.newOutputStream(Paths.get(dexPath)).use { os ->
|
|
||||||
val buffer = ByteArray(1024)
|
|
||||||
var len: Int
|
|
||||||
while (dexStream.read(buffer).also { len = it } > 0) {
|
|
||||||
os.write(buffer, 0, len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// read xml file
|
|
||||||
val `is` = zip.getInputStream(androidManifest)
|
|
||||||
val buf = ByteArray(1024000) // 100 kb
|
|
||||||
`is`.read(buf)
|
|
||||||
`is`.close()
|
|
||||||
zip.close()
|
|
||||||
val xml = decompressXML(buf)
|
|
||||||
try {
|
|
||||||
val xmlDoc = loadXMLFromString(xml)
|
|
||||||
val pkg = xmlDoc.documentElement.getAttribute("package")
|
|
||||||
val nodes = xmlDoc.getElementsByTagName("meta-data")
|
|
||||||
for (i in 0 until nodes.length) {
|
|
||||||
val attributes = nodes.item(i).attributes
|
|
||||||
println(attributes.getNamedItem("name").nodeValue)
|
|
||||||
if (attributes.getNamedItem("name").nodeValue == "tachiyomi.extension.class") return pkg + attributes.getNamedItem("value").nodeValue
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// original Java code
|
|
||||||
|
|
||||||
// package ir.armor.tachidesk.impl.util;
|
|
||||||
//
|
|
||||||
// /*
|
|
||||||
// * Copyright (C) Contributors to the Suwayomi project
|
|
||||||
// *
|
|
||||||
// * This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
// * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
||||||
//
|
|
||||||
// import org.w3c.dom.Document;
|
|
||||||
// import org.w3c.dom.NamedNodeMap;
|
|
||||||
// import org.w3c.dom.NodeList;
|
|
||||||
// import org.xml.sax.InputSource;
|
|
||||||
//
|
|
||||||
// import javax.xml.parsers.DocumentBuilder;
|
|
||||||
// import javax.xml.parsers.DocumentBuilderFactory;
|
|
||||||
// import java.io.*;
|
|
||||||
// import java.nio.file.Files;
|
|
||||||
// import java.nio.file.Paths;
|
|
||||||
// import java.util.zip.ZipEntry;
|
|
||||||
// import java.util.zip.ZipFile;
|
|
||||||
//
|
|
||||||
// public class APKExtractor {
|
|
||||||
// // decompressXML -- Parse the 'compressed' binary form of Android XML docs
|
|
||||||
// // such as for AndroidManifest.xml in .apk files
|
|
||||||
// public static int endDocTag = 0x00100101;
|
|
||||||
// public static int startTag = 0x00100102;
|
|
||||||
// public static int endTag = 0x00100103;
|
|
||||||
//
|
|
||||||
// static void prt(String str) {
|
|
||||||
// //System.err.print(str);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public static String decompressXML(byte[] xml) {
|
|
||||||
//
|
|
||||||
// StringBuilder finalXML = new StringBuilder();
|
|
||||||
//
|
|
||||||
// // Compressed XML file/bytes starts with 24x bytes of data,
|
|
||||||
// // 9 32 bit words in little endian order (LSB first):
|
|
||||||
// // 0th word is 03 00 08 00
|
|
||||||
// // 3rd word SEEMS TO BE: Offset at then of StringTable
|
|
||||||
// // 4th word is: Number of strings in string table
|
|
||||||
// // WARNING: Sometime I indiscriminently display or refer to word in
|
|
||||||
// // little endian storage format, or in integer format (ie MSB first).
|
|
||||||
// int numbStrings = LEW(xml, 4 * 4);
|
|
||||||
//
|
|
||||||
// // StringIndexTable starts at offset 24x, an array of 32 bit LE offsets
|
|
||||||
// // of the length/string data in the StringTable.
|
|
||||||
// int sitOff = 0x24; // Offset of start of StringIndexTable
|
|
||||||
//
|
|
||||||
// // StringTable, each string is represented with a 16 bit little endian
|
|
||||||
// // character count, followed by that number of 16 bit (LE) (Unicode)
|
|
||||||
// // chars.
|
|
||||||
// int stOff = sitOff + numbStrings * 4; // StringTable follows
|
|
||||||
// // StrIndexTable
|
|
||||||
//
|
|
||||||
// // XMLTags, The XML tag tree starts after some unknown content after the
|
|
||||||
// // StringTable. There is some unknown data after the StringTable, scan
|
|
||||||
// // forward from this point to the flag for the start of an XML start
|
|
||||||
// // tag.
|
|
||||||
// int xmlTagOff = LEW(xml, 3 * 4); // Start from the offset in the 3rd
|
|
||||||
// // word.
|
|
||||||
// // Scan forward until we find the bytes: 0x02011000(x00100102 in normal
|
|
||||||
// // int)
|
|
||||||
// for (int ii = xmlTagOff; ii < xml.length - 4; ii += 4) {
|
|
||||||
// if (LEW(xml, ii) == startTag) {
|
|
||||||
// xmlTagOff = ii;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// } // end of hack, scanning for start of first start tag
|
|
||||||
//
|
|
||||||
// // XML tags and attributes:
|
|
||||||
// // Every XML start and end tag consists of 6 32 bit words:
|
|
||||||
// // 0th word: 02011000 for startTag and 03011000 for endTag
|
|
||||||
// // 1st word: a flag?, like 38000000
|
|
||||||
// // 2nd word: Line of where this tag appeared in the original source file
|
|
||||||
// // 3rd word: FFFFFFFF ??
|
|
||||||
// // 4th word: StringIndex of NameSpace name, or FFFFFFFF for default NS
|
|
||||||
// // 5th word: StringIndex of Element Name
|
|
||||||
// // (Note: 01011000 in 0th word means end of XML document, endDocTag)
|
|
||||||
//
|
|
||||||
// // Start tags (not end tags) contain 3 more words:
|
|
||||||
// // 6th word: 14001400 meaning??
|
|
||||||
// // 7th word: Number of Attributes that follow this tag(follow word 8th)
|
|
||||||
// // 8th word: 00000000 meaning??
|
|
||||||
//
|
|
||||||
// // Attributes consist of 5 words:
|
|
||||||
// // 0th word: StringIndex of Attribute Name's Namespace, or FFFFFFFF
|
|
||||||
// // 1st word: StringIndex of Attribute Name
|
|
||||||
// // 2nd word: StringIndex of Attribute Value, or FFFFFFF if ResourceId
|
|
||||||
// // used
|
|
||||||
// // 3rd word: Flags?
|
|
||||||
// // 4th word: str ind of attr value again, or ResourceId of value
|
|
||||||
//
|
|
||||||
// // TMP, dump string table to tr for debugging
|
|
||||||
// // tr.addSelect("strings", null);
|
|
||||||
// // for (int ii=0; ii<numbStrings; ii++) {
|
|
||||||
// // // Length of string starts at StringTable plus offset in StrIndTable
|
|
||||||
// // String str = compXmlString(xml, sitOff, stOff, ii);
|
|
||||||
// // tr.add(String.valueOf(ii), str);
|
|
||||||
// // }
|
|
||||||
// // tr.parent();
|
|
||||||
//
|
|
||||||
// // Step through the XML tree element tags and attributes
|
|
||||||
// int off = xmlTagOff;
|
|
||||||
// int indent = 0;
|
|
||||||
// int startTagLineNo = -2;
|
|
||||||
// while (off < xml.length) {
|
|
||||||
// int tag0 = LEW(xml, off);
|
|
||||||
// // int tag1 = LEW(xml, off+1*4);
|
|
||||||
// int lineNo = LEW(xml, off + 2 * 4);
|
|
||||||
// // int tag3 = LEW(xml, off+3*4);
|
|
||||||
// int nameNsSi = LEW(xml, off + 4 * 4);
|
|
||||||
// int nameSi = LEW(xml, off + 5 * 4);
|
|
||||||
//
|
|
||||||
// if (tag0 == startTag) { // XML START TAG
|
|
||||||
// int tag6 = LEW(xml, off + 6 * 4); // Expected to be 14001400
|
|
||||||
// int numbAttrs = LEW(xml, off + 7 * 4); // Number of Attributes
|
|
||||||
// // to follow
|
|
||||||
// // int tag8 = LEW(xml, off+8*4); // Expected to be 00000000
|
|
||||||
// off += 9 * 4; // Skip over 6+3 words of startTag data
|
|
||||||
// String name = compXmlString(xml, sitOff, stOff, nameSi);
|
|
||||||
// // tr.addSelect(name, null);
|
|
||||||
// startTagLineNo = lineNo;
|
|
||||||
//
|
|
||||||
// // Look for the Attributes
|
|
||||||
// StringBuffer sb = new StringBuffer();
|
|
||||||
// for (int ii = 0; ii < numbAttrs; ii++) {
|
|
||||||
// int attrNameNsSi = LEW(xml, off); // AttrName Namespace Str
|
|
||||||
// // Ind, or FFFFFFFF
|
|
||||||
// int attrNameSi = LEW(xml, off + 1 * 4); // AttrName String
|
|
||||||
// // Index
|
|
||||||
// int attrValueSi = LEW(xml, off + 2 * 4); // AttrValue Str
|
|
||||||
// // Ind, or
|
|
||||||
// // FFFFFFFF
|
|
||||||
// int attrFlags = LEW(xml, off + 3 * 4);
|
|
||||||
// int attrResId = LEW(xml, off + 4 * 4); // AttrValue
|
|
||||||
// // ResourceId or dup
|
|
||||||
// // AttrValue StrInd
|
|
||||||
// off += 5 * 4; // Skip over the 5 words of an attribute
|
|
||||||
//
|
|
||||||
// String attrName = compXmlString(xml, sitOff, stOff,
|
|
||||||
// attrNameSi);
|
|
||||||
// String attrValue = attrValueSi != -1 ? compXmlString(xml,
|
|
||||||
// sitOff, stOff, attrValueSi) : "resourceID 0x"
|
|
||||||
// + Integer.toHexString(attrResId);
|
|
||||||
// sb.append(" " + attrName + "=\"" + attrValue + "\"");
|
|
||||||
// // tr.add(attrName, attrValue);
|
|
||||||
// }
|
|
||||||
// finalXML.append("<" + name + sb + ">");
|
|
||||||
// prtIndent(indent, "<" + name + sb + ">");
|
|
||||||
// indent++;
|
|
||||||
//
|
|
||||||
// } else if (tag0 == endTag) { // XML END TAG
|
|
||||||
// indent--;
|
|
||||||
// off += 6 * 4; // Skip over 6 words of endTag data
|
|
||||||
// String name = compXmlString(xml, sitOff, stOff, nameSi);
|
|
||||||
// finalXML.append("</" + name + ">");
|
|
||||||
// prtIndent(indent, "</" + name + "> (line " + startTagLineNo
|
|
||||||
// + "-" + lineNo + ")");
|
|
||||||
// // tr.parent(); // Step back up the NobTree
|
|
||||||
//
|
|
||||||
// } else if (tag0 == endDocTag) { // END OF XML DOC TAG
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// } else {
|
|
||||||
// prt(" Unrecognized tag code '" + Integer.toHexString(tag0)
|
|
||||||
// + "' at offset " + off);
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// } // end of while loop scanning tags and attributes of XML tree
|
|
||||||
// //prt(" end at offset " + off);
|
|
||||||
// return finalXML.toString();
|
|
||||||
// } // end of decompressXML
|
|
||||||
//
|
|
||||||
// public static String compXmlString(byte[] xml, int sitOff, int stOff, int strInd) {
|
|
||||||
// if (strInd < 0)
|
|
||||||
// return null;
|
|
||||||
// int strOff = stOff + LEW(xml, sitOff + strInd * 4);
|
|
||||||
// return compXmlStringAt(xml, strOff);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public static String spaces = " ";
|
|
||||||
//
|
|
||||||
// public static void prtIndent(int indent, String str) {
|
|
||||||
// prt(spaces.substring(0, Math.min(indent * 2, spaces.length())) + str);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // compXmlStringAt -- Return the string stored in StringTable format at
|
|
||||||
// // offset strOff. This offset points to the 16 bit string length, which
|
|
||||||
// // is followed by that number of 16 bit (Unicode) chars.
|
|
||||||
// public static String compXmlStringAt(byte[] arr, int strOff) {
|
|
||||||
// int strLen = arr[strOff + 1] << 8 & 0xff00 | arr[strOff] & 0xff;
|
|
||||||
// byte[] chars = new byte[strLen];
|
|
||||||
// for (int ii = 0; ii < strLen; ii++) {
|
|
||||||
// chars[ii] = arr[strOff + 2 + ii * 2];
|
|
||||||
// }
|
|
||||||
// return new String(chars); // Hack, just use 8 byte chars
|
|
||||||
// } // end of compXmlStringAt
|
|
||||||
//
|
|
||||||
// // LEW -- Return value of a Little Endian 32 bit word from the byte array
|
|
||||||
// // at offset off.
|
|
||||||
// public static int LEW(byte[] arr, int off) {
|
|
||||||
// return ((arr[off + 3] << 24) & 0xff000000) |
|
|
||||||
// ((arr[off + 2] << 16) & 0xff0000) |
|
|
||||||
// ((arr[off + 1] << 8) & 0xff00) |
|
|
||||||
// (arr[off] & 0xFF);
|
|
||||||
// } // end of LEW
|
|
||||||
//
|
|
||||||
// public static Document loadXMLFromString(String xml) throws Exception {
|
|
||||||
// DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
|
|
||||||
// DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
|
|
||||||
// return docBuilder.parse(new InputSource(new StringReader(xml)));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public static String extract_dex_and_read_className(String filePath, String dexPath) throws IOException {
|
|
||||||
// ZipFile zip = null;
|
|
||||||
//
|
|
||||||
// zip = new ZipFile(filePath);
|
|
||||||
// ZipEntry androidManifest = zip.getEntry("AndroidManifest.xml");
|
|
||||||
// ZipEntry classesDex = zip.getEntry("classes.dex");
|
|
||||||
//
|
|
||||||
// // write dex file
|
|
||||||
// InputStream dexStream = zip.getInputStream(classesDex);
|
|
||||||
// try (OutputStream os = Files.newOutputStream(Paths.get(dexPath))) {
|
|
||||||
// byte[] buffer = new byte[1024];
|
|
||||||
// int len;
|
|
||||||
// while ((len = dexStream.read(buffer)) > 0) {
|
|
||||||
// os.write(buffer, 0, len);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // read xml file
|
|
||||||
// InputStream is = zip.getInputStream(androidManifest);
|
|
||||||
// byte[] buf = new byte[1024000]; // 100 kb
|
|
||||||
// is.read(buf);
|
|
||||||
// is.close();
|
|
||||||
// zip.close();
|
|
||||||
//
|
|
||||||
// String xml = APKExtractor.decompressXML(buf);
|
|
||||||
// try {
|
|
||||||
// Document xmlDoc = loadXMLFromString(xml);
|
|
||||||
// String pkg = xmlDoc.getDocumentElement().getAttribute("package");
|
|
||||||
// NodeList nodes = xmlDoc.getElementsByTagName("meta-data");
|
|
||||||
// for (int i = 0; i < nodes.getLength(); i++) {
|
|
||||||
// NamedNodeMap attributes = nodes.item(i).getAttributes();
|
|
||||||
// System.out.println(attributes.getNamedItem("name").getNodeValue());
|
|
||||||
// if (attributes.getNamedItem("name").getNodeValue().equals("tachiyomi.extension.class"))
|
|
||||||
// return pkg + attributes.getNamedItem("value").getNodeValue();
|
|
||||||
// }
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// return "";
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,260 +0,0 @@
|
|||||||
package ir.armor.tachidesk.server
|
|
||||||
|
|
||||||
import io.javalin.Javalin
|
|
||||||
import ir.armor.tachidesk.Main
|
|
||||||
import ir.armor.tachidesk.impl.addMangaToCategory
|
|
||||||
import ir.armor.tachidesk.impl.addMangaToLibrary
|
|
||||||
import ir.armor.tachidesk.impl.createCategory
|
|
||||||
import ir.armor.tachidesk.impl.getCategoryList
|
|
||||||
import ir.armor.tachidesk.impl.getCategoryMangaList
|
|
||||||
import ir.armor.tachidesk.impl.getChapter
|
|
||||||
import ir.armor.tachidesk.impl.getChapterList
|
|
||||||
import ir.armor.tachidesk.impl.getExtensionIcon
|
|
||||||
import ir.armor.tachidesk.impl.getExtensionList
|
|
||||||
import ir.armor.tachidesk.impl.getLibraryMangas
|
|
||||||
import ir.armor.tachidesk.impl.getManga
|
|
||||||
import ir.armor.tachidesk.impl.getMangaCategories
|
|
||||||
import ir.armor.tachidesk.impl.getMangaList
|
|
||||||
import ir.armor.tachidesk.impl.getPageImage
|
|
||||||
import ir.armor.tachidesk.impl.getSource
|
|
||||||
import ir.armor.tachidesk.impl.getSourceList
|
|
||||||
import ir.armor.tachidesk.impl.getThumbnail
|
|
||||||
import ir.armor.tachidesk.impl.installAPK
|
|
||||||
import ir.armor.tachidesk.impl.removeCategory
|
|
||||||
import ir.armor.tachidesk.impl.removeExtension
|
|
||||||
import ir.armor.tachidesk.impl.removeMangaFromCategory
|
|
||||||
import ir.armor.tachidesk.impl.removeMangaFromLibrary
|
|
||||||
import ir.armor.tachidesk.impl.reorderCategory
|
|
||||||
import ir.armor.tachidesk.impl.sourceFilters
|
|
||||||
import ir.armor.tachidesk.impl.sourceGlobalSearch
|
|
||||||
import ir.armor.tachidesk.impl.sourceSearch
|
|
||||||
import ir.armor.tachidesk.impl.updateCategory
|
|
||||||
import ir.armor.tachidesk.server.util.openInBrowser
|
|
||||||
import mu.KotlinLogging
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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/. */
|
|
||||||
|
|
||||||
private val logger = KotlinLogging.logger {}
|
|
||||||
|
|
||||||
fun javalinSetup() {
|
|
||||||
var hasWebUiBundled = false
|
|
||||||
|
|
||||||
val app = Javalin.create { config ->
|
|
||||||
try {
|
|
||||||
Main::class.java.getResource("/react/index.html")
|
|
||||||
hasWebUiBundled = true
|
|
||||||
config.addStaticFiles("/react")
|
|
||||||
config.addSinglePageRoot("/", "/react/index.html")
|
|
||||||
} catch (e: RuntimeException) {
|
|
||||||
logger.warn("react build files are missing.")
|
|
||||||
hasWebUiBundled = false
|
|
||||||
}
|
|
||||||
config.enableCorsForAllOrigins()
|
|
||||||
}.start(serverConfig.ip, serverConfig.port)
|
|
||||||
if (hasWebUiBundled && serverConfig.initialOpenInBrowserEnabled) {
|
|
||||||
openInBrowser()
|
|
||||||
}
|
|
||||||
|
|
||||||
app.exception(NullPointerException::class.java) { e, ctx ->
|
|
||||||
logger.error("NullPointerException while handling the request", e)
|
|
||||||
ctx.status(404)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get("/api/v1/extension/list") { ctx ->
|
|
||||||
ctx.json(getExtensionList())
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get("/api/v1/extension/install/:apkName") { ctx ->
|
|
||||||
val apkName = ctx.pathParam("apkName")
|
|
||||||
|
|
||||||
ctx.status(
|
|
||||||
installAPK(apkName)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get("/api/v1/extension/uninstall/:apkName") { ctx ->
|
|
||||||
val apkName = ctx.pathParam("apkName")
|
|
||||||
|
|
||||||
removeExtension(apkName)
|
|
||||||
ctx.status(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
// icon for extension named `apkName`
|
|
||||||
app.get("/api/v1/extension/icon/:apkName") { ctx ->
|
|
||||||
val apkName = ctx.pathParam("apkName")
|
|
||||||
val result = getExtensionIcon(apkName)
|
|
||||||
|
|
||||||
ctx.result(result.first)
|
|
||||||
ctx.header("content-type", result.second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// list of sources
|
|
||||||
app.get("/api/v1/source/list") { ctx ->
|
|
||||||
ctx.json(getSourceList())
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch source with id `sourceId`
|
|
||||||
app.get("/api/v1/source/:sourceId") { ctx ->
|
|
||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
|
||||||
ctx.json(getSource(sourceId))
|
|
||||||
}
|
|
||||||
|
|
||||||
// popular mangas from source with id `sourceId`
|
|
||||||
app.get("/api/v1/source/:sourceId/popular/:pageNum") { ctx ->
|
|
||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
|
||||||
val pageNum = ctx.pathParam("pageNum").toInt()
|
|
||||||
ctx.json(getMangaList(sourceId, pageNum, popular = true))
|
|
||||||
}
|
|
||||||
|
|
||||||
// latest mangas from source with id `sourceId`
|
|
||||||
app.get("/api/v1/source/:sourceId/latest/:pageNum") { ctx ->
|
|
||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
|
||||||
val pageNum = ctx.pathParam("pageNum").toInt()
|
|
||||||
ctx.json(getMangaList(sourceId, pageNum, popular = false))
|
|
||||||
}
|
|
||||||
|
|
||||||
// get manga info
|
|
||||||
app.get("/api/v1/manga/:mangaId/") { ctx ->
|
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
||||||
ctx.json(getManga(mangaId))
|
|
||||||
}
|
|
||||||
|
|
||||||
// manga thumbnail
|
|
||||||
app.get("api/v1/manga/:mangaId/thumbnail") { ctx ->
|
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
||||||
val result = getThumbnail(mangaId)
|
|
||||||
|
|
||||||
ctx.result(result.first)
|
|
||||||
ctx.header("content-type", result.second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// adds the manga to library
|
|
||||||
app.get("api/v1/manga/:mangaId/library") { ctx ->
|
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
||||||
addMangaToLibrary(mangaId)
|
|
||||||
ctx.status(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
// removes the manga from the library
|
|
||||||
app.delete("api/v1/manga/:mangaId/library") { ctx ->
|
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
||||||
removeMangaFromLibrary(mangaId)
|
|
||||||
ctx.status(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
// list manga's categories
|
|
||||||
app.get("api/v1/manga/:mangaId/category/") { ctx ->
|
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
||||||
ctx.json(getMangaCategories(mangaId))
|
|
||||||
}
|
|
||||||
|
|
||||||
// adds the manga to category
|
|
||||||
app.get("api/v1/manga/:mangaId/category/:categoryId") { ctx ->
|
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
||||||
val categoryId = ctx.pathParam("categoryId").toInt()
|
|
||||||
addMangaToCategory(mangaId, categoryId)
|
|
||||||
ctx.status(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
// removes the manga from the category
|
|
||||||
app.delete("api/v1/manga/:mangaId/category/:categoryId") { ctx ->
|
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
||||||
val categoryId = ctx.pathParam("categoryId").toInt()
|
|
||||||
removeMangaFromCategory(mangaId, categoryId)
|
|
||||||
ctx.status(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get("/api/v1/manga/:mangaId/chapters") { ctx ->
|
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
||||||
ctx.json(getChapterList(mangaId))
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex") { ctx ->
|
|
||||||
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
|
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
||||||
ctx.json(getChapter(chapterIndex, mangaId))
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex/page/:index") { ctx ->
|
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
||||||
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
|
|
||||||
val index = ctx.pathParam("index").toInt()
|
|
||||||
val result = getPageImage(mangaId, chapterIndex, index)
|
|
||||||
|
|
||||||
ctx.result(result.first)
|
|
||||||
ctx.header("content-type", result.second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// global search
|
|
||||||
app.get("/api/v1/search/:searchTerm") { ctx ->
|
|
||||||
val searchTerm = ctx.pathParam("searchTerm")
|
|
||||||
ctx.json(sourceGlobalSearch(searchTerm))
|
|
||||||
}
|
|
||||||
|
|
||||||
// single source search
|
|
||||||
app.get("/api/v1/source/:sourceId/search/:searchTerm/:pageNum") { ctx ->
|
|
||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
|
||||||
val searchTerm = ctx.pathParam("searchTerm")
|
|
||||||
val pageNum = ctx.pathParam("pageNum").toInt()
|
|
||||||
ctx.json(sourceSearch(sourceId, searchTerm, pageNum))
|
|
||||||
}
|
|
||||||
|
|
||||||
// source filter list
|
|
||||||
app.get("/api/v1/source/:sourceId/filters/") { ctx ->
|
|
||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
|
||||||
ctx.json(sourceFilters(sourceId))
|
|
||||||
}
|
|
||||||
|
|
||||||
// lists mangas that have no category assigned
|
|
||||||
app.get("/api/v1/library/") { ctx ->
|
|
||||||
ctx.json(getLibraryMangas())
|
|
||||||
}
|
|
||||||
|
|
||||||
// category list
|
|
||||||
app.get("/api/v1/category/") { ctx ->
|
|
||||||
ctx.json(getCategoryList())
|
|
||||||
}
|
|
||||||
|
|
||||||
// category create
|
|
||||||
app.post("/api/v1/category/") { ctx ->
|
|
||||||
val name = ctx.formParam("name")!!
|
|
||||||
createCategory(name)
|
|
||||||
ctx.status(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
// category modification
|
|
||||||
app.patch("/api/v1/category/:categoryId") { ctx ->
|
|
||||||
val categoryId = ctx.pathParam("categoryId").toInt()
|
|
||||||
val name = ctx.formParam("name")
|
|
||||||
val isLanding = if (ctx.formParam("isLanding") != null) ctx.formParam("isLanding")?.toBoolean() else null
|
|
||||||
updateCategory(categoryId, name, isLanding)
|
|
||||||
ctx.status(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
// category re-ordering
|
|
||||||
app.patch("/api/v1/category/:categoryId/reorder") { ctx ->
|
|
||||||
val categoryId = ctx.pathParam("categoryId").toInt()
|
|
||||||
val from = ctx.formParam("from")!!.toInt()
|
|
||||||
val to = ctx.formParam("to")!!.toInt()
|
|
||||||
reorderCategory(categoryId, from, to)
|
|
||||||
ctx.status(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
// category delete
|
|
||||||
app.delete("/api/v1/category/:categoryId") { ctx ->
|
|
||||||
val categoryId = ctx.pathParam("categoryId").toInt()
|
|
||||||
removeCategory(categoryId)
|
|
||||||
ctx.status(200)
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the manga list associated with a category
|
|
||||||
app.get("/api/v1/category/:categoryId") { ctx ->
|
|
||||||
val categoryId = ctx.pathParam("categoryId").toInt()
|
|
||||||
ctx.json(getCategoryMangaList(categoryId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package ir.armor.tachidesk.server
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
|
||||||
import io.github.config4k.getValue
|
|
||||||
import xyz.nulldev.ts.config.ConfigModule
|
|
||||||
|
|
||||||
class ServerConfig(config: Config) : ConfigModule(config) {
|
|
||||||
val ip: String by config
|
|
||||||
val port: Int by config
|
|
||||||
|
|
||||||
// proxy
|
|
||||||
val socksProxy: Boolean by config
|
|
||||||
val socksProxyHost: String by config
|
|
||||||
val socksProxyPort: String by config
|
|
||||||
|
|
||||||
// misc
|
|
||||||
val debugLogsEnabled: Boolean by config
|
|
||||||
val systemTrayEnabled: Boolean by config
|
|
||||||
val initialOpenInBrowserEnabled: Boolean by config
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun register(config: Config) = ServerConfig(config.getConfig("server"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,4 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
import ir.armor.tachidesk.database.dataclass.CategoryDataClass
|
import ir.armor.tachidesk.database.dataclass.CategoryDataClass
|
||||||
import ir.armor.tachidesk.database.table.CategoryMangaTable
|
import ir.armor.tachidesk.database.table.CategoryMangaTable
|
||||||
@@ -19,6 +12,10 @@ import org.jetbrains.exposed.sql.selectAll
|
|||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import org.jetbrains.exposed.sql.update
|
import org.jetbrains.exposed.sql.update
|
||||||
|
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
fun createCategory(name: String) {
|
fun createCategory(name: String) {
|
||||||
transaction {
|
transaction {
|
||||||
val count = CategoryTable.selectAll().count()
|
val count = CategoryTable.selectAll().count()
|
||||||
@@ -1,11 +1,4 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
import ir.armor.tachidesk.database.dataclass.CategoryDataClass
|
import ir.armor.tachidesk.database.dataclass.CategoryDataClass
|
||||||
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||||
@@ -21,6 +14,10 @@ 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
|
||||||
|
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
fun addMangaToCategory(mangaId: Int, categoryId: Int) {
|
fun addMangaToCategory(mangaId: Int, categoryId: Int) {
|
||||||
transaction {
|
transaction {
|
||||||
if (CategoryMangaTable.select { (CategoryMangaTable.category eq categoryId) and (CategoryMangaTable.manga eq mangaId) }.firstOrNull() == null) {
|
if (CategoryMangaTable.select { (CategoryMangaTable.category eq categoryId) and (CategoryMangaTable.manga eq mangaId) }.firstOrNull() == null) {
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -126,13 +123,6 @@ fun getChapter(chapterIndex: Int, mangaId: Int): ChapterDataClass {
|
|||||||
it[this.chapter] = chapterId
|
it[this.chapter] = chapterId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
transaction {
|
|
||||||
PageTable.update({ (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }) {
|
|
||||||
it[url] = page.url
|
|
||||||
it[imageUrl] = page.imageUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,26 +1,20 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import com.googlecode.d2j.dex.Dex2jar
|
import com.googlecode.dex2jar.tools.Dex2jarCmd
|
||||||
import com.googlecode.d2j.reader.MultiDexFileReader
|
|
||||||
import com.googlecode.dex2jar.tools.BaksmaliBaseDexExceptionHandler
|
|
||||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.source.SourceFactory
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import ir.armor.tachidesk.APKExtractor
|
||||||
|
import ir.armor.tachidesk.applicationDirs
|
||||||
import ir.armor.tachidesk.database.table.ExtensionTable
|
import ir.armor.tachidesk.database.table.ExtensionTable
|
||||||
import ir.armor.tachidesk.database.table.SourceTable
|
import ir.armor.tachidesk.database.table.SourceTable
|
||||||
import ir.armor.tachidesk.impl.util.APKExtractor
|
|
||||||
import ir.armor.tachidesk.server.applicationDirs
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import mu.KotlinLogging
|
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.sink
|
import okio.sink
|
||||||
@@ -34,45 +28,8 @@ import java.io.File
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
private val logger = KotlinLogging.logger {}
|
|
||||||
|
|
||||||
private fun dex2jar(dexFile: String, jarFile: String, fileNameWithoutType: String) {
|
|
||||||
// adopted from com.googlecode.dex2jar.tools.Dex2jarCmd.doCommandLine
|
|
||||||
// source at: https://github.com/DexPatcher/dex2jar/tree/v2.1-20190905-lanchon/dex-tools/src/main/java/com/googlecode/dex2jar/tools/Dex2jarCmd.java
|
|
||||||
|
|
||||||
val jarFilePath = File(jarFile).toPath()
|
|
||||||
val reader = MultiDexFileReader.open(Files.readAllBytes(File(dexFile).toPath()))
|
|
||||||
val handler = BaksmaliBaseDexExceptionHandler()
|
|
||||||
Dex2jar
|
|
||||||
.from(reader)
|
|
||||||
.withExceptionHandler(handler)
|
|
||||||
.reUseReg(false)
|
|
||||||
.topoLogicalSort()
|
|
||||||
.skipDebug(true)
|
|
||||||
.optimizeSynchronized(false)
|
|
||||||
.printIR(false)
|
|
||||||
.noCode(false)
|
|
||||||
.skipExceptions(false)
|
|
||||||
.to(jarFilePath)
|
|
||||||
if (handler.hasException()) {
|
|
||||||
val errorFile: Path = File(applicationDirs.extensionsRoot).toPath().resolve("$fileNameWithoutType-error.txt")
|
|
||||||
logger.error(
|
|
||||||
"Detail Error Information in File $errorFile\n" +
|
|
||||||
"Please report this file to one of following link if possible (any one).\n" +
|
|
||||||
" https://sourceforge.net/p/dex2jar/tickets/\n" +
|
|
||||||
" https://bitbucket.org/pxb1988/dex2jar/issues\n" +
|
|
||||||
" https://github.com/pxb1988/dex2jar/issues\n" +
|
|
||||||
" dex2jar@googlegroups.com"
|
|
||||||
)
|
|
||||||
handler.dump(errorFile, emptyArray<String>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun installAPK(apkName: String): Int {
|
fun installAPK(apkName: String): Int {
|
||||||
logger.debug("Installing $apkName")
|
|
||||||
val extensionRecord = getExtensionList(true).first { it.apkName == apkName }
|
val extensionRecord = getExtensionList(true).first { it.apkName == apkName }
|
||||||
val fileNameWithoutType = apkName.substringBefore(".apk")
|
val fileNameWithoutType = apkName.substringBefore(".apk")
|
||||||
val dirPathWithoutType = "${applicationDirs.extensionsRoot}/$fileNameWithoutType"
|
val dirPathWithoutType = "${applicationDirs.extensionsRoot}/$fileNameWithoutType"
|
||||||
@@ -92,9 +49,9 @@ fun installAPK(apkName: String): Int {
|
|||||||
downloadAPKFile(apkToDownload, apkFilePath)
|
downloadAPKFile(apkToDownload, apkFilePath)
|
||||||
|
|
||||||
val className: String = APKExtractor.extract_dex_and_read_className(apkFilePath, dexFilePath)
|
val className: String = APKExtractor.extract_dex_and_read_className(apkFilePath, dexFilePath)
|
||||||
logger.debug(className)
|
println(className)
|
||||||
// dex -> jar
|
// dex -> jar
|
||||||
dex2jar(dexFilePath, jarFilePath, fileNameWithoutType)
|
Dex2jarCmd.main(dexFilePath, "-o", jarFilePath, "--force")
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
File(apkFilePath).delete()
|
File(apkFilePath).delete()
|
||||||
@@ -112,6 +69,11 @@ fun installAPK(apkName: String): Int {
|
|||||||
if (instance is HttpSource) { // single source
|
if (instance is HttpSource) { // single source
|
||||||
val httpSource = instance as HttpSource
|
val httpSource = instance as HttpSource
|
||||||
transaction {
|
transaction {
|
||||||
|
// SourceEntity.new {
|
||||||
|
// sourceId = httpSource.id
|
||||||
|
// name = httpSource.name
|
||||||
|
// this.extension = ExtensionEntity.find { ExtensionsTable.name eq extension.name }.first().id
|
||||||
|
// }
|
||||||
if (SourceTable.select { SourceTable.id eq httpSource.id }.count() == 0L) {
|
if (SourceTable.select { SourceTable.id eq httpSource.id }.count() == 0L) {
|
||||||
SourceTable.insert {
|
SourceTable.insert {
|
||||||
it[this.id] = httpSource.id
|
it[this.id] = httpSource.id
|
||||||
@@ -120,7 +82,9 @@ fun installAPK(apkName: String): Int {
|
|||||||
it[extension] = extensionId
|
it[extension] = extensionId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.debug("Installed source ${httpSource.name} with id ${httpSource.id}")
|
// println(httpSource.id)
|
||||||
|
// println(httpSource.name)
|
||||||
|
// println()
|
||||||
}
|
}
|
||||||
} else { // multi source
|
} else { // multi source
|
||||||
val sourceFactory = instance as SourceFactory
|
val sourceFactory = instance as SourceFactory
|
||||||
@@ -137,7 +101,9 @@ fun installAPK(apkName: String): Int {
|
|||||||
it[positionInFactorySource] = index
|
it[positionInFactorySource] = index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.debug("Installed source ${httpSource.name} with id:${httpSource.id}")
|
// println(httpSource.id)
|
||||||
|
// println(httpSource.name)
|
||||||
|
// println()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,11 +134,9 @@ private fun downloadAPKFile(url: String, apkPath: String) {
|
|||||||
sink.close()
|
sink.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeExtension(apkName: String) {
|
fun removeExtension(pkgName: String) {
|
||||||
logger.debug("Uninstalling $apkName")
|
val extensionRecord = getExtensionList(true).first { it.apkName == pkgName }
|
||||||
|
val fileNameWithoutType = pkgName.substringBefore(".apk")
|
||||||
val extensionRecord = getExtensionList(true).first { it.apkName == apkName }
|
|
||||||
val fileNameWithoutType = apkName.substringBefore(".apk")
|
|
||||||
val jarPath = "${applicationDirs.extensionsRoot}/$fileNameWithoutType.jar"
|
val jarPath = "${applicationDirs.extensionsRoot}/$fileNameWithoutType.jar"
|
||||||
transaction {
|
transaction {
|
||||||
val extensionId = ExtensionTable.select { ExtensionTable.name eq extensionRecord.name }.first()[ExtensionTable.id]
|
val extensionId = ExtensionTable.select { ExtensionTable.name eq extensionRecord.name }.first()[ExtensionTable.id]
|
||||||
@@ -194,8 +158,9 @@ fun getExtensionIcon(apkName: String): Pair<InputStream, String> {
|
|||||||
val iconUrl = transaction { ExtensionTable.select { ExtensionTable.apkName eq apkName }.firstOrNull()!! }[ExtensionTable.iconUrl]
|
val iconUrl = transaction { ExtensionTable.select { ExtensionTable.apkName eq apkName }.firstOrNull()!! }[ExtensionTable.iconUrl]
|
||||||
|
|
||||||
val saveDir = "${applicationDirs.extensionsRoot}/icon"
|
val saveDir = "${applicationDirs.extensionsRoot}/icon"
|
||||||
|
val fileName = apkName
|
||||||
|
|
||||||
return getCachedImageResponse(saveDir, apkName) {
|
return getCachedResponse(saveDir, fileName) {
|
||||||
network.client.newCall(
|
network.client.newCall(
|
||||||
GET(iconUrl)
|
GET(iconUrl)
|
||||||
).execute()
|
).execute()
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -12,15 +9,12 @@ import eu.kanade.tachiyomi.extension.model.Extension
|
|||||||
import ir.armor.tachidesk.database.dataclass.ExtensionDataClass
|
import ir.armor.tachidesk.database.dataclass.ExtensionDataClass
|
||||||
import ir.armor.tachidesk.database.table.ExtensionTable
|
import ir.armor.tachidesk.database.table.ExtensionTable
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import mu.KotlinLogging
|
|
||||||
import org.jetbrains.exposed.sql.insert
|
import org.jetbrains.exposed.sql.insert
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import org.jetbrains.exposed.sql.update
|
import org.jetbrains.exposed.sql.update
|
||||||
|
|
||||||
private val logger = KotlinLogging.logger {}
|
|
||||||
|
|
||||||
private object Data {
|
private object Data {
|
||||||
var lastExtensionCheck: Long = 0
|
var lastExtensionCheck: Long = 0
|
||||||
}
|
}
|
||||||
@@ -34,7 +28,7 @@ private fun extensionDatabaseIsEmtpy(): Boolean {
|
|||||||
fun getExtensionList(offline: Boolean = false): List<ExtensionDataClass> {
|
fun getExtensionList(offline: Boolean = false): List<ExtensionDataClass> {
|
||||||
// update if 60 seconds has passed or requested offline and database is empty
|
// update if 60 seconds has passed or requested offline and database is empty
|
||||||
if (Data.lastExtensionCheck + 60 * 1000 < System.currentTimeMillis() || (offline && extensionDatabaseIsEmtpy())) {
|
if (Data.lastExtensionCheck + 60 * 1000 < System.currentTimeMillis() || (offline && extensionDatabaseIsEmtpy())) {
|
||||||
logger.debug("Getting extensions list from the internet")
|
println("Getting extensions list from the internet")
|
||||||
Data.lastExtensionCheck = System.currentTimeMillis()
|
Data.lastExtensionCheck = System.currentTimeMillis()
|
||||||
var foundExtensions: List<Extension.Available>
|
var foundExtensions: List<Extension.Available>
|
||||||
runBlocking {
|
runBlocking {
|
||||||
@@ -72,7 +66,7 @@ fun getExtensionList(offline: Boolean = false): List<ExtensionDataClass> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.debug("used cached extension list")
|
println("used cached extension list")
|
||||||
}
|
}
|
||||||
|
|
||||||
return transaction {
|
return transaction {
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -55,7 +52,7 @@ private fun BufferedSource.saveTo(stream: OutputStream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCachedImageResponse(saveDir: String, fileName: String, fetcher: () -> Response): Pair<InputStream, String> {
|
fun getCachedResponse(saveDir: String, fileName: String, fetcher: () -> Response): Pair<InputStream, String> {
|
||||||
val cachedFile = findFileNameStartingWith(saveDir, fileName)
|
val cachedFile = findFileNameStartingWith(saveDir, fileName)
|
||||||
val filePath = "$saveDir/$fileName"
|
val filePath = "$saveDir/$fileName"
|
||||||
if (cachedFile != null) {
|
if (cachedFile != null) {
|
||||||
@@ -1,22 +1,20 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||||
import ir.armor.tachidesk.database.table.CategoryMangaTable
|
import ir.armor.tachidesk.database.table.CategoryMangaTable
|
||||||
import ir.armor.tachidesk.database.table.MangaTable
|
import ir.armor.tachidesk.database.table.MangaTable
|
||||||
import ir.armor.tachidesk.database.table.toDataClass
|
import ir.armor.tachidesk.database.table.toDataClass
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.deleteWhere
|
import org.jetbrains.exposed.sql.deleteWhere
|
||||||
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
|
||||||
|
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
fun addMangaToLibrary(mangaId: Int) {
|
fun addMangaToLibrary(mangaId: Int) {
|
||||||
val manga = getManga(mangaId)
|
val manga = getManga(mangaId)
|
||||||
if (!manga.inLibrary) {
|
if (!manga.inLibrary) {
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import ir.armor.tachidesk.applicationDirs
|
||||||
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||||
import ir.armor.tachidesk.database.table.MangaStatus
|
import ir.armor.tachidesk.database.table.MangaStatus
|
||||||
import ir.armor.tachidesk.database.table.MangaTable
|
import ir.armor.tachidesk.database.table.MangaTable
|
||||||
import ir.armor.tachidesk.server.applicationDirs
|
|
||||||
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
|
||||||
@@ -93,7 +90,7 @@ fun getThumbnail(mangaId: Int): Pair<InputStream, String> {
|
|||||||
val saveDir = applicationDirs.thumbnailsRoot
|
val saveDir = applicationDirs.thumbnailsRoot
|
||||||
val fileName = mangaId.toString()
|
val fileName = mangaId.toString()
|
||||||
|
|
||||||
return getCachedImageResponse(saveDir, fileName) {
|
return getCachedResponse(saveDir, fileName) {
|
||||||
val sourceId = mangaEntry[MangaTable.sourceReference]
|
val sourceId = mangaEntry[MangaTable.sourceReference]
|
||||||
val source = getHttpSource(sourceId)
|
val source = getHttpSource(sourceId)
|
||||||
var thumbnailUrl = mangaEntry[MangaTable.thumbnail_url]
|
var thumbnailUrl = mangaEntry[MangaTable.thumbnail_url]
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import ir.armor.tachidesk.applicationDirs
|
||||||
import ir.armor.tachidesk.database.table.ChapterTable
|
import ir.armor.tachidesk.database.table.ChapterTable
|
||||||
import ir.armor.tachidesk.database.table.MangaTable
|
import ir.armor.tachidesk.database.table.MangaTable
|
||||||
import ir.armor.tachidesk.database.table.PageTable
|
import ir.armor.tachidesk.database.table.PageTable
|
||||||
import ir.armor.tachidesk.database.table.SourceTable
|
import ir.armor.tachidesk.database.table.SourceTable
|
||||||
import ir.armor.tachidesk.server.applicationDirs
|
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
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
|
||||||
@@ -28,16 +25,10 @@ fun getTrueImageUrl(page: Page, source: HttpSource): String {
|
|||||||
return page.imageUrl!!
|
return page.imageUrl!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPageImage(mangaId: Int, chapterIndex: Int, index: Int): Pair<InputStream, String> {
|
fun getPageImage(mangaId: Int, chapterId: Int, index: Int): Pair<InputStream, String> {
|
||||||
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
|
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
|
||||||
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
|
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
|
||||||
val chapterEntry = transaction {
|
val chapterEntry = transaction { ChapterTable.select { ChapterTable.id eq chapterId }.firstOrNull()!! }
|
||||||
ChapterTable.select {
|
|
||||||
(ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId)
|
|
||||||
}.firstOrNull()!!
|
|
||||||
}
|
|
||||||
val chapterId = chapterEntry[ChapterTable.id].value
|
|
||||||
|
|
||||||
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq index) }.firstOrNull()!! }
|
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq index) }.firstOrNull()!! }
|
||||||
|
|
||||||
val tachiPage = Page(
|
val tachiPage = Page(
|
||||||
@@ -58,7 +49,7 @@ fun getPageImage(mangaId: Int, chapterIndex: Int, index: Int): Pair<InputStream,
|
|||||||
File(saveDir).mkdirs()
|
File(saveDir).mkdirs()
|
||||||
val fileName = index.toString()
|
val fileName = index.toString()
|
||||||
|
|
||||||
return getCachedImageResponse(saveDir, fileName) {
|
return getCachedResponse(saveDir, fileName) {
|
||||||
source.fetchImage(tachiPage).toBlocking().first()
|
source.fetchImage(tachiPage).toBlocking().first()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -1,21 +1,17 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.SourceFactory
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import ir.armor.tachidesk.applicationDirs
|
||||||
import ir.armor.tachidesk.database.dataclass.SourceDataClass
|
import ir.armor.tachidesk.database.dataclass.SourceDataClass
|
||||||
import ir.armor.tachidesk.database.entity.ExtensionEntity
|
import ir.armor.tachidesk.database.entity.ExtensionEntity
|
||||||
import ir.armor.tachidesk.database.entity.SourceEntity
|
import ir.armor.tachidesk.database.entity.SourceEntity
|
||||||
import ir.armor.tachidesk.database.table.ExtensionTable
|
import ir.armor.tachidesk.database.table.ExtensionTable
|
||||||
import ir.armor.tachidesk.database.table.SourceTable
|
import ir.armor.tachidesk.database.table.SourceTable
|
||||||
import ir.armor.tachidesk.server.applicationDirs
|
|
||||||
import mu.KotlinLogging
|
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
@@ -23,8 +19,6 @@ import java.lang.NullPointerException
|
|||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
|
|
||||||
private val logger = KotlinLogging.logger {}
|
|
||||||
|
|
||||||
private val sourceCache = mutableListOf<Pair<Long, HttpSource>>()
|
private val sourceCache = mutableListOf<Pair<Long, HttpSource>>()
|
||||||
private val extensionCache = mutableListOf<Pair<String, Any>>()
|
private val extensionCache = mutableListOf<Pair<String, Any>>()
|
||||||
|
|
||||||
@@ -35,7 +29,7 @@ fun getHttpSource(sourceId: Long): HttpSource {
|
|||||||
|
|
||||||
val cachedResult: Pair<Long, HttpSource>? = sourceCache.firstOrNull { it.first == sourceId }
|
val cachedResult: Pair<Long, HttpSource>? = sourceCache.firstOrNull { it.first == sourceId }
|
||||||
if (cachedResult != null) {
|
if (cachedResult != null) {
|
||||||
logger.debug("used cached HttpSource: ${cachedResult.second.name}")
|
println("used cached HttpSource: ${cachedResult.second.name}")
|
||||||
return cachedResult.second
|
return cachedResult.second
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,15 +41,17 @@ fun getHttpSource(sourceId: Long): HttpSource {
|
|||||||
val jarName = apkName.substringBefore(".apk") + ".jar"
|
val jarName = apkName.substringBefore(".apk") + ".jar"
|
||||||
val jarPath = "${applicationDirs.extensionsRoot}/$jarName"
|
val jarPath = "${applicationDirs.extensionsRoot}/$jarName"
|
||||||
|
|
||||||
|
println(jarName)
|
||||||
|
|
||||||
val cachedExtensionPair = extensionCache.firstOrNull { it.first == jarPath }
|
val cachedExtensionPair = extensionCache.firstOrNull { it.first == jarPath }
|
||||||
var usedCached = false
|
var usedCached = false
|
||||||
val instance =
|
val instance =
|
||||||
if (cachedExtensionPair != null) {
|
if (cachedExtensionPair != null) {
|
||||||
usedCached = true
|
usedCached = true
|
||||||
logger.debug("Used cached Extension")
|
println("Used cached Extension")
|
||||||
cachedExtensionPair.second
|
cachedExtensionPair.second
|
||||||
} else {
|
} else {
|
||||||
logger.debug("No Extension cache")
|
println("No Extension cache")
|
||||||
val child = URLClassLoader(arrayOf<URL>(URL("file:$jarPath")), this::class.java.classLoader)
|
val child = URLClassLoader(arrayOf<URL>(URL("file:$jarPath")), this::class.java.classLoader)
|
||||||
val classToLoad = Class.forName(className, true, child)
|
val classToLoad = Class.forName(className, true, child)
|
||||||
classToLoad.newInstance()
|
classToLoad.newInstance()
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package ir.armor.tachidesk.server.util
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -13,7 +10,6 @@ import dorkbox.systemTray.SystemTray.TrayType
|
|||||||
import dorkbox.util.CacheUtil
|
import dorkbox.util.CacheUtil
|
||||||
import dorkbox.util.Desktop
|
import dorkbox.util.Desktop
|
||||||
import ir.armor.tachidesk.Main
|
import ir.armor.tachidesk.Main
|
||||||
import ir.armor.tachidesk.server.serverConfig
|
|
||||||
import java.awt.event.ActionListener
|
import java.awt.event.ActionListener
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@@ -28,7 +24,7 @@ fun openInBrowser() {
|
|||||||
fun systemTray(): SystemTray? {
|
fun systemTray(): SystemTray? {
|
||||||
try {
|
try {
|
||||||
// ref: https://github.com/dorkbox/SystemTray/blob/master/test/dorkbox/TestTray.java
|
// ref: https://github.com/dorkbox/SystemTray/blob/master/test/dorkbox/TestTray.java
|
||||||
SystemTray.DEBUG = serverConfig.debugLogsEnabled
|
SystemTray.DEBUG = true; // for test apps, we always want to run in debug mode
|
||||||
if (System.getProperty("os.name").startsWith("Windows"))
|
if (System.getProperty("os.name").startsWith("Windows"))
|
||||||
SystemTray.FORCE_TRAY_TYPE = TrayType.Swing
|
SystemTray.FORCE_TRAY_TYPE = TrayType.Swing
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<configuration>
|
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<!-- encoders are assigned the type
|
|
||||||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %logger - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<logger name="Exposed" level="ERROR"/>
|
|
||||||
|
|
||||||
<root level="INFO">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</root>
|
|
||||||
</configuration>
|
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
# Server ip and port bindings
|
# Server ip and port bindings
|
||||||
server.ip = "0.0.0.0"
|
server.ip = 0.0.0.0
|
||||||
server.port = 4567
|
server.port = 4567
|
||||||
|
|
||||||
# Socks5 proxy
|
# Socks5 proxy
|
||||||
server.socksProxy = false
|
server.socksProxy = false
|
||||||
server.socksProxyHost = ""
|
server.socksProxyHost = ""
|
||||||
server.socksProxyPort = ""
|
server.socksProxyPort = ""
|
||||||
|
|
||||||
# misc
|
|
||||||
server.debugLogsEnabled = false
|
|
||||||
server.systemTrayEnabled = true
|
|
||||||
server.initialOpenInBrowserEnabled = true
|
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
/* eslint-disable react/jsx-props-no-spreading */
|
|
||||||
/* eslint-disable react/require-default-props */
|
|
||||||
/*
|
|
||||||
* 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 React from 'react';
|
|
||||||
import { makeStyles } from '@material-ui/core/styles';
|
|
||||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
|
||||||
loading: {
|
|
||||||
margin: '10px auto',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
shouldRender: boolean | (() => boolean)
|
|
||||||
children?: React.ReactNode
|
|
||||||
component?: string | React.FunctionComponent<any> | React.ComponentClass<any, any>
|
|
||||||
componentProps?: any
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function LoadingPlaceholder(props: IProps) {
|
|
||||||
const {
|
|
||||||
children, shouldRender, component, componentProps,
|
|
||||||
} = props;
|
|
||||||
const classes = useStyles();
|
|
||||||
|
|
||||||
const condition = shouldRender instanceof Function ? shouldRender() : shouldRender;
|
|
||||||
|
|
||||||
if (condition) {
|
|
||||||
if (component) {
|
|
||||||
return React.createElement(component, componentProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (children) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classes.loading}>
|
|
||||||
<CircularProgress thickness={5} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -22,13 +19,8 @@ const useStyles = (inLibrary: string) => makeStyles((theme: Theme) => ({
|
|||||||
root: {
|
root: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
[theme.breakpoints.up('md')]: {
|
[theme.breakpoints.up('md')]: {
|
||||||
position: 'sticky',
|
position: 'fixed',
|
||||||
top: '64px',
|
|
||||||
left: '0px',
|
|
||||||
width: '50vw',
|
width: '50vw',
|
||||||
height: 'calc(100vh - 64px)',
|
|
||||||
alignSelf: 'flex-start',
|
|
||||||
overflowY: 'auto',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
top: {
|
top: {
|
||||||
@@ -136,7 +128,7 @@ export default function MangaDetails(props: IProps) {
|
|||||||
|
|
||||||
const { manga } = props;
|
const { manga } = props;
|
||||||
const [inLibrary, setInLibrary] = useState<string>(
|
const [inLibrary, setInLibrary] = useState<string>(
|
||||||
manga.inLibrary ? 'In Library' : 'Add To Library',
|
manga.inLibrary ? 'In Library' : 'Add to Library',
|
||||||
);
|
);
|
||||||
|
|
||||||
const [categoryDialogOpen, setCategoryDialogOpen] = useState<boolean>(false);
|
const [categoryDialogOpen, setCategoryDialogOpen] = useState<boolean>(false);
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
/*
|
// TODO: remove above!
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
*
|
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
/* eslint-disable react/no-unused-prop-types */
|
/* eslint-disable react/no-unused-prop-types */
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -25,10 +22,6 @@ const useStyles = (settings: IReaderSettings) => makeStyles({
|
|||||||
backgroundColor: '#525252',
|
backgroundColor: '#525252',
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
},
|
},
|
||||||
image: {
|
|
||||||
display: 'block',
|
|
||||||
marginBottom: settings.continuesPageGap ? '15px' : 0,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@@ -81,7 +74,6 @@ function LazyImage(props: IProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
className={classes.image}
|
|
||||||
ref={ref}
|
ref={ref}
|
||||||
src={imageSrc}
|
src={imageSrc}
|
||||||
alt={`Page #${index}`}
|
alt={`Page #${index}`}
|
||||||
@@ -105,7 +97,6 @@ export default function Page(props: IProps) {
|
|||||||
<CircularProgress thickness={5} />
|
<CircularProgress thickness={5} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
once
|
|
||||||
>
|
>
|
||||||
<LazyImage
|
<LazyImage
|
||||||
src={src}
|
src={src}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -140,13 +137,11 @@ const useStyles = (settings: IReaderSettings) => makeStyles((theme: Theme) => ({
|
|||||||
export interface IReaderSettings{
|
export interface IReaderSettings{
|
||||||
staticNav: boolean
|
staticNav: boolean
|
||||||
showPageNumber: boolean
|
showPageNumber: boolean
|
||||||
continuesPageGap: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultReaderSettings = () => ({
|
export const defaultReaderSettings = () => ({
|
||||||
staticNav: false,
|
staticNav: false,
|
||||||
showPageNumber: true,
|
showPageNumber: true,
|
||||||
continuesPageGap: false,
|
|
||||||
} as IReaderSettings);
|
} as IReaderSettings);
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@@ -281,16 +276,6 @@ export default function ReaderNavBar(props: IProps) {
|
|||||||
/>
|
/>
|
||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem>
|
|
||||||
<ListItemText primary="Continues Page gap" />
|
|
||||||
<ListItemSecondaryAction>
|
|
||||||
<Switch
|
|
||||||
edge="end"
|
|
||||||
checked={settings.continuesPageGap}
|
|
||||||
onChange={(e) => setSettingValue('continuesPageGap', e.target.checked)}
|
|
||||||
/>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
<hr />
|
<hr />
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
5
webUI/react/src/react-app-env.d.ts
vendored
5
webUI/react/src/react-app-env.d.ts
vendored
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
@@ -14,7 +11,6 @@ import ChapterCard from '../components/ChapterCard';
|
|||||||
import MangaDetails from '../components/MangaDetails';
|
import MangaDetails from '../components/MangaDetails';
|
||||||
import NavbarContext from '../context/NavbarContext';
|
import NavbarContext from '../context/NavbarContext';
|
||||||
import client from '../util/client';
|
import client from '../util/client';
|
||||||
import LoadingPlaceholder from '../components/LoadingPlaceholder';
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
root: {
|
root: {
|
||||||
@@ -27,10 +23,8 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||||||
listStyle: 'none',
|
listStyle: 'none',
|
||||||
padding: 0,
|
padding: 0,
|
||||||
[theme.breakpoints.up('md')]: {
|
[theme.breakpoints.up('md')]: {
|
||||||
width: '50vw',
|
width: '50%',
|
||||||
height: 'calc(100vh - 64px)',
|
marginLeft: '50%',
|
||||||
overflowY: 'auto',
|
|
||||||
margin: 0,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -68,23 +62,20 @@ export default function Manga() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const chapterCards = (
|
const chapterCards = (
|
||||||
<LoadingPlaceholder
|
<ol className={classes.chapters}>
|
||||||
shouldRender={chapters.length > 0}
|
{chapters.length === 0
|
||||||
>
|
&& (
|
||||||
<ol className={classes.chapters}>
|
<div className={classes.loading}>
|
||||||
{chapters.map((chapter) => (<ChapterCard chapter={chapter} />))}
|
<CircularProgress thickness={5} />
|
||||||
</ol>
|
</div>
|
||||||
</LoadingPlaceholder>
|
) }
|
||||||
|
{chapters.map((chapter) => (<ChapterCard chapter={chapter} />))}
|
||||||
|
</ol>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<LoadingPlaceholder
|
{manga && <MangaDetails manga={manga} />}
|
||||||
shouldRender={manga !== undefined}
|
|
||||||
component={MangaDetails}
|
|
||||||
componentProps={{ manga }}
|
|
||||||
/>
|
|
||||||
{chapterCards}
|
{chapterCards}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user