diff --git a/server/src/main/kotlin/ir/armor/tachidesk/Main.kt b/server/src/main/kotlin/ir/armor/tachidesk/Main.kt index 663f4b07..79f796f8 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/Main.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/Main.kt @@ -9,6 +9,7 @@ import io.javalin.Javalin import ir.armor.tachidesk.util.applicationSetup import ir.armor.tachidesk.util.getChapter import ir.armor.tachidesk.util.getChapterList +import ir.armor.tachidesk.util.getExtensionIcon import ir.armor.tachidesk.util.getExtensionList import ir.armor.tachidesk.util.getManga import ir.armor.tachidesk.util.getMangaList @@ -94,6 +95,14 @@ class Main { ctx.status(200) } + 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) + } + app.get("/api/v1/source/list") { ctx -> ctx.json(getSourceList()) } diff --git a/server/src/main/kotlin/ir/armor/tachidesk/util/ٍExtension.kt b/server/src/main/kotlin/ir/armor/tachidesk/util/Extension.kt similarity index 92% rename from server/src/main/kotlin/ir/armor/tachidesk/util/ٍExtension.kt rename to server/src/main/kotlin/ir/armor/tachidesk/util/Extension.kt index fe36b234..3c47f6b3 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/util/ٍExtension.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/util/Extension.kt @@ -6,6 +6,7 @@ package ir.armor.tachidesk.util import com.googlecode.dex2jar.tools.Dex2jarCmd import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi +import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.source.online.HttpSource @@ -24,6 +25,7 @@ import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update import uy.kohesive.injekt.injectLazy import java.io.File +import java.io.InputStream import java.net.URL import java.net.URLClassLoader @@ -149,3 +151,18 @@ fun removeExtension(pkgName: String) { File(jarPath).delete() } } + +val network: NetworkHelper by injectLazy() + +fun getExtensionIcon(apkName: String): Pair { + val iconUrl = transaction { ExtensionsTable.select { ExtensionsTable.apkName eq apkName }.firstOrNull()!! }[ExtensionsTable.iconUrl] + + val saveDir = "${Config.extensionsRoot}/icon" + val fileName = apkName + + return getCachedResponse(saveDir, fileName) { + network.client.newCall( + GET(iconUrl) + ).execute() + } +} diff --git a/server/src/main/kotlin/ir/armor/tachidesk/util/ExtensionsList.kt b/server/src/main/kotlin/ir/armor/tachidesk/util/ExtensionsList.kt index 558d729a..0a16c64b 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/util/ExtensionsList.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/util/ExtensionsList.kt @@ -66,6 +66,8 @@ fun getExtensionList(offline: Boolean = false): List { } } } + } else { + println("used cached extension list") } return transaction { diff --git a/server/src/main/kotlin/ir/armor/tachidesk/util/File.kt b/server/src/main/kotlin/ir/armor/tachidesk/util/File.kt index 41b76921..f37304ac 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/util/File.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/util/File.kt @@ -4,6 +4,7 @@ package ir.armor.tachidesk.util * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import okhttp3.Response import okio.BufferedSource import okio.buffer import okio.sink @@ -15,15 +16,15 @@ import java.io.OutputStream import java.nio.file.Files import java.nio.file.Paths -fun writeStream(fileStream: InputStream, path: String) { - Files.newOutputStream(Paths.get(path)).use { os -> - val buffer = ByteArray(128 * 1024) - var len: Int - while (fileStream.read(buffer).also { len = it } > 0) { - os.write(buffer, 0, len) - } - } -} +// fun writeStream(fileStream: InputStream, path: String) { +// Files.newOutputStream(Paths.get(path)).use { os -> +// val buffer = ByteArray(128 * 1024) +// var len: Int +// while (fileStream.read(buffer).also { len = it } > 0) { +// os.write(buffer, 0, len) +// } +// } +// } fun pathToInputStream(path: String): InputStream { return BufferedInputStream(FileInputStream(path)) @@ -42,7 +43,7 @@ fun findFileNameStartingWith(directoryPath: String, fileName: String): String? { * * @param stream the stream where the source is copied. */ -fun BufferedSource.saveTo(stream: OutputStream) { +private fun BufferedSource.saveTo(stream: OutputStream) { use { input -> stream.sink().buffer().use { it.writeAll(input) @@ -50,3 +51,32 @@ fun BufferedSource.saveTo(stream: OutputStream) { } } } + +fun getCachedResponse(saveDir: String, fileName: String, fetcher: () -> Response): Pair { + val cachedFile = findFileNameStartingWith(saveDir, fileName) + val filePath = "$saveDir/$fileName" + if (cachedFile != null) { + val fileType = cachedFile.substringAfter(filePath) + return Pair( + pathToInputStream(cachedFile), + "image/$fileType" + ) + } + + val response = fetcher() + + if (response.code == 200) { + val contentType = response.headers["content-type"]!! + val fullPath = filePath + "." + contentType.substringAfter("image/") + + Files.newOutputStream(Paths.get(fullPath)).use { os -> + response.body!!.source().saveTo(os) + } + return Pair( + pathToInputStream(fullPath), + contentType + ) + } else { + throw Exception("request error! ${response.code}") + } +} diff --git a/server/src/main/kotlin/ir/armor/tachidesk/util/Manga.kt b/server/src/main/kotlin/ir/armor/tachidesk/util/Manga.kt index c7020656..708fdc4d 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/util/Manga.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/util/Manga.kt @@ -83,40 +83,19 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass { fun getThumbnail(mangaId: Int): Pair { val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! } - var filePath = "${Config.thumbnailsRoot}/$mangaId." + val saveDir = Config.thumbnailsRoot + val fileName = mangaId.toString() - val potentialCache = findFileNameStartingWith(Config.thumbnailsRoot, mangaId.toString()) - if (potentialCache != null) { - println("using cached thumbnail file") - return Pair( - pathToInputStream(potentialCache), - "image/${potentialCache.substringAfter(filePath)}" - ) - } + return getCachedResponse(saveDir, fileName) { + val sourceId = mangaEntry[MangaTable.sourceReference].value + val source = getHttpSource(sourceId) + var thumbnailUrl = mangaEntry[MangaTable.thumbnail_url] + if (thumbnailUrl == null || thumbnailUrl.isEmpty()) { + thumbnailUrl = getManga(mangaId, proxyThumbnail = false).thumbnailUrl!! + } - val sourceId = mangaEntry[MangaTable.sourceReference].value - println("getting source for $mangaId") - val source = getHttpSource(sourceId) - var thumbnailUrl = mangaEntry[MangaTable.thumbnail_url] - if (thumbnailUrl == null || thumbnailUrl.isEmpty()) { - thumbnailUrl = getManga(mangaId, proxyThumbnail = false).thumbnailUrl!! - } - println(thumbnailUrl) - val response = source.client.newCall( - GET(thumbnailUrl, source.headers) - ).execute() - - if (response.code == 200) { - val contentType = response.headers["content-type"]!! - filePath += contentType.substringAfter("image/") - - writeStream(response.body!!.byteStream(), filePath) - - return Pair( - pathToInputStream(filePath), - contentType - ) - } else { - throw Exception("request error! ${response.code}") + source.client.newCall( + GET(thumbnailUrl, source.headers) + ).execute() } } diff --git a/server/src/main/kotlin/ir/armor/tachidesk/util/Page.kt b/server/src/main/kotlin/ir/armor/tachidesk/util/Page.kt index 14ddb494..e8bc198c 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/util/Page.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/util/Page.kt @@ -18,8 +18,6 @@ import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update import java.io.File import java.io.InputStream -import java.nio.file.Files -import java.nio.file.Paths fun getTrueImageUrl(page: Page, source: HttpSource): String { if (page.imageUrl == null) { @@ -50,36 +48,10 @@ fun getPageImage(mangaId: Int, chapterId: Int, index: Int): Pair - - response.body!!.source().saveTo(os) - } - -// writeStream(response.body!!.source(), filePath) - - return Pair( - pathToInputStream(filePath), - contentType - ) - } else { - throw Exception("request error! ${response.code}") + return getCachedResponse(saveDir, fileName) { + source.fetchImage(tachiPage).toBlocking().first() } } diff --git a/server/src/main/kotlin/ir/armor/tachidesk/util/Util.kt b/server/src/main/kotlin/ir/armor/tachidesk/util/Util.kt index 54077fd1..f59e16c5 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/util/Util.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/util/Util.kt @@ -12,6 +12,7 @@ fun applicationSetup() { // make dirs we need File(Config.dataRoot).mkdirs() File(Config.extensionsRoot).mkdirs() + File("${Config.extensionsRoot}/icon").mkdirs() File(Config.thumbnailsRoot).mkdirs() makeDataBaseTables() diff --git a/webUI/react/src/components/ExtensionCard.tsx b/webUI/react/src/components/ExtensionCard.tsx index 3a3d3023..78b0c624 100644 --- a/webUI/react/src/components/ExtensionCard.tsx +++ b/webUI/react/src/components/ExtensionCard.tsx @@ -43,7 +43,7 @@ interface IProps { export default function ExtensionCard(props: IProps) { const { extension: { - name, lang, versionName, iconUrl, installed, apkName, + name, lang, versionName, installed, apkName, }, } = props; const [installedState, setInstalledState] = useState((installed ? 'uninstall' : 'install')); @@ -81,7 +81,7 @@ export default function ExtensionCard(props: IProps) { variant="rounded" className={classes.icon} alt={name} - src={iconUrl} + src={`http://127.0.0.1:4567/api/v1/extension/icon/${apkName}`} />
diff --git a/webUI/react/src/typings.d.ts b/webUI/react/src/typings.d.ts index 11c6cece..0f1cb882 100644 --- a/webUI/react/src/typings.d.ts +++ b/webUI/react/src/typings.d.ts @@ -9,6 +9,7 @@ interface IExtension { iconUrl: string installed: boolean apkName: string + pkgName: string } interface ISource {