Rename
This commit is contained in:
1 parent
b86d90021c
commit
6c0757c91b
972 files changed
+6885
-7321
No files matched your search
@@ -0,0 +1,625 @@
|
||||
package io.legato.kazusa.model
|
||||
|
||||
import io.legato.kazusa.constant.AppLog
|
||||
import io.legato.kazusa.data.appDb
|
||||
import io.legato.kazusa.data.entities.Book
|
||||
import io.legato.kazusa.data.entities.BookChapter
|
||||
import io.legato.kazusa.data.entities.BookProgress
|
||||
import io.legato.kazusa.data.entities.BookSource
|
||||
import io.legato.kazusa.data.entities.ReadRecord
|
||||
import io.legato.kazusa.help.AppWebDav
|
||||
import io.legato.kazusa.help.ConcurrentRateLimiter
|
||||
import io.legato.kazusa.help.book.BookHelp
|
||||
import io.legato.kazusa.help.book.ContentProcessor
|
||||
import io.legato.kazusa.help.book.isLocal
|
||||
import io.legato.kazusa.help.book.isSameNameAuthor
|
||||
import io.legato.kazusa.help.book.readSimulating
|
||||
import io.legato.kazusa.help.book.simulatedTotalChapterNum
|
||||
import io.legato.kazusa.help.book.update
|
||||
import io.legato.kazusa.help.config.AppConfig
|
||||
import io.legato.kazusa.help.coroutine.Coroutine
|
||||
import io.legato.kazusa.help.globalExecutor
|
||||
import io.legato.kazusa.model.webBook.WebBook
|
||||
import io.legato.kazusa.ui.book.manga.entities.BaseMangaPage
|
||||
import io.legato.kazusa.ui.book.manga.entities.MangaChapter
|
||||
import io.legato.kazusa.ui.book.manga.entities.MangaContent
|
||||
import io.legato.kazusa.ui.book.manga.entities.MangaPage
|
||||
import io.legato.kazusa.ui.book.manga.entities.ReaderLoading
|
||||
import io.legato.kazusa.utils.mapIndexed
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlin.math.min
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
object ReadManga : CoroutineScope by MainScope() {
|
||||
var inBookshelf = false
|
||||
var book: Book? = null
|
||||
val executor = globalExecutor
|
||||
var durChapterIndex = 0 //章节位置
|
||||
var chapterSize = 0//总章节
|
||||
var durChapterPos = 0
|
||||
var chapterChanged = false
|
||||
var prevMangaChapter: MangaChapter? = null
|
||||
var curMangaChapter: MangaChapter? = null
|
||||
var nextMangaChapter: MangaChapter? = null
|
||||
var bookSource: BookSource? = null
|
||||
var readStartTime: Long = System.currentTimeMillis()
|
||||
private val readRecord = ReadRecord()
|
||||
private val loadingChapters = arrayListOf<Int>()
|
||||
var simulatedChapterSize = 0
|
||||
var mCallback: Callback? = null
|
||||
var preDownloadTask: Job? = null
|
||||
val downloadedChapters = hashSetOf<Int>()
|
||||
val downloadFailChapters = hashMapOf<Int, Int>()
|
||||
val downloadScope = CoroutineScope(SupervisorJob() + IO)
|
||||
val preDownloadSemaphore = Semaphore(2)
|
||||
var rateLimiter = ConcurrentRateLimiter(null)
|
||||
val mangaContents get() = buildMangaContent()
|
||||
val hasNextChapter get() = durChapterIndex < simulatedChapterSize - 1
|
||||
|
||||
fun resetData(book: Book) {
|
||||
ReadManga.book = book
|
||||
readRecord.bookName = book.name
|
||||
readRecord.readTime = appDb.readRecordDao.getReadTime(book.name) ?: 0
|
||||
chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl)
|
||||
simulatedChapterSize = if (book.readSimulating()) {
|
||||
book.simulatedTotalChapterNum()
|
||||
} else {
|
||||
chapterSize
|
||||
}
|
||||
durChapterIndex = book.durChapterIndex
|
||||
durChapterPos = book.durChapterPos
|
||||
clearMangaChapter()
|
||||
upWebBook(book)
|
||||
synchronized(this) {
|
||||
loadingChapters.clear()
|
||||
downloadedChapters.clear()
|
||||
downloadFailChapters.clear()
|
||||
}
|
||||
}
|
||||
|
||||
fun upData(book: Book) {
|
||||
ReadManga.book = book
|
||||
chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl)
|
||||
simulatedChapterSize = if (book.readSimulating()) {
|
||||
book.simulatedTotalChapterNum()
|
||||
} else {
|
||||
chapterSize
|
||||
}
|
||||
|
||||
if (durChapterIndex != book.durChapterIndex) {
|
||||
durChapterIndex = book.durChapterIndex
|
||||
durChapterPos = book.durChapterPos
|
||||
clearMangaChapter()
|
||||
}
|
||||
upWebBook(book)
|
||||
synchronized(this) {
|
||||
loadingChapters.clear()
|
||||
downloadedChapters.clear()
|
||||
downloadFailChapters.clear()
|
||||
}
|
||||
}
|
||||
|
||||
fun upWebBook(book: Book) {
|
||||
appDb.bookSourceDao.getBookSource(book.origin)?.let {
|
||||
bookSource = it
|
||||
rateLimiter = ConcurrentRateLimiter(it)
|
||||
} ?: let {
|
||||
bookSource = null
|
||||
}
|
||||
}
|
||||
|
||||
fun clearMangaChapter() {
|
||||
prevMangaChapter = null
|
||||
curMangaChapter = null
|
||||
nextMangaChapter = null
|
||||
}
|
||||
|
||||
//每次切换章节更新阅读记录
|
||||
fun upReadTime() {
|
||||
executor.execute {
|
||||
if (!AppConfig.enableReadRecord) {
|
||||
return@execute
|
||||
}
|
||||
readRecord.readTime = readRecord.readTime + System.currentTimeMillis() - readStartTime
|
||||
readStartTime = System.currentTimeMillis()
|
||||
readRecord.lastRead = System.currentTimeMillis()
|
||||
appDb.readRecordDao.insert(readRecord)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun addLoading(index: Int): Boolean {
|
||||
if (loadingChapters.contains(index)) return false
|
||||
loadingChapters.add(index)
|
||||
return true
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeLoading(index: Int) {
|
||||
loadingChapters.remove(index)
|
||||
}
|
||||
|
||||
fun loadContent() {
|
||||
clearMangaChapter()
|
||||
loadContent(durChapterIndex)
|
||||
loadContent(durChapterIndex + 1)
|
||||
loadContent(durChapterIndex - 1)
|
||||
}
|
||||
|
||||
fun loadOrUpContent() {
|
||||
if (curMangaChapter == null) {
|
||||
loadContent(durChapterIndex)
|
||||
} else {
|
||||
mCallback?.upContent()
|
||||
}
|
||||
if (nextMangaChapter == null) {
|
||||
loadContent(durChapterIndex + 1)
|
||||
}
|
||||
if (prevMangaChapter == null) {
|
||||
loadContent(durChapterIndex - 1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadContent(index: Int) {
|
||||
Coroutine.async {
|
||||
val book = book!!
|
||||
val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, index) ?: return@async
|
||||
if (addLoading(index)) {
|
||||
BookHelp.getContent(book, chapter)?.let {
|
||||
contentLoadFinish(chapter, it)
|
||||
} ?: run {
|
||||
download(downloadScope, chapter)
|
||||
}
|
||||
}
|
||||
}.onError {
|
||||
AppLog.put("加载正文出错\n${it.localizedMessage}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容加载完成
|
||||
*/
|
||||
suspend fun contentLoadFinish(
|
||||
chapter: BookChapter,
|
||||
content: String?,
|
||||
errorMsg: String = "加载内容失败",
|
||||
canceled: Boolean = false
|
||||
) {
|
||||
removeLoading(chapter.index)
|
||||
if (canceled || chapter.index !in durChapterIndex - 1..durChapterIndex + 1) {
|
||||
return
|
||||
}
|
||||
when (val offset = chapter.index - durChapterIndex) {
|
||||
0 -> {
|
||||
if (content == null) {
|
||||
mCallback?.loadFail(errorMsg)
|
||||
return
|
||||
}
|
||||
if (content.isEmpty() && !chapter.isVolume) {
|
||||
mCallback?.loadFail("正文内容为空")
|
||||
return
|
||||
}
|
||||
val mangaChapter = getManageChapter(chapter, content)
|
||||
if (mangaChapter.imageCount == 0 && !chapter.isVolume) {
|
||||
mCallback?.loadFail("正文没有图片")
|
||||
return
|
||||
}
|
||||
curMangaChapter = mangaChapter
|
||||
mCallback?.upContent()
|
||||
}
|
||||
|
||||
-1, 1 -> {
|
||||
if (content == null || (!chapter.isVolume && content.isEmpty())) {
|
||||
return
|
||||
}
|
||||
val mangaChapter = getManageChapter(chapter, content)
|
||||
if (mangaChapter.imageCount == 0 && !chapter.isVolume) {
|
||||
return
|
||||
}
|
||||
|
||||
when (offset) {
|
||||
-1 -> prevMangaChapter = mangaChapter
|
||||
1 -> nextMangaChapter = mangaChapter
|
||||
}
|
||||
|
||||
mCallback?.upContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildMangaContent(): MangaContent {
|
||||
val items = arrayListOf<BaseMangaPage>()
|
||||
var pos = 0
|
||||
var curFinish = false
|
||||
var nextFinish = false
|
||||
prevMangaChapter?.let {
|
||||
pos += it.pages.size
|
||||
items.addAll(it.pages)
|
||||
}
|
||||
curMangaChapter?.let {
|
||||
curFinish = true
|
||||
items.addAll(it.pages)
|
||||
durChapterPos = if (it.imageCount > 0) {
|
||||
durChapterPos.coerceIn(0, it.imageCount - 1)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
pos += durChapterPos
|
||||
if (!AppConfig.hideMangaTitle && it.imageCount > 0) {
|
||||
pos++
|
||||
}
|
||||
}
|
||||
nextMangaChapter?.let {
|
||||
nextFinish = true
|
||||
items.addAll(it.pages)
|
||||
}
|
||||
return MangaContent(pos, items, curFinish, nextFinish)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载下一章
|
||||
*/
|
||||
fun moveToNextChapter(toFirst: Boolean = false): Boolean {
|
||||
if (durChapterIndex < simulatedChapterSize - 1) {
|
||||
if (toFirst) {
|
||||
mCallback?.showLoading()
|
||||
durChapterPos = 0
|
||||
}
|
||||
durChapterIndex++
|
||||
prevMangaChapter = curMangaChapter
|
||||
curMangaChapter = nextMangaChapter
|
||||
nextMangaChapter = null
|
||||
if (curMangaChapter == null) {
|
||||
mCallback?.startLoad()
|
||||
loadContent(durChapterIndex)
|
||||
} else {
|
||||
mCallback?.upContent()
|
||||
}
|
||||
loadContent(durChapterIndex + 1)
|
||||
saveRead()
|
||||
AppLog.putDebug("moveToNextChapter-curPageChanged()")
|
||||
curPageChanged()
|
||||
return true
|
||||
} else {
|
||||
AppLog.putDebug("跳转下一章失败,没有下一章")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun moveToPrevChapter(toFirst: Boolean = false): Boolean {
|
||||
if (durChapterIndex > 0) {
|
||||
if (toFirst) {
|
||||
mCallback?.showLoading()
|
||||
durChapterPos = 0
|
||||
}
|
||||
durChapterIndex--
|
||||
nextMangaChapter = curMangaChapter
|
||||
curMangaChapter = prevMangaChapter
|
||||
prevMangaChapter = null
|
||||
if (curMangaChapter == null) {
|
||||
loadContent(durChapterIndex)
|
||||
} else {
|
||||
mCallback?.upContent()
|
||||
}
|
||||
loadContent(durChapterIndex - 1)
|
||||
saveRead()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun curPageChanged() {
|
||||
upReadTime()
|
||||
preDownload()
|
||||
}
|
||||
|
||||
fun saveRead(pageChanged: Boolean = false) {
|
||||
executor.execute {
|
||||
val book = book ?: return@execute
|
||||
book.lastCheckCount = 0
|
||||
book.durChapterTime = System.currentTimeMillis()
|
||||
val chapterChanged = book.durChapterIndex != durChapterIndex
|
||||
book.durChapterIndex = durChapterIndex
|
||||
book.durChapterPos = durChapterPos
|
||||
if (!pageChanged || chapterChanged) {
|
||||
appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let {
|
||||
book.durChapterTitle = it.getDisplayTitle(
|
||||
ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(),
|
||||
book.getUseReplaceRule()
|
||||
)
|
||||
}
|
||||
}
|
||||
appDb.bookDao.update(book)
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadNetworkContent(
|
||||
bookSource: BookSource,
|
||||
scope: CoroutineScope,
|
||||
chapter: BookChapter,
|
||||
book: Book,
|
||||
semaphore: Semaphore?,
|
||||
success: suspend (String) -> Unit = {},
|
||||
error: suspend () -> Unit = {},
|
||||
cancel: suspend () -> Unit = {},
|
||||
) {
|
||||
WebBook.getContent(
|
||||
scope,
|
||||
bookSource,
|
||||
book,
|
||||
chapter,
|
||||
start = CoroutineStart.LAZY,
|
||||
executeContext = IO,
|
||||
semaphore = semaphore
|
||||
).onSuccess { content ->
|
||||
success.invoke(content)
|
||||
}.onError {
|
||||
error.invoke()
|
||||
}.onCancel {
|
||||
cancel.invoke()
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun preDownload() {
|
||||
if (book?.isLocal == true) return
|
||||
executor.execute {
|
||||
if (AppConfig.preDownloadNum < 2) {
|
||||
return@execute
|
||||
}
|
||||
preDownloadTask?.cancel()
|
||||
preDownloadTask = launch(IO) {
|
||||
//预下载
|
||||
launch {
|
||||
val maxChapterIndex =
|
||||
min(durChapterIndex + AppConfig.preDownloadNum, chapterSize)
|
||||
for (i in durChapterIndex.plus(2)..maxChapterIndex) {
|
||||
if (downloadedChapters.contains(i)) continue
|
||||
if ((downloadFailChapters[i] ?: 0) >= 3) continue
|
||||
downloadIndex(i)
|
||||
}
|
||||
}
|
||||
launch {
|
||||
val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
|
||||
for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
|
||||
if (downloadedChapters.contains(i)) continue
|
||||
if ((downloadFailChapters[i] ?: 0) >= 3) continue
|
||||
downloadIndex(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelPreDownloadTask() {
|
||||
if (curMangaChapter != null && nextMangaChapter != null) {
|
||||
preDownloadTask?.cancel()
|
||||
downloadScope.coroutineContext.cancelChildren()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun downloadIndex(index: Int) {
|
||||
if (index < 0) return
|
||||
if (index > chapterSize - 1) {
|
||||
upToc()
|
||||
return
|
||||
}
|
||||
val book = book ?: return
|
||||
val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, index) ?: return
|
||||
if (BookHelp.hasContent(book, chapter)) {
|
||||
downloadedChapters.add(chapter.index)
|
||||
} else {
|
||||
delay(1000)
|
||||
if (addLoading(index)) {
|
||||
download(downloadScope, chapter, preDownloadSemaphore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取正文
|
||||
*/
|
||||
private suspend fun download(
|
||||
scope: CoroutineScope,
|
||||
chapter: BookChapter,
|
||||
semaphore: Semaphore? = null,
|
||||
) {
|
||||
val book = book ?: return removeLoading(chapter.index)
|
||||
val bookSource = bookSource
|
||||
if (bookSource != null) {
|
||||
downloadNetworkContent(bookSource, scope, chapter, book, semaphore, success = {
|
||||
downloadedChapters.add(chapter.index)
|
||||
downloadFailChapters.remove(chapter.index)
|
||||
contentLoadFinish(chapter, it)
|
||||
}, error = {
|
||||
downloadFailChapters[chapter.index] =
|
||||
(downloadFailChapters[chapter.index] ?: 0) + 1
|
||||
contentLoadFinish(chapter, null)
|
||||
}, cancel = {
|
||||
contentLoadFinish(chapter, null, canceled = true)
|
||||
})
|
||||
} else {
|
||||
contentLoadFinish(chapter, null, "加载内容失败 没有书源")
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun upToc() {
|
||||
val bookSource = bookSource ?: return
|
||||
val book = book ?: return
|
||||
if (!book.canUpdate) return
|
||||
if (System.currentTimeMillis() - book.lastCheckTime < 600000) return
|
||||
book.lastCheckTime = System.currentTimeMillis()
|
||||
WebBook.getChapterList(this, bookSource, book).onSuccess(IO) { cList ->
|
||||
if (book.bookUrl == ReadManga.book?.bookUrl
|
||||
&& cList.size > chapterSize
|
||||
) {
|
||||
appDb.bookChapterDao.delByBook(book.bookUrl)
|
||||
appDb.bookChapterDao.insert(*cList.toTypedArray())
|
||||
saveRead()
|
||||
chapterSize = cList.size
|
||||
simulatedChapterSize = book.simulatedTotalChapterNum()
|
||||
nextMangaChapter ?: loadContent(durChapterIndex + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun uploadProgress(successAction: (() -> Unit)? = null) {
|
||||
book?.let {
|
||||
launch(IO) {
|
||||
AppWebDav.uploadBookProgress(it)
|
||||
ensureActive()
|
||||
it.update()
|
||||
successAction?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步阅读进度
|
||||
* 如果当前进度快于服务器进度或者没有进度进行上传,如果慢与服务器进度则执行传入动作
|
||||
*/
|
||||
fun syncProgress(
|
||||
newProgressAction: ((progress: BookProgress) -> Unit)? = null,
|
||||
uploadSuccessAction: (() -> Unit)? = null,
|
||||
syncSuccessAction: (() -> Unit)? = null,
|
||||
) {
|
||||
if (!AppConfig.syncBookProgress) return
|
||||
val book = book ?: return
|
||||
Coroutine.async {
|
||||
AppWebDav.getBookProgress(book)
|
||||
}.onError {
|
||||
AppLog.put("拉取阅读进度失败", it)
|
||||
}.onSuccess { progress ->
|
||||
if (progress == null || progress.durChapterIndex < book.durChapterIndex ||
|
||||
(progress.durChapterIndex == book.durChapterIndex
|
||||
&& progress.durChapterPos < book.durChapterPos)
|
||||
) {
|
||||
// 服务器没有进度或者进度比服务器快,上传现有进度
|
||||
Coroutine.async {
|
||||
AppWebDav.uploadBookProgress(BookProgress(book), uploadSuccessAction)
|
||||
book.update()
|
||||
}
|
||||
} else if (progress.durChapterIndex > book.durChapterIndex ||
|
||||
progress.durChapterPos > book.durChapterPos
|
||||
) {
|
||||
// 进度比服务器慢,执行传入动作
|
||||
newProgressAction?.invoke(progress)
|
||||
} else {
|
||||
syncSuccessAction?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setProgress(progress: BookProgress) {
|
||||
if (progress.durChapterIndex < chapterSize &&
|
||||
(durChapterIndex != progress.durChapterIndex
|
||||
|| durChapterPos != progress.durChapterPos)
|
||||
) {
|
||||
mCallback?.showLoading()
|
||||
if (progress.durChapterIndex == durChapterIndex) {
|
||||
durChapterPos = progress.durChapterPos
|
||||
mCallback?.upContent()
|
||||
} else {
|
||||
durChapterIndex = progress.durChapterIndex
|
||||
durChapterPos = progress.durChapterPos
|
||||
loadContent()
|
||||
}
|
||||
saveRead()
|
||||
}
|
||||
}
|
||||
|
||||
fun showLoading() {
|
||||
mCallback?.showLoading()
|
||||
}
|
||||
|
||||
fun onChapterListUpdated(newBook: Book) {
|
||||
if (newBook.isSameNameAuthor(book)) {
|
||||
book = newBook
|
||||
chapterSize = newBook.totalChapterNum
|
||||
simulatedChapterSize = newBook.simulatedTotalChapterNum()
|
||||
if (simulatedChapterSize > 0 && durChapterIndex > simulatedChapterSize - 1) {
|
||||
durChapterIndex = simulatedChapterSize - 1
|
||||
}
|
||||
if (mCallback == null) {
|
||||
clearMangaChapter()
|
||||
} else {
|
||||
loadContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册回调
|
||||
*/
|
||||
fun register(cb: Callback) {
|
||||
mCallback = cb
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消注册回调
|
||||
*/
|
||||
fun unregister(cb: Callback) {
|
||||
if (mCallback === cb) {
|
||||
mCallback = null
|
||||
}
|
||||
preDownloadTask?.cancel()
|
||||
preDownloadTask = null
|
||||
downloadScope.coroutineContext.cancelChildren()
|
||||
coroutineContext.cancelChildren()
|
||||
}
|
||||
|
||||
private suspend fun getManageChapter(chapter: BookChapter, content: String): MangaChapter {
|
||||
val list = BookHelp.flowImages(chapter, content)
|
||||
.distinctUntilChanged().mapIndexed { index, src ->
|
||||
MangaPage(
|
||||
chapterIndex = chapter.index,
|
||||
chapterSize = chapterSize,
|
||||
mImageUrl = src,
|
||||
index = index,
|
||||
mChapterName = chapter.title
|
||||
)
|
||||
}.toList()
|
||||
|
||||
val imageCount = list.size
|
||||
|
||||
list.forEach {
|
||||
it.imageCount = imageCount
|
||||
}
|
||||
|
||||
if (AppConfig.hideMangaTitle && imageCount > 0) {
|
||||
return MangaChapter(chapter, list, imageCount)
|
||||
}
|
||||
|
||||
val pages = mutableListOf<BaseMangaPage>()
|
||||
|
||||
if (imageCount == 0 && chapter.isVolume) {
|
||||
pages.add(ReaderLoading(chapter.index, -1, chapter.title, true))
|
||||
} else {
|
||||
pages.add(ReaderLoading(chapter.index, -1, "阅读 ${chapter.title}"))
|
||||
pages.addAll(list)
|
||||
pages.add(ReaderLoading(chapter.index, imageCount, "已读完 ${chapter.title}"))
|
||||
}
|
||||
|
||||
return MangaChapter(chapter, pages, imageCount)
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun upContent()
|
||||
fun loadFail(msg: String)
|
||||
fun sureNewProgress(progress: BookProgress)
|
||||
fun showLoading()
|
||||
fun startLoad()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user