| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- package io.legato.kazusa.api.controller
- import android.graphics.Bitmap
- import android.graphics.drawable.Drawable
- import androidx.core.graphics.drawable.toBitmap
- import com.bumptech.glide.Glide
- import io.legato.kazusa.api.ReturnData
- import io.legato.kazusa.data.appDb
- import io.legato.kazusa.data.entities.Book
- import io.legato.kazusa.data.entities.BookProgress
- import io.legato.kazusa.data.entities.BookSource
- import io.legato.kazusa.help.AppWebDav
- import io.legato.kazusa.help.CacheManager
- 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.config.AppConfig
- import io.legato.kazusa.help.glide.ImageLoader
- import io.legato.kazusa.model.BookCover
- import io.legato.kazusa.model.ImageProvider
- import io.legato.kazusa.model.ReadBook
- import io.legato.kazusa.model.localBook.LocalBook
- import io.legato.kazusa.model.webBook.WebBook
- import io.legato.kazusa.utils.GSON
- import io.legato.kazusa.utils.cnCompare
- import io.legato.kazusa.utils.fromJsonObject
- import io.legato.kazusa.utils.printOnDebug
- import io.legato.kazusa.utils.stackTraceStr
- import kotlinx.coroutines.delay
- import kotlinx.coroutines.runBlocking
- import splitties.init.appCtx
- import java.io.File
- import java.util.WeakHashMap
- import java.util.concurrent.TimeUnit
- object BookController {
- private lateinit var book: Book
- private var bookSource: BookSource? = null
- private var bookUrl: String = ""
- private val defaultCoverCache by lazy { WeakHashMap<Drawable, Bitmap>() }
- /**
- * 书架所有书籍
- */
- val bookshelf: ReturnData
- get() {
- val books = appDb.bookDao.all
- val returnData = ReturnData()
- return if (books.isEmpty()) {
- returnData.setErrorMsg("还没有添加小说")
- } else {
- val data = when (AppConfig.bookshelfSort) {
- 1 -> books.sortedByDescending { it.latestChapterTime }
- 2 -> books.sortedWith { o1, o2 ->
- o1.name.cnCompare(o2.name)
- }
- 3 -> books.sortedBy { it.order }
- else -> books.sortedByDescending { it.durChapterTime }
- }
- returnData.setData(data)
- }
- }
- /**
- * 获取封面
- */
- fun getCover(parameters: Map<String, List<String>>): ReturnData {
- val returnData = ReturnData()
- val coverPath = parameters["path"]?.firstOrNull()
- val ftBitmap = ImageLoader.loadBitmap(appCtx, coverPath)
- .override(84, 112)
- .centerCrop()
- .submit()
- return try {
- returnData.setData(ftBitmap.get(3, TimeUnit.SECONDS))
- } catch (e: Exception) {
- try {
- val defaultBitmap = defaultCoverCache.getOrPut(BookCover.defaultDrawable) {
- Glide.with(appCtx)
- .asBitmap()
- .load(BookCover.defaultDrawable.toBitmap())
- .override(84, 112)
- .centerCrop()
- .submit()
- .get()
- }
- returnData.setData(defaultBitmap)
- } catch (e: Exception) {
- returnData.setErrorMsg(e.localizedMessage ?: "getCover error")
- }
- }
- }
- /**
- * 获取正文图片
- */
- fun getImg(parameters: Map<String, List<String>>): ReturnData {
- val returnData = ReturnData()
- val bookUrl = parameters["url"]?.firstOrNull()
- ?: return returnData.setErrorMsg("bookUrl为空")
- val src = parameters["path"]?.firstOrNull()
- ?: return returnData.setErrorMsg("图片链接为空")
- val width = parameters["width"]?.firstOrNull()?.toInt() ?: 640
- if (this.bookUrl != bookUrl) {
- this.book = appDb.bookDao.getBook(bookUrl)
- ?: return returnData.setErrorMsg("bookUrl不对")
- this.bookSource = appDb.bookSourceDao.getBookSource(book.origin)
- }
- this.bookUrl = bookUrl
- val bitmap = runBlocking {
- ImageProvider.cacheImage(book, src, bookSource)
- ImageProvider.getImage(book, src, width)
- }
- return returnData.setData(bitmap)
- }
- /**
- * 更新目录
- */
- fun refreshToc(parameters: Map<String, List<String>>): ReturnData {
- val returnData = ReturnData()
- try {
- val bookUrl = parameters["url"]?.firstOrNull()
- if (bookUrl.isNullOrEmpty()) {
- return returnData.setErrorMsg("参数url不能为空,请指定书籍地址")
- }
- val book = appDb.bookDao.getBook(bookUrl)
- ?: return returnData.setErrorMsg("未在数据库找到对应书籍,请先添加")
- if (book.isLocal) {
- val toc = LocalBook.getChapterList(book)
- appDb.bookChapterDao.delByBook(book.bookUrl)
- appDb.bookChapterDao.insert(*toc.toTypedArray())
- appDb.bookDao.update(book)
- return returnData.setData(toc)
- } else {
- val bookSource = appDb.bookSourceDao.getBookSource(book.origin)
- ?: return returnData.setErrorMsg("未找到对应书源,请换源")
- val toc = runBlocking {
- if (book.tocUrl.isBlank()) {
- WebBook.getBookInfoAwait(bookSource, book)
- }
- WebBook.getChapterListAwait(bookSource, book).getOrThrow()
- }
- appDb.bookChapterDao.delByBook(book.bookUrl)
- appDb.bookChapterDao.insert(*toc.toTypedArray())
- appDb.bookDao.update(book)
- return returnData.setData(toc)
- }
- } catch (e: Exception) {
- return returnData.setErrorMsg(e.localizedMessage ?: "refresh toc error")
- }
- }
- /**
- * 获取目录
- */
- fun getChapterList(parameters: Map<String, List<String>>): ReturnData {
- val bookUrl = parameters["url"]?.firstOrNull()
- val returnData = ReturnData()
- if (bookUrl.isNullOrEmpty()) {
- return returnData.setErrorMsg("参数url不能为空,请指定书籍地址")
- }
- val chapterList = appDb.bookChapterDao.getChapterList(bookUrl)
- if (chapterList.isEmpty()) {
- return refreshToc(parameters)
- }
- return returnData.setData(chapterList)
- }
- /**
- * 获取正文
- */
- fun getBookContent(parameters: Map<String, List<String>>): ReturnData {
- val bookUrl = parameters["url"]?.firstOrNull()
- val index = parameters["index"]?.firstOrNull()?.toInt()
- val returnData = ReturnData()
- if (bookUrl.isNullOrEmpty()) {
- return returnData.setErrorMsg("参数url不能为空,请指定书籍地址")
- }
- if (index == null) {
- return returnData.setErrorMsg("参数index不能为空, 请指定目录序号")
- }
- val book = appDb.bookDao.getBook(bookUrl)
- val chapter = runBlocking {
- var chapter = appDb.bookChapterDao.getChapter(bookUrl, index)
- var wait = 0
- while (chapter == null && wait < 30) {
- delay(1000)
- chapter = appDb.bookChapterDao.getChapter(bookUrl, index)
- wait++
- }
- chapter
- }
- if (book == null || chapter == null) {
- return returnData.setErrorMsg("未找到")
- }
- var content: String? = BookHelp.getContent(book, chapter)
- if (content != null) {
- val contentProcessor = ContentProcessor.get(book.name, book.origin)
- content = runBlocking {
- contentProcessor.getContent(book, chapter, content, includeTitle = false)
- .toString()
- }
- return returnData.setData(content)
- }
- val bookSource = appDb.bookSourceDao.getBookSource(book.origin)
- ?: return returnData.setErrorMsg("未找到书源")
- try {
- content = runBlocking {
- WebBook.getContentAwait(bookSource, book, chapter).let {
- val contentProcessor = ContentProcessor.get(book.name, book.origin)
- contentProcessor.getContent(book, chapter, it, includeTitle = false)
- .toString()
- }
- }
- returnData.setData(content)
- } catch (e: Exception) {
- returnData.setErrorMsg(e.stackTraceStr)
- }
- return returnData
- }
- /**
- * 保存书籍
- */
- suspend fun saveBook(postData: String?): ReturnData {
- val returnData = ReturnData()
- GSON.fromJsonObject<Book>(postData).getOrNull()?.let { book ->
- AppWebDav.uploadBookProgress(book)
- book.save()
- return returnData.setData("")
- }
- return returnData.setErrorMsg("格式不对")
- }
- /**
- * 删除书籍
- */
- fun deleteBook(postData: String?): ReturnData {
- val returnData = ReturnData()
- GSON.fromJsonObject<Book>(postData).getOrNull()?.let { book ->
- book.delete()
- return returnData.setData("")
- }
- return returnData.setErrorMsg("格式不对")
- }
- /**
- * 保存进度
- */
- suspend fun saveBookProgress(postData: String?): ReturnData {
- val returnData = ReturnData()
- GSON.fromJsonObject<BookProgress>(postData)
- .onFailure { it.printOnDebug() }
- .getOrNull()?.let { bookProgress ->
- appDb.bookDao.getBook(bookProgress.name, bookProgress.author)?.let { book ->
- book.durChapterIndex = bookProgress.durChapterIndex
- book.durChapterPos = bookProgress.durChapterPos
- book.durChapterTitle = bookProgress.durChapterTitle
- book.durChapterTime = bookProgress.durChapterTime
- AppWebDav.uploadBookProgress(bookProgress) {
- book.syncTime = System.currentTimeMillis()
- }
- appDb.bookDao.update(book)
- ReadBook.book?.let {
- if (it.name == bookProgress.name &&
- it.author == bookProgress.author
- ) {
- ReadBook.webBookProgress = bookProgress
- }
- }
- return returnData.setData("")
- }
- }
- return returnData.setErrorMsg("格式不对")
- }
- /**
- * 添加本地书籍
- */
- fun addLocalBook(
- parameters: Map<String, List<String>>,
- files: Map<String, String>
- ): ReturnData {
- val returnData = ReturnData()
- val fileName = parameters["fileName"]?.firstOrNull()
- ?: return returnData.setErrorMsg("fileName 不能为空")
- val fileData = files["fileData"]
- ?: return returnData.setErrorMsg("fileData 不能为空")
- kotlin.runCatching {
- val uri = LocalBook.saveBookFile(File(fileData).inputStream(), fileName)
- LocalBook.importFile(uri)
- }.onFailure {
- return when (it) {
- is SecurityException -> returnData.setErrorMsg("需重新设置书籍保存位置!")
- else -> returnData.setErrorMsg("保存书籍错误\n${it.localizedMessage}")
- }
- }
- return returnData.setData(true)
- }
- /**
- * 保存web阅读界面配置
- */
- fun saveWebReadConfig(postData: String?): ReturnData {
- val returnData = ReturnData()
- postData?.let {
- CacheManager.put("webReadConfig", postData)
- } ?: CacheManager.delete("webReadConfig")
- return returnData.setData("")
- }
- /**
- * 获取web阅读界面配置
- */
- fun getWebReadConfig(): ReturnData {
- val returnData = ReturnData()
- val data = CacheManager.get("webReadConfig")
- ?: return returnData.setErrorMsg("没有配置")
- return returnData.setData(data)
- }
- }
|