diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index 47a5c7c64..5cf81230b 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -54,6 +54,7 @@ object PreferKey { const val webDavAccount = "web_dav_account" const val webDavPassword = "web_dav_password" const val webDavDir = "webDavDir" + const val enableCustomExport = "enableCustomExport" const val exportToWebDav = "webDavCacheBackup" const val exportNoChapterName = "exportNoChapterName" const val exportType = "exportType" diff --git a/app/src/main/java/io/legado/app/help/book/BookExtensions.kt b/app/src/main/java/io/legado/app/help/book/BookExtensions.kt index d7a820246..a5a95db19 100644 --- a/app/src/main/java/io/legado/app/help/book/BookExtensions.kt +++ b/app/src/main/java/io/legado/app/help/book/BookExtensions.kt @@ -243,4 +243,25 @@ fun Book.getExportFileName(suffix: String): String { }.onFailure { AppLog.put("导出书名规则错误,使用默认规则\n${it.localizedMessage}", it) }.getOrDefault("${name} 作者:${getRealAuthor()}.$suffix") +} + +/** + * 获取分割文件后的文件名 + */ +fun Book.getExportFileName(suffix: String, epubIndex: Int): String { + val jsStr = AppConfig.bookExportFileName + // 默认规则 + val default = "$name 作者:${getRealAuthor()} [${epubIndex}].$suffix" + if (jsStr.isNullOrBlank()) { + return default + } + val bindings = SimpleBindings() + bindings["name"] = name + bindings["author"] = getRealAuthor() + bindings["epubIndex"] = epubIndex + return kotlin.runCatching { + RhinoScriptEngine.eval(jsStr, bindings).toString() + "." + suffix + }.onFailure { + AppLog.put("导出书名规则错误,使用默认规则\n${it.localizedMessage}", it) + }.getOrDefault(default) } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/config/AppConfig.kt b/app/src/main/java/io/legado/app/help/config/AppConfig.kt index dec18efa9..369a06236 100644 --- a/app/src/main/java/io/legado/app/help/config/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/config/AppConfig.kt @@ -268,6 +268,12 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { set(value) { appCtx.putPrefBoolean(PreferKey.exportNoChapterName, value) } + var enableCustomExport: Boolean + get() = appCtx.getPrefBoolean(PreferKey.enableCustomExport) + set(value) { + appCtx.putPrefBoolean(PreferKey.enableCustomExport, value) + } + var exportType: Int get() = appCtx.getPrefInt(PreferKey.exportType) set(value) { diff --git a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt index f98802f25..5f67bbd3d 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt @@ -4,7 +4,9 @@ import android.annotation.SuppressLint import android.os.Bundle import android.view.Menu import android.view.MenuItem +import android.view.View import androidx.activity.viewModels +import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.VMBaseActivity @@ -17,6 +19,7 @@ import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookGroup import io.legado.app.databinding.ActivityCacheBookBinding import io.legado.app.databinding.DialogEditTextBinding +import io.legado.app.databinding.DialogSelectSectionExportBinding import io.legado.app.help.book.isAudio import io.legado.app.help.config.AppConfig import io.legado.app.lib.dialogs.SelectItem @@ -33,6 +36,9 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +/** + * cache/download 缓存界面 + */ class CacheActivity : VMBaseActivity(), CacheAdapter.CallBack { @@ -88,6 +94,8 @@ class CacheActivity : VMBaseActivity() override fun onMenuOpened(featureId: Int, menu: Menu): Boolean { menu.findItem(R.id.menu_enable_replace)?.isChecked = AppConfig.exportUseReplace + // 菜单打开时读取状态[enableCustomExport] + menu.findItem(R.id.menu_enable_custom_export)?.isChecked = AppConfig.enableCustomExport menu.findItem(R.id.menu_export_no_chapter_name)?.isChecked = AppConfig.exportNoChapterName menu.findItem(R.id.menu_export_web_dav)?.isChecked = AppConfig.exportToWebDav menu.findItem(R.id.menu_export_pics_file)?.isChecked = AppConfig.exportPictureFile @@ -108,6 +116,9 @@ class CacheActivity : VMBaseActivity() } } + /** + * 菜单按下回调 + */ override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.menu_download -> { @@ -124,8 +135,11 @@ class CacheActivity : VMBaseActivity() CacheBook.stop(this@CacheActivity) } } + R.id.menu_export_all -> exportAll() R.id.menu_enable_replace -> AppConfig.exportUseReplace = !item.isChecked + // 更改菜单状态[enableCustomExport] + R.id.menu_enable_custom_export -> AppConfig.enableCustomExport = !item.isChecked R.id.menu_export_no_chapter_name -> AppConfig.exportNoChapterName = !item.isChecked R.id.menu_export_web_dav -> AppConfig.exportToWebDav = !item.isChecked R.id.menu_export_pics_file -> AppConfig.exportPictureFile = !item.isChecked @@ -133,6 +147,7 @@ class CacheActivity : VMBaseActivity() R.id.menu_export_folder -> { selectExportFolder(-1) } + R.id.menu_export_file_name -> alertExportFileName() R.id.menu_export_type -> showExportTypeConfig() R.id.menu_export_charset -> showCharsetConfig() @@ -170,6 +185,7 @@ class CacheActivity : VMBaseActivity() 2 -> booksDownload.sortedWith { o1, o2 -> o1.name.cnCompare(o2.name) } + 3 -> booksDownload.sortedBy { it.order } else -> booksDownload.sortedByDescending { it.durChapterTime } } @@ -233,6 +249,8 @@ class CacheActivity : VMBaseActivity() val path = ACache.get().getAsString(exportBookPathKey) if (path.isNullOrEmpty()) { selectExportFolder(position) + } else if (AppConfig.enableCustomExport && AppConfig.exportType == 1) {// 启用自定义导出 and 导出类型为Epub + configExportSection(path, position) } else { startExport(path, position) } @@ -247,6 +265,89 @@ class CacheActivity : VMBaseActivity() } } + /** + * 配置自定义导出对话框 + * + * @param path 导出路径 + * @param position book位置 + * @author Discut + * @since 1.0.0 + */ + private fun configExportSection(path: String, position: Int) { + val alertBinding = DialogSelectSectionExportBinding.inflate(layoutInflater) + .apply { + etEpubSize.setText("1") + tvAllExport.setOnClickListener { + cbAllExport.callOnClick() + } + tvSelectExport.setOnClickListener { + cbSelectExport.callOnClick() + } + cbSelectExport.onCheckedChangeListener = { _, isChecked -> + if (isChecked) { + etEpubSize.isEnabled = true + etInputScope.isEnabled = true + cbAllExport.isChecked = false + } + } + cbAllExport.onCheckedChangeListener = { _, isChecked -> + if (isChecked) { + etEpubSize.isEnabled = false + etInputScope.isEnabled = false + cbSelectExport.isChecked = false + } + } + + etInputScope.onFocusChangeListener = + View.OnFocusChangeListener { _, hasFocus -> + if (hasFocus) { + etInputScope.hint = "1-5,8,10-18" + } else { + etInputScope.hint = "" + } + } + cbAllExport.callOnClick() + } + val alertDialog = alert(titleResource = R.string.select_section_export) { + customView { alertBinding.root } + positiveButton(R.string.ok) + cancelButton() + } + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + alertBinding.apply { + if (cbAllExport.isChecked) { + startExport(path, position) + alertDialog.hide() + return@apply + } + val text = etInputScope.text + if (!verificationField(text.toString())) { + etInputScope.error = + applicationContext.getString(R.string.error_scope_input)//"请输入正确的范围" + return@apply + } + etInputScope.error = null + val toInt = etEpubSize.text.toString().toInt() + startExport(path, position, toInt, text.toString()) + alertDialog.hide() + + } + + } + } + + /** + * 验证 输入的范围 是否正确 + * + * @since 1.0.0 + * @author Discut + * @param text 输入的范围 字符串 + * @return 是否正确 + */ + private fun verificationField(text: String): Boolean { + return text.matches(Regex("\\d+(-\\d+)?(,\\d+(-\\d+)?)*")) + } + private fun selectExportFolder(exportPosition: Int) { val default = arrayListOf>() val path = ACache.get().getAsString(exportBookPathKey) @@ -259,6 +360,18 @@ class CacheActivity : VMBaseActivity() } } + private fun startExport(path: String, exportPosition: Int, size: Int, scope: String) { + if (exportPosition >= 0) { + adapter.getItem(exportPosition)?.let { book -> + when (AppConfig.exportType) { + 1 -> viewModel.exportEPUBs(path, book, size, scope) + // 目前仅支持 epub + //else -> viewModel.export(path, book) + } + } + } + } + private fun startExport(path: String, exportPosition: Int) { if (exportPosition == -10) { if (adapter.getItems().isNotEmpty()) { @@ -284,7 +397,12 @@ class CacheActivity : VMBaseActivity() @SuppressLint("SetTextI18n") private fun alertExportFileName() { alert(R.string.export_file_name) { - setMessage("js内有name和author变量,返回书名") + var message = + "js内有name和author变量,返回书名\n启用自定义epub导出章节时包含额外变量[epubIndex]" + if (AppConfig.bookExportFileName.isNullOrBlank()) { + message += "\n例如:\nname+\"-\"+author+(epubIndex?\"(\"+epubIndex+\")\":\"\")" + } + setMessage(message) val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply { editView.hint = "file name js" editView.setText(AppConfig.bookExportFileName) diff --git a/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt b/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt index acd859b5c..7d4a26759 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.net.Uri +import android.util.ArraySet import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.MutableLiveData import com.bumptech.glide.Glide @@ -41,6 +42,7 @@ import java.nio.charset.Charset import java.nio.file.* import java.util.* import java.util.concurrent.ConcurrentHashMap +import kotlin.collections.ArrayList import kotlin.coroutines.coroutineContext @@ -248,7 +250,56 @@ 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() + 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){ + AppLog.put("Error expression : $s; left > right") + continue + } + for (i in left..right) + result.add(i - 1) + } + return result.toIntArray() + } + + //////////////////Start EPUB + /** + * 导出Epub 根据自定义导出范围 + * + * @param path 导出路径 + * @param book 书籍 + * @param size 每本Epub包含的章节 + * @param scope 导出范围 + * @since 2023/5/22 + */ + fun exportEPUBs(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.export(path, book) + } + } + /** * 导出Epub */ @@ -373,6 +424,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { ) ) } + else -> { //其他格式文件当做资源文件 folder.listFiles().forEach { @@ -575,4 +627,282 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { } //////end of EPUB + + //////start of custom exporter + /** + * 自定义Exporter + * + * @since 2023/5/23 + */ + class CustomExporter(private val context: CacheViewModel) { + var scope: IntArray = IntArray(0) + var size: Int = 1 + + /** + * 导出Epub + * + * from [io.legado.app.ui.book.cache.CacheViewModel.exportEPUB] + * @param path 导出的路径 + * @param book 书籍 + */ + fun export( + 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("获取导出文档失败") + exportEpub(doc, book) + } else { + 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(file: File, book: Book) { + val (contentModel, epubList) = createEpubs(book) + epubList.forEachIndexed { index, ep -> + val (filename, epubBook) = ep + //设置正文 + this.setEpubContent(contentModel, book, epubBook, index) + save2Drive(filename, epubBook, file) + } + + } + + /** + * 导出 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, ep -> + val (filename, epubBook) = ep + //设置正文 + this.setEpubContent(contentModel, book, epubBook, index) + save2Drive(filename, epubBook, doc) + } + + } + + + /** + * 设置epub正文 + * + * from [io.legado.app.ui.book.cache.CacheViewModel.setEpubContent] + * + * @param contentModel 正文模板 + * @param book 书籍 + * @param epubBook 分割后的epub + * @param epubBookIndex 分割后的epub序号 + */ + 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 = ArrayList() + appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter -> + if (scope.indexOf(index) >= 0) { + chapterList.add(chapter) + } + if (scope.size == chapterList.size) { + return@forEachIndexed + } + } + val totalChapterNum = book.totalChapterNum / scope.size + if (chapterList.size == 0) { + throw RuntimeException("书籍<${book.name}>(${epubBookIndex + 1})未找到章节信息") + } + chapterList = chapterList.subList( + epubBookIndex * size, + if ((epubBookIndex + 1) * size > scope.size) scope.size else (epubBookIndex + 1) * size + ) + chapterList.forEachIndexed { index, chapter -> + coroutineContext.ensureActive() + context.upAdapterLiveData.postValue(book.bookUrl) + context.exportProgress[book.bookUrl] = + totalChapterNum * (epubBookIndex * size + 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 对象 + * + * 分割epub时,一个书籍需要创建多个epub对象 + * + * @param doc 导出文档 + * @param book 书籍 + * + * @return <内容模板字符串, > + */ + private fun createEpubs( + doc: DocumentFile, + book: Book + ): Pair>> { + val paresNumOfEpub = paresNumOfEpub(scope.size, size) + val result: MutableList> = ArrayList(paresNumOfEpub) + var contentModel = "" + for (i in 1..paresNumOfEpub) { + val filename = book.getExportFileName("epub", i) + 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(Pair(filename, epubBook)) + } + return Pair(contentModel, result) + } + + /** + * 创建多个epub 对象 + * + * 分割epub时,一个书籍需要创建多个epub对象 + * + * @param book 书籍 + * + * @return <内容模板字符串, > + */ + private fun createEpubs( + book: Book + ): Pair>> { + val paresNumOfEpub = paresNumOfEpub(scope.size, size) + val result: MutableList> = ArrayList(paresNumOfEpub) + var contentModel = "" + for (i in 1..paresNumOfEpub) { + val filename = book.getExportFileName("epub", i) + val epubBook = EpubBook() + epubBook.version = "2.0" + //set metadata + context.setEpubMetadata(book, epubBook) + //set cover + context.setCover(book, epubBook) + //set css + contentModel = context.setAssets(book, epubBook) + + // add epubBook + result.add(Pair(filename, 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) + } + } + } + + /** + * 保存文件到 设备 + */ + private suspend fun save2Drive(filename: String, epubBook: EpubBook, file: File) { + val bookPath = FileUtils.getPath(file, filename) + val bookFile = FileUtils.createFileWithReplace(bookPath) + @Suppress("BlockingMethodInNonBlockingContext") + EpubWriter().write(epubBook, FileOutputStream(bookFile)) + if (AppConfig.exportToWebDav) { + // 导出到webdav + AppWebDav.exportWebDav(Uri.fromFile(bookFile), filename) + } + } + + /** + * 解析 分割epub后的数量 + * + * @param total 章节总数 + * @param size 每个epub文件包含多少章节 + */ + private fun paresNumOfEpub(total: Int, size: Int): Int { + val i = total % size + var result = total / size + if (i > 0) { + result++ + } + return result + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_select_section_export.xml b/app/src/main/res/layout/dialog_select_section_export.xml new file mode 100644 index 000000000..6bff4baff --- /dev/null +++ b/app/src/main/res/layout/dialog_select_section_export.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/book_cache.xml b/app/src/main/res/menu/book_cache.xml index 8e06e17b4..a943d2669 100644 --- a/app/src/main/res/menu/book_cache.xml +++ b/app/src/main/res/menu/book_cache.xml @@ -31,6 +31,13 @@ android:checkable="true" app:showAsAction="never" /> + + + Reemplazar Reemplazo Configurar reglas de reemplazo + Custom export chapter of epub No disponible Activar Buscar reemplazo @@ -1104,4 +1105,9 @@ 排除范围,选填书名或者书源 URL 格式化规则(formatJs) 调整位置 + Choose some chapters to be exported + Please enter the correct range + Custom Export + The number of chapters contained in each file + The section index that needs to be exported diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index a3d2beb15..a3856efbe 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -44,6 +44,7 @@ Subscription All Recent reading + カスタムEPUBエクスポートの章 Last reading What\'s new The bookshelf is still empty. Search for books or add them from discovery! \n if you use it for the first time, please open legado.top get help! @@ -1107,4 +1108,9 @@ 排除范围,选填书名或者书源 URL 格式化规则(formatJs) 调整位置 + Choose some chapters to be exported + Please enter the correct range + Custom Export + The number of chapters contained in each file + The section index that needs to be exported diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 5d1ddc05d..0735f2ff0 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -33,6 +33,7 @@ Substituir Substituição Configurar regras de substituição + Custom export chapter of epub Indisponível Ativar Procurar o substituto @@ -1107,4 +1108,9 @@ 排除范围,选填书名或者书源 URL 格式化规则(formatJs) 调整位置 + Choose some chapters to be exported + Please enter the correct range + Custom Export + The index of chapters contained in each file + The section index that needs to be exported diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 0394474f2..ba211f181 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -33,6 +33,7 @@ 暫無 啟用 替換淨化-搜尋 + 自定義Epub導出章節 書架 收藏夾 收藏 @@ -1104,4 +1105,9 @@ 排除范围,选填书名或者书源 URL 格式化规则(formatJs) 调整位置 + 選擇待導出章節 + 請輸入正確的範圍 + 自定義導出 + 每個文件包含的章節數量 + 需要輸出的章節 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 180d998c1..96926e0cc 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -32,6 +32,7 @@ 取代 取代淨化 配置取代淨化規則 + 自定義Epub導出章節 暫無 啟用 取代淨化-搜尋 @@ -1106,4 +1107,9 @@ 排除范围,选填书名或者书源 URL 格式化规则(formatJs) 调整位置 + 選擇待導出章節 + 請輸入正確的範圍 + 自定義導出 + 每個文件包含的章節數量 + 需要輸出的章節 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 344b8228f..cf8435b52 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -31,6 +31,7 @@ 删除所有 替换 替换净化 + 自定义Epub导出章节 配置替换净化规则 暂无 启用 @@ -346,6 +347,7 @@ 关闭则只显示勾选源的发现 更新目录 TXT 目录规则 + 选择待导出章节 设置编码 倒序-顺序 排序 @@ -1106,4 +1108,8 @@ 排除范围,选填书名或者书源 URL 格式化规则(formatJs) 调整位置 + 请输入正确的范围 + 自定义导出 + 每个文件包含的章节数量 + 需要输出的章节 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cb06feb3e..1b04c5ec4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,6 +30,7 @@ Edit Delete Delete all + Custom export chapter of epub Replace Replacement Configure replacement rules @@ -347,6 +348,7 @@ Display the selected origin\'s Discovery if closed Update chapters Txt Chapters Rule + Choose some chapters to be exported Text encoding Ascending/Descending order Sort @@ -1107,4 +1109,8 @@ 排除范围,选填书名或者书源 URL 格式化规则(formatJs) 调整位置 + Please enter the correct range + Custom Export + The number of chapters contained in each file + The section index that needs to be exported