cached extension icon
This commit is contained in:
@@ -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())
|
||||
}
|
||||
|
||||
+17
@@ -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<InputStream, String> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,8 @@ fun getExtensionList(offline: Boolean = false): List<ExtensionDataClass> {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println("used cached extension list")
|
||||
}
|
||||
|
||||
return transaction {
|
||||
|
||||
@@ -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<InputStream, String> {
|
||||
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}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,40 +83,19 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
|
||||
|
||||
fun getThumbnail(mangaId: Int): Pair<InputStream, String> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<InputStream, St
|
||||
|
||||
val saveDir = getChapterDir(mangaId, chapterId)
|
||||
File(saveDir).mkdirs()
|
||||
var filePath = "$saveDir/$index."
|
||||
val fileName = index.toString()
|
||||
|
||||
val potentialCache = findFileNameStartingWith(saveDir, index.toString())
|
||||
if (potentialCache != null) {
|
||||
println("using cached page file for $index")
|
||||
return Pair(
|
||||
pathToInputStream(potentialCache),
|
||||
"image/${potentialCache.substringAfter("$filePath")}"
|
||||
)
|
||||
}
|
||||
|
||||
val response = source.fetchImage(tachiPage).toBlocking().first()
|
||||
|
||||
if (response.code == 200) {
|
||||
val contentType = response.headers["content-type"]!!
|
||||
filePath += contentType.substringAfter("image/")
|
||||
|
||||
Files.newOutputStream(Paths.get(filePath)).use { os ->
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user