mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-16 09:14:09 -05:00
let's not polute the namespace together
This commit is contained in:
@@ -7,8 +7,8 @@ package ir.armor.tachidesk
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import ir.armor.tachidesk.server.JavalinSetup.javalinSetup
|
||||
import ir.armor.tachidesk.server.applicationSetup
|
||||
import ir.armor.tachidesk.server.javalinSetup
|
||||
|
||||
class Main {
|
||||
companion object {
|
||||
|
||||
@@ -47,7 +47,7 @@ object Category {
|
||||
/**
|
||||
* Move the category from position `from` to `to`
|
||||
*/
|
||||
fun reorderCategory(categoryId: Int, from: Int, to: Int) {
|
||||
fun reorderCategory(categoryId: Int, from: Int, to: Int) {
|
||||
transaction {
|
||||
val categories = CategoryTable.selectAll().orderBy(CategoryTable.order to SortOrder.ASC).toMutableList()
|
||||
categories.add(to - 1, categories.removeAt(from - 1))
|
||||
@@ -57,22 +57,22 @@ fun reorderCategory(categoryId: Int, from: Int, to: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeCategory(categoryId: Int) {
|
||||
fun removeCategory(categoryId: Int) {
|
||||
transaction {
|
||||
CategoryMangaTable.select { CategoryMangaTable.category eq categoryId }.forEach {
|
||||
removeMangaFromCategory(it[CategoryMangaTable.manga].value, categoryId)
|
||||
}
|
||||
CategoryTable.deleteWhere { CategoryTable.id eq categoryId }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCategoryList(): List<CategoryDataClass> {
|
||||
fun getCategoryList(): List<CategoryDataClass> {
|
||||
return transaction {
|
||||
CategoryTable.selectAll().orderBy(CategoryTable.order to SortOrder.ASC).map {
|
||||
CategoryTable.toDataClass(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.jetbrains.exposed.sql.update
|
||||
|
||||
object CategoryManga {
|
||||
fun addMangaToCategory(mangaId: Int, categoryId: Int) {
|
||||
fun addMangaToCategory(mangaId: Int, categoryId: Int) {
|
||||
transaction {
|
||||
if (CategoryMangaTable.select { (CategoryMangaTable.category eq categoryId) and (CategoryMangaTable.manga eq mangaId) }.firstOrNull() == null) {
|
||||
CategoryMangaTable.insert {
|
||||
@@ -35,9 +35,9 @@ fun addMangaToCategory(mangaId: Int, categoryId: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeMangaFromCategory(mangaId: Int, categoryId: Int) {
|
||||
fun removeMangaFromCategory(mangaId: Int, categoryId: Int) {
|
||||
transaction {
|
||||
CategoryMangaTable.deleteWhere { (CategoryMangaTable.category eq categoryId) and (CategoryMangaTable.manga eq mangaId) }
|
||||
if (CategoryMangaTable.select { CategoryMangaTable.manga eq mangaId }.count() == 0L) {
|
||||
@@ -46,28 +46,27 @@ fun removeMangaFromCategory(mangaId: Int, categoryId: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* list of mangas that belong to a category
|
||||
*/
|
||||
fun getCategoryMangaList(categoryId: Int): List<MangaDataClass> {
|
||||
fun getCategoryMangaList(categoryId: Int): List<MangaDataClass> {
|
||||
return transaction {
|
||||
CategoryMangaTable.innerJoin(MangaTable).select { CategoryMangaTable.category eq categoryId }.map {
|
||||
MangaTable.toDataClass(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list of categories that a manga belongs to
|
||||
*/
|
||||
fun getMangaCategories(mangaId: Int): List<CategoryDataClass> {
|
||||
fun getMangaCategories(mangaId: Int): List<CategoryDataClass> {
|
||||
return transaction {
|
||||
CategoryMangaTable.innerJoin(CategoryTable).select { CategoryMangaTable.manga eq mangaId }.orderBy(CategoryTable.order to SortOrder.ASC).map {
|
||||
CategoryTable.toDataClass(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,10 @@ import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import ir.armor.tachidesk.impl.ExtensionsList.extensionTableAsDataClass
|
||||
import ir.armor.tachidesk.impl.util.APKExtractor
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.impl.util.APKExtractor
|
||||
import ir.armor.tachidesk.server.applicationDirs
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mu.KotlinLogging
|
||||
import okhttp3.Request
|
||||
@@ -39,12 +39,12 @@ import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
object Extension {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
/**
|
||||
* Convert dex to jar, a wrapper for the dex2jar library
|
||||
*/
|
||||
private fun dex2jar(dexFile: String, jarFile: String, fileNameWithoutType: String) {
|
||||
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
|
||||
|
||||
@@ -63,7 +63,7 @@ private fun dex2jar(dexFile: String, jarFile: String, fileNameWithoutType: Strin
|
||||
.skipExceptions(false)
|
||||
.to(jarFilePath)
|
||||
if (handler.hasException()) {
|
||||
val errorFile: Path = File(applicationDirs.extensionsRoot).toPath().resolve("$fileNameWithoutType-error.txt")
|
||||
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" +
|
||||
@@ -74,26 +74,26 @@ private fun dex2jar(dexFile: String, jarFile: String, fileNameWithoutType: Strin
|
||||
)
|
||||
handler.dump(errorFile, emptyArray<String>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* loads the extension main class called $className from the jar located at $jarPath
|
||||
* It may return an instance of HttpSource or SourceFactory depending on the extension.
|
||||
*/
|
||||
fun loadExtensionInstance(jarPath: String, className: String): Any {
|
||||
fun loadExtensionInstance(jarPath: String, className: String): Any {
|
||||
val classLoader = URLClassLoader(arrayOf<URL>(URL("file:$jarPath")))
|
||||
val classToLoad = Class.forName(className, true, classLoader)
|
||||
return classToLoad.getDeclaredConstructor().newInstance()
|
||||
}
|
||||
}
|
||||
|
||||
fun installExtension(pkgName: String): Int {
|
||||
fun installExtension(pkgName: String): Int {
|
||||
logger.debug("Installing $pkgName")
|
||||
val extensionRecord = extensionTableAsDataClass().first { it.pkgName == pkgName }
|
||||
val fileNameWithoutType = extensionRecord.apkName.substringBefore(".apk")
|
||||
val dirPathWithoutType = "${applicationDirs.extensionsRoot}/$fileNameWithoutType"
|
||||
val dirPathWithoutType = "${ApplicationDirs.extensionsRoot}/$fileNameWithoutType"
|
||||
|
||||
// check if we don't have the dex file already downloaded
|
||||
val jarPath = "${applicationDirs.extensionsRoot}/$fileNameWithoutType.jar"
|
||||
val jarPath = "${ApplicationDirs.extensionsRoot}/$fileNameWithoutType.jar"
|
||||
if (!File(jarPath).exists()) {
|
||||
runBlocking {
|
||||
val apkToDownload = ExtensionGithubApi.getApkUrl(extensionRecord)
|
||||
@@ -169,11 +169,11 @@ fun installExtension(pkgName: String): Int {
|
||||
} else {
|
||||
return 302
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val networkHelper: NetworkHelper by injectLazy()
|
||||
val networkHelper: NetworkHelper by injectLazy()
|
||||
|
||||
private fun downloadAPKFile(url: String, apkPath: String) {
|
||||
private fun downloadAPKFile(url: String, apkPath: String) {
|
||||
val request = Request.Builder().url(url).build()
|
||||
val response = networkHelper.client.newCall(request).execute()
|
||||
|
||||
@@ -181,14 +181,14 @@ private fun downloadAPKFile(url: String, apkPath: String) {
|
||||
val sink = downloadedFile.sink().buffer()
|
||||
sink.writeAll(response.body!!.source())
|
||||
sink.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun uninstallExtension(pkgName: String) {
|
||||
fun uninstallExtension(pkgName: String) {
|
||||
logger.debug("Uninstalling $pkgName")
|
||||
|
||||
val extensionRecord = transaction { ExtensionTable.select { ExtensionTable.pkgName eq pkgName }.firstOrNull()!! }
|
||||
val fileNameWithoutType = extensionRecord[ExtensionTable.apkName].substringBefore(".apk")
|
||||
val jarPath = "${applicationDirs.extensionsRoot}/$fileNameWithoutType.jar"
|
||||
val jarPath = "${ApplicationDirs.extensionsRoot}/$fileNameWithoutType.jar"
|
||||
transaction {
|
||||
val extensionId = extensionRecord[ExtensionTable.id].value
|
||||
|
||||
@@ -204,9 +204,9 @@ fun uninstallExtension(pkgName: String) {
|
||||
if (File(jarPath).exists()) {
|
||||
File(jarPath).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateExtension(pkgName: String): Int {
|
||||
fun updateExtension(pkgName: String): Int {
|
||||
val targetExtension = ExtensionsList.updateMap.remove(pkgName)!!
|
||||
uninstallExtension(pkgName)
|
||||
transaction {
|
||||
@@ -222,23 +222,23 @@ fun updateExtension(pkgName: String): Int {
|
||||
}
|
||||
}
|
||||
return installExtension(pkgName)
|
||||
}
|
||||
}
|
||||
|
||||
val network: NetworkHelper by injectLazy()
|
||||
val network: NetworkHelper by injectLazy()
|
||||
|
||||
fun getExtensionIcon(apkName: String): Pair<InputStream, String> {
|
||||
fun getExtensionIcon(apkName: String): Pair<InputStream, String> {
|
||||
val iconUrl = transaction { ExtensionTable.select { ExtensionTable.apkName eq apkName }.firstOrNull()!! }[ExtensionTable.iconUrl]
|
||||
|
||||
val saveDir = "${applicationDirs.extensionsRoot}/icon"
|
||||
val saveDir = "${ApplicationDirs.extensionsRoot}/icon"
|
||||
|
||||
return getCachedImageResponse(saveDir, apkName) {
|
||||
network.client.newCall(
|
||||
GET(iconUrl)
|
||||
).execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getExtensionIconUrl(apkName: String): String {
|
||||
fun getExtensionIconUrl(apkName: String): String {
|
||||
return "/api/v1/extension/icon/$apkName"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,17 +23,15 @@ import org.jetbrains.exposed.sql.update
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
object ExtensionsList {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
var lastUpdateCheck: Long = 0
|
||||
var updateMap = ConcurrentHashMap<String, Extension.Available>()
|
||||
|
||||
|
||||
// const val ExtensionUpdateDelayTime = 60 * 1000 // 60,000 milliseconds = 60 seconds
|
||||
const val ExtensionUpdateDelayTime = 0
|
||||
const val ExtensionUpdateDelayTime = 0
|
||||
|
||||
fun getExtensionList(): List<ExtensionDataClass> {
|
||||
fun getExtensionList(): List<ExtensionDataClass> {
|
||||
// update if {ExtensionUpdateDelayTime} seconds has passed or requested offline and database is empty
|
||||
if (lastUpdateCheck + ExtensionUpdateDelayTime < System.currentTimeMillis()) {
|
||||
logger.debug("Getting extensions list from the internet")
|
||||
@@ -47,9 +45,9 @@ fun getExtensionList(): List<ExtensionDataClass> {
|
||||
}
|
||||
|
||||
return extensionTableAsDataClass()
|
||||
}
|
||||
}
|
||||
|
||||
fun extensionTableAsDataClass() = transaction {
|
||||
fun extensionTableAsDataClass() = transaction {
|
||||
ExtensionTable.selectAll().map {
|
||||
ExtensionDataClass(
|
||||
it[ExtensionTable.name],
|
||||
@@ -65,9 +63,9 @@ fun extensionTableAsDataClass() = transaction {
|
||||
it[ExtensionTable.isObsolete],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateExtensionDatabase(foundExtensions: List<Extension.Available>) {
|
||||
private fun updateExtensionDatabase(foundExtensions: List<Extension.Available>) {
|
||||
transaction {
|
||||
foundExtensions.forEach { foundExtension ->
|
||||
val extensionRecord = ExtensionTable.select { ExtensionTable.pkgName eq foundExtension.pkgName }.firstOrNull()
|
||||
@@ -132,5 +130,5 @@ private fun updateExtensionDatabase(foundExtensions: List<Extension.Available>)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ object Library {
|
||||
// TODO: `Category.isLanding` is to handle the default categories a new library manga gets,
|
||||
// ..implement that shit at some time...
|
||||
// ..also Consider to rename it to `isDefault`
|
||||
fun addMangaToLibrary(mangaId: Int) {
|
||||
fun addMangaToLibrary(mangaId: Int) {
|
||||
val manga = getManga(mangaId)
|
||||
if (!manga.inLibrary) {
|
||||
transaction {
|
||||
@@ -31,9 +31,9 @@ fun addMangaToLibrary(mangaId: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeMangaFromLibrary(mangaId: Int) {
|
||||
fun removeMangaFromLibrary(mangaId: Int) {
|
||||
val manga = getManga(mangaId)
|
||||
if (manga.inLibrary) {
|
||||
transaction {
|
||||
@@ -44,13 +44,13 @@ fun removeMangaFromLibrary(mangaId: Int) {
|
||||
CategoryMangaTable.deleteWhere { CategoryMangaTable.manga eq mangaId }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getLibraryMangas(): List<MangaDataClass> {
|
||||
fun getLibraryMangas(): List<MangaDataClass> {
|
||||
return transaction {
|
||||
MangaTable.select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) }.map {
|
||||
MangaTable.toDataClass(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@ import ir.armor.tachidesk.impl.Source.getSource
|
||||
import ir.armor.tachidesk.model.database.MangaStatus
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.dataclass.MangaDataClass
|
||||
import ir.armor.tachidesk.server.applicationDirs
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.jetbrains.exposed.sql.update
|
||||
import java.io.InputStream
|
||||
|
||||
object Manga {
|
||||
fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
||||
fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
||||
var mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
|
||||
|
||||
return if (mangaEntry[MangaTable.initialized]) {
|
||||
@@ -90,11 +90,11 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
||||
getSource(mangaEntry[MangaTable.sourceReference])
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getMangaThumbnail(mangaId: Int): Pair<InputStream, String> {
|
||||
fun getMangaThumbnail(mangaId: Int): Pair<InputStream, String> {
|
||||
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
|
||||
val saveDir = applicationDirs.thumbnailsRoot
|
||||
val saveDir = ApplicationDirs.thumbnailsRoot
|
||||
val fileName = mangaId.toString()
|
||||
|
||||
return getCachedImageResponse(saveDir, fileName) {
|
||||
@@ -109,5 +109,5 @@ fun getMangaThumbnail(mangaId: Int): Pair<InputStream, String> {
|
||||
GET(thumbnailUrl, source.headers)
|
||||
).execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,11 @@ import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
object MangaList {
|
||||
fun proxyThumbnailUrl(mangaId: Int): String {
|
||||
fun proxyThumbnailUrl(mangaId: Int): String {
|
||||
return "/api/v1/manga/$mangaId/thumbnail"
|
||||
}
|
||||
}
|
||||
|
||||
fun getMangaList(sourceId: Long, pageNum: Int = 1, popular: Boolean): PagedMangaListDataClass {
|
||||
fun getMangaList(sourceId: Long, pageNum: Int = 1, popular: Boolean): PagedMangaListDataClass {
|
||||
val source = getHttpSource(sourceId.toLong())
|
||||
val mangasPage = if (popular) {
|
||||
source.fetchPopularManga(pageNum).toBlocking().first()
|
||||
@@ -33,9 +33,9 @@ fun getMangaList(sourceId: Long, pageNum: Int = 1, popular: Boolean): PagedManga
|
||||
throw Exception("Source $source doesn't support latest")
|
||||
}
|
||||
return mangasPage.processEntries(sourceId)
|
||||
}
|
||||
}
|
||||
|
||||
fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
||||
fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
||||
val mangasPage = this
|
||||
val mangaList = transaction {
|
||||
return@transaction mangasPage.mangas.map { manga ->
|
||||
@@ -97,5 +97,5 @@ fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
||||
mangaList,
|
||||
mangasPage.hasNextPage
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import ir.armor.tachidesk.model.database.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.PageTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.server.applicationDirs
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
@@ -22,19 +22,19 @@ import org.jetbrains.exposed.sql.update
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
object Page{
|
||||
object Page {
|
||||
/**
|
||||
* A page might have a imageUrl ready from the get go, or we might need to
|
||||
* go an extra step and call fetchImageUrl to get it.
|
||||
*/
|
||||
fun getTrueImageUrl(page: Page, source: HttpSource): String {
|
||||
fun getTrueImageUrl(page: Page, source: HttpSource): String {
|
||||
if (page.imageUrl == null) {
|
||||
page.imageUrl = source.fetchImageUrl(page).toBlocking().first()!!
|
||||
}
|
||||
return page.imageUrl!!
|
||||
}
|
||||
}
|
||||
|
||||
fun getPageImage(mangaId: Int, chapterIndex: Int, index: Int): Pair<InputStream, String> {
|
||||
fun getPageImage(mangaId: Int, chapterIndex: Int, index: Int): Pair<InputStream, String> {
|
||||
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
|
||||
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
|
||||
val chapterEntry = transaction {
|
||||
@@ -67,10 +67,10 @@ fun getPageImage(mangaId: Int, chapterIndex: Int, index: Int): Pair<InputStream,
|
||||
return getCachedImageResponse(saveDir, fileName) {
|
||||
source.fetchImage(tachiPage).toBlocking().first()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: rewrite this to match tachiyomi
|
||||
fun getChapterDir(mangaId: Int, chapterId: Int): String {
|
||||
fun getChapterDir(mangaId: Int, chapterId: Int): String {
|
||||
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
|
||||
val sourceId = mangaEntry[MangaTable.sourceReference]
|
||||
val source = getHttpSource(sourceId)
|
||||
@@ -85,9 +85,9 @@ fun getChapterDir(mangaId: Int, chapterId: Int): String {
|
||||
val mangaTitle = mangaEntry[MangaTable.title]
|
||||
val sourceName = source.toString()
|
||||
|
||||
val mangaDir = "${applicationDirs.mangaRoot}/$sourceName/$mangaTitle/$chapterDir"
|
||||
val mangaDir = "${ApplicationDirs.mangaRoot}/$sourceName/$mangaTitle/$chapterDir"
|
||||
// make sure dirs exist
|
||||
File(mangaDir).mkdirs()
|
||||
return mangaDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,25 +13,25 @@ import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass
|
||||
|
||||
object Search {
|
||||
// TODO
|
||||
fun sourceFilters(sourceId: Long) {
|
||||
fun sourceFilters(sourceId: Long) {
|
||||
val source = getHttpSource(sourceId)
|
||||
// source.getFilterList().toItems()
|
||||
}
|
||||
}
|
||||
|
||||
fun sourceSearch(sourceId: Long, searchTerm: String, pageNum: Int): PagedMangaListDataClass {
|
||||
fun sourceSearch(sourceId: Long, searchTerm: String, pageNum: Int): PagedMangaListDataClass {
|
||||
val source = getHttpSource(sourceId)
|
||||
val searchManga = source.fetchSearchManga(pageNum, searchTerm, source.getFilterList()).toBlocking().first()
|
||||
return searchManga.processEntries(sourceId)
|
||||
}
|
||||
}
|
||||
|
||||
fun sourceGlobalSearch(searchTerm: String) {
|
||||
fun sourceGlobalSearch(searchTerm: String) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
data class FilterWrapper(
|
||||
data class FilterWrapper(
|
||||
val type: String,
|
||||
val filter: Any
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* Note: Exhentai had a filter serializer (now in SY) that we might be able to steal
|
||||
|
||||
@@ -14,7 +14,7 @@ import ir.armor.tachidesk.impl.Extension.loadExtensionInstance
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.model.dataclass.SourceDataClass
|
||||
import ir.armor.tachidesk.server.applicationDirs
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import mu.KotlinLogging
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
@@ -22,11 +22,11 @@ import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
object Source {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
private val sourceCache = ConcurrentHashMap<Long, HttpSource>()
|
||||
private val sourceCache = ConcurrentHashMap<Long, HttpSource>()
|
||||
|
||||
fun getHttpSource(sourceId: Long): HttpSource {
|
||||
fun getHttpSource(sourceId: Long): HttpSource {
|
||||
val cachedResult: HttpSource? = sourceCache[sourceId]
|
||||
if (cachedResult != null) {
|
||||
logger.debug("used cached HttpSource: ${cachedResult.name}")
|
||||
@@ -41,7 +41,7 @@ fun getHttpSource(sourceId: Long): HttpSource {
|
||||
val apkName = extensionRecord[ExtensionTable.apkName]
|
||||
val className = extensionRecord[ExtensionTable.classFQName]
|
||||
val jarName = apkName.substringBefore(".apk") + ".jar"
|
||||
val jarPath = "${applicationDirs.extensionsRoot}/$jarName"
|
||||
val jarPath = "${ApplicationDirs.extensionsRoot}/$jarName"
|
||||
|
||||
val extensionInstance = loadExtensionInstance(jarPath, className)
|
||||
|
||||
@@ -56,9 +56,9 @@ fun getHttpSource(sourceId: Long): HttpSource {
|
||||
}
|
||||
}
|
||||
return sourceCache[sourceId]!!
|
||||
}
|
||||
}
|
||||
|
||||
fun getSourceList(): List<SourceDataClass> {
|
||||
fun getSourceList(): List<SourceDataClass> {
|
||||
return transaction {
|
||||
SourceTable.selectAll().map {
|
||||
SourceDataClass(
|
||||
@@ -70,9 +70,9 @@ fun getSourceList(): List<SourceDataClass> {
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSource(sourceId: Long): SourceDataClass {
|
||||
fun getSource(sourceId: Long): SourceDataClass {
|
||||
return transaction {
|
||||
val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
|
||||
|
||||
@@ -84,5 +84,5 @@ fun getSource(sourceId: Long): SourceDataClass {
|
||||
source?.let { getHttpSource(sourceId).supportsLatest }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@ import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.PageTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.server.applicationDirs
|
||||
import ir.armor.tachidesk.server.ApplicationDirs
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
object DBMangaer {
|
||||
val db by lazy {
|
||||
Database.connect("jdbc:h2:${applicationDirs.dataRoot}/database", "org.h2.Driver")
|
||||
Database.connect("jdbc:h2:${ApplicationDirs.dataRoot}/database", "org.h2.Driver")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,9 +41,10 @@ import java.io.IOException
|
||||
* 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 {}
|
||||
object JavalinSetup {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
fun javalinSetup() {
|
||||
fun javalinSetup() {
|
||||
var hasWebUiBundled = false
|
||||
|
||||
val app = Javalin.create { config ->
|
||||
@@ -285,4 +286,5 @@ fun javalinSetup() {
|
||||
val categoryId = ctx.pathParam("categoryId").toInt()
|
||||
ctx.json(getCategoryMangaList(categoryId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.io.File
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
object applicationDirs {
|
||||
object ApplicationDirs {
|
||||
val dataRoot = AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)!!
|
||||
val extensionsRoot = "$dataRoot/extensions"
|
||||
val thumbnailsRoot = "$dataRoot/thumbnails"
|
||||
@@ -50,17 +50,17 @@ fun applicationSetup() {
|
||||
|
||||
// make dirs we need
|
||||
listOf(
|
||||
applicationDirs.dataRoot,
|
||||
applicationDirs.extensionsRoot,
|
||||
"${applicationDirs.extensionsRoot}/icon",
|
||||
applicationDirs.thumbnailsRoot
|
||||
ApplicationDirs.dataRoot,
|
||||
ApplicationDirs.extensionsRoot,
|
||||
"${ApplicationDirs.extensionsRoot}/icon",
|
||||
ApplicationDirs.thumbnailsRoot
|
||||
).forEach {
|
||||
File(it).mkdirs()
|
||||
}
|
||||
|
||||
// create conf file if doesn't exist
|
||||
try {
|
||||
val dataConfFile = File("${applicationDirs.dataRoot}/server.conf")
|
||||
val dataConfFile = File("${ApplicationDirs.dataRoot}/server.conf")
|
||||
if (!dataConfFile.exists()) {
|
||||
Main::class.java.getResourceAsStream("/server-reference.conf").use { input ->
|
||||
dataConfFile.outputStream().use { output ->
|
||||
|
||||
@@ -20,8 +20,8 @@ import java.io.IOException
|
||||
fun openInBrowser() {
|
||||
try {
|
||||
Desktop.browseURL("http://127.0.0.1:4567")
|
||||
} catch (e1: IOException) {
|
||||
e1.printStackTrace()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user