2020-12-23 17:52:05 +03:30
|
|
|
package ir.armor.tachidesk
|
|
|
|
|
|
2021-01-26 23:32:12 +03:30
|
|
|
/* 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/. */
|
|
|
|
|
|
2020-12-24 02:08:03 +03:30
|
|
|
import io.javalin.Javalin
|
2021-02-14 01:10:43 +03:30
|
|
|
import ir.armor.tachidesk.util.addMangaToCategory
|
2021-02-13 21:12:18 +03:30
|
|
|
import ir.armor.tachidesk.util.addMangaToLibrary
|
2021-02-14 01:10:43 +03:30
|
|
|
import ir.armor.tachidesk.util.createCategory
|
|
|
|
|
import ir.armor.tachidesk.util.getCategoryList
|
|
|
|
|
import ir.armor.tachidesk.util.getCategoryMangaList
|
2021-02-04 03:42:30 +03:30
|
|
|
import ir.armor.tachidesk.util.getChapter
|
2021-01-22 12:34:03 +03:30
|
|
|
import ir.armor.tachidesk.util.getChapterList
|
2021-02-04 14:47:27 +03:30
|
|
|
import ir.armor.tachidesk.util.getExtensionIcon
|
2021-01-22 12:34:03 +03:30
|
|
|
import ir.armor.tachidesk.util.getExtensionList
|
2021-02-13 21:12:18 +03:30
|
|
|
import ir.armor.tachidesk.util.getLibraryMangas
|
2021-01-22 12:34:03 +03:30
|
|
|
import ir.armor.tachidesk.util.getManga
|
2021-02-21 04:27:41 +03:30
|
|
|
import ir.armor.tachidesk.util.getMangaCategories
|
2021-01-22 12:34:03 +03:30
|
|
|
import ir.armor.tachidesk.util.getMangaList
|
2021-02-04 03:42:30 +03:30
|
|
|
import ir.armor.tachidesk.util.getPageImage
|
2021-01-22 17:00:33 +03:30
|
|
|
import ir.armor.tachidesk.util.getSource
|
2021-01-22 12:34:03 +03:30
|
|
|
import ir.armor.tachidesk.util.getSourceList
|
2021-01-29 14:19:24 +03:30
|
|
|
import ir.armor.tachidesk.util.getThumbnail
|
2021-01-22 12:34:03 +03:30
|
|
|
import ir.armor.tachidesk.util.installAPK
|
2021-02-04 18:02:46 +03:30
|
|
|
import ir.armor.tachidesk.util.openInBrowser
|
2021-02-14 01:10:43 +03:30
|
|
|
import ir.armor.tachidesk.util.removeCategory
|
2021-01-29 15:23:29 +03:30
|
|
|
import ir.armor.tachidesk.util.removeExtension
|
2021-02-14 01:10:43 +03:30
|
|
|
import ir.armor.tachidesk.util.removeMangaFromCategory
|
2021-02-13 21:12:18 +03:30
|
|
|
import ir.armor.tachidesk.util.removeMangaFromLibrary
|
2021-02-20 01:23:52 +03:30
|
|
|
import ir.armor.tachidesk.util.reorderCategory
|
2021-01-22 12:34:03 +03:30
|
|
|
import ir.armor.tachidesk.util.sourceFilters
|
|
|
|
|
import ir.armor.tachidesk.util.sourceGlobalSearch
|
|
|
|
|
import ir.armor.tachidesk.util.sourceSearch
|
2021-02-14 01:10:43 +03:30
|
|
|
import ir.armor.tachidesk.util.updateCategory
|
2020-12-24 02:08:03 +03:30
|
|
|
|
2020-12-23 19:52:16 +03:30
|
|
|
class Main {
|
2020-12-23 17:52:05 +03:30
|
|
|
companion object {
|
2020-12-24 02:08:03 +03:30
|
|
|
@JvmStatic
|
|
|
|
|
fun main(args: Array<String>) {
|
2021-03-11 14:43:29 +03:30
|
|
|
serverSetup()
|
2021-01-02 04:57:20 +03:30
|
|
|
|
2021-02-04 18:02:46 +03:30
|
|
|
var hasWebUiBundled: Boolean = false
|
2021-01-29 14:19:24 +03:30
|
|
|
|
2021-01-20 03:05:40 +03:30
|
|
|
val app = Javalin.create { config ->
|
2021-01-20 15:26:52 +03:30
|
|
|
try {
|
|
|
|
|
this::class.java.classLoader.getResource("/react/index.html")
|
2021-02-04 18:02:46 +03:30
|
|
|
hasWebUiBundled = true
|
2021-01-20 15:26:52 +03:30
|
|
|
config.addStaticFiles("/react")
|
2021-01-22 12:34:03 +03:30
|
|
|
config.addSinglePageRoot("/", "/react/index.html")
|
2021-01-20 15:26:52 +03:30
|
|
|
} catch (e: RuntimeException) {
|
|
|
|
|
println("Warning: react build files are missing.")
|
2021-02-04 18:02:46 +03:30
|
|
|
hasWebUiBundled = false
|
2021-01-20 15:26:52 +03:30
|
|
|
}
|
2021-02-13 21:12:18 +03:30
|
|
|
config.enableCorsForAllOrigins()
|
2021-03-11 14:43:29 +03:30
|
|
|
}.start(serverConfig.ip, serverConfig.port)
|
2021-02-04 18:02:46 +03:30
|
|
|
if (hasWebUiBundled) {
|
|
|
|
|
openInBrowser()
|
|
|
|
|
}
|
2021-01-20 03:05:40 +03:30
|
|
|
|
2021-03-14 23:57:33 +03:30
|
|
|
app.exception(NullPointerException::class.java) { _, ctx ->
|
|
|
|
|
ctx.status(404)
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-25 17:16:26 +03:30
|
|
|
app.get("/api/v1/extension/list") { ctx ->
|
2020-12-25 02:39:30 +03:30
|
|
|
ctx.json(getExtensionList())
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-25 17:16:26 +03:30
|
|
|
app.get("/api/v1/extension/install/:apkName") { ctx ->
|
2020-12-25 02:39:30 +03:30
|
|
|
val apkName = ctx.pathParam("apkName")
|
2021-01-29 15:23:29 +03:30
|
|
|
println("installing $apkName")
|
|
|
|
|
|
2020-12-25 02:39:30 +03:30
|
|
|
ctx.status(
|
2021-01-22 12:34:03 +03:30
|
|
|
installAPK(apkName)
|
2020-12-25 02:39:30 +03:30
|
|
|
)
|
|
|
|
|
}
|
2021-01-29 15:23:29 +03:30
|
|
|
|
|
|
|
|
app.get("/api/v1/extension/uninstall/:apkName") { ctx ->
|
|
|
|
|
val apkName = ctx.pathParam("apkName")
|
|
|
|
|
println("uninstalling $apkName")
|
|
|
|
|
removeExtension(apkName)
|
|
|
|
|
ctx.status(200)
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-14 01:26:52 +03:30
|
|
|
// icon for extension named `apkName`
|
2021-02-04 14:47:27 +03:30
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-14 01:26:52 +03:30
|
|
|
// list of sources
|
2020-12-25 17:16:26 +03:30
|
|
|
app.get("/api/v1/source/list") { ctx ->
|
2020-12-25 06:15:52 +03:30
|
|
|
ctx.json(getSourceList())
|
|
|
|
|
}
|
2020-12-25 17:16:26 +03:30
|
|
|
|
2021-03-14 01:26:52 +03:30
|
|
|
// fetch source with id `sourceId`
|
2021-01-22 17:00:33 +03:30
|
|
|
app.get("/api/v1/source/:sourceId") { ctx ->
|
|
|
|
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
|
|
|
|
ctx.json(getSource(sourceId))
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-14 01:26:52 +03:30
|
|
|
// popular mangas from source with id `sourceId`
|
2021-01-19 20:20:28 +03:30
|
|
|
app.get("/api/v1/source/:sourceId/popular/:pageNum") { ctx ->
|
|
|
|
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
2021-01-19 15:07:20 +03:30
|
|
|
val pageNum = ctx.pathParam("pageNum").toInt()
|
2021-01-20 15:26:52 +03:30
|
|
|
ctx.json(getMangaList(sourceId, pageNum, popular = true))
|
2020-12-25 17:16:26 +03:30
|
|
|
}
|
2021-03-14 01:26:52 +03:30
|
|
|
|
|
|
|
|
// latest mangas from source with id `sourceId`
|
2021-01-19 20:20:28 +03:30
|
|
|
app.get("/api/v1/source/:sourceId/latest/:pageNum") { ctx ->
|
|
|
|
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
2021-01-19 15:07:20 +03:30
|
|
|
val pageNum = ctx.pathParam("pageNum").toInt()
|
2021-01-20 15:26:52 +03:30
|
|
|
ctx.json(getMangaList(sourceId, pageNum, popular = false))
|
2021-01-19 14:47:25 +03:30
|
|
|
}
|
2021-01-19 20:20:28 +03:30
|
|
|
|
2021-03-14 01:26:52 +03:30
|
|
|
// get manga info
|
2021-01-19 20:20:28 +03:30
|
|
|
app.get("/api/v1/manga/:mangaId/") { ctx ->
|
|
|
|
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
|
|
|
ctx.json(getManga(mangaId))
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-14 01:26:52 +03:30
|
|
|
// manga thumbnail
|
2021-02-04 03:42:30 +03:30
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 18:48:59 +03:30
|
|
|
// adds the manga to library
|
|
|
|
|
app.get("api/v1/manga/:mangaId/library") { ctx ->
|
2021-02-13 21:12:18 +03:30
|
|
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
|
|
|
addMangaToLibrary(mangaId)
|
|
|
|
|
ctx.status(200)
|
2021-02-06 18:48:59 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// removes the manga from the library
|
|
|
|
|
app.delete("api/v1/manga/:mangaId/library") { ctx ->
|
2021-02-13 21:12:18 +03:30
|
|
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
|
|
|
removeMangaFromLibrary(mangaId)
|
|
|
|
|
ctx.status(200)
|
2021-02-06 18:48:59 +03:30
|
|
|
}
|
|
|
|
|
|
2021-03-14 01:26:52 +03:30
|
|
|
// list manga's categories
|
2021-02-21 04:27:41 +03:30
|
|
|
app.get("api/v1/manga/:mangaId/category/") { ctx ->
|
|
|
|
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
|
|
|
ctx.json(getMangaCategories(mangaId))
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-06 18:48:59 +03:30
|
|
|
// adds the manga to category
|
|
|
|
|
app.get("api/v1/manga/:mangaId/category/:categoryId") { ctx ->
|
2021-02-14 01:10:43 +03:30
|
|
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
|
|
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
|
|
|
|
addMangaToCategory(mangaId, categoryId)
|
|
|
|
|
ctx.status(200)
|
2021-02-06 18:48:59 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// removes the manga from the category
|
|
|
|
|
app.delete("api/v1/manga/:mangaId/category/:categoryId") { ctx ->
|
2021-02-14 01:10:43 +03:30
|
|
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
|
|
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
|
|
|
|
removeMangaFromCategory(mangaId, categoryId)
|
|
|
|
|
ctx.status(200)
|
2021-02-06 18:48:59 +03:30
|
|
|
}
|
|
|
|
|
|
2021-01-20 00:04:12 +03:30
|
|
|
app.get("/api/v1/manga/:mangaId/chapters") { ctx ->
|
2021-01-19 21:02:57 +03:30
|
|
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
|
|
|
|
ctx.json(getChapterList(mangaId))
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-20 01:05:24 +03:30
|
|
|
app.get("/api/v1/manga/:mangaId/chapter/:chapterId") { ctx ->
|
2021-01-20 00:04:12 +03:30
|
|
|
val chapterId = ctx.pathParam("chapterId").toInt()
|
2021-01-20 01:05:24 +03:30
|
|
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
2021-02-04 03:42:30 +03:30
|
|
|
ctx.json(getChapter(chapterId, mangaId))
|
2021-01-20 00:04:12 +03:30
|
|
|
}
|
2021-01-21 14:15:05 +03:30
|
|
|
|
2021-02-04 03:42:30 +03:30
|
|
|
app.get("/api/v1/manga/:mangaId/chapter/:chapterId/page/:index") { ctx ->
|
|
|
|
|
val chapterId = ctx.pathParam("chapterId").toInt()
|
2021-01-29 14:19:24 +03:30
|
|
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
2021-02-04 03:42:30 +03:30
|
|
|
val index = ctx.pathParam("index").toInt()
|
|
|
|
|
val result = getPageImage(mangaId, chapterId, index)
|
2021-01-29 14:19:24 +03:30
|
|
|
|
|
|
|
|
ctx.result(result.first)
|
|
|
|
|
ctx.header("content-type", result.second)
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-21 14:15:05 +03:30
|
|
|
// global search
|
|
|
|
|
app.get("/api/v1/search/:searchTerm") { ctx ->
|
|
|
|
|
val searchTerm = ctx.pathParam("searchTerm")
|
|
|
|
|
ctx.json(sourceGlobalSearch(searchTerm))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// single source search
|
2021-01-22 18:07:31 +03:30
|
|
|
app.get("/api/v1/source/:sourceId/search/:searchTerm/:pageNum") { ctx ->
|
2021-01-21 14:15:05 +03:30
|
|
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
|
|
|
|
val searchTerm = ctx.pathParam("searchTerm")
|
2021-01-22 18:07:31 +03:30
|
|
|
val pageNum = ctx.pathParam("pageNum").toInt()
|
|
|
|
|
ctx.json(sourceSearch(sourceId, searchTerm, pageNum))
|
2021-01-21 14:15:05 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// source filter list
|
|
|
|
|
app.get("/api/v1/source/:sourceId/filters/") { ctx ->
|
|
|
|
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
|
|
|
|
ctx.json(sourceFilters(sourceId))
|
|
|
|
|
}
|
2021-02-06 18:48:59 +03:30
|
|
|
|
2021-02-20 01:23:52 +03:30
|
|
|
// lists mangas that have no category assigned
|
2021-02-06 18:48:59 +03:30
|
|
|
app.get("/api/v1/library/") { ctx ->
|
2021-02-13 21:12:18 +03:30
|
|
|
ctx.json(getLibraryMangas())
|
2021-02-06 18:48:59 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// category list
|
|
|
|
|
app.get("/api/v1/category/") { ctx ->
|
2021-02-14 01:10:43 +03:30
|
|
|
ctx.json(getCategoryList())
|
2021-02-06 18:48:59 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// category create
|
|
|
|
|
app.post("/api/v1/category/") { ctx ->
|
2021-02-14 01:10:43 +03:30
|
|
|
val name = ctx.formParam("name")!!
|
2021-02-20 01:23:52 +03:30
|
|
|
createCategory(name)
|
2021-02-14 01:10:43 +03:30
|
|
|
ctx.status(200)
|
2021-02-06 18:48:59 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// category modification
|
2021-02-20 01:23:52 +03:30
|
|
|
app.patch("/api/v1/category/:categoryId") { ctx ->
|
2021-03-16 15:44:50 +03:30
|
|
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
2021-02-20 01:23:52 +03:30
|
|
|
val name = ctx.formParam("name")
|
|
|
|
|
val isLanding = if (ctx.formParam("isLanding") != null) ctx.formParam("isLanding")?.toBoolean() else null
|
2021-02-14 01:10:43 +03:30
|
|
|
updateCategory(categoryId, name, isLanding)
|
|
|
|
|
ctx.status(200)
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-20 01:23:52 +03:30
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-14 01:10:43 +03:30
|
|
|
// category delete
|
|
|
|
|
app.delete("/api/v1/category/:categoryId") { ctx ->
|
|
|
|
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
|
|
|
|
removeCategory(categoryId)
|
|
|
|
|
ctx.status(200)
|
2021-02-06 18:48:59 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns the manga list associated with a category
|
|
|
|
|
app.get("/api/v1/category/:categoryId") { ctx ->
|
2021-02-14 01:10:43 +03:30
|
|
|
val categoryId = ctx.pathParam("categoryId").toInt()
|
|
|
|
|
ctx.json(getCategoryMangaList(categoryId))
|
2021-02-06 18:48:59 +03:30
|
|
|
}
|
2021-01-21 14:15:05 +03:30
|
|
|
}
|
2020-12-23 17:52:05 +03:30
|
|
|
}
|
|
|
|
|
}
|