fix(CustomExportDialog): 暂存
This commit is contained in:
@@ -4,6 +4,7 @@ import android.app.Application
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.util.ArraySet
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
@@ -41,6 +42,7 @@ import java.nio.charset.Charset
|
|||||||
import java.nio.file.*
|
import java.nio.file.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.coroutines.coroutineContext
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
|
|
||||||
@@ -248,7 +250,55 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析范围字符串
|
||||||
|
*
|
||||||
|
* @param scope 范围字符串
|
||||||
|
* @return 范围
|
||||||
|
*
|
||||||
|
* @since 2023/5/22
|
||||||
|
* @author Discut
|
||||||
|
*/
|
||||||
|
private fun paresScope(scope: String): IntArray {
|
||||||
|
val split = scope.split(",")
|
||||||
|
val result = ArraySet<Int>()
|
||||||
|
for (s in split) {
|
||||||
|
val v = s.split("-")
|
||||||
|
if (v.size != 2) {
|
||||||
|
result.add(s.toInt() - 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val left = v[0].toInt();
|
||||||
|
val right = v[1].toInt();
|
||||||
|
if (left > right)
|
||||||
|
return IntArray(0)
|
||||||
|
for (i in left..right)
|
||||||
|
result.add(i - 1)
|
||||||
|
}
|
||||||
|
return result.toIntArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////Start EPUB
|
//////////////////Start EPUB
|
||||||
|
/**
|
||||||
|
* 导出Epub 自定义导出范围
|
||||||
|
*
|
||||||
|
* @param path 导出路径
|
||||||
|
* @param book 书籍
|
||||||
|
* @param size 每本Epub包含的章节
|
||||||
|
* @param scope 导出范围
|
||||||
|
* @author Discut
|
||||||
|
* @since 2023/5/22
|
||||||
|
*/
|
||||||
|
fun exportEPUB(path: String, book: Book, size: Int = 1, scope: String) {
|
||||||
|
if (exportProgress.contains(book.bookUrl)) return
|
||||||
|
CustomExporter(this).let {
|
||||||
|
it.scope = paresScope(scope)
|
||||||
|
it.size = size
|
||||||
|
it.exportEPUB(path, book)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出Epub
|
* 导出Epub
|
||||||
*/
|
*/
|
||||||
@@ -373,6 +423,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
//其他格式文件当做资源文件
|
//其他格式文件当做资源文件
|
||||||
folder.listFiles().forEach {
|
folder.listFiles().forEach {
|
||||||
@@ -575,4 +626,183 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//////end of EPUB
|
//////end of EPUB
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义Exporter
|
||||||
|
*/
|
||||||
|
class CustomExporter(private val context: CacheViewModel) {
|
||||||
|
var scope: IntArray = IntArray(0);
|
||||||
|
var size: Int = 1;
|
||||||
|
|
||||||
|
fun exportEPUB(
|
||||||
|
path: String,
|
||||||
|
book: Book
|
||||||
|
) {
|
||||||
|
context.exportProgress[book.bookUrl] = 0
|
||||||
|
context.exportMsg.remove(book.bookUrl)
|
||||||
|
context.upAdapterLiveData.sendValue(book.bookUrl)
|
||||||
|
context.execute {
|
||||||
|
context.mutex.withLock {
|
||||||
|
while (context.exportNumber > 0) {
|
||||||
|
delay(1000)
|
||||||
|
}
|
||||||
|
context.exportNumber++
|
||||||
|
}
|
||||||
|
if (path.isContentScheme()) {
|
||||||
|
val uri = Uri.parse(path)
|
||||||
|
val doc = DocumentFile.fromTreeUri(context.context, uri)
|
||||||
|
?: throw NoStackTraceException("获取导出文档失败")
|
||||||
|
book.totalChapterNum
|
||||||
|
// TODO
|
||||||
|
exportEpub(doc, book)
|
||||||
|
} else {
|
||||||
|
context.exportEpub(File(path).createFolderIfNotExist(), book)
|
||||||
|
}
|
||||||
|
}.onError {
|
||||||
|
context.exportProgress.remove(book.bookUrl)
|
||||||
|
context.exportMsg[book.bookUrl] = it.localizedMessage ?: "ERROR"
|
||||||
|
context.upAdapterLiveData.postValue(book.bookUrl)
|
||||||
|
it.printStackTrace()
|
||||||
|
AppLog.put("导出epub书籍<${book.name}>出错\n${it.localizedMessage}", it)
|
||||||
|
}.onSuccess {
|
||||||
|
context.exportProgress.remove(book.bookUrl)
|
||||||
|
context.exportMsg[book.bookUrl] = context.context.getString(R.string.export_success)
|
||||||
|
context.upAdapterLiveData.postValue(book.bookUrl)
|
||||||
|
}.onFinally {
|
||||||
|
context.exportNumber--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出 epub
|
||||||
|
*
|
||||||
|
* from [io.legado.app.ui.book.cache.CacheViewModel.exportEpub]
|
||||||
|
*/
|
||||||
|
private suspend fun exportEpub(doc: DocumentFile, book: Book) {
|
||||||
|
val (contentModel, epubList) = createEpubs(doc, book)
|
||||||
|
epubList.forEachIndexed { index, epubBook ->
|
||||||
|
//设置正文
|
||||||
|
this.setEpubContent(contentModel, book, epubBook, index)
|
||||||
|
save2Drive(book.name+index+".epub", epubBook, doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置epub正文
|
||||||
|
*
|
||||||
|
* from [io.legado.app.ui.book.cache.CacheViewModel.setEpubContent]
|
||||||
|
*/
|
||||||
|
private suspend fun setEpubContent(
|
||||||
|
contentModel: String,
|
||||||
|
book: Book,
|
||||||
|
epubBook: EpubBook,
|
||||||
|
epubBookIndex: Int
|
||||||
|
) {
|
||||||
|
|
||||||
|
//正文
|
||||||
|
val useReplace = AppConfig.exportUseReplace && book.getUseReplaceRule()
|
||||||
|
val contentProcessor = ContentProcessor.get(book.name, book.origin)
|
||||||
|
var chapterList: MutableList<BookChapter> = ArrayList();
|
||||||
|
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter ->
|
||||||
|
if (scope.indexOf(index) >= 0) {
|
||||||
|
chapterList.add(chapter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chapterList = chapterList.subList(epubBookIndex * size, (epubBookIndex + 1) * size)
|
||||||
|
chapterList.forEachIndexed { index, chapter ->
|
||||||
|
coroutineContext.ensureActive()
|
||||||
|
context.upAdapterLiveData.postValue(book.bookUrl)
|
||||||
|
context.exportProgress[book.bookUrl] = index
|
||||||
|
BookHelp.getContent(book, chapter).let { content ->
|
||||||
|
var content1 = context.fixPic(
|
||||||
|
epubBook,
|
||||||
|
book,
|
||||||
|
content ?: if (chapter.isVolume) "" else "null",
|
||||||
|
chapter
|
||||||
|
)
|
||||||
|
content1 = contentProcessor
|
||||||
|
.getContent(
|
||||||
|
book,
|
||||||
|
chapter,
|
||||||
|
content1,
|
||||||
|
includeTitle = false,
|
||||||
|
useReplace = useReplace,
|
||||||
|
chineseConvert = false,
|
||||||
|
reSegment = false
|
||||||
|
).toString()
|
||||||
|
val title = chapter.run {
|
||||||
|
// 不导出vip标识
|
||||||
|
isVip = false
|
||||||
|
getDisplayTitle(
|
||||||
|
contentProcessor.getTitleReplaceRules(),
|
||||||
|
useReplace = useReplace
|
||||||
|
)
|
||||||
|
}
|
||||||
|
epubBook.addSection(
|
||||||
|
title,
|
||||||
|
ResourceUtil.createChapterResource(
|
||||||
|
title.replace("\uD83D\uDD12", ""),
|
||||||
|
content1,
|
||||||
|
contentModel,
|
||||||
|
"Text/chapter_${index}.html"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建多个epub 对象
|
||||||
|
*/
|
||||||
|
private fun createEpubs(doc: DocumentFile, book: Book): Pair<String, List<EpubBook>> {
|
||||||
|
val paresNumOfEpub = paresNumOfEpub(scope.size, size)
|
||||||
|
val result: MutableList<EpubBook> = ArrayList(paresNumOfEpub)
|
||||||
|
var contentModel = "";
|
||||||
|
for (i in 1..paresNumOfEpub) {
|
||||||
|
val filename = book.getExportFileName("epub")
|
||||||
|
DocumentUtils.delete(doc, filename)
|
||||||
|
val epubBook = EpubBook()
|
||||||
|
epubBook.version = "2.0"
|
||||||
|
//set metadata
|
||||||
|
context.setEpubMetadata(book, epubBook)
|
||||||
|
//set cover
|
||||||
|
context.setCover(book, epubBook)
|
||||||
|
//set css
|
||||||
|
contentModel = context.setAssets(doc, book, epubBook)
|
||||||
|
|
||||||
|
// add epubBook
|
||||||
|
result.add(epubBook)
|
||||||
|
}
|
||||||
|
return Pair(contentModel, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存文件到 设备
|
||||||
|
*/
|
||||||
|
private suspend fun save2Drive(filename: String, epubBook: EpubBook, doc: DocumentFile) {
|
||||||
|
DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc ->
|
||||||
|
context.context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs ->
|
||||||
|
EpubWriter().write(epubBook, bookOs)
|
||||||
|
}
|
||||||
|
if (AppConfig.exportToWebDav) {
|
||||||
|
// 导出到webdav
|
||||||
|
AppWebDav.exportWebDav(bookDoc.uri, filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 分割epub后的数量
|
||||||
|
*/
|
||||||
|
private fun paresNumOfEpub(total: Int, size: Int): Int {
|
||||||
|
val i = total % size
|
||||||
|
var result = total / size;
|
||||||
|
if (i > 0) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user