Smarter Chapters and cleanup (#87)

* Smarter Chapters and cleanup

* Fix check
This commit is contained in:
Syer10 2021-05-18 13:52:15 -04:00 committed by GitHub
parent be930bb68b
commit e8df84416c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 131 additions and 115 deletions

View File

@ -27,96 +27,105 @@ import org.jetbrains.exposed.sql.update
object Chapter {
/** get chapter list when showing a manga */
suspend fun getChapterList(mangaId: Int, onlineFetch: Boolean): List<ChapterDataClass> {
return if (!onlineFetch) {
suspend fun getChapterList(mangaId: Int, onlineFetch: Boolean?): List<ChapterDataClass> {
return if (onlineFetch == true) {
getSourceChapters(mangaId)
} else {
transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }.orderBy(ChapterTable.chapterIndex to DESC)
.map {
ChapterTable.toDataClass(it)
}
}.ifEmpty {
// If it was explicitly set to offline dont grab chapters
if (onlineFetch == null) {
getSourceChapters(mangaId)
} else emptyList()
}
} else {
}
}
val mangaDetails = getManga(mangaId)
val source = getHttpSource(mangaDetails.sourceId.toLong())
val chapterList = source.fetchChapterList(
SManga.create().apply {
title = mangaDetails.title
url = mangaDetails.url
}
).awaitSingle()
private suspend fun getSourceChapters(mangaId: Int): List<ChapterDataClass> {
val mangaDetails = getManga(mangaId)
val source = getHttpSource(mangaDetails.sourceId.toLong())
val chapterList = source.fetchChapterList(
SManga.create().apply {
title = mangaDetails.title
url = mangaDetails.url
}
).awaitSingle()
val chapterCount = chapterList.count()
val chapterCount = chapterList.count()
transaction {
chapterList.reversed().forEachIndexed { index, fetchedChapter ->
val chapterEntry = ChapterTable.select { ChapterTable.url eq fetchedChapter.url }.firstOrNull()
if (chapterEntry == null) {
ChapterTable.insert {
it[url] = fetchedChapter.url
it[name] = fetchedChapter.name
it[date_upload] = fetchedChapter.date_upload
it[chapter_number] = fetchedChapter.chapter_number
it[scanlator] = fetchedChapter.scanlator
transaction {
chapterList.reversed().forEachIndexed { index, fetchedChapter ->
val chapterEntry = ChapterTable.select { ChapterTable.url eq fetchedChapter.url }.firstOrNull()
if (chapterEntry == null) {
ChapterTable.insert {
it[url] = fetchedChapter.url
it[name] = fetchedChapter.name
it[date_upload] = fetchedChapter.date_upload
it[chapter_number] = fetchedChapter.chapter_number
it[scanlator] = fetchedChapter.scanlator
it[chapterIndex] = index + 1
it[manga] = mangaId
}
} else {
ChapterTable.update({ ChapterTable.url eq fetchedChapter.url }) {
it[name] = fetchedChapter.name
it[date_upload] = fetchedChapter.date_upload
it[chapter_number] = fetchedChapter.chapter_number
it[scanlator] = fetchedChapter.scanlator
it[chapterIndex] = index + 1
it[manga] = mangaId
}
} else {
ChapterTable.update({ ChapterTable.url eq fetchedChapter.url }) {
it[name] = fetchedChapter.name
it[date_upload] = fetchedChapter.date_upload
it[chapter_number] = fetchedChapter.chapter_number
it[scanlator] = fetchedChapter.scanlator
it[chapterIndex] = index + 1
it[manga] = mangaId
}
it[chapterIndex] = index + 1
it[manga] = mangaId
}
}
}
}
// clear any orphaned chapters that are in the db but not in `chapterList`
val dbChapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() }
if (dbChapterCount > chapterCount) { // we got some clean up due
val dbChapterList = transaction { ChapterTable.select { ChapterTable.manga eq mangaId } }
// clear any orphaned chapters that are in the db but not in `chapterList`
val dbChapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() }
if (dbChapterCount > chapterCount) { // we got some clean up due
val dbChapterList = transaction { ChapterTable.select { ChapterTable.manga eq mangaId } }
dbChapterList.forEach {
if (it[ChapterTable.chapterIndex] >= chapterList.size ||
chapterList[it[ChapterTable.chapterIndex] - 1].url != it[ChapterTable.url]
) {
transaction {
PageTable.deleteWhere { PageTable.chapter eq it[ChapterTable.id] }
ChapterTable.deleteWhere { ChapterTable.id eq it[ChapterTable.id] }
}
dbChapterList.forEach {
if (it[ChapterTable.chapterIndex] >= chapterList.size ||
chapterList[it[ChapterTable.chapterIndex] - 1].url != it[ChapterTable.url]
) {
transaction {
PageTable.deleteWhere { PageTable.chapter eq it[ChapterTable.id] }
ChapterTable.deleteWhere { ChapterTable.id eq it[ChapterTable.id] }
}
}
}
}
val dbChapterMap = transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }
.associateBy({ it[ChapterTable.url] }, { it })
}
val dbChapterMap = transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }
.associateBy({ it[ChapterTable.url] }, { it })
}
return chapterList.mapIndexed { index, it ->
return chapterList.mapIndexed { index, it ->
val dbChapter = dbChapterMap.getValue(it.url)
val dbChapter = dbChapterMap.getValue(it.url)
ChapterDataClass(
it.url,
it.name,
it.date_upload,
it.chapter_number,
it.scanlator,
mangaId,
ChapterDataClass(
it.url,
it.name,
it.date_upload,
it.chapter_number,
it.scanlator,
mangaId,
dbChapter[ChapterTable.isRead],
dbChapter[ChapterTable.isBookmarked],
dbChapter[ChapterTable.lastPageRead],
dbChapter[ChapterTable.isRead],
dbChapter[ChapterTable.isBookmarked],
dbChapter[ChapterTable.lastPageRead],
chapterCount - index,
)
}
chapterCount - index,
chapterList.size
)
}
}
@ -125,9 +134,9 @@ object Chapter {
val chapterEntry = transaction {
ChapterTable.select {
(ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId)
}.firstOrNull()!!
}.first()
}
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
val pageList = source.fetchPageList(

View File

@ -159,7 +159,7 @@ object Extension {
it[this.classFQName] = className
}
val extensionId = ExtensionTable.select { ExtensionTable.pkgName eq pkgName }.firstOrNull()!![ExtensionTable.id].value
val extensionId = ExtensionTable.select { ExtensionTable.pkgName eq pkgName }.first()[ExtensionTable.id].value
sources.forEach { httpSource ->
SourceTable.insert {
@ -195,7 +195,7 @@ object Extension {
fun uninstallExtension(pkgName: String) {
logger.debug("Uninstalling $pkgName")
val extensionRecord = transaction { ExtensionTable.select { ExtensionTable.pkgName eq pkgName }.firstOrNull()!! }
val extensionRecord = transaction { ExtensionTable.select { ExtensionTable.pkgName eq pkgName }.first() }
val fileNameWithoutType = extensionRecord[ExtensionTable.apkName].substringBefore(".apk")
val jarPath = "${applicationDirs.extensionsRoot}/$fileNameWithoutType.jar"
transaction {
@ -234,7 +234,7 @@ object Extension {
}
suspend 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 }.first() }[ExtensionTable.iconUrl]
val saveDir = "${applicationDirs.extensionsRoot}/icon"

View File

@ -37,7 +37,7 @@ object Manga {
}
suspend fun getManga(mangaId: Int, onlineFetch: Boolean = false): MangaDataClass {
var mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
var mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
return if (mangaEntry[MangaTable.initialized] && !onlineFetch) {
MangaDataClass(
@ -78,14 +78,14 @@ object Manga {
it[MangaTable.description] = truncate(fetchedManga.description, 4096)
it[MangaTable.genre] = fetchedManga.genre
it[MangaTable.status] = fetchedManga.status
if (fetchedManga.thumbnail_url != null && fetchedManga.thumbnail_url!!.isNotEmpty())
if (fetchedManga.thumbnail_url != null && fetchedManga.thumbnail_url.orEmpty().isNotEmpty())
it[MangaTable.thumbnail_url] = fetchedManga.thumbnail_url
}
}
clearMangaThumbnail(mangaId)
mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
MangaDataClass(
mangaId,
@ -117,7 +117,7 @@ object Manga {
return getCachedImageResponse(saveDir, fileName) {
getManga(mangaId) // make sure is initialized
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
val sourceId = mangaEntry[MangaTable.sourceReference]
val source = getHttpSource(sourceId)
@ -130,7 +130,7 @@ object Manga {
}
}
suspend fun clearMangaThumbnail(mangaId: Int) {
private fun clearMangaThumbnail(mangaId: Int) {
val saveDir = applicationDirs.thumbnailsRoot
val fileName = mangaId.toString()

View File

@ -40,7 +40,7 @@ object MangaList {
val mangasPage = this
val mangaList = transaction {
return@transaction mangasPage.mangas.map { manga ->
var mangaEntry = MangaTable.select { MangaTable.url eq manga.url }.firstOrNull()
val mangaEntry = MangaTable.select { MangaTable.url eq manga.url }.firstOrNull()
if (mangaEntry == null) { // create manga entry
val mangaId = MangaTable.insertAndGetId {
it[url] = manga.url

View File

@ -40,16 +40,16 @@ object Page {
}
suspend 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 }.first() }
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
val chapterEntry = transaction {
ChapterTable.select {
(ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId)
}.firstOrNull()!!
}.first()
}
val chapterId = chapterEntry[ChapterTable.id].value
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq index) }.firstOrNull()!! }
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq index) }.first() }
val tachiPage = Page(
pageEntry[PageTable.index],
@ -78,11 +78,11 @@ object Page {
// TODO: rewrite this to match tachiyomi
private val applicationDirs by DI.global.instance<ApplicationDirs>()
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 }.first() }
val sourceId = mangaEntry[MangaTable.sourceReference]
val source = getHttpSource(sourceId)
val sourceEntry = transaction { SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()!! }
val chapterEntry = transaction { ChapterTable.select { ChapterTable.id eq chapterId }.firstOrNull()!! }
val sourceEntry = transaction { SourceTable.select { SourceTable.id eq sourceId }.first() }
val chapterEntry = transaction { ChapterTable.select { ChapterTable.id eq chapterId }.first() }
val chapterDir = when {
chapterEntry[ChapterTable.scanlator] != null -> "${chapterEntry[ChapterTable.scanlator]}_${chapterEntry[ChapterTable.name]}"

View File

@ -198,7 +198,7 @@ object LegacyBackupImport : LegacyBackupBase() {
it[description] = fetchedManga.description
it[genre] = fetchedManga.genre
it[status] = fetchedManga.status
if (fetchedManga.thumbnail_url != null && fetchedManga.thumbnail_url!!.isNotEmpty())
if (fetchedManga.thumbnail_url != null && fetchedManga.thumbnail_url.orEmpty().isNotEmpty())
it[MangaTable.thumbnail_url] = fetchedManga.thumbnail_url
}
}

View File

@ -10,7 +10,6 @@ package ir.armor.tachidesk.impl.util
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
@ -19,12 +18,12 @@ import java.nio.file.Paths
object CachedImageResponse {
private fun pathToInputStream(path: String): InputStream {
return BufferedInputStream(FileInputStream(path))
return FileInputStream(path).buffered()
}
private fun findFileNameStartingWith(directoryPath: String, fileName: String): String? {
val target = "$fileName."
File(directoryPath).listFiles().forEach { file ->
File(directoryPath).listFiles().orEmpty().forEach { file ->
if (file.name.startsWith(target))
return "$directoryPath/${file.name}"
}
@ -57,16 +56,13 @@ object CachedImageResponse {
}
}
}
return Pair(
pathToInputStream(fullPath),
contentType
)
return pathToInputStream(fullPath) to contentType
} else {
throw Exception("request error! ${response.code}")
}
}
suspend fun clearCachedImage(saveDir: String, fileName: String) {
fun clearCachedImage(saveDir: String, fileName: String) {
val cachedFile = findFileNameStartingWith(saveDir, fileName)
cachedFile?.also {
File(it).delete()

View File

@ -32,12 +32,12 @@ object GetHttpSource {
}
val sourceRecord = transaction {
SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()!!
SourceTable.select { SourceTable.id eq sourceId }.first()
}
val extensionId = sourceRecord[SourceTable.extension]
val extensionRecord = transaction {
ExtensionTable.select { ExtensionTable.id eq extensionId }.firstOrNull()!!
ExtensionTable.select { ExtensionTable.id eq extensionId }.first()
}
val apkName = extensionRecord[ExtensionTable.apkName]

View File

@ -11,6 +11,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Response
import okhttp3.internal.closeQuietly
import java.io.IOException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
@ -26,7 +27,9 @@ suspend fun Call.await(): Response {
return
}
continuation.resume(response)
continuation.resume(response) {
response.body?.closeQuietly()
}
}
override fun onFailure(call: Call, e: IOException) {

View File

@ -71,12 +71,14 @@ object PackageTools {
if (handler.hasException()) {
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" +
" https://sourceforge.net/p/dex2jar/tickets/\n" +
" https://bitbucket.org/pxb1988/dex2jar/issues\n" +
" https://github.com/pxb1988/dex2jar/issues\n" +
" dex2jar@googlegroups.com"
"""
Detail Error Information in File $errorFile
Please report this file to one of following link if possible (any one).
https://sourceforge.net/p/dex2jar/tickets/
https://bitbucket.org/pxb1988/dex2jar/issues
https://github.com/pxb1988/dex2jar/issues
dex2jar@googlegroups.com
""".trimIndent()
)
handler.dump(errorFile, emptyArray<String>())
}
@ -98,18 +100,21 @@ object PackageTools {
applicationInfo.metaData = Bundle().apply {
val appTag = doc.getElementsByTagName("application").item(0)
appTag?.childNodes?.toList()?.filter {
it.nodeType == Node.ELEMENT_NODE
}?.map {
it as Element
}?.filter {
it.tagName == "meta-data"
}?.map {
putString(
it.attributes.getNamedItem("android:name").nodeValue,
it.attributes.getNamedItem("android:value").nodeValue
)
}
appTag?.childNodes?.toList()
.orEmpty()
.asSequence()
.filter {
it.nodeType == Node.ELEMENT_NODE
}.map {
it as Element
}.filter {
it.tagName == "meta-data"
}.forEach {
putString(
it.attributes.getNamedItem("android:name").nodeValue,
it.attributes.getNamedItem("android:value").nodeValue
)
}
}
signatures = (

View File

@ -25,7 +25,7 @@ data class ChapterDataClass(
val lastPageRead: Int,
/** this chapter's index, starts with 1 */
val index: Int? = null,
val index: Int,
/** total chapter count, used to calculate if there's a next and prev chapter */
val chapterCount: Int? = null,

View File

@ -96,7 +96,10 @@ object JavalinSetup {
logger.error("NullPointerException while handling the request", e)
ctx.status(404)
}
app.exception(NoSuchElementException::class.java) { e, ctx ->
logger.error("NoSuchElementException while handling the request", e)
ctx.status(404)
}
app.exception(IOException::class.java) { e, ctx ->
logger.error("IOException while handling the request", e)
ctx.status(500)
@ -257,7 +260,7 @@ object JavalinSetup {
app.get("/api/v1/manga/:mangaId/chapters") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt()
val onlineFetch = ctx.queryParam("onlineFetch", "false").toBoolean()
val onlineFetch = ctx.queryParam("onlineFetch")?.toBoolean()
ctx.json(future { getChapterList(mangaId, onlineFetch) })
}