|
|
@ -23,7 +23,6 @@ import io.legado.app.help.BookHelp |
|
|
|
import io.legado.app.help.ContentProcessor |
|
|
|
import io.legado.app.help.ContentProcessor |
|
|
|
import io.legado.app.help.config.AppConfig |
|
|
|
import io.legado.app.help.config.AppConfig |
|
|
|
import io.legado.app.utils.* |
|
|
|
import io.legado.app.utils.* |
|
|
|
import kotlinx.coroutines.CoroutineScope |
|
|
|
|
|
|
|
import kotlinx.coroutines.delay |
|
|
|
import kotlinx.coroutines.delay |
|
|
|
import kotlinx.coroutines.ensureActive |
|
|
|
import kotlinx.coroutines.ensureActive |
|
|
|
import kotlinx.coroutines.sync.Mutex |
|
|
|
import kotlinx.coroutines.sync.Mutex |
|
|
@ -37,6 +36,7 @@ import java.io.File |
|
|
|
import java.io.FileOutputStream |
|
|
|
import java.io.FileOutputStream |
|
|
|
import java.nio.charset.Charset |
|
|
|
import java.nio.charset.Charset |
|
|
|
import java.util.concurrent.ConcurrentHashMap |
|
|
|
import java.util.concurrent.ConcurrentHashMap |
|
|
|
|
|
|
|
import kotlin.coroutines.coroutineContext |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
@ -79,9 +79,9 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
val uri = Uri.parse(path) |
|
|
|
val uri = Uri.parse(path) |
|
|
|
val doc = DocumentFile.fromTreeUri(context, uri) |
|
|
|
val doc = DocumentFile.fromTreeUri(context, uri) |
|
|
|
?: throw NoStackTraceException("获取导出文档失败") |
|
|
|
?: throw NoStackTraceException("获取导出文档失败") |
|
|
|
export(this, doc, book) |
|
|
|
export(doc, book) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
export(this, FileUtils.createFolderIfNotExist(path), book) |
|
|
|
export(FileUtils.createFolderIfNotExist(path), book) |
|
|
|
} |
|
|
|
} |
|
|
|
}.onError { |
|
|
|
}.onError { |
|
|
|
exportProgress.remove(book.bookUrl) |
|
|
|
exportProgress.remove(book.bookUrl) |
|
|
@ -98,14 +98,14 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Suppress("BlockingMethodInNonBlockingContext") |
|
|
|
@Suppress("BlockingMethodInNonBlockingContext") |
|
|
|
private suspend fun export(scope: CoroutineScope, doc: DocumentFile, book: Book) { |
|
|
|
private suspend fun export(doc: DocumentFile, book: Book) { |
|
|
|
val filename = "${getExportFileName(book)}.txt" |
|
|
|
val filename = "${getExportFileName(book)}.txt" |
|
|
|
DocumentUtils.delete(doc, filename) |
|
|
|
DocumentUtils.delete(doc, filename) |
|
|
|
val bookDoc = DocumentUtils.createFileIfNotExist(doc, filename) |
|
|
|
val bookDoc = DocumentUtils.createFileIfNotExist(doc, filename) |
|
|
|
?: throw NoStackTraceException("创建文档失败,请尝试重新设置导出文件夹") |
|
|
|
?: throw NoStackTraceException("创建文档失败,请尝试重新设置导出文件夹") |
|
|
|
val stringBuilder = StringBuilder() |
|
|
|
val stringBuilder = StringBuilder() |
|
|
|
context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs -> |
|
|
|
context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs -> |
|
|
|
getAllContents(scope, book) { text, srcList -> |
|
|
|
getAllContents(book) { text, srcList -> |
|
|
|
bookOs.write(text.toByteArray(Charset.forName(AppConfig.exportCharset))) |
|
|
|
bookOs.write(text.toByteArray(Charset.forName(AppConfig.exportCharset))) |
|
|
|
stringBuilder.append(text) |
|
|
|
stringBuilder.append(text) |
|
|
|
srcList?.forEach { |
|
|
|
srcList?.forEach { |
|
|
@ -128,12 +128,12 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private suspend fun export(scope: CoroutineScope, file: File, book: Book) { |
|
|
|
private suspend fun export(file: File, book: Book) { |
|
|
|
val filename = "${getExportFileName(book)}.txt" |
|
|
|
val filename = "${getExportFileName(book)}.txt" |
|
|
|
val bookPath = FileUtils.getPath(file, filename) |
|
|
|
val bookPath = FileUtils.getPath(file, filename) |
|
|
|
val bookFile = FileUtils.createFileWithReplace(bookPath) |
|
|
|
val bookFile = FileUtils.createFileWithReplace(bookPath) |
|
|
|
val stringBuilder = StringBuilder() |
|
|
|
val stringBuilder = StringBuilder() |
|
|
|
getAllContents(scope, book) { text, srcList -> |
|
|
|
getAllContents(book) { text, srcList -> |
|
|
|
bookFile.appendText(text, Charset.forName(AppConfig.exportCharset)) |
|
|
|
bookFile.appendText(text, Charset.forName(AppConfig.exportCharset)) |
|
|
|
stringBuilder.append(text) |
|
|
|
stringBuilder.append(text) |
|
|
|
srcList?.forEach { |
|
|
|
srcList?.forEach { |
|
|
@ -157,7 +157,6 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private suspend fun getAllContents( |
|
|
|
private suspend fun getAllContents( |
|
|
|
scope: CoroutineScope, |
|
|
|
|
|
|
|
book: Book, |
|
|
|
book: Book, |
|
|
|
append: (text: String, srcList: ArrayList<Triple<String, Int, String>>?) -> Unit |
|
|
|
append: (text: String, srcList: ArrayList<Triple<String, Int, String>>?) -> Unit |
|
|
|
) { |
|
|
|
) { |
|
|
@ -173,7 +172,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
}" |
|
|
|
}" |
|
|
|
append(qy, null) |
|
|
|
append(qy, null) |
|
|
|
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter -> |
|
|
|
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter -> |
|
|
|
scope.ensureActive() |
|
|
|
coroutineContext.ensureActive() |
|
|
|
upAdapterLiveData.postValue(book.bookUrl) |
|
|
|
upAdapterLiveData.postValue(book.bookUrl) |
|
|
|
exportProgress[book.bookUrl] = index |
|
|
|
exportProgress[book.bookUrl] = index |
|
|
|
BookHelp.getContent(book, chapter).let { content -> |
|
|
|
BookHelp.getContent(book, chapter).let { content -> |
|
|
@ -227,9 +226,9 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
val uri = Uri.parse(path) |
|
|
|
val uri = Uri.parse(path) |
|
|
|
val doc = DocumentFile.fromTreeUri(context, uri) |
|
|
|
val doc = DocumentFile.fromTreeUri(context, uri) |
|
|
|
?: throw NoStackTraceException("获取导出文档失败") |
|
|
|
?: throw NoStackTraceException("获取导出文档失败") |
|
|
|
exportEpub(this, doc, book) |
|
|
|
exportEpub(doc, book) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
exportEpub(this, FileUtils.createFolderIfNotExist(path), book) |
|
|
|
exportEpub(FileUtils.createFolderIfNotExist(path), book) |
|
|
|
} |
|
|
|
} |
|
|
|
}.onError { |
|
|
|
}.onError { |
|
|
|
exportProgress.remove(book.bookUrl) |
|
|
|
exportProgress.remove(book.bookUrl) |
|
|
@ -246,7 +245,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Suppress("BlockingMethodInNonBlockingContext") |
|
|
|
@Suppress("BlockingMethodInNonBlockingContext") |
|
|
|
private suspend fun exportEpub(scope: CoroutineScope, doc: DocumentFile, book: Book) { |
|
|
|
private suspend fun exportEpub(doc: DocumentFile, book: Book) { |
|
|
|
val filename = "${getExportFileName(book)}.epub" |
|
|
|
val filename = "${getExportFileName(book)}.epub" |
|
|
|
DocumentUtils.delete(doc, filename) |
|
|
|
DocumentUtils.delete(doc, filename) |
|
|
|
val epubBook = EpubBook() |
|
|
|
val epubBook = EpubBook() |
|
|
@ -259,7 +258,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
val contentModel = setAssets(doc, book, epubBook) |
|
|
|
val contentModel = setAssets(doc, book, epubBook) |
|
|
|
|
|
|
|
|
|
|
|
//设置正文 |
|
|
|
//设置正文 |
|
|
|
setEpubContent(scope, contentModel, book, epubBook) |
|
|
|
setEpubContent(contentModel, book, epubBook) |
|
|
|
DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc -> |
|
|
|
DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc -> |
|
|
|
context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs -> |
|
|
|
context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs -> |
|
|
|
EpubWriter().write(epubBook, bookOs) |
|
|
|
EpubWriter().write(epubBook, bookOs) |
|
|
@ -269,7 +268,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun exportEpub(scope: CoroutineScope, file: File, book: Book) { |
|
|
|
private suspend fun exportEpub(file: File, book: Book) { |
|
|
|
val filename = "${getExportFileName(book)}.epub" |
|
|
|
val filename = "${getExportFileName(book)}.epub" |
|
|
|
val epubBook = EpubBook() |
|
|
|
val epubBook = EpubBook() |
|
|
|
epubBook.version = "2.0" |
|
|
|
epubBook.version = "2.0" |
|
|
@ -283,7 +282,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
val bookPath = FileUtils.getPath(file, filename) |
|
|
|
val bookPath = FileUtils.getPath(file, filename) |
|
|
|
val bookFile = FileUtils.createFileWithReplace(bookPath) |
|
|
|
val bookFile = FileUtils.createFileWithReplace(bookPath) |
|
|
|
//设置正文 |
|
|
|
//设置正文 |
|
|
|
setEpubContent(scope, contentModel, book, epubBook) |
|
|
|
setEpubContent(contentModel, book, epubBook) |
|
|
|
@Suppress("BlockingMethodInNonBlockingContext") |
|
|
|
@Suppress("BlockingMethodInNonBlockingContext") |
|
|
|
EpubWriter().write(epubBook, FileOutputStream(bookFile)) |
|
|
|
EpubWriter().write(epubBook, FileOutputStream(bookFile)) |
|
|
|
} |
|
|
|
} |
|
|
@ -438,7 +437,6 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private suspend fun setEpubContent( |
|
|
|
private suspend fun setEpubContent( |
|
|
|
scope: CoroutineScope, |
|
|
|
|
|
|
|
contentModel: String, |
|
|
|
contentModel: String, |
|
|
|
book: Book, |
|
|
|
book: Book, |
|
|
|
epubBook: EpubBook |
|
|
|
epubBook: EpubBook |
|
|
@ -447,7 +445,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
val useReplace = AppConfig.exportUseReplace && book.getUseReplaceRule() |
|
|
|
val useReplace = AppConfig.exportUseReplace && book.getUseReplaceRule() |
|
|
|
val contentProcessor = ContentProcessor.get(book.name, book.origin) |
|
|
|
val contentProcessor = ContentProcessor.get(book.name, book.origin) |
|
|
|
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter -> |
|
|
|
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter -> |
|
|
|
scope.ensureActive() |
|
|
|
coroutineContext.ensureActive() |
|
|
|
upAdapterLiveData.postValue(book.bookUrl) |
|
|
|
upAdapterLiveData.postValue(book.bookUrl) |
|
|
|
exportProgress[book.bookUrl] = index |
|
|
|
exportProgress[book.bookUrl] = index |
|
|
|
BookHelp.getContent(book, chapter).let { content -> |
|
|
|
BookHelp.getContent(book, chapter).let { content -> |
|
|
|