mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-02 10:24:35 -05:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cd93d467c | ||
|
|
257f8a5a27 | ||
|
|
79bab08cae | ||
|
|
4e699e4f5a | ||
|
|
1128f40bac | ||
|
|
53ef836326 | ||
|
|
b8df0e89e5 | ||
|
|
472bfec6bf | ||
|
|
c1b86cedd2 | ||
|
|
428c65f075 |
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -34,10 +34,10 @@ Note that the issue will be automatically closed if you do not fill out the titl
|
||||
2. Second Step
|
||||
|
||||
### Expected behavior
|
||||
Describe what should have happened
|
||||
Describe what should have happened. Remove this line after you are done.
|
||||
|
||||
### Actual behavior
|
||||
Describe what happens instead
|
||||
Describe what happens instead. Remove this line after you are done.
|
||||
|
||||
## Other details
|
||||
Describe additional details If necessary
|
||||
Describe additional details If necessary. Remove this line after you are done.
|
||||
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -23,7 +23,7 @@ Note that the issue will be automatically closed if you do not fill out the titl
|
||||
---
|
||||
|
||||
## What feature should be added to Tachidesk?
|
||||
Explain What the feature is and how it should work in detail
|
||||
Explain What the feature is and how it should work in detail. Remove this line after you are done.
|
||||
|
||||
## Why/Project's Benefit/Existing Problem
|
||||
Explain why this should be added
|
||||
Explain why this should be added. Remove this line after you are done.
|
||||
5
.github/workflows/issue_closer.yml
vendored
5
.github/workflows/issue_closer.yml
vendored
@@ -28,5 +28,10 @@ jobs:
|
||||
"type": "body",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"type": "body",
|
||||
"regex": ".*Remove this line after you are done.*",
|
||||
"message": "The lines requesting to be removed were not removed."
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# foolproof against running from AndroidCompat dir instead of running from project root
|
||||
if [ "$(basename $(pwd))" = "AndroidCompat" ]; then
|
||||
cd ..
|
||||
fi
|
||||
|
||||
|
||||
echo "Getting required Android.jar..."
|
||||
rm -rf "tmp"
|
||||
mkdir -p "tmp"
|
||||
21
README.md
21
README.md
@@ -21,19 +21,18 @@ Here is a list of current features:
|
||||
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
|
||||
#### Prerequisites
|
||||
You should have The Java Runtime Environment(JRE) 8 or newer (if you're not planning to use the Windows specific build) and a modern browser installed. Also an internet connection is required as almost everything this app does is downloading stuff.
|
||||
|
||||
#### Download the app
|
||||
### Downloading the app
|
||||
Download the latest jar or windows(win32) release from [the releases section](https://github.com/AriaMoradi/Tachidesk/releases).
|
||||
|
||||
#### Running pre-built jar packages
|
||||
### 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.
|
||||
|
||||
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.
|
||||
|
||||
#### Running pre-built Windows packages
|
||||
Windows specific builds have java bundled inside them, 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 will work like the jar release.
|
||||
### Windows only
|
||||
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`.
|
||||
|
||||
#### Running on Docker
|
||||
### Running on Docker
|
||||
Check [arbuilder's repo](https://github.com/arbuilder/Tachidesk-docker) out for more details and the dockerfile.
|
||||
|
||||
## General troubleshooting
|
||||
@@ -43,7 +42,7 @@ On Mac OS X : `/Users/<Account>/Library/Application Support/Tachidesk`
|
||||
|
||||
On Windows XP : `C:\Documents and Settings\<Account>\Application Data\Local Settings\Tachidesk`
|
||||
|
||||
On Windows 7 and later : `C:\Users\<Account>\AppData\Tachidesk`
|
||||
On Windows 7 and later : `C:\Users\<Account>\AppData\Local\Tachidesk`
|
||||
|
||||
On Unix/Linux : `/home/<account>/.local/share/Tachidesk`
|
||||
|
||||
@@ -60,7 +59,7 @@ This project has two components:
|
||||
#### Manual download
|
||||
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)
|
||||
Run `scripts/getAndroid.sh` 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.
|
||||
### building the jar
|
||||
Run `./gradlew shadowJar`, the resulting built jar file will be `server/build/Tachidesk-vX.Y.Z-rxxx.jar`.
|
||||
### building the Windows package
|
||||
@@ -76,7 +75,7 @@ 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.
|
||||
|
||||
## Credit
|
||||
The `AndroidCompat` module and `scripts/getAndroid.sh` 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`.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ plugins {
|
||||
id("edu.sc.seis.launch4j") version "2.4.9"
|
||||
}
|
||||
|
||||
val TachideskVersion = "v0.2.4"
|
||||
val TachideskVersion = "v0.2.5"
|
||||
|
||||
|
||||
repositories {
|
||||
@@ -63,7 +63,7 @@ dependencies {
|
||||
val coroutinesVersion = "1.3.9"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
|
||||
|
||||
// dex2jar
|
||||
// dex2jar: https://github.com/DexPatcher/dex2jar/releases/tag/v2.1-20190905-lanchon
|
||||
implementation(fileTree("lib/dex2jar/"))
|
||||
|
||||
// api
|
||||
@@ -88,8 +88,8 @@ dependencies {
|
||||
implementation(project(":AndroidCompat:Config"))
|
||||
|
||||
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
|
||||
// testImplementation("org.jetbrains.kotlin:kotlin-test")
|
||||
// testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
|
||||
}
|
||||
|
||||
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
@@ -58,6 +58,10 @@ class Main {
|
||||
openInBrowser()
|
||||
}
|
||||
|
||||
app.exception(NullPointerException::class.java) { _, ctx ->
|
||||
ctx.status(404)
|
||||
}
|
||||
|
||||
app.get("/api/v1/extension/list") { ctx ->
|
||||
ctx.json(getExtensionList())
|
||||
}
|
||||
@@ -78,6 +82,7 @@ class Main {
|
||||
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)
|
||||
@@ -86,31 +91,38 @@ class Main {
|
||||
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)
|
||||
@@ -133,7 +145,7 @@ class Main {
|
||||
ctx.status(200)
|
||||
}
|
||||
|
||||
// adds the manga to category
|
||||
// list manga's categories
|
||||
app.get("api/v1/manga/:mangaId/category/") { ctx ->
|
||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||
ctx.json(getMangaCategories(mangaId))
|
||||
@@ -215,7 +227,7 @@ class Main {
|
||||
|
||||
// category modification
|
||||
app.patch("/api/v1/category/:categoryId") { ctx ->
|
||||
val categoryId = ctx.pathParam("categoryId")!!.toInt()
|
||||
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)
|
||||
|
||||
@@ -8,7 +8,7 @@ import ir.armor.tachidesk.database.table.MangaStatus
|
||||
|
||||
data class MangaDataClass(
|
||||
val id: Int,
|
||||
val sourceId: Long,
|
||||
val sourceId: String,
|
||||
|
||||
val url: String,
|
||||
val title: String,
|
||||
@@ -21,7 +21,8 @@ data class MangaDataClass(
|
||||
val description: String? = null,
|
||||
val genre: String? = null,
|
||||
val status: String = MangaStatus.UNKNOWN.name,
|
||||
val inLibrary: Boolean = false
|
||||
val inLibrary: Boolean = false,
|
||||
val source: SourceDataClass? = null
|
||||
)
|
||||
|
||||
data class PagedMangaListDataClass(
|
||||
|
||||
@@ -6,8 +6,8 @@ package ir.armor.tachidesk.database.dataclass
|
||||
|
||||
data class SourceDataClass(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val lang: String,
|
||||
val iconUrl: String,
|
||||
val supportsLatest: Boolean
|
||||
val name: String?,
|
||||
val lang: String?,
|
||||
val iconUrl: String?,
|
||||
val supportsLatest: Boolean?
|
||||
)
|
||||
|
||||
@@ -28,13 +28,13 @@ object MangaTable : IntIdTable() {
|
||||
val defaultCategory = bool("default_category").default(true)
|
||||
|
||||
// source is used by some ancestor of IntIdTable
|
||||
val sourceReference = reference("source", SourceTable)
|
||||
val sourceReference = long("source")
|
||||
}
|
||||
|
||||
fun MangaTable.toDataClass(mangaEntry: ResultRow) =
|
||||
MangaDataClass(
|
||||
mangaEntry[MangaTable.id].value,
|
||||
mangaEntry[sourceReference].value,
|
||||
mangaEntry[sourceReference].toString(),
|
||||
|
||||
mangaEntry[MangaTable.url],
|
||||
mangaEntry[MangaTable.title],
|
||||
|
||||
@@ -18,7 +18,7 @@ import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
fun getChapterList(mangaId: Int): List<ChapterDataClass> {
|
||||
val mangaDetails = getManga(mangaId)
|
||||
val source = getHttpSource(mangaDetails.sourceId)
|
||||
val source = getHttpSource(mangaDetails.sourceId.toLong())
|
||||
|
||||
val chapterList = source.fetchChapterList(
|
||||
SManga.create().apply {
|
||||
@@ -62,7 +62,7 @@ fun getChapter(chapterId: Int, mangaId: Int): ChapterDataClass {
|
||||
val chapterEntry = ChapterTable.select { ChapterTable.id eq chapterId }.firstOrNull()!!
|
||||
assert(mangaId == chapterEntry[ChapterTable.manga].value) // sanity check
|
||||
val mangaEntry = MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!!
|
||||
val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value)
|
||||
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
|
||||
|
||||
val pageList = source.fetchPageList(
|
||||
SChapter.create().apply {
|
||||
|
||||
@@ -21,7 +21,7 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
||||
return if (mangaEntry[MangaTable.initialized]) {
|
||||
MangaDataClass(
|
||||
mangaId,
|
||||
mangaEntry[MangaTable.sourceReference].value,
|
||||
mangaEntry[MangaTable.sourceReference].toString(),
|
||||
|
||||
mangaEntry[MangaTable.url],
|
||||
mangaEntry[MangaTable.title],
|
||||
@@ -34,10 +34,11 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
||||
mangaEntry[MangaTable.description],
|
||||
mangaEntry[MangaTable.genre],
|
||||
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
||||
mangaEntry[MangaTable.inLibrary]
|
||||
mangaEntry[MangaTable.inLibrary],
|
||||
getSource(mangaEntry[MangaTable.sourceReference])
|
||||
)
|
||||
} else { // initialize manga
|
||||
val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value)
|
||||
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
|
||||
val fetchedManga = source.fetchMangaDetails(
|
||||
SManga.create().apply {
|
||||
url = mangaEntry[MangaTable.url]
|
||||
@@ -65,7 +66,7 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
||||
|
||||
MangaDataClass(
|
||||
mangaId,
|
||||
mangaEntry[MangaTable.sourceReference].value,
|
||||
mangaEntry[MangaTable.sourceReference].toString(),
|
||||
|
||||
mangaEntry[MangaTable.url],
|
||||
mangaEntry[MangaTable.title],
|
||||
@@ -78,7 +79,8 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
||||
fetchedManga.description,
|
||||
fetchedManga.genre,
|
||||
MangaStatus.valueOf(fetchedManga.status).name,
|
||||
false
|
||||
false,
|
||||
getSource(mangaEntry[MangaTable.sourceReference])
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -89,7 +91,7 @@ fun getThumbnail(mangaId: Int): Pair<InputStream, String> {
|
||||
val fileName = mangaId.toString()
|
||||
|
||||
return getCachedResponse(saveDir, fileName) {
|
||||
val sourceId = mangaEntry[MangaTable.sourceReference].value
|
||||
val sourceId = mangaEntry[MangaTable.sourceReference]
|
||||
val source = getHttpSource(sourceId)
|
||||
var thumbnailUrl = mangaEntry[MangaTable.thumbnail_url]
|
||||
if (thumbnailUrl == null || thumbnailUrl.isEmpty()) {
|
||||
|
||||
@@ -52,7 +52,7 @@ fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
||||
|
||||
MangaDataClass(
|
||||
mangaId,
|
||||
sourceId,
|
||||
sourceId.toString(),
|
||||
|
||||
manga.url,
|
||||
manga.title,
|
||||
@@ -70,7 +70,7 @@ fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
||||
val mangaId = mangaEntry[MangaTable.id].value
|
||||
MangaDataClass(
|
||||
mangaId,
|
||||
sourceId,
|
||||
sourceId.toString(),
|
||||
|
||||
manga.url,
|
||||
manga.title,
|
||||
|
||||
@@ -27,7 +27,7 @@ fun getTrueImageUrl(page: Page, source: HttpSource): String {
|
||||
|
||||
fun getPageImage(mangaId: Int, chapterId: Int, index: Int): Pair<InputStream, String> {
|
||||
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
|
||||
val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value)
|
||||
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
|
||||
val chapterEntry = transaction { ChapterTable.select { ChapterTable.id eq chapterId }.firstOrNull()!! }
|
||||
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq index) }.firstOrNull()!! }
|
||||
|
||||
@@ -56,7 +56,7 @@ fun getPageImage(mangaId: Int, chapterId: Int, index: Int): Pair<InputStream, St
|
||||
|
||||
fun getChapterDir(mangaId: Int, chapterId: Int): String {
|
||||
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
|
||||
val sourceId = mangaEntry[MangaTable.sourceReference].value
|
||||
val sourceId = mangaEntry[MangaTable.sourceReference]
|
||||
val source = getHttpSource(sourceId)
|
||||
val sourceEntry = transaction { SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()!! }
|
||||
val chapterEntry = transaction { ChapterTable.select { ChapterTable.id eq chapterId }.firstOrNull()!! }
|
||||
|
||||
@@ -18,6 +18,7 @@ fun sourceSearch(sourceId: Long, searchTerm: String, pageNum: Int): PagedMangaLi
|
||||
}
|
||||
|
||||
fun sourceGlobalSearch(searchTerm: String) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
data class FilterWrapper(
|
||||
|
||||
@@ -15,14 +15,18 @@ import ir.armor.tachidesk.database.table.SourceTable
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import java.lang.NullPointerException
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.util.Locale
|
||||
|
||||
private val sourceCache = mutableListOf<Pair<Long, HttpSource>>()
|
||||
private val extensionCache = mutableListOf<Pair<String, Any>>()
|
||||
|
||||
fun getHttpSource(sourceId: Long): HttpSource {
|
||||
val sourceRecord = transaction {
|
||||
SourceEntity.findById(sourceId)
|
||||
} ?: throw NullPointerException("Source with id $sourceId is not installed")
|
||||
|
||||
val cachedResult: Pair<Long, HttpSource>? = sourceCache.firstOrNull { it.first == sourceId }
|
||||
if (cachedResult != null) {
|
||||
println("used cached HttpSource: ${cachedResult.second.name}")
|
||||
@@ -30,7 +34,6 @@ fun getHttpSource(sourceId: Long): HttpSource {
|
||||
}
|
||||
|
||||
val result: HttpSource = transaction {
|
||||
val sourceRecord = SourceEntity.findById(sourceId)!!
|
||||
val extensionId = sourceRecord.extension.id.value
|
||||
val extensionRecord = ExtensionEntity.findById(extensionId)!!
|
||||
val apkName = extensionRecord.apkName
|
||||
@@ -87,14 +90,14 @@ fun getSourceList(): List<SourceDataClass> {
|
||||
|
||||
fun getSource(sourceId: Long): SourceDataClass {
|
||||
return transaction {
|
||||
val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()!!
|
||||
val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
|
||||
|
||||
return@transaction SourceDataClass(
|
||||
source[SourceTable.id].value.toString(),
|
||||
source[SourceTable.name],
|
||||
Locale(source[SourceTable.lang]).getDisplayLanguage(Locale(source[SourceTable.lang])),
|
||||
ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] }.first()[ExtensionTable.iconUrl],
|
||||
getHttpSource(source[SourceTable.id].value).supportsLatest
|
||||
sourceId.toString(),
|
||||
source?.get(SourceTable.name),
|
||||
source?.get(SourceTable.lang),
|
||||
source?.let { ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] }.first()[ExtensionTable.iconUrl] },
|
||||
source?.let { getHttpSource(sourceId).supportsLatest }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* This Kotlin source file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
package ir.armor.tachidesk
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class AppTest {
|
||||
@Test fun testAppHasAGreeting() {
|
||||
assertTrue(true)
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,17 @@ const useStyles = makeStyles(() => createStyles({
|
||||
|
||||
interface IProps{
|
||||
manga: IManga
|
||||
source: ISource
|
||||
}
|
||||
|
||||
function getSourceName(source: ISource) {
|
||||
if (source.name !== null) { return source.name; }
|
||||
return source.id;
|
||||
}
|
||||
|
||||
export default function MangaDetails(props: IProps) {
|
||||
const classes = useStyles();
|
||||
const { manga } = props;
|
||||
const { manga, source } = props;
|
||||
const [inLibrary, setInLibrary] = useState<string>(
|
||||
manga.inLibrary ? 'In Library' : 'Not In Library',
|
||||
);
|
||||
@@ -54,8 +60,13 @@ export default function MangaDetails(props: IProps) {
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
{manga && manga.title}
|
||||
{manga.title}
|
||||
</h1>
|
||||
<h3>
|
||||
Source:
|
||||
{' '}
|
||||
{getSourceName(source)}
|
||||
</h3>
|
||||
<div className={classes.root}>
|
||||
<Button variant="outlined" onClick={() => handleButtonClick()}>{inLibrary}</Button>
|
||||
{inLibrary === 'In Library'
|
||||
|
||||
@@ -35,9 +35,13 @@ function groupExtensions(extensions: IExtension[]) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function extensionDefaultLangs() {
|
||||
return [...defualtLangs(), 'all'];
|
||||
}
|
||||
|
||||
export default function Extensions() {
|
||||
const { setTitle, setAction } = useContext(NavbarContext);
|
||||
const [shownLangs, setShownLangs] = useLocalStorage<string[]>('shownExtensionLangs', defualtLangs());
|
||||
const [shownLangs, setShownLangs] = useLocalStorage<string[]>('shownExtensionLangs', extensionDefaultLangs());
|
||||
|
||||
useEffect(() => {
|
||||
setTitle('Extensions');
|
||||
@@ -76,7 +80,7 @@ export default function Extensions() {
|
||||
<>
|
||||
{
|
||||
Object.entries(extensions).map(([lang, list]) => (
|
||||
(['installed', ...shownLangs].indexOf(lang) !== -1
|
||||
((['installed', ...shownLangs].indexOf(lang) !== -1 && (list as []).length > 0)
|
||||
&& (
|
||||
<React.Fragment key={lang}>
|
||||
<h1 key={lang} style={{ marginLeft: 25 }}>
|
||||
|
||||
@@ -16,6 +16,7 @@ export default function Manga() {
|
||||
const { id } = useParams<{id: string}>();
|
||||
|
||||
const [manga, setManga] = useState<IManga>();
|
||||
const [source, setSource] = useState<ISource>();
|
||||
const [chapters, setChapters] = useState<IChapter[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -27,6 +28,16 @@ export default function Manga() {
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (manga !== undefined) {
|
||||
client.get(`/api/v1/source/${manga.sourceId}`)
|
||||
.then((response) => response.data)
|
||||
.then((data: ISource) => {
|
||||
setSource(data);
|
||||
});
|
||||
}
|
||||
}, [manga]);
|
||||
|
||||
useEffect(() => {
|
||||
client.get(`/api/v1/manga/${id}/chapters`)
|
||||
.then((response) => response.data)
|
||||
@@ -41,7 +52,7 @@ export default function Manga() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{manga && <MangaDetails manga={manga} />}
|
||||
{(manga && source) && <MangaDetails manga={manga} source={source} />}
|
||||
{chapterCards}
|
||||
</>
|
||||
);
|
||||
|
||||
1
webUI/react/src/typings.d.ts
vendored
1
webUI/react/src/typings.d.ts
vendored
@@ -23,6 +23,7 @@ interface ISource {
|
||||
|
||||
interface IManga {
|
||||
id: number
|
||||
sourceId?: string
|
||||
title: string
|
||||
thumbnailUrl: string
|
||||
inLibrary?: boolean
|
||||
|
||||
Reference in New Issue
Block a user