@@ -25,7 +25,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div>源类型 :</div>
|
<div>源类型 :</div>
|
||||||
<textarea rows="1" id="bookSourceType" class="base" title="bookSourceType"
|
<textarea rows="1" id="bookSourceType" class="base" title="bookSourceType"
|
||||||
placeholder="<必填>0:文本 1:音频 2:图片"></textarea>
|
placeholder="<必填>0:文本 1:音频 2:图片 3:文件(只提供下载的网站)"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div>源名称 :</div>
|
<div>源名称 :</div>
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package io.legado.app.api.controller
|
package io.legado.app.api.controller
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Base64
|
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import io.legado.app.api.ReturnData
|
import io.legado.app.api.ReturnData
|
||||||
import io.legado.app.constant.PreferKey
|
import io.legado.app.constant.PreferKey
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
@@ -12,7 +10,6 @@ import io.legado.app.data.entities.BookSource
|
|||||||
import io.legado.app.help.BookHelp
|
import io.legado.app.help.BookHelp
|
||||||
import io.legado.app.help.CacheManager
|
import io.legado.app.help.CacheManager
|
||||||
import io.legado.app.help.ContentProcessor
|
import io.legado.app.help.ContentProcessor
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.glide.ImageLoader
|
import io.legado.app.help.glide.ImageLoader
|
||||||
import io.legado.app.help.storage.AppWebDav
|
import io.legado.app.help.storage.AppWebDav
|
||||||
import io.legado.app.model.BookCover
|
import io.legado.app.model.BookCover
|
||||||
@@ -27,7 +24,6 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
|
|
||||||
object BookController {
|
object BookController {
|
||||||
|
|
||||||
@@ -248,45 +244,7 @@ object BookController {
|
|||||||
val fileData = parameters["fileData"]?.firstOrNull()
|
val fileData = parameters["fileData"]?.firstOrNull()
|
||||||
?: return returnData.setErrorMsg("fileData 不能为空")
|
?: return returnData.setErrorMsg("fileData 不能为空")
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val defaultBookTreeUri = AppConfig.defaultBookTreeUri
|
LocalBook.importFile(fileData, fileName)
|
||||||
if (defaultBookTreeUri.isNullOrBlank()) return returnData.setErrorMsg("没有设置书籍保存位置!")
|
|
||||||
val treeUri = Uri.parse(defaultBookTreeUri)
|
|
||||||
val fileBytes =
|
|
||||||
Base64.decode(fileData.substringAfter("base64,"), Base64.DEFAULT)
|
|
||||||
val uri = if (treeUri.isContentScheme()) {
|
|
||||||
val treeDoc = DocumentFile.fromTreeUri(appCtx, treeUri)
|
|
||||||
var doc = treeDoc!!.findFile(fileName)
|
|
||||||
if (doc == null) {
|
|
||||||
doc = treeDoc.createFile(FileUtils.getMimeType(fileName), fileName)
|
|
||||||
?: throw SecurityException("Permission Denial")
|
|
||||||
}
|
|
||||||
appCtx.contentResolver.openOutputStream(doc.uri)!!.use { oStream ->
|
|
||||||
oStream.write(fileBytes)
|
|
||||||
}
|
|
||||||
doc.uri
|
|
||||||
} else {
|
|
||||||
val treeFile = File(treeUri.path!!)
|
|
||||||
val file = treeFile.getFile(fileName)
|
|
||||||
FileOutputStream(file).use { oStream ->
|
|
||||||
oStream.write(fileBytes)
|
|
||||||
}
|
|
||||||
Uri.fromFile(file)
|
|
||||||
}
|
|
||||||
val nameAuthor = LocalBook.analyzeNameAuthor(fileName)
|
|
||||||
val book = Book(
|
|
||||||
bookUrl = uri.toString(),
|
|
||||||
name = nameAuthor.first,
|
|
||||||
author = nameAuthor.second,
|
|
||||||
originName = fileName,
|
|
||||||
coverUrl = FileUtils.getPath(
|
|
||||||
appCtx.externalFiles,
|
|
||||||
"covers",
|
|
||||||
"${MD5Utils.md5Encode16(uri.toString())}.jpg"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (book.isEpub()) EpubFile.upBookInfo(book)
|
|
||||||
if (book.isUmd()) UmdFile.upBookInfo(book)
|
|
||||||
appDb.bookDao.insert(book)
|
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
return when (it) {
|
return when (it) {
|
||||||
is SecurityException -> returnData.setErrorMsg("需重新设置书籍保存位置!")
|
is SecurityException -> returnData.setErrorMsg("需重新设置书籍保存位置!")
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import androidx.annotation.IntDef
|
|||||||
object BookType {
|
object BookType {
|
||||||
const val default = 0 // 0 文本
|
const val default = 0 // 0 文本
|
||||||
const val audio = 1 // 1 音频
|
const val audio = 1 // 1 音频
|
||||||
const val image = 2 //图片
|
const val image = 2 // 2 图片
|
||||||
|
const val file = 3 // 3 只提供下载服务的网站
|
||||||
const val local = "loc_book"
|
const val local = "loc_book"
|
||||||
|
|
||||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ data class BookSource(
|
|||||||
var bookSourceName: String = "",
|
var bookSourceName: String = "",
|
||||||
// 分组
|
// 分组
|
||||||
var bookSourceGroup: String? = null,
|
var bookSourceGroup: String? = null,
|
||||||
// 类型,0 文本,1 音频, 2 图片
|
// 类型,0 文本,1 音频, 2 图片, 3 文件(指的是类似知轩藏书只提供下载的网站)
|
||||||
@BookType.Type
|
@BookType.Type
|
||||||
var bookSourceType: Int = 0,
|
var bookSourceType: Int = 0,
|
||||||
// 详情页url正则
|
// 详情页url正则
|
||||||
|
|||||||
@@ -197,12 +197,10 @@ interface JsExtensions {
|
|||||||
*js实现读取cookie
|
*js实现读取cookie
|
||||||
*/
|
*/
|
||||||
fun getCookie(tag: String, key: String? = null): String {
|
fun getCookie(tag: String, key: String? = null): String {
|
||||||
val cookie = CookieStore.getCookie(tag)
|
|
||||||
val cookieMap = CookieStore.cookieToMap(cookie)
|
|
||||||
return if (key != null) {
|
return if (key != null) {
|
||||||
cookieMap[key] ?: ""
|
CookieStore.getKey(tag, key)
|
||||||
} else {
|
} else {
|
||||||
cookie
|
CookieStore.getCookie(tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,10 +468,8 @@ interface JsExtensions {
|
|||||||
* @return zip指定文件的数据
|
* @return zip指定文件的数据
|
||||||
*/
|
*/
|
||||||
fun getZipByteArrayContent(url: String, path: String): ByteArray? {
|
fun getZipByteArrayContent(url: String, path: String): ByteArray? {
|
||||||
val bytes = if (url.startsWith("http://") || url.startsWith("https://")) {
|
val bytes = if (url.isAbsUrl()) {
|
||||||
runBlocking {
|
AnalyzeUrl(url, source = getSource()).getByteArray()
|
||||||
return@runBlocking okHttpClient.newCallResponseBody { url(url) }.bytes()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
StringUtils.hexStringToByte(url)
|
StringUtils.hexStringToByte(url)
|
||||||
}
|
}
|
||||||
@@ -517,7 +513,7 @@ interface JsExtensions {
|
|||||||
str.isAbsUrl() -> runBlocking {
|
str.isAbsUrl() -> runBlocking {
|
||||||
var x = CacheManager.getByteArray(key)
|
var x = CacheManager.getByteArray(key)
|
||||||
if (x == null) {
|
if (x == null) {
|
||||||
x = okHttpClient.newCallResponseBody { url(str) }.bytes()
|
x = AnalyzeUrl(str, source = getSource()).getByteArray()
|
||||||
x.let {
|
x.let {
|
||||||
CacheManager.put(key, it)
|
CacheManager.put(key, it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.legado.app.model.localBook
|
package io.legado.app.model.localBook
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.util.Base64
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import com.script.SimpleBindings
|
import com.script.SimpleBindings
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
@@ -8,13 +9,17 @@ import io.legado.app.constant.AppConst
|
|||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
import io.legado.app.data.entities.BookChapter
|
import io.legado.app.data.entities.BookChapter
|
||||||
|
import io.legado.app.data.entities.BaseSource
|
||||||
import io.legado.app.exception.TocEmptyException
|
import io.legado.app.exception.TocEmptyException
|
||||||
|
import io.legado.app.exception.NoStackTraceException
|
||||||
import io.legado.app.help.BookHelp
|
import io.legado.app.help.BookHelp
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
|
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||||
import io.legado.app.utils.*
|
import io.legado.app.utils.*
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
import java.io.FileOutputStream
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
@@ -79,6 +84,32 @@ object LocalBook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//导入在线的文件
|
||||||
|
fun importFile(
|
||||||
|
str: String,
|
||||||
|
fileName: String,
|
||||||
|
source: BaseSource? = null
|
||||||
|
): Book {
|
||||||
|
val bytes = when {
|
||||||
|
str.isAbsUrl() -> AnalyzeUrl(str, source = source).getByteArray()
|
||||||
|
str.isDataUrl() -> Base64.decode(str.substringAfter("base64,"), Base64.DEFAULT)
|
||||||
|
else -> throw NoStackTraceException("在线导入书籍支持http/https/DataURL")
|
||||||
|
}
|
||||||
|
//从文件头识别文件格式
|
||||||
|
|
||||||
|
return importFile(bytes, fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun importFile(
|
||||||
|
bytes: ByteArray,
|
||||||
|
fileName: String
|
||||||
|
): Book {
|
||||||
|
return saveBookFile(bytes, fileName).let {
|
||||||
|
importFile(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//导入本地文件
|
||||||
fun importFile(uri: Uri): Book {
|
fun importFile(uri: Uri): Book {
|
||||||
val bookUrl: String
|
val bookUrl: String
|
||||||
val updateTime: Long
|
val updateTime: Long
|
||||||
@@ -119,7 +150,7 @@ object LocalBook {
|
|||||||
return book
|
return book
|
||||||
}
|
}
|
||||||
|
|
||||||
fun analyzeNameAuthor(fileName: String): Pair<String, String> {
|
private fun analyzeNameAuthor(fileName: String): Pair<String, String> {
|
||||||
val tempFileName = fileName.substringBeforeLast(".")
|
val tempFileName = fileName.substringBeforeLast(".")
|
||||||
var name: String
|
var name: String
|
||||||
var author: String
|
var author: String
|
||||||
@@ -171,4 +202,32 @@ object LocalBook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun saveBookFile(
|
||||||
|
bytes: ByteArray,
|
||||||
|
fileName: String
|
||||||
|
): Uri {
|
||||||
|
val defaultBookTreeUri = AppConfig.defaultBookTreeUri
|
||||||
|
if (defaultBookTreeUri.isNullOrBlank()) throw NoStackTraceException("没有设置书籍保存位置!")
|
||||||
|
val treeUri = Uri.parse(defaultBookTreeUri)
|
||||||
|
return if (treeUri.isContentScheme()) {
|
||||||
|
val treeDoc = DocumentFile.fromTreeUri(appCtx, treeUri)
|
||||||
|
var doc = treeDoc!!.findFile(fileName)
|
||||||
|
if (doc == null) {
|
||||||
|
doc = treeDoc.createFile(FileUtils.getMimeType(fileName), fileName)
|
||||||
|
?: throw SecurityException("Permission Denial")
|
||||||
|
}
|
||||||
|
appCtx.contentResolver.openOutputStream(doc.uri)!!.use { oStream ->
|
||||||
|
oStream.write(bytes)
|
||||||
|
}
|
||||||
|
doc.uri
|
||||||
|
} else {
|
||||||
|
val treeFile = File(treeUri.path!!)
|
||||||
|
val file = treeFile.getFile(fileName)
|
||||||
|
FileOutputStream(file).use { oStream ->
|
||||||
|
oStream.write(bytes)
|
||||||
|
}
|
||||||
|
Uri.fromFile(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ class BookSourceEditActivity :
|
|||||||
binding.cbIsEnableFind.isChecked = it.enabledExplore
|
binding.cbIsEnableFind.isChecked = it.enabledExplore
|
||||||
binding.spType.setSelection(
|
binding.spType.setSelection(
|
||||||
when (it.bookSourceType) {
|
when (it.bookSourceType) {
|
||||||
|
BookType.file -> 3
|
||||||
BookType.image -> 2
|
BookType.image -> 2
|
||||||
BookType.audio -> 1
|
BookType.audio -> 1
|
||||||
else -> 0
|
else -> 0
|
||||||
@@ -296,6 +297,7 @@ class BookSourceEditActivity :
|
|||||||
source.enabled = binding.cbIsEnable.isChecked
|
source.enabled = binding.cbIsEnable.isChecked
|
||||||
source.enabledExplore = binding.cbIsEnableFind.isChecked
|
source.enabledExplore = binding.cbIsEnableFind.isChecked
|
||||||
source.bookSourceType = when (binding.spType.selectedItemPosition) {
|
source.bookSourceType = when (binding.spType.selectedItemPosition) {
|
||||||
|
3 -> BookType.file
|
||||||
2 -> BookType.image
|
2 -> BookType.image
|
||||||
1 -> BookType.audio
|
1 -> BookType.audio
|
||||||
else -> BookType.default
|
else -> BookType.default
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<item>Texto</item>
|
<item>Texto</item>
|
||||||
<item>Audio</item>
|
<item>Audio</item>
|
||||||
<item>Image</item>
|
<item>Image</item>
|
||||||
|
<item>File</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="group_style">
|
<string-array name="group_style">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<item>Texto</item>
|
<item>Texto</item>
|
||||||
<item>Áudio</item>
|
<item>Áudio</item>
|
||||||
<item>Image</item>
|
<item>Image</item>
|
||||||
|
<item>File</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="group_style">
|
<string-array name="group_style">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<item>文本</item>
|
<item>文本</item>
|
||||||
<item>音頻</item>
|
<item>音頻</item>
|
||||||
<item>图片</item>
|
<item>图片</item>
|
||||||
|
<item>文件</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="group_style">
|
<string-array name="group_style">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<item>文字</item>
|
<item>文字</item>
|
||||||
<item>音訊</item>
|
<item>音訊</item>
|
||||||
<item>图片</item>
|
<item>图片</item>
|
||||||
|
<item>文件</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="group_style">
|
<string-array name="group_style">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<item>文本</item>
|
<item>文本</item>
|
||||||
<item>音频</item>
|
<item>音频</item>
|
||||||
<item>图片</item>
|
<item>图片</item>
|
||||||
|
<item>文件</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="group_style">
|
<string-array name="group_style">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<item>Text</item>
|
<item>Text</item>
|
||||||
<item>Audio</item>
|
<item>Audio</item>
|
||||||
<item>Image</item>
|
<item>Image</item>
|
||||||
|
<item>File</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="group_style">
|
<string-array name="group_style">
|
||||||
|
|||||||
Reference in New Issue
Block a user