let's not polute the namespace together

This commit is contained in:
Aria Moradi 2021-03-30 21:10:41 +04:30
parent 90ae180b3e
commit 5656016700
16 changed files with 833 additions and 834 deletions

View File

@ -7,8 +7,8 @@ package ir.armor.tachidesk
* 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.JavalinSetup.javalinSetup
import ir.armor.tachidesk.server.applicationSetup import ir.armor.tachidesk.server.applicationSetup
import ir.armor.tachidesk.server.javalinSetup
class Main { class Main {
companion object { companion object {

View File

@ -47,7 +47,7 @@ object Category {
/** /**
* Move the category from position `from` to `to` * 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 { transaction {
val categories = CategoryTable.selectAll().orderBy(CategoryTable.order to SortOrder.ASC).toMutableList() val categories = CategoryTable.selectAll().orderBy(CategoryTable.order to SortOrder.ASC).toMutableList()
categories.add(to - 1, categories.removeAt(from - 1)) 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 { transaction {
CategoryMangaTable.select { CategoryMangaTable.category eq categoryId }.forEach { CategoryMangaTable.select { CategoryMangaTable.category eq categoryId }.forEach {
removeMangaFromCategory(it[CategoryMangaTable.manga].value, categoryId) removeMangaFromCategory(it[CategoryMangaTable.manga].value, categoryId)
} }
CategoryTable.deleteWhere { CategoryTable.id eq categoryId } CategoryTable.deleteWhere { CategoryTable.id eq categoryId }
} }
} }
fun getCategoryList(): List<CategoryDataClass> { fun getCategoryList(): List<CategoryDataClass> {
return transaction { return transaction {
CategoryTable.selectAll().orderBy(CategoryTable.order to SortOrder.ASC).map { CategoryTable.selectAll().orderBy(CategoryTable.order to SortOrder.ASC).map {
CategoryTable.toDataClass(it) CategoryTable.toDataClass(it)
} }
} }
} }
} }

View File

@ -22,7 +22,7 @@ import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
object CategoryManga { object CategoryManga {
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) {
CategoryMangaTable.insert { 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 { transaction {
CategoryMangaTable.deleteWhere { (CategoryMangaTable.category eq categoryId) and (CategoryMangaTable.manga eq mangaId) } CategoryMangaTable.deleteWhere { (CategoryMangaTable.category eq categoryId) and (CategoryMangaTable.manga eq mangaId) }
if (CategoryMangaTable.select { CategoryMangaTable.manga eq mangaId }.count() == 0L) { 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 * list of mangas that belong to a category
*/ */
fun getCategoryMangaList(categoryId: Int): List<MangaDataClass> { fun getCategoryMangaList(categoryId: Int): List<MangaDataClass> {
return transaction { return transaction {
CategoryMangaTable.innerJoin(MangaTable).select { CategoryMangaTable.category eq categoryId }.map { CategoryMangaTable.innerJoin(MangaTable).select { CategoryMangaTable.category eq categoryId }.map {
MangaTable.toDataClass(it) MangaTable.toDataClass(it)
} }
} }
} }
/** /**
* list of categories that a manga belongs to * list of categories that a manga belongs to
*/ */
fun getMangaCategories(mangaId: Int): List<CategoryDataClass> { fun getMangaCategories(mangaId: Int): List<CategoryDataClass> {
return transaction { return transaction {
CategoryMangaTable.innerJoin(CategoryTable).select { CategoryMangaTable.manga eq mangaId }.orderBy(CategoryTable.order to SortOrder.ASC).map { CategoryMangaTable.innerJoin(CategoryTable).select { CategoryMangaTable.manga eq mangaId }.orderBy(CategoryTable.order to SortOrder.ASC).map {
CategoryTable.toDataClass(it) CategoryTable.toDataClass(it)
} }
} }
} }
} }

View File

@ -16,10 +16,10 @@ 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.impl.ExtensionsList.extensionTableAsDataClass 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.ExtensionTable
import ir.armor.tachidesk.model.database.SourceTable 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 kotlinx.coroutines.runBlocking
import mu.KotlinLogging import mu.KotlinLogging
import okhttp3.Request import okhttp3.Request
@ -39,12 +39,12 @@ import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
object Extension { object Extension {
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
/** /**
* Convert dex to jar, a wrapper for the dex2jar library * 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 // 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 // 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) .skipExceptions(false)
.to(jarFilePath) .to(jarFilePath)
if (handler.hasException()) { 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( logger.error(
"Detail Error Information in File $errorFile\n" + "Detail Error Information in File $errorFile\n" +
"Please report this file to one of following link if possible (any one).\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>()) handler.dump(errorFile, emptyArray<String>())
} }
} }
/** /**
* loads the extension main class called $className from the jar located at $jarPath * 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. * 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 classLoader = URLClassLoader(arrayOf<URL>(URL("file:$jarPath")))
val classToLoad = Class.forName(className, true, classLoader) val classToLoad = Class.forName(className, true, classLoader)
return classToLoad.getDeclaredConstructor().newInstance() return classToLoad.getDeclaredConstructor().newInstance()
} }
fun installExtension(pkgName: String): Int { fun installExtension(pkgName: String): Int {
logger.debug("Installing $pkgName") logger.debug("Installing $pkgName")
val extensionRecord = extensionTableAsDataClass().first { it.pkgName == pkgName } val extensionRecord = extensionTableAsDataClass().first { it.pkgName == pkgName }
val fileNameWithoutType = extensionRecord.apkName.substringBefore(".apk") 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 // 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()) { if (!File(jarPath).exists()) {
runBlocking { runBlocking {
val apkToDownload = ExtensionGithubApi.getApkUrl(extensionRecord) val apkToDownload = ExtensionGithubApi.getApkUrl(extensionRecord)
@ -169,11 +169,11 @@ fun installExtension(pkgName: String): Int {
} else { } else {
return 302 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 request = Request.Builder().url(url).build()
val response = networkHelper.client.newCall(request).execute() val response = networkHelper.client.newCall(request).execute()
@ -181,14 +181,14 @@ private fun downloadAPKFile(url: String, apkPath: String) {
val sink = downloadedFile.sink().buffer() val sink = downloadedFile.sink().buffer()
sink.writeAll(response.body!!.source()) sink.writeAll(response.body!!.source())
sink.close() sink.close()
} }
fun uninstallExtension(pkgName: String) { fun uninstallExtension(pkgName: String) {
logger.debug("Uninstalling $pkgName") logger.debug("Uninstalling $pkgName")
val extensionRecord = transaction { ExtensionTable.select { ExtensionTable.pkgName eq pkgName }.firstOrNull()!! } val extensionRecord = transaction { ExtensionTable.select { ExtensionTable.pkgName eq pkgName }.firstOrNull()!! }
val fileNameWithoutType = extensionRecord[ExtensionTable.apkName].substringBefore(".apk") val fileNameWithoutType = extensionRecord[ExtensionTable.apkName].substringBefore(".apk")
val jarPath = "${applicationDirs.extensionsRoot}/$fileNameWithoutType.jar" val jarPath = "${ApplicationDirs.extensionsRoot}/$fileNameWithoutType.jar"
transaction { transaction {
val extensionId = extensionRecord[ExtensionTable.id].value val extensionId = extensionRecord[ExtensionTable.id].value
@ -204,9 +204,9 @@ fun uninstallExtension(pkgName: String) {
if (File(jarPath).exists()) { if (File(jarPath).exists()) {
File(jarPath).delete() File(jarPath).delete()
} }
} }
fun updateExtension(pkgName: String): Int { fun updateExtension(pkgName: String): Int {
val targetExtension = ExtensionsList.updateMap.remove(pkgName)!! val targetExtension = ExtensionsList.updateMap.remove(pkgName)!!
uninstallExtension(pkgName) uninstallExtension(pkgName)
transaction { transaction {
@ -222,23 +222,23 @@ fun updateExtension(pkgName: String): Int {
} }
} }
return installExtension(pkgName) 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 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) { return getCachedImageResponse(saveDir, apkName) {
network.client.newCall( network.client.newCall(
GET(iconUrl) GET(iconUrl)
).execute() ).execute()
} }
} }
fun getExtensionIconUrl(apkName: String): String { fun getExtensionIconUrl(apkName: String): String {
return "/api/v1/extension/icon/$apkName" return "/api/v1/extension/icon/$apkName"
} }
} }

View File

@ -23,17 +23,15 @@ import org.jetbrains.exposed.sql.update
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
object ExtensionsList { object ExtensionsList {
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
var lastUpdateCheck: Long = 0 var lastUpdateCheck: Long = 0
var updateMap = ConcurrentHashMap<String, Extension.Available>() var updateMap = ConcurrentHashMap<String, Extension.Available>()
// const val ExtensionUpdateDelayTime = 60 * 1000 // 60,000 milliseconds = 60 seconds // 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 // update if {ExtensionUpdateDelayTime} seconds has passed or requested offline and database is empty
if (lastUpdateCheck + ExtensionUpdateDelayTime < System.currentTimeMillis()) { if (lastUpdateCheck + ExtensionUpdateDelayTime < System.currentTimeMillis()) {
logger.debug("Getting extensions list from the internet") logger.debug("Getting extensions list from the internet")
@ -47,9 +45,9 @@ fun getExtensionList(): List<ExtensionDataClass> {
} }
return extensionTableAsDataClass() return extensionTableAsDataClass()
} }
fun extensionTableAsDataClass() = transaction { fun extensionTableAsDataClass() = transaction {
ExtensionTable.selectAll().map { ExtensionTable.selectAll().map {
ExtensionDataClass( ExtensionDataClass(
it[ExtensionTable.name], it[ExtensionTable.name],
@ -65,9 +63,9 @@ fun extensionTableAsDataClass() = transaction {
it[ExtensionTable.isObsolete], it[ExtensionTable.isObsolete],
) )
} }
} }
private fun updateExtensionDatabase(foundExtensions: List<Extension.Available>) { private fun updateExtensionDatabase(foundExtensions: List<Extension.Available>) {
transaction { transaction {
foundExtensions.forEach { foundExtension -> foundExtensions.forEach { foundExtension ->
val extensionRecord = ExtensionTable.select { ExtensionTable.pkgName eq foundExtension.pkgName }.firstOrNull() val extensionRecord = ExtensionTable.select { ExtensionTable.pkgName eq foundExtension.pkgName }.firstOrNull()
@ -132,5 +130,5 @@ private fun updateExtensionDatabase(foundExtensions: List<Extension.Available>)
} }
} }
} }
} }
} }

View File

@ -22,7 +22,7 @@ object Library {
// TODO: `Category.isLanding` is to handle the default categories a new library manga gets, // TODO: `Category.isLanding` is to handle the default categories a new library manga gets,
// ..implement that shit at some time... // ..implement that shit at some time...
// ..also Consider to rename it to `isDefault` // ..also Consider to rename it to `isDefault`
fun addMangaToLibrary(mangaId: Int) { fun addMangaToLibrary(mangaId: Int) {
val manga = getManga(mangaId) val manga = getManga(mangaId)
if (!manga.inLibrary) { if (!manga.inLibrary) {
transaction { transaction {
@ -31,9 +31,9 @@ fun addMangaToLibrary(mangaId: Int) {
} }
} }
} }
} }
fun removeMangaFromLibrary(mangaId: Int) { fun removeMangaFromLibrary(mangaId: Int) {
val manga = getManga(mangaId) val manga = getManga(mangaId)
if (manga.inLibrary) { if (manga.inLibrary) {
transaction { transaction {
@ -44,13 +44,13 @@ fun removeMangaFromLibrary(mangaId: Int) {
CategoryMangaTable.deleteWhere { CategoryMangaTable.manga eq mangaId } CategoryMangaTable.deleteWhere { CategoryMangaTable.manga eq mangaId }
} }
} }
} }
fun getLibraryMangas(): List<MangaDataClass> { fun getLibraryMangas(): List<MangaDataClass> {
return transaction { return transaction {
MangaTable.select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) }.map { MangaTable.select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) }.map {
MangaTable.toDataClass(it) MangaTable.toDataClass(it)
} }
} }
} }
} }

View File

@ -15,14 +15,14 @@ import ir.armor.tachidesk.impl.Source.getSource
import ir.armor.tachidesk.model.database.MangaStatus import ir.armor.tachidesk.model.database.MangaStatus
import ir.armor.tachidesk.model.database.MangaTable import ir.armor.tachidesk.model.database.MangaTable
import ir.armor.tachidesk.model.dataclass.MangaDataClass 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.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
import java.io.InputStream import java.io.InputStream
object Manga { 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()!! } var mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
return if (mangaEntry[MangaTable.initialized]) { return if (mangaEntry[MangaTable.initialized]) {
@ -90,11 +90,11 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
getSource(mangaEntry[MangaTable.sourceReference]) 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 mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
val saveDir = applicationDirs.thumbnailsRoot val saveDir = ApplicationDirs.thumbnailsRoot
val fileName = mangaId.toString() val fileName = mangaId.toString()
return getCachedImageResponse(saveDir, fileName) { return getCachedImageResponse(saveDir, fileName) {
@ -109,5 +109,5 @@ fun getMangaThumbnail(mangaId: Int): Pair<InputStream, String> {
GET(thumbnailUrl, source.headers) GET(thumbnailUrl, source.headers)
).execute() ).execute()
} }
} }
} }

View File

@ -18,11 +18,11 @@ import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
object MangaList { object MangaList {
fun proxyThumbnailUrl(mangaId: Int): String { fun proxyThumbnailUrl(mangaId: Int): String {
return "/api/v1/manga/$mangaId/thumbnail" 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 source = getHttpSource(sourceId.toLong())
val mangasPage = if (popular) { val mangasPage = if (popular) {
source.fetchPopularManga(pageNum).toBlocking().first() 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") throw Exception("Source $source doesn't support latest")
} }
return mangasPage.processEntries(sourceId) return mangasPage.processEntries(sourceId)
} }
fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass { fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
val mangasPage = this val mangasPage = this
val mangaList = transaction { val mangaList = transaction {
return@transaction mangasPage.mangas.map { manga -> return@transaction mangasPage.mangas.map { manga ->
@ -97,5 +97,5 @@ fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
mangaList, mangaList,
mangasPage.hasNextPage mangasPage.hasNextPage
) )
} }
} }

View File

@ -14,7 +14,7 @@ import ir.armor.tachidesk.model.database.ChapterTable
import ir.armor.tachidesk.model.database.MangaTable import ir.armor.tachidesk.model.database.MangaTable
import ir.armor.tachidesk.model.database.PageTable import ir.armor.tachidesk.model.database.PageTable
import ir.armor.tachidesk.model.database.SourceTable 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.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
@ -22,19 +22,19 @@ import org.jetbrains.exposed.sql.update
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
object Page{ object Page {
/** /**
* A page might have a imageUrl ready from the get go, or we might need to * 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. * 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) { if (page.imageUrl == null) {
page.imageUrl = source.fetchImageUrl(page).toBlocking().first()!! page.imageUrl = source.fetchImageUrl(page).toBlocking().first()!!
} }
return page.imageUrl!! 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 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 {
@ -67,10 +67,10 @@ fun getPageImage(mangaId: Int, chapterIndex: Int, index: Int): Pair<InputStream,
return getCachedImageResponse(saveDir, fileName) { return getCachedImageResponse(saveDir, fileName) {
source.fetchImage(tachiPage).toBlocking().first() source.fetchImage(tachiPage).toBlocking().first()
} }
} }
// TODO: rewrite this to match tachiyomi // 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 mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
val sourceId = mangaEntry[MangaTable.sourceReference] val sourceId = mangaEntry[MangaTable.sourceReference]
val source = getHttpSource(sourceId) val source = getHttpSource(sourceId)
@ -85,9 +85,9 @@ fun getChapterDir(mangaId: Int, chapterId: Int): String {
val mangaTitle = mangaEntry[MangaTable.title] val mangaTitle = mangaEntry[MangaTable.title]
val sourceName = source.toString() val sourceName = source.toString()
val mangaDir = "${applicationDirs.mangaRoot}/$sourceName/$mangaTitle/$chapterDir" val mangaDir = "${ApplicationDirs.mangaRoot}/$sourceName/$mangaTitle/$chapterDir"
// make sure dirs exist // make sure dirs exist
File(mangaDir).mkdirs() File(mangaDir).mkdirs()
return mangaDir return mangaDir
} }
} }

View File

@ -13,25 +13,25 @@ import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass
object Search { object Search {
// TODO // TODO
fun sourceFilters(sourceId: Long) { fun sourceFilters(sourceId: Long) {
val source = getHttpSource(sourceId) val source = getHttpSource(sourceId)
// source.getFilterList().toItems() // 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 source = getHttpSource(sourceId)
val searchManga = source.fetchSearchManga(pageNum, searchTerm, source.getFilterList()).toBlocking().first() val searchManga = source.fetchSearchManga(pageNum, searchTerm, source.getFilterList()).toBlocking().first()
return searchManga.processEntries(sourceId) return searchManga.processEntries(sourceId)
} }
fun sourceGlobalSearch(searchTerm: String) { fun sourceGlobalSearch(searchTerm: String) {
// TODO // TODO
} }
data class FilterWrapper( data class FilterWrapper(
val type: String, val type: String,
val filter: Any val filter: Any
) )
/** /**
* Note: Exhentai had a filter serializer (now in SY) that we might be able to steal * Note: Exhentai had a filter serializer (now in SY) that we might be able to steal

View File

@ -14,7 +14,7 @@ import ir.armor.tachidesk.impl.Extension.loadExtensionInstance
import ir.armor.tachidesk.model.database.ExtensionTable import ir.armor.tachidesk.model.database.ExtensionTable
import ir.armor.tachidesk.model.database.SourceTable import ir.armor.tachidesk.model.database.SourceTable
import ir.armor.tachidesk.model.dataclass.SourceDataClass import ir.armor.tachidesk.model.dataclass.SourceDataClass
import ir.armor.tachidesk.server.applicationDirs import ir.armor.tachidesk.server.ApplicationDirs
import mu.KotlinLogging 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
@ -22,11 +22,11 @@ import org.jetbrains.exposed.sql.transactions.transaction
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
object Source { 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] val cachedResult: HttpSource? = sourceCache[sourceId]
if (cachedResult != null) { if (cachedResult != null) {
logger.debug("used cached HttpSource: ${cachedResult.name}") logger.debug("used cached HttpSource: ${cachedResult.name}")
@ -41,7 +41,7 @@ fun getHttpSource(sourceId: Long): HttpSource {
val apkName = extensionRecord[ExtensionTable.apkName] val apkName = extensionRecord[ExtensionTable.apkName]
val className = extensionRecord[ExtensionTable.classFQName] val className = extensionRecord[ExtensionTable.classFQName]
val jarName = apkName.substringBefore(".apk") + ".jar" val jarName = apkName.substringBefore(".apk") + ".jar"
val jarPath = "${applicationDirs.extensionsRoot}/$jarName" val jarPath = "${ApplicationDirs.extensionsRoot}/$jarName"
val extensionInstance = loadExtensionInstance(jarPath, className) val extensionInstance = loadExtensionInstance(jarPath, className)
@ -56,9 +56,9 @@ fun getHttpSource(sourceId: Long): HttpSource {
} }
} }
return sourceCache[sourceId]!! return sourceCache[sourceId]!!
} }
fun getSourceList(): List<SourceDataClass> { fun getSourceList(): List<SourceDataClass> {
return transaction { return transaction {
SourceTable.selectAll().map { SourceTable.selectAll().map {
SourceDataClass( SourceDataClass(
@ -70,9 +70,9 @@ fun getSourceList(): List<SourceDataClass> {
) )
} }
} }
} }
fun getSource(sourceId: Long): SourceDataClass { fun getSource(sourceId: Long): SourceDataClass {
return transaction { return transaction {
val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull() val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
@ -84,5 +84,5 @@ fun getSource(sourceId: Long): SourceDataClass {
source?.let { getHttpSource(sourceId).supportsLatest } source?.let { getHttpSource(sourceId).supportsLatest }
) )
} }
} }
} }

View File

@ -14,14 +14,14 @@ import ir.armor.tachidesk.model.database.ExtensionTable
import ir.armor.tachidesk.model.database.MangaTable import ir.armor.tachidesk.model.database.MangaTable
import ir.armor.tachidesk.model.database.PageTable import ir.armor.tachidesk.model.database.PageTable
import ir.armor.tachidesk.model.database.SourceTable 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.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
object DBMangaer { object DBMangaer {
val db by lazy { val db by lazy {
Database.connect("jdbc:h2:${applicationDirs.dataRoot}/database", "org.h2.Driver") Database.connect("jdbc:h2:${ApplicationDirs.dataRoot}/database", "org.h2.Driver")
} }
} }

View File

@ -41,9 +41,10 @@ import java.io.IOException
* 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/. */
private val logger = KotlinLogging.logger {} object JavalinSetup {
private val logger = KotlinLogging.logger {}
fun javalinSetup() { fun javalinSetup() {
var hasWebUiBundled = false var hasWebUiBundled = false
val app = Javalin.create { config -> val app = Javalin.create { config ->
@ -285,4 +286,5 @@ fun javalinSetup() {
val categoryId = ctx.pathParam("categoryId").toInt() val categoryId = ctx.pathParam("categoryId").toInt()
ctx.json(getCategoryMangaList(categoryId)) ctx.json(getCategoryMangaList(categoryId))
} }
}
} }

View File

@ -24,7 +24,7 @@ import java.io.File
private val logger = KotlinLogging.logger {} 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"
val thumbnailsRoot = "$dataRoot/thumbnails" val thumbnailsRoot = "$dataRoot/thumbnails"
@ -50,17 +50,17 @@ fun applicationSetup() {
// make dirs we need // make dirs we need
listOf( listOf(
applicationDirs.dataRoot, ApplicationDirs.dataRoot,
applicationDirs.extensionsRoot, ApplicationDirs.extensionsRoot,
"${applicationDirs.extensionsRoot}/icon", "${ApplicationDirs.extensionsRoot}/icon",
applicationDirs.thumbnailsRoot ApplicationDirs.thumbnailsRoot
).forEach { ).forEach {
File(it).mkdirs() File(it).mkdirs()
} }
// create conf file if doesn't exist // create conf file if doesn't exist
try { try {
val dataConfFile = File("${applicationDirs.dataRoot}/server.conf") val dataConfFile = File("${ApplicationDirs.dataRoot}/server.conf")
if (!dataConfFile.exists()) { if (!dataConfFile.exists()) {
Main::class.java.getResourceAsStream("/server-reference.conf").use { input -> Main::class.java.getResourceAsStream("/server-reference.conf").use { input ->
dataConfFile.outputStream().use { output -> dataConfFile.outputStream().use { output ->

View File

@ -20,8 +20,8 @@ import java.io.IOException
fun openInBrowser() { fun openInBrowser() {
try { try {
Desktop.browseURL("http://127.0.0.1:4567") Desktop.browseURL("http://127.0.0.1:4567")
} catch (e1: IOException) { } catch (e: Exception) {
e1.printStackTrace() e.printStackTrace()
} }
} }