diff --git a/app/src/main/java/io/legado/app/data/dao/BookDao.kt b/app/src/main/java/io/legado/app/data/dao/BookDao.kt index 064eb29b2..04e76d3bd 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookDao.kt @@ -30,6 +30,9 @@ interface BookDao { @Query("SELECT * FROM books WHERE bookUrl = :bookUrl") fun getBook(bookUrl: String): Book? + @get:Query("SELECT * FROM books") + val allBooks: List + @get:Query("SELECT * FROM books ORDER BY durChapterTime DESC limit 1") val lastReadBook: Book? diff --git a/app/src/main/java/io/legado/app/web/ReadMe.md b/app/src/main/java/io/legado/app/web/ReadMe.md new file mode 100644 index 000000000..cd5544c3e --- /dev/null +++ b/app/src/main/java/io/legado/app/web/ReadMe.md @@ -0,0 +1 @@ +# web服务 \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/web/controller/BookshelfController.kt b/app/src/main/java/io/legado/app/web/controller/BookshelfController.kt new file mode 100644 index 000000000..e0d9cae21 --- /dev/null +++ b/app/src/main/java/io/legado/app/web/controller/BookshelfController.kt @@ -0,0 +1,56 @@ +package io.legado.app.web.controller + +import io.legado.app.App +import io.legado.app.data.entities.Book +import io.legado.app.utils.GSON +import io.legado.app.utils.fromJsonObject +import io.legado.app.web.utils.ReturnData + +class BookshelfController { + + val bookshelf: ReturnData + get() { + val books = App.db.bookDao().allBooks + val returnData = ReturnData() + return if (books.isEmpty()) { + returnData.setErrorMsg("还没有添加小说") + } else returnData.setData(books) + } + + fun getChapterList(parameters: Map>): ReturnData { + val strings = parameters["url"] + val returnData = ReturnData() + if (strings == null) { + return returnData.setErrorMsg("参数url不能为空,请指定书籍地址") + } + val chapterList = App.db.bookChapterDao().getChapterList(strings[0]) + return returnData.setData(chapterList) + } + + fun getBookContent(parameters: Map>): ReturnData { + val strings = parameters["url"] + val returnData = ReturnData() + if (strings == null) { + return returnData.setErrorMsg("参数url不能为空,请指定内容地址") + } + val book = App.db.bookDao().getBook(strings[0]) + val chapter = App.db.bookChapterDao().getChapter(strings[0], strings[1].toInt()) + if (book == null || chapter == null) { + returnData.setErrorMsg("未找到") + } else { + + } + return returnData + } + + fun saveBook(postData: String): ReturnData { + val book = GSON.fromJsonObject(postData) + val returnData = ReturnData() + if (book != null) { + App.db.bookDao().insert(book) + return returnData.setData("") + } + return returnData.setErrorMsg("格式不对") + } + +} diff --git a/app/src/main/java/io/legado/app/web/controller/SourceController.kt b/app/src/main/java/io/legado/app/web/controller/SourceController.kt new file mode 100644 index 000000000..dafeb1c7d --- /dev/null +++ b/app/src/main/java/io/legado/app/web/controller/SourceController.kt @@ -0,0 +1,79 @@ +package io.legado.app.web.controller + + +import android.text.TextUtils +import io.legado.app.App +import io.legado.app.data.entities.BookSource +import io.legado.app.utils.GSON +import io.legado.app.utils.fromJsonArray +import io.legado.app.utils.fromJsonObject +import io.legado.app.web.utils.ReturnData + +class SourceController { + + val sources: ReturnData + get() { + val bookSources = App.db.bookSourceDao().all + val returnData = ReturnData() + return if (bookSources.isEmpty()) { + returnData.setErrorMsg("设备书源列表为空") + } else returnData.setData(bookSources) + } + + fun saveSource(postData: String): ReturnData { + val returnData = ReturnData() + try { + val bookSource = GSON.fromJsonObject(postData) + if (bookSource != null) { + if (TextUtils.isEmpty(bookSource.bookSourceName) || TextUtils.isEmpty(bookSource.bookSourceUrl)) { + returnData.setErrorMsg("书源名称和URL不能为空") + } else { + App.db.bookSourceDao().insert(bookSource) + returnData.setData("") + } + } else { + returnData.setErrorMsg("转换书源失败") + } + } catch (e: Exception) { + returnData.setErrorMsg(e.localizedMessage) + } + return returnData + } + + fun saveSources(postData: String): ReturnData { + val okSources = arrayListOf() + kotlin.runCatching { + val bookSources = GSON.fromJsonArray(postData) + if (bookSources != null) { + for (bookSource in bookSources) { + if (bookSource.bookSourceName.isBlank() || bookSource.bookSourceUrl.isBlank()) { + continue + } + App.db.bookSourceDao().insert(bookSource) + okSources.add(bookSource) + } + } + } + return ReturnData().setData(okSources) + } + + fun getSource(parameters: Map>): ReturnData { + val strings = parameters["url"] + val returnData = ReturnData() + if (strings == null) { + return returnData.setErrorMsg("参数url不能为空,请指定书源地址") + } + val bookSource = App.db.bookSourceDao().getBookSource(strings[0]) + ?: return returnData.setErrorMsg("未找到书源,请检查书源地址") + return returnData.setData(bookSource) + } + + fun deleteSources(postData: String): ReturnData { + kotlin.runCatching { + GSON.fromJsonArray(postData)?.let { + App.db.bookSourceDao().delete(*it.toTypedArray()) + } + } + return ReturnData().setData("已执行"/*okSources*/) + } +} diff --git a/app/src/main/java/io/legado/app/web/controller/SourceDebugWebSocket.kt b/app/src/main/java/io/legado/app/web/controller/SourceDebugWebSocket.kt new file mode 100644 index 000000000..999ae7872 --- /dev/null +++ b/app/src/main/java/io/legado/app/web/controller/SourceDebugWebSocket.kt @@ -0,0 +1,82 @@ +package io.legado.app.web.controller + + +import fi.iki.elonen.NanoHTTPD +import fi.iki.elonen.NanoWSD +import io.legado.app.App +import io.legado.app.R +import io.legado.app.model.WebBook +import io.legado.app.model.webbook.SourceDebug +import io.legado.app.utils.GSON +import io.legado.app.utils.fromJsonObject +import io.legado.app.utils.isJson +import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers.IO +import java.io.IOException + + +class SourceDebugWebSocket(handshakeRequest: NanoHTTPD.IHTTPSession) : + NanoWSD.WebSocket(handshakeRequest), + CoroutineScope by MainScope(), + SourceDebug.Callback { + + + override fun onOpen() { + launch(IO) { + do { + delay(30000) + runCatching { + ping(byteArrayOf("ping".toByte())) + } + } while (isOpen) + } + } + + override fun onClose( + code: NanoWSD.WebSocketFrame.CloseCode, + reason: String, + initiatedByRemote: Boolean + ) { + cancel() + SourceDebug.cancelDebug(true) + } + + override fun onMessage(message: NanoWSD.WebSocketFrame) { + if (!message.textPayload.isJson()) return + kotlin.runCatching { + val debugBean = GSON.fromJsonObject>(message.textPayload) + if (debugBean != null) { + val tag = debugBean["tag"] + val key = debugBean["key"] + if (tag.isNullOrBlank() || key.isNullOrBlank()) { + kotlin.runCatching { + send(App.INSTANCE.getString(R.string.cannot_empty)) + close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false) + } + return + } + App.db.bookSourceDao().getBookSource(tag)?.let { + SourceDebug(WebBook(it), this).startDebug(key) + } + } + } + } + + override fun onPong(pong: NanoWSD.WebSocketFrame) { + + } + + override fun onException(exception: IOException) { + SourceDebug.cancelDebug(true) + } + + override fun printLog(state: Int, msg: String) { + kotlin.runCatching { + send(msg) + if (state == -1 || state == 1000) { + SourceDebug.cancelDebug(true) + } + } + } + +} diff --git a/app/src/main/java/io/legado/app/web/utils/AssetsWeb.kt b/app/src/main/java/io/legado/app/web/utils/AssetsWeb.kt new file mode 100644 index 000000000..503f7aad5 --- /dev/null +++ b/app/src/main/java/io/legado/app/web/utils/AssetsWeb.kt @@ -0,0 +1,44 @@ +package io.legado.app.web.utils + +import android.content.res.AssetManager +import android.text.TextUtils +import fi.iki.elonen.NanoHTTPD +import io.legado.app.App +import java.io.File +import java.io.IOException + + +class AssetsWeb(rootPath: String) { + private val assetManager: AssetManager = App.INSTANCE.assets + private var rootPath = "web" + + init { + if (!TextUtils.isEmpty(rootPath)) { + this.rootPath = rootPath + } + } + + @Throws(IOException::class) + fun getResponse(path: String): NanoHTTPD.Response { + var path1 = path + path1 = (rootPath + path1).replace("/+".toRegex(), File.separator) + val inputStream = assetManager.open(path1) + return NanoHTTPD.newChunkedResponse( + NanoHTTPD.Response.Status.OK, + getMimeType(path1), + inputStream + ) + } + + private fun getMimeType(path: String): String { + val suffix = path.substring(path.lastIndexOf(".")) + return when { + suffix.equals(".html", ignoreCase = true) + || suffix.equals(".htm", ignoreCase = true) -> "text/html" + suffix.equals(".js", ignoreCase = true) -> "text/javascript" + suffix.equals(".css", ignoreCase = true) -> "text/css" + suffix.equals(".ico", ignoreCase = true) -> "image/x-icon" + else -> "text/html" + } + } +} diff --git a/app/src/main/java/io/legado/app/web/utils/ReturnData.kt b/app/src/main/java/io/legado/app/web/utils/ReturnData.kt new file mode 100644 index 000000000..5ea07243f --- /dev/null +++ b/app/src/main/java/io/legado/app/web/utils/ReturnData.kt @@ -0,0 +1,39 @@ +package io.legado.app.web.utils + + +class ReturnData { + + var isSuccess: Boolean = false + + var errorCode: Int = 0 + + private var errorMsg: String? = null + + private var data: Any? = null + + init { + this.isSuccess = false + this.errorMsg = "未知错误,请联系开发者!" + } + + fun getErrorMsg(): String? { + return errorMsg + } + + fun setErrorMsg(errorMsg: String): ReturnData { + this.isSuccess = false + this.errorMsg = errorMsg + return this + } + + fun getData(): Any? { + return data + } + + fun setData(data: Any): ReturnData { + this.isSuccess = true + this.errorMsg = "" + this.data = data + return this + } +}