diff --git a/build.gradle.kts b/build.gradle.kts index 354282bf..22cbbef6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -56,7 +56,7 @@ apply(plugin = "kotlin") apply(plugin = "io.github.fvarrui.javapackager.plugin") group = "com.htmake" -version = "2.5.3" +version = "2.5.4" java { sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/cli.gradle b/cli.gradle index 600d20cf..97998ea7 100644 --- a/cli.gradle +++ b/cli.gradle @@ -17,7 +17,7 @@ apply plugin: 'io.spring.dependency-management' apply plugin: 'kotlin' group = 'com.htmake' -version = '2.5.3' +version = '2.5.4' sourceCompatibility = '1.8' repositories { diff --git a/src/main/java/com/htmake/reader/api/YueduApi.kt b/src/main/java/com/htmake/reader/api/YueduApi.kt index 6d60ea00..de098e00 100644 --- a/src/main/java/com/htmake/reader/api/YueduApi.kt +++ b/src/main/java/com/htmake/reader/api/YueduApi.kt @@ -228,6 +228,7 @@ class YueduApi : RestVerticle() { router.get("/reader3/getBookGroups").coroutineHandler { bookController.getBookGroups(it) } router.post("/reader3/saveBookGroup").coroutineHandler { bookController.saveBookGroup(it) } router.post("/reader3/deleteBookGroup").coroutineHandler { bookController.deleteBookGroup(it) } + router.post("/reader3/saveBookGroupOrder").coroutineHandler { bookController.saveBookGroupOrder(it) } // 书仓功能 // 获取书仓文件列表 diff --git a/src/main/java/com/htmake/reader/api/controller/BookController.kt b/src/main/java/com/htmake/reader/api/controller/BookController.kt index 68b1ab2d..04ad30d1 100644 --- a/src/main/java/com/htmake/reader/api/controller/BookController.kt +++ b/src/main/java/com/htmake/reader/api/controller/BookController.kt @@ -78,6 +78,7 @@ import io.legado.app.model.analyzeRule.AnalyzeUrl import java.nio.file.Paths import kotlinx.coroutines.withContext import kotlinx.coroutines.async +import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.Deferred import kotlinx.coroutines.CoroutineScope import me.ag2s.epublib.domain.* @@ -1587,10 +1588,16 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti } var userNameSpace = getUserNameSpace(context) var bookGroupList: JsonArray? = asJsonArray(getUserStorage(userNameSpace, "bookGroup")) - if (bookGroupList != null) { - return returnData.setData(bookGroupList.getList()) + if (bookGroupList == null) { + bookGroupList = asJsonArray(""" + [{"groupId":-1,"groupName":"全部","order":-10,"show":true},{"groupId":-2,"groupName":"本地","order":-9,"show":true},{"groupId":-3,"groupName":"音频","order":-8,"show":true},{"groupId":-4,"groupName":"未分组","order":-7,"show":true}] + """) + if (bookGroupList == null) { + return returnData.setData(arrayListOf()) + } + saveUserStorage(userNameSpace, "bookGroup", bookGroupList) } - return returnData.setData(arrayListOf()) + return returnData.setData(bookGroupList.getList()) } suspend fun saveBookGroup(context: RoutingContext): ReturnData { @@ -1646,6 +1653,40 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti return returnData.setData("") } + suspend fun saveBookGroupOrder(context: RoutingContext): ReturnData { + val returnData = ReturnData() + if (!checkAuth(context)) { + return returnData.setData("NEED_LOGIN").setErrorMsg("请登录后使用") + } + val bookGroupOrder = context.bodyAsJson.getJsonArray("order", null) + if (bookGroupOrder == null) { + return returnData.setErrorMsg("参数错误") + } + + var userNameSpace = getUserNameSpace(context) + var bookGroupList: JsonArray? = asJsonArray(getUserStorage(userNameSpace, "bookGroup")) + if (bookGroupList == null) { + bookGroupList = JsonArray() + } + var orderMap: MutableMap = mutableMapOf() + for (i in 0 until bookGroupOrder.size()) { + orderMap.put(bookGroupOrder.getJsonObject(i).getInteger("groupId"), bookGroupOrder.getJsonObject(i).getInteger("order")) + } + // 遍历判断书本是否存在 + var groupList = bookGroupList.getList() + for (i in 0 until bookGroupList.size()) { + var bookGroup = bookGroupList.getJsonObject(i).mapTo(BookGroup::class.java) + if (orderMap.containsKey(bookGroup.groupId)) { + bookGroup.order = orderMap.get(bookGroup.groupId) as? Int ?: bookGroup.order + groupList.set(i, JsonObject.mapFrom(bookGroup)) + } + } + bookGroupList = JsonArray(groupList) + + // logger.info("bookGroupList: {}", bookGroupList) + saveUserStorage(userNameSpace, "bookGroup", bookGroupList) + return returnData.setData("") + } suspend fun deleteBookGroup(context: RoutingContext): ReturnData { val returnData = ReturnData() @@ -1658,7 +1699,7 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti if (bookGroupList == null) { bookGroupList = JsonArray() } - // 遍历判断书本是否存在 + // 遍历判断分组是否存在 var existIndex: Int = -1 for (i in 0 until bookGroupList.size()) { var _bookGroup = bookGroupList.getJsonObject(i).mapTo(BookGroup::class.java) @@ -1706,6 +1747,7 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti var bookList = arrayListOf() val concurrentCount = 16 val userBookSourceStringList = loadBookSourceStringList(userNameSpace) + val mutex = Mutex() limitConcurrent(concurrentCount, 0, bookshelf.size()) { var book = bookshelf.getJsonObject(it).mapTo(Book::class.java) if (!book.isLocalBook() && book.canUpdate && refresh) { @@ -1713,7 +1755,7 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti var bookSource = getBookSourceStringBySourceURL(book.origin, userNameSpace, userBookSourceStringList) if (bookSource != null) { withContext(Dispatchers.IO) { - var bookChapterList = getLocalChapterList(book, bookSource, refresh, userNameSpace, false) + var bookChapterList = getLocalChapterList(book, bookSource, refresh, userNameSpace, false, mutex) if (bookChapterList.size > 0) { var bookChapter = bookChapterList.last() book.latestChapterTitle = bookChapter.title @@ -1735,7 +1777,7 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti } - suspend fun getLocalChapterList(book: Book, bookSource: String, refresh: Boolean = false, userNameSpace: String, debugLog: Boolean = true): List { + suspend fun getLocalChapterList(book: Book, bookSource: String, refresh: Boolean = false, userNameSpace: String, debugLog: Boolean = true, mutex: Mutex? = null): List { val md5Encode = MD5Utils.md5Encode(book.bookUrl).toString() var chapterList: JsonArray? = asJsonArray(getUserStorage(userNameSpace, book.name + "_" + book.author, md5Encode)) @@ -1769,7 +1811,7 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti } } saveUserStorage(userNameSpace, getRelativePath(book.name + "_" + book.author, md5Encode), newChapterList) - saveShelfBookLatestChapter(book, newChapterList, userNameSpace) + saveShelfBookLatestChapter(book, newChapterList, userNameSpace, mutex) return newChapterList } var localChapterList = arrayListOf() @@ -1879,21 +1921,26 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti } } - fun saveShelfBookLatestChapter(book: Book, bookChapterList: List, userNameSpace: String) { - editShelfBook(book, userNameSpace) { existBook -> - if (bookChapterList.size > 0) { - var bookChapter = bookChapterList.last() - existBook.latestChapterTitle = bookChapter.title - } - if (bookChapterList.size - existBook.totalChapterNum > 0) { - existBook.lastCheckCount = bookChapterList.size - existBook.totalChapterNum - existBook.lastCheckTime = System.currentTimeMillis() - } - existBook.totalChapterNum = bookChapterList.size - // TODO 最新章节更新时间 - // existBook.latestChapterTime = System.currentTimeMillis() - // logger.info("saveShelfBookLatestChapter: {}", existBook) - existBook + suspend fun saveShelfBookLatestChapter(book: Book, bookChapterList: List, userNameSpace: String, mutex: Mutex? = null) { + try { + mutex?.lock() + editShelfBook(book, userNameSpace) { existBook -> + if (bookChapterList.size > 0) { + var bookChapter = bookChapterList.last() + existBook.latestChapterTitle = bookChapter.title + } + if (bookChapterList.size - existBook.totalChapterNum > 0) { + existBook.lastCheckCount = bookChapterList.size - existBook.totalChapterNum + existBook.lastCheckTime = System.currentTimeMillis() + } + existBook.totalChapterNum = bookChapterList.size + // TODO 最新章节更新时间 + // existBook.latestChapterTime = System.currentTimeMillis() + // logger.info("saveShelfBookLatestChapter: {}", existBook) + existBook + } + } finally { + mutex?.unlock() } } @@ -2890,19 +2937,19 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti private fun setAssets(book: Book, epubBook: EpubBook): String { epubBook.resources.add( Resource( - BookController::class.java.getResource("${File.separator}epub${File.separator}fonts.css").readBytes(), + BookController::class.java.getResource("/epub/fonts.css").readBytes(), "Styles/fonts.css" ) ) epubBook.resources.add( Resource( - BookController::class.java.getResource("${File.separator}epub${File.separator}main.css").readBytes(), + BookController::class.java.getResource("/epub/main.css").readBytes(), "Styles/main.css" ) ) epubBook.resources.add( Resource( - BookController::class.java.getResource("${File.separator}epub${File.separator}logo.png").readBytes(), + BookController::class.java.getResource("/epub/logo.png").readBytes(), "Images/logo.png" ) ) @@ -2914,7 +2961,7 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti book.getDisplayIntro(), book.kind, book.wordCount, - String(BookController::class.java.getResource("${File.separator}epub${File.separator}cover.html").readBytes()), + String(BookController::class.java.getResource("/epub/cover.html").readBytes()), "Text/cover.html" ) ) @@ -2926,12 +2973,12 @@ class BookController(coroutineContext: CoroutineContext): BaseController(corouti book.getDisplayIntro(), book.kind, book.wordCount, - String(BookController::class.java.getResource("${File.separator}epub${File.separator}intro.html").readBytes()), + String(BookController::class.java.getResource("/epub/intro.html").readBytes()), "Text/intro.html" ) ) - return String(BookController::class.java.getResource("${File.separator}epub${File.separator}chapter.html").readBytes()) + return String(BookController::class.java.getResource("/epub/chapter.html").readBytes()) } private suspend fun setCover(book: Book, epubBook: EpubBook, bookSourceString: String) { diff --git a/src/main/java/com/htmake/reader/utils/VertExt.kt b/src/main/java/com/htmake/reader/utils/VertExt.kt index 5cd80e2b..4aa1bf09 100644 --- a/src/main/java/com/htmake/reader/utils/VertExt.kt +++ b/src/main/java/com/htmake/reader/utils/VertExt.kt @@ -73,8 +73,10 @@ fun getWorkDir(subPath: String = ""): String { var currentDir = System.getProperty("user.dir") logger.info("osName: {} currentDir: {}", osName, currentDir) // MacOS 存放目录为用户目录 - if (osName.startsWith("Mac OS") && !currentDir.startsWith("/Users/")) { + if (osName.startsWith("Mac OS", true) && !currentDir.startsWith("/Users/")) { workDirPath = Paths.get(System.getProperty("user.home"), ".reader").toString() + } else { + workDirPath = currentDir } workDirInit = true } @@ -141,7 +143,7 @@ fun saveStorage(vararg name: String, value: Any, pretty: Boolean = false) { val filename = name.last() val file = File(getRelativePath(storagePath, *name.copyOfRange(0, name.size - 1), "${filename}.json")) // val file = File(storagePath + "/${name}.json") - logger.info("storage key: {} path: {}", name, file.absoluteFile) + logger.info("Save file to storage name: {} path: {}", name, file.absoluteFile) if (!file.parentFile.exists()) { file.parentFile.mkdirs() @@ -162,7 +164,7 @@ fun getStorage(vararg name: String): String? { val filename = name.last() val file = File(getRelativePath(storagePath, *name.copyOfRange(0, name.size - 1), "${filename}.json")) - logger.info("storage key: {} path: {}", name, file.absoluteFile) + logger.info("Read file from storage name: {} path: {}", name, file.absoluteFile) if (!file.exists()) { return null } diff --git a/src/main/java/io/legado/app/constant/BookType.kt b/src/main/java/io/legado/app/constant/BookType.kt index 0e48ffc8..c51d102f 100644 --- a/src/main/java/io/legado/app/constant/BookType.kt +++ b/src/main/java/io/legado/app/constant/BookType.kt @@ -3,6 +3,7 @@ package io.legado.app.constant object BookType { const val default = 0 // 0 文本 const val audio = 1 // 1 音频 - const val image = 3 //图片 + const val image = 2 // 2 图片 + const val file = 3 // 3 只提供下载服务的网站 const val local = "loc_book" } \ No newline at end of file diff --git a/src/main/java/io/legado/app/help/DefaultData.kt b/src/main/java/io/legado/app/help/DefaultData.kt index ba2044a0..ab920222 100644 --- a/src/main/java/io/legado/app/help/DefaultData.kt +++ b/src/main/java/io/legado/app/help/DefaultData.kt @@ -10,7 +10,7 @@ object DefaultData { const val txtTocRuleFileName = "txtTocRule.json" val txtTocRules: List by lazy { - val json = String(DefaultData::class.java.getResource("${File.separator}defaultData${File.separator}$txtTocRuleFileName").readBytes()) + val json = String(DefaultData::class.java.getResource("/defaultData/${txtTocRuleFileName}").readBytes()) GSON.fromJsonArray(json).getOrNull() ?: emptyList() } diff --git a/src/main/java/io/legado/app/model/Debugger.kt b/src/main/java/io/legado/app/model/Debugger.kt index faaaa129..9af8568c 100644 --- a/src/main/java/io/legado/app/model/Debugger.kt +++ b/src/main/java/io/legado/app/model/Debugger.kt @@ -48,6 +48,7 @@ class Debugger(val logMsg: (String) -> Unit) : DebugLog { suspend fun startDebug(webBook: WebBook, key: String) { val bookSource = webBook.bookSource webBook.debugLogger = this@Debugger + startTime = System.currentTimeMillis() when { key.isAbsUrl() -> { val book = Book() diff --git a/src/main/java/io/legado/app/utils/SourceAnalyzer.kt b/src/main/java/io/legado/app/utils/SourceAnalyzer.kt index 51671993..4f98c6dc 100644 --- a/src/main/java/io/legado/app/utils/SourceAnalyzer.kt +++ b/src/main/java/io/legado/app/utils/SourceAnalyzer.kt @@ -88,8 +88,19 @@ object SourceAnalyzer { header = uaToHeader(jsonItem.readString("httpUserAgent")) searchUrl = toNewUrl(jsonItem.readString("ruleSearchUrl")) exploreUrl = toNewUrls(jsonItem.readString("ruleFindUrl")) - bookSourceType = - if (jsonItem.readString("bookSourceType") == "AUDIO") BookType.audio else BookType.default + val sourceType = jsonItem.readString("bookSourceType") + bookSourceType = when(sourceType) { + "AUDIO" -> BookType.audio + "audio" -> BookType.audio + "1" -> BookType.audio + "IMAGE" -> BookType.image + "image" -> BookType.image + "2" -> BookType.image + "FILE" -> BookType.file + "file" -> BookType.file + "3" -> BookType.file + else -> BookType.default + } enabled = jsonItem.readBool("enable") ?: true if (exploreUrl.isNullOrBlank()) { enabledExplore = false diff --git a/web/package.json b/web/package.json index 2684fd1c..708aaff8 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "reader", - "version": "2.5.3", + "version": "2.5.4", "private": true, "scripts": { "serve": "vue-cli-service serve", @@ -16,6 +16,7 @@ "localforage": "^1.10.0", "prismjs": "^1.25.0", "register-service-worker": "^1.7.1", + "sortablejs": "^1.15.0", "stylus": "^0.54.7", "stylus-loader": "^3.0.2", "vue": "^2.6.10", diff --git a/web/public/bookSourceDebug/index.js b/web/public/bookSourceDebug/index.js index 03910ca1..20006d56 100644 --- a/web/public/bookSourceDebug/index.js +++ b/web/public/bookSourceDebug/index.js @@ -173,9 +173,8 @@ function rule2json() { RuleJSON.customOrder = RuleJSON.customOrder == "" ? 0 : parseInt(RuleJSON.customOrder); RuleJSON.weight = RuleJSON.weight == "" ? 0 : parseInt(RuleJSON.weight); - (RuleJSON.bookSourceType == RuleJSON.bookSourceType) == "" - ? 0 - : parseInt(RuleJSON.bookSourceType); + RuleJSON.bookSourceType = + RuleJSON.bookSourceType == "" ? 0 : parseInt(RuleJSON.bookSourceType); RuleJSON.enabled = RuleJSON.enabled == "" || String(RuleJSON.enabled) diff --git a/web/public/index.html b/web/public/index.html index 592100c0..9807833d 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -20,7 +20,6 @@ - 阅读