Make loader implementation classes internal

(cherry picked from commit 418e6a8b3ac23c235e392fe64314a2e4994e209b)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt
This commit is contained in:
arkon 2023-04-22 22:36:12 -04:00 committed by Jobobby04
parent 351331a525
commit 864de368ee
9 changed files with 109 additions and 137 deletions

View File

@ -28,7 +28,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.toDomainChapter import eu.kanade.tachiyomi.data.database.models.toDomainChapter
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
import tachiyomi.domain.chapter.service.calculateChapterGap import tachiyomi.domain.chapter.service.calculateChapterGap
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
@ -43,7 +42,7 @@ fun ChapterTransition(
manga ?: return manga ?: return
val currChapter = transition.from.chapter val currChapter = transition.from.chapter
val currChapterDownloaded = transition.from.pageLoader is DownloadPageLoader val currChapterDownloaded = transition.from.pageLoader?.isLocal == true
val goingToChapter = transition.to?.chapter val goingToChapter = transition.to?.chapter
val goingToChapterDownloaded = if (goingToChapter != null) { val goingToChapterDownloaded = if (goingToChapter != null) {

View File

@ -31,8 +31,6 @@ import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterItem import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterItem
import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader
import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader
import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader
import eu.kanade.tachiyomi.ui.reader.model.InsertPage import eu.kanade.tachiyomi.ui.reader.model.InsertPage
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
@ -449,7 +447,7 @@ class ReaderViewModel(
return return
} }
if (chapter.pageLoader is HttpPageLoader) { if (chapter.pageLoader?.isLocal == false) {
val manga = manga ?: return val manga = manga ?: return
val dbChapter = chapter.chapter val dbChapter = chapter.chapter
val isDownloaded = downloadManager.isChapterDownloaded( val isDownloaded = downloadManager.isChapterDownloaded(
@ -539,7 +537,7 @@ class ReaderViewModel(
if (amount == 0 || !manga.favorite) return if (amount == 0 || !manga.favorite) return
// Only download ahead if current + next chapter is already downloaded too to avoid jank // Only download ahead if current + next chapter is already downloaded too to avoid jank
if (getCurrentChapter()?.pageLoader !is DownloadPageLoader) return if (getCurrentChapter()?.pageLoader?.isLocal == true) return
val nextChapter = state.value.viewerChapters?.nextChapter?.chapter ?: return val nextChapter = state.value.viewerChapters?.nextChapter?.chapter ?: return
viewModelScope.launchIO { viewModelScope.launchIO {

View File

@ -10,11 +10,10 @@ import java.io.FileInputStream
/** /**
* Loader used to load a chapter from a directory given on [file]. * Loader used to load a chapter from a directory given on [file].
*/ */
class DirectoryPageLoader(val file: File) : PageLoader() { internal class DirectoryPageLoader(val file: File) : PageLoader() {
override var isLocal: Boolean = true
/**
* Returns the pages found on this directory ordered with a natural comparator.
*/
override suspend fun getPages(): List<ReaderPage> { override suspend fun getPages(): List<ReaderPage> {
return file.listFiles() return file.listFiles()
?.filter { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } } ?.filter { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
@ -28,9 +27,4 @@ class DirectoryPageLoader(val file: File) : PageLoader() {
} }
.orEmpty() .orEmpty()
} }
/**
* No additional action required to load the page
*/
override suspend fun loadPage(page: ReaderPage) {}
} }

View File

@ -17,7 +17,7 @@ import java.io.File
/** /**
* Loader used to load a chapter from the downloaded chapters. * Loader used to load a chapter from the downloaded chapters.
*/ */
class DownloadPageLoader( internal class DownloadPageLoader(
private val chapter: ReaderChapter, private val chapter: ReaderChapter,
private val manga: Manga, private val manga: Manga,
private val source: Source, private val source: Source,
@ -25,19 +25,12 @@ class DownloadPageLoader(
private val downloadProvider: DownloadProvider, private val downloadProvider: DownloadProvider,
) : PageLoader() { ) : PageLoader() {
// Needed to open input streams
private val context: Application by injectLazy() private val context: Application by injectLazy()
private var zipPageLoader: ZipPageLoader? = null private var zipPageLoader: ZipPageLoader? = null
override fun recycle() { override var isLocal: Boolean = true
super.recycle()
zipPageLoader?.recycle()
}
/**
* Returns the pages found on this downloaded chapter.
*/
override suspend fun getPages(): List<ReaderPage> { override suspend fun getPages(): List<ReaderPage> {
val dbChapter = chapter.chapter val dbChapter = chapter.chapter
val chapterPath = downloadProvider.findChapterDir(dbChapter.name, dbChapter.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, source) val chapterPath = downloadProvider.findChapterDir(dbChapter.name, dbChapter.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, source)
@ -48,6 +41,11 @@ class DownloadPageLoader(
} }
} }
override fun recycle() {
super.recycle()
zipPageLoader?.recycle()
}
private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> { private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> {
// SY --> // SY -->
val loader = ZipPageLoader(File(chapterPath.filePath!!), context).also { zipPageLoader = it } val loader = ZipPageLoader(File(chapterPath.filePath!!), context).also { zipPageLoader = it }

View File

@ -8,24 +8,12 @@ import java.io.File
/** /**
* Loader used to load a chapter from a .epub file. * Loader used to load a chapter from a .epub file.
*/ */
class EpubPageLoader(file: File) : PageLoader() { internal class EpubPageLoader(file: File) : PageLoader() {
/**
* The epub file.
*/
private val epub = EpubFile(file) private val epub = EpubFile(file)
/** override var isLocal: Boolean = true
* Recycles this loader and the open zip.
*/
override fun recycle() {
super.recycle()
epub.close()
}
/**
* Returns the pages found on this zip archive ordered with a natural comparator.
*/
override suspend fun getPages(): List<ReaderPage> { override suspend fun getPages(): List<ReaderPage> {
return epub.getImagesFromPages() return epub.getImagesFromPages()
.mapIndexed { i, path -> .mapIndexed { i, path ->
@ -37,10 +25,12 @@ class EpubPageLoader(file: File) : PageLoader() {
} }
} }
/**
* No additional action required to load the page
*/
override suspend fun loadPage(page: ReaderPage) { override suspend fun loadPage(page: ReaderPage) {
check(!isRecycled) check(!isRecycled)
} }
override fun recycle() {
super.recycle()
epub.close()
}
} }

View File

@ -32,7 +32,7 @@ import kotlin.math.min
/** /**
* Loader used to load chapters from an online source. * Loader used to load chapters from an online source.
*/ */
class HttpPageLoader( internal class HttpPageLoader(
private val chapter: ReaderChapter, private val chapter: ReaderChapter,
private val source: HttpSource, private val source: HttpSource,
private val chapterCache: ChapterCache = Injekt.get(), private val chapterCache: ChapterCache = Injekt.get(),
@ -75,30 +75,7 @@ class HttpPageLoader(
// EXH <-- // EXH <--
} }
/** override var isLocal: Boolean = false
* Recycles this loader and the active subscriptions and queue.
*/
override fun recycle() {
super.recycle()
scope.cancel()
queue.clear()
// Cache current page list progress for online chapters to allow a faster reopen
val pages = chapter.pages
if (pages != null) {
launchIO {
try {
// Convert to pages without reader information
val pagesToSave = pages.map { Page(it.index, it.url, it.imageUrl) }
chapterCache.putPageListToCache(chapter.chapter.toDomainChapter()!!, pagesToSave)
} catch (e: Throwable) {
if (e is CancellationException) {
throw e
}
}
}
}
}
/** /**
* Returns the page list for a chapter. It tries to return the page list from the local cache, * Returns the page list for a chapter. It tries to return the page list from the local cache,
@ -164,26 +141,6 @@ class HttpPageLoader(
} }
} }
/**
* Preloads the given [amount] of pages after the [currentPage] with a lower priority.
* @return a list of [PriorityPage] that were added to the [queue]
*/
private fun preloadNextPages(currentPage: ReaderPage, amount: Int): List<PriorityPage> {
val pageIndex = currentPage.index
val pages = currentPage.chapter.pages ?: return emptyList()
if (pageIndex == pages.lastIndex) return emptyList()
return pages
.subList(pageIndex + 1, min(pageIndex + 1 + amount, pages.size))
.mapNotNull {
if (it.status == Page.State.QUEUE) {
PriorityPage(it, 0).apply { queue.offer(this) }
} else {
null
}
}
}
/** /**
* Retries a page. This method is only called from user interaction on the viewer. * Retries a page. This method is only called from user interaction on the viewer.
*/ */
@ -206,23 +163,47 @@ class HttpPageLoader(
} }
} }
override fun recycle() {
super.recycle()
scope.cancel()
queue.clear()
// Cache current page list progress for online chapters to allow a faster reopen
val pages = chapter.pages
if (pages != null) {
launchIO {
try {
// Convert to pages without reader information
val pagesToSave = pages.map { Page(it.index, it.url, it.imageUrl) }
chapterCache.putPageListToCache(chapter.chapter.toDomainChapter()!!, pagesToSave)
} catch (e: Throwable) {
if (e is CancellationException) {
throw e
}
}
}
}
}
/** /**
* Data class used to keep ordering of pages in order to maintain priority. * Preloads the given [amount] of pages after the [currentPage] with a lower priority.
*
* @return a list of [PriorityPage] that were added to the [queue]
*/ */
private class PriorityPage( private fun preloadNextPages(currentPage: ReaderPage, amount: Int): List<PriorityPage> {
val page: ReaderPage, val pageIndex = currentPage.index
val priority: Int, val pages = currentPage.chapter.pages ?: return emptyList()
) : Comparable<PriorityPage> { if (pageIndex == pages.lastIndex) return emptyList()
companion object {
private val idGenerator = AtomicInteger()
}
private val identifier = idGenerator.incrementAndGet() return pages
.subList(pageIndex + 1, min(pageIndex + 1 + amount, pages.size))
override fun compareTo(other: PriorityPage): Int { .mapNotNull {
val p = other.priority.compareTo(priority) if (it.status == Page.State.QUEUE) {
return if (p != 0) p else identifier.compareTo(other.identifier) PriorityPage(it, 0).apply { queue.offer(this) }
} } else {
null
}
}
} }
/** /**
@ -265,3 +246,22 @@ class HttpPageLoader(
} }
// EXH <-- // EXH <--
} }
/**
* Data class used to keep ordering of pages in order to maintain priority.
*/
private class PriorityPage(
val page: ReaderPage,
val priority: Int,
) : Comparable<PriorityPage> {
companion object {
private val idGenerator = AtomicInteger()
}
private val identifier = idGenerator.incrementAndGet()
override fun compareTo(other: PriorityPage): Int {
val p = other.priority.compareTo(priority)
return if (p != 0) p else identifier.compareTo(other.identifier)
}
}

View File

@ -15,14 +15,7 @@ abstract class PageLoader {
var isRecycled = false var isRecycled = false
private set private set
/** abstract var isLocal: Boolean
* Recycles this loader. Implementations must override this method to clean up any active
* resources.
*/
@CallSuper
open fun recycle() {
isRecycled = true
}
/** /**
* Returns the list of pages of a chapter. * Returns the list of pages of a chapter.
@ -34,11 +27,20 @@ abstract class PageLoader {
* Progress of the page loading should be followed via [page.statusFlow]. * Progress of the page loading should be followed via [page.statusFlow].
* [loadPage] is not currently guaranteed to complete, so it should be launched asynchronously. * [loadPage] is not currently guaranteed to complete, so it should be launched asynchronously.
*/ */
abstract suspend fun loadPage(page: ReaderPage) open suspend fun loadPage(page: ReaderPage) {}
/** /**
* Retries the given [page] in case it failed to load. This method only makes sense when an * Retries the given [page] in case it failed to load. This method only makes sense when an
* online source is used. * online source is used.
*/ */
open fun retryPage(page: ReaderPage) {} open fun retryPage(page: ReaderPage) {}
/**
* Recycles this loader. Implementations must override this method to clean up any active
* resources.
*/
@CallSuper
open fun recycle() {
isRecycled = true
}
} }

View File

@ -15,34 +15,20 @@ import java.util.concurrent.Executors
/** /**
* Loader used to load a chapter from a .rar or .cbr file. * Loader used to load a chapter from a .rar or .cbr file.
*/ */
class RarPageLoader(file: File) : PageLoader() { internal class RarPageLoader(file: File) : PageLoader() {
/** private val rar = Archive(file)
* The rar archive to load pages from.
*/
private val archive = Archive(file)
/** /**
* Pool for copying compressed files to an input stream. * Pool for copying compressed files to an input stream.
*/ */
private val pool = Executors.newFixedThreadPool(1) private val pool = Executors.newFixedThreadPool(1)
/** override var isLocal: Boolean = true
* Recycles this loader and the open archive.
*/
override fun recycle() {
super.recycle()
archive.close()
pool.shutdown()
}
/**
* Returns an RxJava Single containing the pages found on this rar archive ordered with a natural
* comparator.
*/
override suspend fun getPages(): List<ReaderPage> { override suspend fun getPages(): List<ReaderPage> {
return archive.fileHeaders.asSequence() return rar.fileHeaders.asSequence()
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } } .filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { rar.getInputStream(it) } }
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
.mapIndexed { i, header -> .mapIndexed { i, header ->
ReaderPage(i).apply { ReaderPage(i).apply {
@ -53,13 +39,16 @@ class RarPageLoader(file: File) : PageLoader() {
.toList() .toList()
} }
/**
* No additional action required to load the page
*/
override suspend fun loadPage(page: ReaderPage) { override suspend fun loadPage(page: ReaderPage) {
check(!isRecycled) check(!isRecycled)
} }
override fun recycle() {
super.recycle()
rar.close()
pool.shutdown()
}
/** /**
* Returns an input stream for the given [header]. * Returns an input stream for the given [header].
*/ */
@ -69,7 +58,7 @@ class RarPageLoader(file: File) : PageLoader() {
pool.execute { pool.execute {
try { try {
pipeOut.use { pipeOut.use {
archive.extractFile(header, it) rar.extractFile(header, it)
} }
} catch (e: Exception) { } catch (e: Exception) {
} }

View File

@ -15,7 +15,7 @@ import java.nio.charset.StandardCharsets
/** /**
* Loader used to load a chapter from a .zip or .cbz file. * Loader used to load a chapter from a .zip or .cbz file.
*/ */
class ZipPageLoader( internal class ZipPageLoader(
file: File, file: File,
// SY --> // SY -->
context: Context, context: Context,
@ -58,7 +58,7 @@ class ZipPageLoader(
/** /**
* Returns the pages found on this zip archive ordered with a natural comparator. * Returns the pages found on this zip archive ordered with a natural comparator.
*/
override suspend fun getPages(): List<ReaderPage> { override suspend fun getPages(): List<ReaderPage> {
// SY --> // SY -->
// Part can be removed after testing that there are no bugs with zip4j on some users devices // Part can be removed after testing that there are no bugs with zip4j on some users devices
@ -95,10 +95,12 @@ class ZipPageLoader(
// SY <-- // SY <--
} }
/**
* No additional action required to load the page
*/
override suspend fun loadPage(page: ReaderPage) { override suspend fun loadPage(page: ReaderPage) {
check(!isRecycled) check(!isRecycled)
} }
override fun recycle() {
super.recycle()
zip.close()
}
} }