mirror of
https://github.com/tachiyomiorg/tachiyomi-extensions-inspector.git
synced 2024-12-25 16:21:50 +01:00
refactor and more
This commit is contained in:
parent
f983f0e359
commit
035105adf0
@ -9,24 +9,22 @@ package ir.armor.tachidesk.impl
|
|||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
|
||||||
import eu.kanade.tachiyomi.util.lang.awaitSingle
|
|
||||||
import ir.armor.tachidesk.impl.Manga.getManga
|
import ir.armor.tachidesk.impl.Manga.getManga
|
||||||
import ir.armor.tachidesk.impl.Source.getHttpSource
|
import ir.armor.tachidesk.impl.Source.getHttpSource
|
||||||
|
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||||
import ir.armor.tachidesk.model.database.ChapterTable
|
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.dataclass.ChapterDataClass
|
import ir.armor.tachidesk.model.dataclass.ChapterDataClass
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.insert
|
import org.jetbrains.exposed.sql.insert
|
||||||
import org.jetbrains.exposed.sql.insertAndGetId
|
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
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
|
||||||
|
|
||||||
object Chapter {
|
object Chapter {
|
||||||
|
/** get chapter list when showing a manga */
|
||||||
suspend fun getChapterList(mangaId: Int): List<ChapterDataClass> {
|
suspend fun getChapterList(mangaId: Int): List<ChapterDataClass> {
|
||||||
val mangaDetails = getManga(mangaId)
|
val mangaDetails = getManga(mangaId)
|
||||||
val source = getHttpSource(mangaDetails.sourceId.toLong())
|
val source = getHttpSource(mangaDetails.sourceId.toLong())
|
||||||
@ -44,7 +42,7 @@ object Chapter {
|
|||||||
chapterList.reversed().forEachIndexed { index, fetchedChapter ->
|
chapterList.reversed().forEachIndexed { index, fetchedChapter ->
|
||||||
val chapterEntry = ChapterTable.select { ChapterTable.url eq fetchedChapter.url }.firstOrNull()
|
val chapterEntry = ChapterTable.select { ChapterTable.url eq fetchedChapter.url }.firstOrNull()
|
||||||
if (chapterEntry == null) {
|
if (chapterEntry == null) {
|
||||||
ChapterTable.insertAndGetId {
|
ChapterTable.insert {
|
||||||
it[url] = fetchedChapter.url
|
it[url] = fetchedChapter.url
|
||||||
it[name] = fetchedChapter.name
|
it[name] = fetchedChapter.name
|
||||||
it[date_upload] = fetchedChapter.date_upload
|
it[date_upload] = fetchedChapter.date_upload
|
||||||
@ -67,89 +65,75 @@ object Chapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear any orphaned chapters
|
// clear any orphaned chapters that are in the db but not in `chapterList`
|
||||||
val dbChapterCount = transaction { ChapterTable.selectAll().count() }
|
val dbChapterCount = transaction { ChapterTable.selectAll().count() }
|
||||||
if (dbChapterCount > chapterCount) { // we got some clean up due
|
if (dbChapterCount > chapterCount) { // we got some clean up due
|
||||||
// TODO: delete orphan chapters
|
// TODO: delete orphan chapters
|
||||||
}
|
}
|
||||||
|
|
||||||
chapterList.mapIndexed { index, it ->
|
chapterList.map { it ->
|
||||||
ChapterDataClass(
|
ChapterDataClass(
|
||||||
ChapterTable.select { ChapterTable.url eq it.url }.firstOrNull()!![ChapterTable.id].value,
|
|
||||||
it.url,
|
it.url,
|
||||||
it.name,
|
it.name,
|
||||||
it.date_upload,
|
it.date_upload,
|
||||||
it.chapter_number,
|
it.chapter_number,
|
||||||
it.scanlator,
|
it.scanlator,
|
||||||
mangaId,
|
mangaId,
|
||||||
chapterCount - index,
|
|
||||||
chapterCount
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** used to display a chapter, get a chapter in order to show it's pages */
|
||||||
suspend fun getChapter(chapterIndex: Int, mangaId: Int): ChapterDataClass {
|
suspend fun getChapter(chapterIndex: Int, mangaId: Int): ChapterDataClass {
|
||||||
var chapterEntry: ResultRow? = null
|
val chapterEntry = transaction {
|
||||||
var source: HttpSource? = null
|
ChapterTable.select {
|
||||||
var sChapter: SChapter? = null
|
(ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId)
|
||||||
transaction {
|
|
||||||
chapterEntry = ChapterTable.select {
|
|
||||||
ChapterTable.chapterIndex eq chapterIndex and (ChapterTable.manga eq mangaId)
|
|
||||||
}.firstOrNull()!!
|
}.firstOrNull()!!
|
||||||
val mangaEntry = MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!!
|
|
||||||
source = getHttpSource(mangaEntry[MangaTable.sourceReference])
|
|
||||||
sChapter = SChapter.create().apply {
|
|
||||||
url = chapterEntry!![ChapterTable.url]
|
|
||||||
name = chapterEntry!![ChapterTable.name]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val pageList = source!!.fetchPageList(
|
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
|
||||||
sChapter!!
|
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
|
||||||
|
|
||||||
|
val pageList = source.fetchPageList(
|
||||||
|
SChapter.create().apply {
|
||||||
|
url = chapterEntry[ChapterTable.url]
|
||||||
|
name = chapterEntry[ChapterTable.name]
|
||||||
|
}
|
||||||
).awaitSingle()
|
).awaitSingle()
|
||||||
|
|
||||||
return transaction {
|
val chapterId = chapterEntry[ChapterTable.id].value
|
||||||
val chapterRow = chapterEntry!!
|
val chapterCount = transaction { ChapterTable.selectAll().count() }
|
||||||
|
|
||||||
val chapterId = chapterRow[ChapterTable.id].value
|
|
||||||
val chapterCount = transaction { ChapterTable.selectAll().count() }
|
|
||||||
|
|
||||||
val chapter = ChapterDataClass(
|
|
||||||
chapterId,
|
|
||||||
chapterRow[ChapterTable.url],
|
|
||||||
chapterRow[ChapterTable.name],
|
|
||||||
chapterRow[ChapterTable.date_upload],
|
|
||||||
chapterRow[ChapterTable.chapter_number],
|
|
||||||
chapterRow[ChapterTable.scanlator],
|
|
||||||
mangaId,
|
|
||||||
chapterRow[ChapterTable.chapterIndex],
|
|
||||||
chapterCount.toInt(),
|
|
||||||
|
|
||||||
pageList.count()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
// update page list for this chapter
|
||||||
|
transaction {
|
||||||
pageList.forEach { page ->
|
pageList.forEach { page ->
|
||||||
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }.firstOrNull() }
|
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }.firstOrNull() }
|
||||||
if (pageEntry == null) {
|
if (pageEntry == null) {
|
||||||
transaction {
|
PageTable.insert {
|
||||||
PageTable.insert {
|
it[index] = page.index
|
||||||
it[index] = page.index
|
it[url] = page.url
|
||||||
it[url] = page.url
|
it[imageUrl] = page.imageUrl
|
||||||
it[imageUrl] = page.imageUrl
|
it[chapter] = chapterId
|
||||||
it[this.chapter] = chapterId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
transaction {
|
PageTable.update({ (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }) {
|
||||||
PageTable.update({ (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }) {
|
it[url] = page.url
|
||||||
it[url] = page.url
|
it[imageUrl] = page.imageUrl
|
||||||
it[imageUrl] = page.imageUrl
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chapter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ChapterDataClass(
|
||||||
|
chapterEntry[ChapterTable.url],
|
||||||
|
chapterEntry[ChapterTable.name],
|
||||||
|
chapterEntry[ChapterTable.date_upload],
|
||||||
|
chapterEntry[ChapterTable.chapter_number],
|
||||||
|
chapterEntry[ChapterTable.scanlator],
|
||||||
|
mangaId,
|
||||||
|
chapterEntry[ChapterTable.chapterIndex],
|
||||||
|
chapterCount.toInt(),
|
||||||
|
pageList.count()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,11 @@ 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.impl.util.APKExtractor
|
||||||
|
import ir.armor.tachidesk.impl.util.CachedImageResponse.getCachedImageResponse
|
||||||
|
import ir.armor.tachidesk.impl.util.await
|
||||||
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.server.ApplicationDirs
|
import ir.armor.tachidesk.server.ApplicationDirs
|
||||||
import ir.armor.tachidesk.util.await
|
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
@ -136,7 +137,7 @@ object Extension {
|
|||||||
}
|
}
|
||||||
is SourceFactory -> { // theme source or multi lang
|
is SourceFactory -> { // theme source or multi lang
|
||||||
transaction {
|
transaction {
|
||||||
instance.createSources().forEachIndexed { index, source ->
|
instance.createSources().forEach { source ->
|
||||||
val httpSource = source as HttpSource
|
val httpSource = source as HttpSource
|
||||||
if (SourceTable.select { SourceTable.id eq httpSource.id }.count() == 0L) {
|
if (SourceTable.select { SourceTable.id eq httpSource.id }.count() == 0L) {
|
||||||
SourceTable.insert {
|
SourceTable.insert {
|
||||||
|
@ -9,15 +9,16 @@ package ir.armor.tachidesk.impl
|
|||||||
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.util.lang.awaitSingle
|
|
||||||
import ir.armor.tachidesk.impl.MangaList.proxyThumbnailUrl
|
import ir.armor.tachidesk.impl.MangaList.proxyThumbnailUrl
|
||||||
import ir.armor.tachidesk.impl.Source.getHttpSource
|
import ir.armor.tachidesk.impl.Source.getHttpSource
|
||||||
import ir.armor.tachidesk.impl.Source.getSource
|
import ir.armor.tachidesk.impl.Source.getSource
|
||||||
|
import ir.armor.tachidesk.impl.util.CachedImageResponse.getCachedImageResponse
|
||||||
|
import ir.armor.tachidesk.impl.util.await
|
||||||
|
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||||
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 ir.armor.tachidesk.util.await
|
|
||||||
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
|
||||||
|
@ -8,8 +8,8 @@ package ir.armor.tachidesk.impl
|
|||||||
* 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 eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.util.lang.awaitSingle
|
|
||||||
import ir.armor.tachidesk.impl.Source.getHttpSource
|
import ir.armor.tachidesk.impl.Source.getHttpSource
|
||||||
|
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||||
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
|
||||||
|
@ -9,8 +9,9 @@ package ir.armor.tachidesk.impl
|
|||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.util.lang.awaitSingle
|
|
||||||
import ir.armor.tachidesk.impl.Source.getHttpSource
|
import ir.armor.tachidesk.impl.Source.getHttpSource
|
||||||
|
import ir.armor.tachidesk.impl.util.CachedImageResponse.getCachedImageResponse
|
||||||
|
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||||
import ir.armor.tachidesk.model.database.ChapterTable
|
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
|
||||||
|
@ -7,9 +7,9 @@ package ir.armor.tachidesk.impl
|
|||||||
* 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 eu.kanade.tachiyomi.util.lang.awaitSingle
|
|
||||||
import ir.armor.tachidesk.impl.MangaList.processEntries
|
import ir.armor.tachidesk.impl.MangaList.processEntries
|
||||||
import ir.armor.tachidesk.impl.Source.getHttpSource
|
import ir.armor.tachidesk.impl.Source.getHttpSource
|
||||||
|
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||||
import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass
|
import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass
|
||||||
|
|
||||||
object Search {
|
object Search {
|
||||||
|
@ -175,8 +175,8 @@ object APKExtractor {
|
|||||||
return compXmlStringAt(xml, strOff)
|
return compXmlStringAt(xml, strOff)
|
||||||
}
|
}
|
||||||
|
|
||||||
var spaces = " "
|
private var spaces = " "
|
||||||
fun prtIndent(indent: Int, str: String) {
|
private fun prtIndent(indent: Int, str: String) {
|
||||||
logger.debug(spaces.substring(0, Math.min(indent * 2, spaces.length)) + str)
|
logger.debug(spaces.substring(0, Math.min(indent * 2, spaces.length)) + str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
package ir.armor.tachidesk.impl.util
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
import okhttp3.Response
|
||||||
|
import okio.buffer
|
||||||
|
import okio.sink
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
object CachedImageResponse {
|
||||||
|
private fun pathToInputStream(path: String): InputStream {
|
||||||
|
return BufferedInputStream(FileInputStream(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findFileNameStartingWith(directoryPath: String, fileName: String): String? {
|
||||||
|
File(directoryPath).listFiles().forEach { file ->
|
||||||
|
if (file.name.startsWith(fileName))
|
||||||
|
return "$directoryPath/${file.name}"
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** fetch a cached image response, calls `fetcher` if cache fails */
|
||||||
|
suspend fun getCachedImageResponse(saveDir: String, fileName: String, fetcher: suspend () -> 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 { output ->
|
||||||
|
response.body!!.source().use { input ->
|
||||||
|
output.sink().buffer().use {
|
||||||
|
it.writeAll(input)
|
||||||
|
it.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Pair(
|
||||||
|
pathToInputStream(fullPath),
|
||||||
|
contentType
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
throw Exception("request error! ${response.code}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,85 +0,0 @@
|
|||||||
package ir.armor.tachidesk.impl
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
|
||||||
* 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/. */
|
|
||||||
|
|
||||||
import okhttp3.Response
|
|
||||||
import okio.BufferedSource
|
|
||||||
import okio.buffer
|
|
||||||
import okio.sink
|
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
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 pathToInputStream(path: String): InputStream {
|
|
||||||
return BufferedInputStream(FileInputStream(path))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findFileNameStartingWith(directoryPath: String, fileName: String): String? {
|
|
||||||
File(directoryPath).listFiles().forEach { file ->
|
|
||||||
if (file.name.startsWith(fileName))
|
|
||||||
return "$directoryPath/${file.name}"
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the given source to an output stream and closes both resources.
|
|
||||||
*
|
|
||||||
* @param stream the stream where the source is copied.
|
|
||||||
*/
|
|
||||||
private fun BufferedSource.saveTo(stream: OutputStream) {
|
|
||||||
use { input ->
|
|
||||||
stream.sink().buffer().use {
|
|
||||||
it.writeAll(input)
|
|
||||||
it.flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getCachedImageResponse(saveDir: String, fileName: String, fetcher: suspend () -> 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}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,11 @@
|
|||||||
package ir.armor.tachidesk.util
|
package ir.armor.tachidesk.impl.util
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import okhttp3.Call
|
import okhttp3.Call
|
@ -1,19 +1,21 @@
|
|||||||
package eu.kanade.tachiyomi.util.lang
|
package ir.armor.tachidesk.impl.util
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
import kotlinx.coroutines.CancellableContinuation
|
import kotlinx.coroutines.CancellableContinuation
|
||||||
import kotlinx.coroutines.CancellationException
|
|
||||||
import kotlinx.coroutines.CoroutineStart
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import rx.Emitter
|
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscriber
|
import rx.Subscriber
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.resumeWithException
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
|
// source: https://github.com/jobobby04/TachiyomiSY/blob/9320221a4e8b118ef68deb60d8c4c32bcbb9e06f/app/src/main/java/eu/kanade/tachiyomi/util/lang/RxCoroutineBridge.kt
|
||||||
/*
|
/*
|
||||||
* Util functions for bridging RxJava and coroutines. Taken from TachiyomiEH/SY.
|
* Util functions for bridging RxJava and coroutines. Taken from TachiyomiEH/SY.
|
||||||
*/
|
*/
|
||||||
@ -56,5 +58,5 @@ private suspend fun <T> Observable<T>.awaitOne(): T = suspendCancellableCoroutin
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun <T> CancellableContinuation<T>.unsubscribeOnCancellation(sub: Subscription) =
|
private fun <T> CancellableContinuation<T>.unsubscribeOnCancellation(sub: Subscription) =
|
||||||
invokeOnCancellation { sub.unsubscribe() }
|
invokeOnCancellation { sub.unsubscribe() }
|
@ -8,14 +8,19 @@ package ir.armor.tachidesk.model.dataclass
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
data class ChapterDataClass(
|
data class ChapterDataClass(
|
||||||
val id: Int,
|
|
||||||
val url: String,
|
val url: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val date_upload: Long,
|
val date_upload: Long,
|
||||||
val chapter_number: Float,
|
val chapter_number: Float,
|
||||||
val scanlator: String?,
|
val scanlator: String?,
|
||||||
val mangaId: Int,
|
val mangaId: Int,
|
||||||
val chapterIndex: Int,
|
|
||||||
val chapterCount: Int,
|
/** this chapter's index */
|
||||||
|
val chapterIndex: Int? = null,
|
||||||
|
|
||||||
|
/** total chapter count, used to calculate if there's a next and prev chapter */
|
||||||
|
val chapterCount: Int? = null,
|
||||||
|
|
||||||
|
/** used to construct pages in the front-end */
|
||||||
val pageCount: Int? = null,
|
val pageCount: Int? = null,
|
||||||
)
|
)
|
||||||
|
@ -33,7 +33,6 @@ import ir.armor.tachidesk.impl.Source.getSourceList
|
|||||||
import ir.armor.tachidesk.server.util.openInBrowser
|
import ir.armor.tachidesk.server.util.openInBrowser
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.future.future
|
import kotlinx.coroutines.future.future
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
@ -198,11 +197,13 @@ object JavalinSetup {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get chapter list when showing a manga
|
||||||
app.get("/api/v1/manga/:mangaId/chapters") { ctx ->
|
app.get("/api/v1/manga/:mangaId/chapters") { ctx ->
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
ctx.json(scope.future { getChapterList(mangaId) })
|
ctx.json(scope.future { getChapterList(mangaId) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// used to display a chapter, get a chapter in order to show it's pages
|
||||||
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex") { ctx ->
|
app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex") { ctx ->
|
||||||
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
|
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
|
||||||
val mangaId = ctx.pathParam("mangaId").toInt()
|
val mangaId = ctx.pathParam("mangaId").toInt()
|
||||||
|
Loading…
Reference in New Issue
Block a user