mirror of
https://github.com/gedoor/legado.git
synced 2024-07-19 01:17:25 +08:00
优化
This commit is contained in:
parent
b9d6616ca6
commit
e3aa72b315
@ -20,7 +20,6 @@ import kotlinx.coroutines.Dispatchers.IO
|
|||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.BufferedOutputStream
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
@ -206,14 +205,12 @@ object Backup {
|
|||||||
withContext(IO) {
|
withContext(IO) {
|
||||||
if (list.isNotEmpty()) {
|
if (list.isNotEmpty()) {
|
||||||
val file = FileUtils.createFileIfNotExist(path + File.separator + fileName)
|
val file = FileUtils.createFileIfNotExist(path + File.separator + fileName)
|
||||||
FileOutputStream(file).use { fos ->
|
file.outputStream().buffered().use {
|
||||||
BufferedOutputStream(fos, 64 * 1024).use {
|
|
||||||
GSON.writeToOutputStream(it, list)
|
GSON.writeToOutputStream(it, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
@Suppress("SameParameterValue")
|
@Suppress("SameParameterValue")
|
||||||
|
@ -75,10 +75,8 @@ import me.ag2s.epublib.epub.EpubWriterProcessor
|
|||||||
import me.ag2s.epublib.util.ResourceUtil
|
import me.ag2s.epublib.util.ResourceUtil
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import splitties.systemservices.notificationManager
|
import splitties.systemservices.notificationManager
|
||||||
import java.io.BufferedOutputStream
|
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
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
|
import kotlin.coroutines.coroutineContext
|
||||||
@ -257,10 +255,9 @@ class ExportBookService : BaseService() {
|
|||||||
val bookDoc = DocumentUtils.createFileIfNotExist(doc, filename)
|
val bookDoc = DocumentUtils.createFileIfNotExist(doc, filename)
|
||||||
?: throw NoStackTraceException("创建文档失败,请尝试重新设置导出文件夹")
|
?: throw NoStackTraceException("创建文档失败,请尝试重新设置导出文件夹")
|
||||||
val charset = Charset.forName(AppConfig.exportCharset)
|
val charset = Charset.forName(AppConfig.exportCharset)
|
||||||
contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs ->
|
contentResolver.openOutputStream(bookDoc.uri, "wa")?.bufferedWriter(charset)?.use { bw ->
|
||||||
BufferedOutputStream(bookOs, 64 * 1024).use { bos ->
|
|
||||||
getAllContents(book) { text, srcList ->
|
getAllContents(book) { text, srcList ->
|
||||||
bos.write(text.toByteArray(charset))
|
bw.write(text)
|
||||||
srcList?.forEach {
|
srcList?.forEach {
|
||||||
val vFile = BookHelp.getImage(book, it.src)
|
val vFile = BookHelp.getImage(book, it.src)
|
||||||
if (vFile.exists()) {
|
if (vFile.exists()) {
|
||||||
@ -277,7 +274,6 @@ class ExportBookService : BaseService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (AppConfig.exportToWebDav) {
|
if (AppConfig.exportToWebDav) {
|
||||||
// 导出到webdav
|
// 导出到webdav
|
||||||
AppWebDav.exportWebDav(bookDoc.uri, filename)
|
AppWebDav.exportWebDav(bookDoc.uri, filename)
|
||||||
@ -289,10 +285,10 @@ class ExportBookService : BaseService() {
|
|||||||
val bookPath = FileUtils.getPath(file, filename)
|
val bookPath = FileUtils.getPath(file, filename)
|
||||||
val bookFile = FileUtils.createFileWithReplace(bookPath)
|
val bookFile = FileUtils.createFileWithReplace(bookPath)
|
||||||
val charset = Charset.forName(AppConfig.exportCharset)
|
val charset = Charset.forName(AppConfig.exportCharset)
|
||||||
val bos = BufferedOutputStream(bookFile.outputStream(true), 64 * 1024)
|
val bw = bookFile.outputStream(true).bufferedWriter(charset)
|
||||||
bos.use {
|
bw.use {
|
||||||
getAllContents(book) { text, srcList ->
|
getAllContents(book) { text, srcList ->
|
||||||
bos.write(text.toByteArray(charset))
|
it.write(text)
|
||||||
srcList?.forEach {
|
srcList?.forEach {
|
||||||
val vFile = BookHelp.getImage(book, it.src)
|
val vFile = BookHelp.getImage(book, it.src)
|
||||||
if (vFile.exists()) {
|
if (vFile.exists()) {
|
||||||
@ -315,7 +311,7 @@ class ExportBookService : BaseService() {
|
|||||||
private suspend fun getAllContents(
|
private suspend fun getAllContents(
|
||||||
book: Book,
|
book: Book,
|
||||||
append: (text: String, srcList: ArrayList<SrcData>?) -> Unit
|
append: (text: String, srcList: ArrayList<SrcData>?) -> Unit
|
||||||
) {
|
) = coroutineScope {
|
||||||
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)
|
||||||
val qy = "${book.name}\n${
|
val qy = "${book.name}\n${
|
||||||
@ -327,8 +323,11 @@ class ExportBookService : BaseService() {
|
|||||||
)
|
)
|
||||||
}"
|
}"
|
||||||
append(qy, null)
|
append(qy, null)
|
||||||
if (AppConfig.parallelExportBook) {
|
val threads = if (AppConfig.parallelExportBook) {
|
||||||
coroutineScope {
|
AppConst.MAX_THREAD
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
flow {
|
flow {
|
||||||
appDb.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter ->
|
appDb.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter ->
|
||||||
val task = async(Default, start = CoroutineStart.LAZY) {
|
val task = async(Default, start = CoroutineStart.LAZY) {
|
||||||
@ -337,7 +336,7 @@ class ExportBookService : BaseService() {
|
|||||||
emit(task)
|
emit(task)
|
||||||
}
|
}
|
||||||
}.onEach { it.start() }
|
}.onEach { it.start() }
|
||||||
.buffer(AppConfig.threadCount)
|
.buffer(threads)
|
||||||
.map { it.await() }
|
.map { it.await() }
|
||||||
.withIndex()
|
.withIndex()
|
||||||
.collect { (index, result) ->
|
.collect { (index, result) ->
|
||||||
@ -345,16 +344,6 @@ class ExportBookService : BaseService() {
|
|||||||
exportProgress[book.bookUrl] = index
|
exportProgress[book.bookUrl] = index
|
||||||
append.invoke(result.first, result.second)
|
append.invoke(result.first, result.second)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter ->
|
|
||||||
coroutineContext.ensureActive()
|
|
||||||
postEvent(EventBus.EXPORT_BOOK, book.bookUrl)
|
|
||||||
exportProgress[book.bookUrl] = index
|
|
||||||
val result = getExportData(book, chapter, contentProcessor, useReplace)
|
|
||||||
append.invoke(result.first, result.second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +353,7 @@ class ExportBookService : BaseService() {
|
|||||||
contentProcessor: ContentProcessor,
|
contentProcessor: ContentProcessor,
|
||||||
useReplace: Boolean
|
useReplace: Boolean
|
||||||
): Pair<String, ArrayList<SrcData>?> {
|
): Pair<String, ArrayList<SrcData>?> {
|
||||||
BookHelp.getContent(book, chapter).let { content ->
|
val content = BookHelp.getContent(book, chapter)
|
||||||
val content1 = contentProcessor
|
val content1 = contentProcessor
|
||||||
.getContent(
|
.getContent(
|
||||||
book,
|
book,
|
||||||
@ -393,7 +382,6 @@ class ExportBookService : BaseService() {
|
|||||||
return Pair("\n\n$content1", null)
|
return Pair("\n\n$content1", null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析范围字符串
|
* 解析范围字符串
|
||||||
@ -463,8 +451,8 @@ class ExportBookService : BaseService() {
|
|||||||
//设置正文
|
//设置正文
|
||||||
setEpubContent(contentModel, book, epubBook)
|
setEpubContent(contentModel, book, epubBook)
|
||||||
DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc ->
|
DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc ->
|
||||||
contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs ->
|
contentResolver.openOutputStream(bookDoc.uri, "wa")?.buffered().use { bookOs ->
|
||||||
EpubWriter().write(epubBook, BufferedOutputStream(bookOs))
|
EpubWriter().write(epubBook, bookOs)
|
||||||
}
|
}
|
||||||
if (AppConfig.exportToWebDav) {
|
if (AppConfig.exportToWebDav) {
|
||||||
// 导出到webdav
|
// 导出到webdav
|
||||||
@ -489,8 +477,7 @@ class ExportBookService : BaseService() {
|
|||||||
val bookFile = FileUtils.createFileWithReplace(bookPath)
|
val bookFile = FileUtils.createFileWithReplace(bookPath)
|
||||||
//设置正文
|
//设置正文
|
||||||
setEpubContent(contentModel, book, epubBook)
|
setEpubContent(contentModel, book, epubBook)
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
EpubWriter().write(epubBook, bookFile.outputStream().buffered())
|
||||||
EpubWriter().write(epubBook, BufferedOutputStream(FileOutputStream(bookFile)))
|
|
||||||
if (AppConfig.exportToWebDav) {
|
if (AppConfig.exportToWebDav) {
|
||||||
// 导出到webdav
|
// 导出到webdav
|
||||||
AppWebDav.exportWebDav(Uri.fromFile(bookFile), filename)
|
AppWebDav.exportWebDav(Uri.fromFile(bookFile), filename)
|
||||||
@ -647,26 +634,31 @@ class ExportBookService : BaseService() {
|
|||||||
contentModel: String,
|
contentModel: String,
|
||||||
book: Book,
|
book: Book,
|
||||||
epubBook: EpubBook
|
epubBook: EpubBook
|
||||||
) {
|
) = coroutineScope {
|
||||||
//正文
|
//正文
|
||||||
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)
|
||||||
|
val threads = if (AppConfig.parallelExportBook) {
|
||||||
|
AppConst.MAX_THREAD
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
flow {
|
||||||
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter ->
|
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter ->
|
||||||
coroutineContext.ensureActive()
|
val task = async(Default, start = CoroutineStart.LAZY) {
|
||||||
postEvent(EventBus.EXPORT_BOOK, book.bookUrl)
|
val content = BookHelp.getContent(book, chapter)
|
||||||
exportProgress[book.bookUrl] = index
|
val (contentFix, resources) = fixPic(
|
||||||
BookHelp.getContent(book, chapter).let { content ->
|
|
||||||
var content1 = fixPic(
|
|
||||||
epubBook,
|
|
||||||
book,
|
book,
|
||||||
content ?: if (chapter.isVolume) "" else "null",
|
content ?: if (chapter.isVolume) "" else "null",
|
||||||
chapter
|
chapter
|
||||||
)
|
)
|
||||||
content1 = contentProcessor
|
// 不导出vip标识
|
||||||
|
chapter.isVip = false
|
||||||
|
val content1 = contentProcessor
|
||||||
.getContent(
|
.getContent(
|
||||||
book,
|
book,
|
||||||
chapter,
|
chapter,
|
||||||
content1,
|
contentFix,
|
||||||
includeTitle = false,
|
includeTitle = false,
|
||||||
useReplace = useReplace,
|
useReplace = useReplace,
|
||||||
chineseConvert = false,
|
chineseConvert = false,
|
||||||
@ -680,26 +672,41 @@ class ExportBookService : BaseService() {
|
|||||||
useReplace = useReplace
|
useReplace = useReplace
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
epubBook.addSection(
|
val chapterResource = ResourceUtil.createChapterResource(
|
||||||
title,
|
|
||||||
ResourceUtil.createChapterResource(
|
|
||||||
title.replace("\uD83D\uDD12", ""),
|
title.replace("\uD83D\uDD12", ""),
|
||||||
content1,
|
content1,
|
||||||
contentModel,
|
contentModel,
|
||||||
"Text/chapter_${index}.html"
|
"Text/chapter_${index}.html"
|
||||||
)
|
)
|
||||||
)
|
ExportChapter(title, chapterResource, resources)
|
||||||
}
|
}
|
||||||
|
emit(task)
|
||||||
|
}
|
||||||
|
}.onEach { it.start() }
|
||||||
|
.buffer(threads)
|
||||||
|
.map { it.await() }
|
||||||
|
.withIndex()
|
||||||
|
.collect { (index, exportChapter) ->
|
||||||
|
postEvent(EventBus.EXPORT_BOOK, book.bookUrl)
|
||||||
|
exportProgress[book.bookUrl] = index
|
||||||
|
epubBook.resources.addAll(exportChapter.resources)
|
||||||
|
epubBook.addSection(exportChapter.title, exportChapter.chapterResource)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class ExportChapter(
|
||||||
|
val title: String,
|
||||||
|
val chapterResource: Resource,
|
||||||
|
val resources: ArrayList<Resource>
|
||||||
|
)
|
||||||
|
|
||||||
private fun fixPic(
|
private fun fixPic(
|
||||||
epubBook: EpubBook,
|
|
||||||
book: Book,
|
book: Book,
|
||||||
content: String,
|
content: String,
|
||||||
chapter: BookChapter
|
chapter: BookChapter
|
||||||
): String {
|
): Pair<String, ArrayList<Resource>> {
|
||||||
val data = StringBuilder("")
|
val data = StringBuilder("")
|
||||||
|
val resources = arrayListOf<Resource>()
|
||||||
content.split("\n").forEach { text ->
|
content.split("\n").forEach { text ->
|
||||||
var text1 = text
|
var text1 = text
|
||||||
val matcher = AppPattern.imgPattern.matcher(text)
|
val matcher = AppPattern.imgPattern.matcher(text)
|
||||||
@ -714,14 +721,14 @@ class ExportBookService : BaseService() {
|
|||||||
val fp = FileResourceProvider(vFile.parent)
|
val fp = FileResourceProvider(vFile.parent)
|
||||||
if (vFile.exists()) {
|
if (vFile.exists()) {
|
||||||
val img = LazyResource(fp, href, originalHref)
|
val img = LazyResource(fp, href, originalHref)
|
||||||
epubBook.resources.add(img)
|
resources.add(img)
|
||||||
}
|
}
|
||||||
text1 = text1.replace(src, "../${href}")
|
text1 = text1.replace(src, "../${href}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.append(text1).append("\n")
|
data.append(text1).append("\n")
|
||||||
}
|
}
|
||||||
return data.toString()
|
return data.toString() to resources
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setEpubMetadata(book: Book, epubBook: EpubBook) {
|
private fun setEpubMetadata(book: Book, epubBook: EpubBook) {
|
||||||
@ -875,17 +882,17 @@ class ExportBookService : BaseService() {
|
|||||||
coroutineContext.ensureActive()
|
coroutineContext.ensureActive()
|
||||||
updateProgress(chapterList, index)
|
updateProgress(chapterList, index)
|
||||||
BookHelp.getContent(book, chapter).let { content ->
|
BookHelp.getContent(book, chapter).let { content ->
|
||||||
var content1 = fixPic(
|
val (contentFix, resources) = fixPic(
|
||||||
epubBook,
|
|
||||||
book,
|
book,
|
||||||
content ?: if (chapter.isVolume) "" else "null",
|
content ?: if (chapter.isVolume) "" else "null",
|
||||||
chapter
|
chapter
|
||||||
)
|
)
|
||||||
content1 = contentProcessor
|
epubBook.resources.addAll(resources)
|
||||||
|
val content1 = contentProcessor
|
||||||
.getContent(
|
.getContent(
|
||||||
book,
|
book,
|
||||||
chapter,
|
chapter,
|
||||||
content1,
|
contentFix,
|
||||||
includeTitle = false,
|
includeTitle = false,
|
||||||
useReplace = useReplace,
|
useReplace = useReplace,
|
||||||
chineseConvert = false,
|
chineseConvert = false,
|
||||||
@ -960,14 +967,14 @@ class ExportBookService : BaseService() {
|
|||||||
callback: (total: Int, progress: Int) -> Unit
|
callback: (total: Int, progress: Int) -> Unit
|
||||||
) {
|
) {
|
||||||
DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc ->
|
DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc ->
|
||||||
contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs ->
|
contentResolver.openOutputStream(bookDoc.uri, "wa")?.buffered().use { bookOs ->
|
||||||
EpubWriter()
|
EpubWriter()
|
||||||
.setCallback(object : EpubWriterProcessor.Callback {
|
.setCallback(object : EpubWriterProcessor.Callback {
|
||||||
override fun onProgressing(total: Int, progress: Int) {
|
override fun onProgressing(total: Int, progress: Int) {
|
||||||
callback(total, progress)
|
callback(total, progress)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.write(epubBook, BufferedOutputStream(bookOs))
|
.write(epubBook, bookOs)
|
||||||
}
|
}
|
||||||
if (AppConfig.exportToWebDav) {
|
if (AppConfig.exportToWebDav) {
|
||||||
// 导出到webdav
|
// 导出到webdav
|
||||||
@ -987,14 +994,13 @@ class ExportBookService : BaseService() {
|
|||||||
) {
|
) {
|
||||||
val bookPath = FileUtils.getPath(file, filename)
|
val bookPath = FileUtils.getPath(file, filename)
|
||||||
val bookFile = FileUtils.createFileWithReplace(bookPath)
|
val bookFile = FileUtils.createFileWithReplace(bookPath)
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
EpubWriter()
|
EpubWriter()
|
||||||
.setCallback(object : EpubWriterProcessor.Callback {
|
.setCallback(object : EpubWriterProcessor.Callback {
|
||||||
override fun onProgressing(total: Int, progress: Int) {
|
override fun onProgressing(total: Int, progress: Int) {
|
||||||
callback(total, progress)
|
callback(total, progress)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.write(epubBook, BufferedOutputStream(FileOutputStream(bookFile)))
|
.write(epubBook, bookFile.outputStream().buffered())
|
||||||
if (AppConfig.exportToWebDav) {
|
if (AppConfig.exportToWebDav) {
|
||||||
// 导出到webdav
|
// 导出到webdav
|
||||||
AppWebDav.exportWebDav(Uri.fromFile(bookFile), filename)
|
AppWebDav.exportWebDav(Uri.fromFile(bookFile), filename)
|
||||||
|
@ -22,9 +22,7 @@ import io.legado.app.utils.stackTraceStr
|
|||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
import io.legado.app.utils.writeToOutputStream
|
import io.legado.app.utils.writeToOutputStream
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import java.io.BufferedOutputStream
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
|
|
||||||
|
|
||||||
class BookshelfManageViewModel(application: Application) : BaseViewModel(application) {
|
class BookshelfManageViewModel(application: Application) : BaseViewModel(application) {
|
||||||
@ -64,18 +62,15 @@ class BookshelfManageViewModel(application: Application) : BaseViewModel(applica
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
fun saveAllUseBookSourceToFile(success: (file: File) -> Unit) {
|
fun saveAllUseBookSourceToFile(success: (file: File) -> Unit) {
|
||||||
execute {
|
execute {
|
||||||
val path = "${context.filesDir}/shareBookSource.json"
|
val path = "${context.filesDir}/shareBookSource.json"
|
||||||
FileUtils.delete(path)
|
FileUtils.delete(path)
|
||||||
val file = FileUtils.createFileWithReplace(path)
|
val file = FileUtils.createFileWithReplace(path)
|
||||||
val sources = appDb.bookDao.getAllUseBookSource()
|
val sources = appDb.bookDao.getAllUseBookSource()
|
||||||
FileOutputStream(file).use { out ->
|
file.outputStream().buffered().use {
|
||||||
BufferedOutputStream(out, 64 * 1024).use {
|
|
||||||
GSON.writeToOutputStream(it, sources)
|
GSON.writeToOutputStream(it, sources)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
file
|
file
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
success.invoke(it)
|
success.invoke(it)
|
||||||
|
@ -9,11 +9,16 @@ import io.legado.app.data.entities.BookSource
|
|||||||
import io.legado.app.data.entities.BookSourcePart
|
import io.legado.app.data.entities.BookSourcePart
|
||||||
import io.legado.app.data.entities.toBookSource
|
import io.legado.app.data.entities.toBookSource
|
||||||
import io.legado.app.help.config.SourceConfig
|
import io.legado.app.help.config.SourceConfig
|
||||||
import io.legado.app.utils.*
|
import io.legado.app.utils.FileUtils
|
||||||
|
import io.legado.app.utils.GSON
|
||||||
|
import io.legado.app.utils.cnCompare
|
||||||
|
import io.legado.app.utils.outputStream
|
||||||
|
import io.legado.app.utils.splitNotBlank
|
||||||
|
import io.legado.app.utils.stackTraceStr
|
||||||
|
import io.legado.app.utils.toastOnUi
|
||||||
|
import io.legado.app.utils.writeToOutputStream
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.BufferedOutputStream
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 书源管理数据修改
|
* 书源管理数据修改
|
||||||
@ -121,17 +126,14 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
private fun saveToFile(sources: List<BookSource>, success: (file: File) -> Unit) {
|
private fun saveToFile(sources: List<BookSource>, success: (file: File) -> Unit) {
|
||||||
execute {
|
execute {
|
||||||
val path = "${context.filesDir}/shareBookSource.json"
|
val path = "${context.filesDir}/shareBookSource.json"
|
||||||
FileUtils.delete(path)
|
FileUtils.delete(path)
|
||||||
val file = FileUtils.createFileWithReplace(path)
|
val file = FileUtils.createFileWithReplace(path)
|
||||||
FileOutputStream(file).use { out ->
|
file.outputStream().buffered().use {
|
||||||
BufferedOutputStream(out, 64 * 1024).use {
|
|
||||||
GSON.writeToOutputStream(it, sources)
|
GSON.writeToOutputStream(it, sources)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
file
|
file
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
success.invoke(it)
|
success.invoke(it)
|
||||||
|
30
app/src/main/java/io/legado/app/utils/RhinoExtensions.kt
Normal file
30
app/src/main/java/io/legado/app/utils/RhinoExtensions.kt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package io.legado.app.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.supervisorScope
|
||||||
|
import org.mozilla.javascript.Context
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
|
inline fun <T> suspendContinuation(crossinline block: suspend CoroutineScope.() -> T): T {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
val cx = Context.enter()
|
||||||
|
try {
|
||||||
|
val pending = cx.captureContinuation()
|
||||||
|
pending.applicationState = suspend {
|
||||||
|
supervisorScope {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw pending
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
return runBlocking { block() }
|
||||||
|
} finally {
|
||||||
|
Context.exit()
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:tools="http://schemas.android.com/tools"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:ignore="AlwaysShowAction">
|
tools:ignore="AlwaysShowAction">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_download"
|
android:id="@+id/menu_download"
|
||||||
android:title="@string/action_download"
|
|
||||||
android:icon="@drawable/ic_play_24dp"
|
android:icon="@drawable/ic_play_24dp"
|
||||||
|
android:title="@string/action_download"
|
||||||
app:showAsAction="always" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_book_group"
|
android:id="@+id/menu_book_group"
|
||||||
android:title="@string/group"
|
|
||||||
android:icon="@drawable/ic_groups"
|
android:icon="@drawable/ic_groups"
|
||||||
|
android:title="@string/group"
|
||||||
app:showAsAction="always">
|
app:showAsAction="always">
|
||||||
|
|
||||||
<menu />
|
<menu />
|
||||||
@ -27,39 +27,39 @@
|
|||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_enable_replace"
|
android:id="@+id/menu_enable_replace"
|
||||||
android:title="@string/replace_purify"
|
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
|
android:title="@string/replace_purify"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<!--自定义导出章节 选择菜单-->
|
<!--自定义导出章节 选择菜单-->
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_enable_custom_export"
|
android:id="@+id/menu_enable_custom_export"
|
||||||
android:title="@string/custom_export_section"
|
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
|
android:title="@string/custom_export_section"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_export_web_dav"
|
android:id="@+id/menu_export_web_dav"
|
||||||
android:title="@string/export_to_web_dav"
|
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
|
android:title="@string/export_to_web_dav"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_export_no_chapter_name"
|
android:id="@+id/menu_export_no_chapter_name"
|
||||||
android:title="@string/export_no_chapter_name"
|
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
|
android:title="@string/export_no_chapter_name"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_export_pics_file"
|
android:id="@+id/menu_export_pics_file"
|
||||||
android:title="@string/export_pics_file"
|
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
|
android:title="@string/export_pics_file"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_parallel_export"
|
android:id="@+id/menu_parallel_export"
|
||||||
android:title="@string/parallel_export_book"
|
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
|
android:title="@string/parallel_export_book"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
|
@ -1038,7 +1038,7 @@
|
|||||||
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
|
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
|
||||||
<string name="net_no_group">网络未分组</string>
|
<string name="net_no_group">网络未分组</string>
|
||||||
<string name="local_no_group">本地未分组</string>
|
<string name="local_no_group">本地未分组</string>
|
||||||
<string name="parallel_export_book">多线程导出TXT</string>
|
<string name="parallel_export_book">多线程导出</string>
|
||||||
<string name="progress_bar_behavior">进度条行为</string>
|
<string name="progress_bar_behavior">进度条行为</string>
|
||||||
<string name="source_edit_text_max_line">源编辑框最大行数</string>
|
<string name="source_edit_text_max_line">源编辑框最大行数</string>
|
||||||
<string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
|
<string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
|
||||||
|
@ -1041,7 +1041,7 @@
|
|||||||
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
|
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
|
||||||
<string name="net_no_group">网络未分组</string>
|
<string name="net_no_group">网络未分组</string>
|
||||||
<string name="local_no_group">本地未分组</string>
|
<string name="local_no_group">本地未分组</string>
|
||||||
<string name="parallel_export_book">多线程导出TXT</string>
|
<string name="parallel_export_book">多线程导出</string>
|
||||||
<string name="progress_bar_behavior">进度条行为</string>
|
<string name="progress_bar_behavior">进度条行为</string>
|
||||||
<string name="source_edit_text_max_line">源编辑框最大行数</string>
|
<string name="source_edit_text_max_line">源编辑框最大行数</string>
|
||||||
<string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
|
<string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
|
||||||
|
@ -1041,7 +1041,7 @@
|
|||||||
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
|
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
|
||||||
<string name="net_no_group">网络未分组</string>
|
<string name="net_no_group">网络未分组</string>
|
||||||
<string name="local_no_group">本地未分组</string>
|
<string name="local_no_group">本地未分组</string>
|
||||||
<string name="parallel_export_book">多线程导出TXT</string>
|
<string name="parallel_export_book">多线程导出</string>
|
||||||
<string name="progress_bar_behavior">进度条行为</string>
|
<string name="progress_bar_behavior">进度条行为</string>
|
||||||
<string name="source_edit_text_max_line">源编辑框最大行数</string>
|
<string name="source_edit_text_max_line">源编辑框最大行数</string>
|
||||||
<string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
|
<string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
|
||||||
|
@ -1037,7 +1037,7 @@ Còn </string>
|
|||||||
<string name="cover_decode_js">Giải mã Bìa Js(coverDecodeJs)</string>
|
<string name="cover_decode_js">Giải mã Bìa Js(coverDecodeJs)</string>
|
||||||
<string name="net_no_group">Đã tách nhóm mạng</string>
|
<string name="net_no_group">Đã tách nhóm mạng</string>
|
||||||
<string name="local_no_group">Đã tách nhóm cục bộ</string>
|
<string name="local_no_group">Đã tách nhóm cục bộ</string>
|
||||||
<string name="parallel_export_book">XT xuất đa luồng</string>
|
<string name="parallel_export_book">Xuất đa luồng</string>
|
||||||
<string name="progress_bar_behavior">Hành vi của thanh tiến trình</string>
|
<string name="progress_bar_behavior">Hành vi của thanh tiến trình</string>
|
||||||
<string name="source_edit_text_max_line">Số hàng tối đa trong hộp chỉnh sửa nguồn</string>
|
<string name="source_edit_text_max_line">Số hàng tối đa trong hộp chỉnh sửa nguồn</string>
|
||||||
<string name="source_edit_max_line_summary">%s,Đặt số hàng ít hơn số hàng tối đa có thể hiển thị trên màn hình giúp trượt sang các trường khác để chỉnh sửa dễ dàng hơn.</string>
|
<string name="source_edit_max_line_summary">%s,Đặt số hàng ít hơn số hàng tối đa có thể hiển thị trên màn hình giúp trượt sang các trường khác để chỉnh sửa dễ dàng hơn.</string>
|
||||||
|
@ -1038,7 +1038,7 @@
|
|||||||
<string name="cover_decode_js">封面解密(coverDecodeJs)</string>
|
<string name="cover_decode_js">封面解密(coverDecodeJs)</string>
|
||||||
<string name="net_no_group">网络未分组</string>
|
<string name="net_no_group">网络未分组</string>
|
||||||
<string name="local_no_group">本地未分组</string>
|
<string name="local_no_group">本地未分组</string>
|
||||||
<string name="parallel_export_book">多线程导出TXT</string>
|
<string name="parallel_export_book">多线程导出</string>
|
||||||
<string name="progress_bar_behavior">进度条行为</string>
|
<string name="progress_bar_behavior">进度条行为</string>
|
||||||
<string name="source_edit_text_max_line">源编辑框最大行数</string>
|
<string name="source_edit_text_max_line">源编辑框最大行数</string>
|
||||||
<string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
|
<string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
|
||||||
|
@ -1040,7 +1040,7 @@
|
|||||||
<string name="cover_decode_js">封面解密(coverDecodeJs)</string>
|
<string name="cover_decode_js">封面解密(coverDecodeJs)</string>
|
||||||
<string name="net_no_group">網路未分組</string>
|
<string name="net_no_group">網路未分組</string>
|
||||||
<string name="local_no_group">本機未分組</string>
|
<string name="local_no_group">本機未分組</string>
|
||||||
<string name="parallel_export_book">多執行緒匯出TXT</string>
|
<string name="parallel_export_book">多執行緒匯出</string>
|
||||||
<string name="progress_bar_behavior">進度條行為</string>
|
<string name="progress_bar_behavior">進度條行為</string>
|
||||||
<string name="source_edit_text_max_line">源編輯框最大行數</string>
|
<string name="source_edit_text_max_line">源編輯框最大行數</string>
|
||||||
<string name="source_edit_max_line_summary">%s,設定行數小於螢幕可顯示的最大行數可以更方便的滑動到其他的欄位進行編輯</string>
|
<string name="source_edit_max_line_summary">%s,設定行數小於螢幕可顯示的最大行數可以更方便的滑動到其他的欄位進行編輯</string>
|
||||||
|
@ -1041,7 +1041,7 @@
|
|||||||
<string name="cover_decode_js">封面解密(coverDecodeJs)</string>
|
<string name="cover_decode_js">封面解密(coverDecodeJs)</string>
|
||||||
<string name="net_no_group">网络未分组</string>
|
<string name="net_no_group">网络未分组</string>
|
||||||
<string name="local_no_group">本地未分组</string>
|
<string name="local_no_group">本地未分组</string>
|
||||||
<string name="parallel_export_book">多线程导出TXT</string>
|
<string name="parallel_export_book">多线程导出</string>
|
||||||
<string name="progress_bar_behavior">进度条行为</string>
|
<string name="progress_bar_behavior">进度条行为</string>
|
||||||
<string name="source_edit_text_max_line">源编辑框最大行数</string>
|
<string name="source_edit_text_max_line">源编辑框最大行数</string>
|
||||||
<string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
|
<string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
|
||||||
|
@ -1041,7 +1041,7 @@
|
|||||||
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
|
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
|
||||||
<string name="net_no_group">Network ungrouped</string>
|
<string name="net_no_group">Network ungrouped</string>
|
||||||
<string name="local_no_group">Local ungrouped</string>
|
<string name="local_no_group">Local ungrouped</string>
|
||||||
<string name="parallel_export_book">Multithreaded export TXT</string>
|
<string name="parallel_export_book">Multithreaded export</string>
|
||||||
<string name="progress_bar_behavior">Progress bar behavior</string>
|
<string name="progress_bar_behavior">Progress bar behavior</string>
|
||||||
<string name="source_edit_text_max_line">Maximum number of rows in the source edit box</string>
|
<string name="source_edit_text_max_line">Maximum number of rows in the source edit box</string>
|
||||||
<string name="source_edit_max_line_summary">%s,Setting the number of rows less than the maximum number of rows that can be displayed on the screen makes it easier to slide to other fields for editing.</string>
|
<string name="source_edit_max_line_summary">%s,Setting the number of rows less than the maximum number of rows that can be displayed on the screen makes it easier to slide to other fields for editing.</string>
|
||||||
|
@ -32,4 +32,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(fileTree(dir: 'lib', include: ['rhino-1.7.13-2.jar']))
|
api(fileTree(dir: 'lib', include: ['rhino-1.7.13-2.jar']))
|
||||||
|
|
||||||
|
def coroutines_version = '1.7.3'
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
|
||||||
}
|
}
|
@ -51,6 +51,10 @@ abstract class AbstractScriptEngine(val bindings: Bindings? = null) : ScriptEngi
|
|||||||
return getBindings(100)?.get(key)
|
return getBindings(100)?.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun evalSuspend(script: String, scope: Scriptable): Any? {
|
||||||
|
return this.evalSuspend(StringReader(script), scope)
|
||||||
|
}
|
||||||
|
|
||||||
override fun eval(script: String, scope: Scriptable): Any? {
|
override fun eval(script: String, scope: Scriptable): Any? {
|
||||||
return this.eval(StringReader(script), scope)
|
return this.eval(StringReader(script), scope)
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,9 @@ abstract class CompiledScript {
|
|||||||
@Throws(ScriptException::class)
|
@Throws(ScriptException::class)
|
||||||
abstract fun eval(scope: Scriptable): Any?
|
abstract fun eval(scope: Scriptable): Any?
|
||||||
|
|
||||||
|
@Throws(ScriptException::class)
|
||||||
|
abstract suspend fun evalSuspend(scope: Scriptable): Any?
|
||||||
|
|
||||||
@Throws(ScriptException::class)
|
@Throws(ScriptException::class)
|
||||||
fun eval(bindings: Bindings?): Any? {
|
fun eval(bindings: Bindings?): Any? {
|
||||||
var ctxt = getEngine().context
|
var ctxt = getEngine().context
|
||||||
|
@ -14,6 +14,12 @@ interface ScriptEngine {
|
|||||||
@Throws(ScriptException::class)
|
@Throws(ScriptException::class)
|
||||||
fun eval(reader: Reader, scope: Scriptable): Any?
|
fun eval(reader: Reader, scope: Scriptable): Any?
|
||||||
|
|
||||||
|
@Throws(ScriptException::class)
|
||||||
|
suspend fun evalSuspend(reader: Reader, scope: Scriptable): Any?
|
||||||
|
|
||||||
|
@Throws(ScriptException::class)
|
||||||
|
suspend fun evalSuspend(script: String, scope: Scriptable): Any?
|
||||||
|
|
||||||
@Throws(ScriptException::class)
|
@Throws(ScriptException::class)
|
||||||
fun eval(script: String, scope: Scriptable): Any?
|
fun eval(script: String, scope: Scriptable): Any?
|
||||||
|
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.script.rhino
|
||||||
|
|
||||||
|
import kotlinx.coroutines.ThreadContextElement
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class ContextElement : ThreadContextElement<Any?> {
|
||||||
|
|
||||||
|
companion object Key : CoroutineContext.Key<ContextElement>
|
||||||
|
|
||||||
|
override val key: CoroutineContext.Key<ContextElement>
|
||||||
|
get() = Key
|
||||||
|
|
||||||
|
private val contextHelper: Any? = VMBridgeReflect.contextLocal.get()
|
||||||
|
|
||||||
|
override fun updateThreadContext(context: CoroutineContext): Any? {
|
||||||
|
val oldState = VMBridgeReflect.contextLocal.get()
|
||||||
|
VMBridgeReflect.contextLocal.set(contextHelper)
|
||||||
|
return oldState
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun restoreThreadContext(context: CoroutineContext, oldState: Any?) {
|
||||||
|
VMBridgeReflect.contextLocal.set(oldState)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,7 +28,11 @@ import com.script.CompiledScript
|
|||||||
import com.script.ScriptContext
|
import com.script.ScriptContext
|
||||||
import com.script.ScriptEngine
|
import com.script.ScriptEngine
|
||||||
import com.script.ScriptException
|
import com.script.ScriptException
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.mozilla.javascript.*
|
import org.mozilla.javascript.*
|
||||||
|
import java.io.IOException
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents compiled JavaScript code.
|
* Represents compiled JavaScript code.
|
||||||
@ -91,4 +95,48 @@ internal class RhinoCompiledScript(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun evalSuspend(scope: Scriptable): Any? {
|
||||||
|
val cx = Context.enter()
|
||||||
|
var ret: Any?
|
||||||
|
withContext(ContextElement()) {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
ret = cx.executeScriptWithContinuations(script, scope)
|
||||||
|
} catch (e: ContinuationPending) {
|
||||||
|
var pending = e
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val suspendFunction =
|
||||||
|
pending.applicationState as Function1<Continuation<Any?>, Any?>
|
||||||
|
val functionResult = suspendCoroutineUninterceptedOrReturn { cout ->
|
||||||
|
suspendFunction.invoke(cout)
|
||||||
|
}
|
||||||
|
val continuation = pending.continuation
|
||||||
|
ret = cx.resumeContinuation(continuation, scope, functionResult)
|
||||||
|
break
|
||||||
|
} catch (e: ContinuationPending) {
|
||||||
|
pending = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (re: RhinoException) {
|
||||||
|
val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
|
||||||
|
val msg: String = if (re is JavaScriptException) {
|
||||||
|
re.value.toString()
|
||||||
|
} else {
|
||||||
|
re.toString()
|
||||||
|
}
|
||||||
|
val se = ScriptException(msg, re.sourceName(), line)
|
||||||
|
se.initCause(re)
|
||||||
|
throw se
|
||||||
|
} catch (var14: IOException) {
|
||||||
|
throw ScriptException(var14)
|
||||||
|
} finally {
|
||||||
|
Context.exit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return engine.unwrapReturnValue(ret)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -25,6 +25,7 @@
|
|||||||
package com.script.rhino
|
package com.script.rhino
|
||||||
|
|
||||||
import com.script.*
|
import com.script.*
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.mozilla.javascript.*
|
import org.mozilla.javascript.*
|
||||||
import org.mozilla.javascript.Function
|
import org.mozilla.javascript.Function
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -32,6 +33,8 @@ import java.io.Reader
|
|||||||
import java.io.StringReader
|
import java.io.StringReader
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.security.*
|
import java.security.*
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of `ScriptEngine` using the Mozilla Rhino
|
* Implementation of `ScriptEngine` using the Mozilla Rhino
|
||||||
@ -80,6 +83,54 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
|
|||||||
return unwrapReturnValue(ret)
|
return unwrapReturnValue(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(ContinuationPending::class)
|
||||||
|
override suspend fun evalSuspend(reader: Reader, scope: Scriptable): Any? {
|
||||||
|
val cx = Context.enter()
|
||||||
|
var ret: Any?
|
||||||
|
withContext(ContextElement()) {
|
||||||
|
try {
|
||||||
|
var filename = this@RhinoScriptEngine["javax.script.filename"] as? String
|
||||||
|
filename = filename ?: "<Unknown source>"
|
||||||
|
val script = cx.compileReader(reader, filename, 1, null)
|
||||||
|
try {
|
||||||
|
ret = cx.executeScriptWithContinuations(script, scope)
|
||||||
|
} catch (e: ContinuationPending) {
|
||||||
|
var pending = e
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val suspendFunction =
|
||||||
|
pending.applicationState as Function1<Continuation<Any?>, Any?>
|
||||||
|
val functionResult = suspendCoroutineUninterceptedOrReturn { cout ->
|
||||||
|
suspendFunction.invoke(cout)
|
||||||
|
}
|
||||||
|
val continuation = pending.continuation
|
||||||
|
ret = cx.resumeContinuation(continuation, scope, functionResult)
|
||||||
|
break
|
||||||
|
} catch (e: ContinuationPending) {
|
||||||
|
pending = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (re: RhinoException) {
|
||||||
|
val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
|
||||||
|
val msg: String = if (re is JavaScriptException) {
|
||||||
|
re.value.toString()
|
||||||
|
} else {
|
||||||
|
re.toString()
|
||||||
|
}
|
||||||
|
val se = ScriptException(msg, re.sourceName(), line)
|
||||||
|
se.initCause(re)
|
||||||
|
throw se
|
||||||
|
} catch (var14: IOException) {
|
||||||
|
throw ScriptException(var14)
|
||||||
|
} finally {
|
||||||
|
Context.exit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unwrapReturnValue(ret)
|
||||||
|
}
|
||||||
|
|
||||||
override fun createBindings(): Bindings {
|
override fun createBindings(): Bindings {
|
||||||
return SimpleBindings()
|
return SimpleBindings()
|
||||||
}
|
}
|
||||||
@ -227,7 +278,7 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
|
|||||||
callable: Callable,
|
callable: Callable,
|
||||||
cx: Context,
|
cx: Context,
|
||||||
scope: Scriptable,
|
scope: Scriptable,
|
||||||
thisObj: Scriptable,
|
thisObj: Scriptable?,
|
||||||
args: Array<Any>
|
args: Array<Any>
|
||||||
): Any? {
|
): Any? {
|
||||||
var accContext: AccessControlContext? = null
|
var accContext: AccessControlContext? = null
|
||||||
@ -253,7 +304,7 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
|
|||||||
callable: Callable,
|
callable: Callable,
|
||||||
cx: Context,
|
cx: Context,
|
||||||
scope: Scriptable,
|
scope: Scriptable,
|
||||||
thisObj: Scriptable,
|
thisObj: Scriptable?,
|
||||||
args: Array<Any>
|
args: Array<Any>
|
||||||
): Any? {
|
): Any? {
|
||||||
return super.doTopCall(callable, cx, scope, thisObj, args)
|
return super.doTopCall(callable, cx, scope, thisObj, args)
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.script.rhino
|
||||||
|
|
||||||
|
import org.mozilla.javascript.VMBridge
|
||||||
|
|
||||||
|
object VMBridgeReflect {
|
||||||
|
|
||||||
|
val instance: VMBridge by lazy {
|
||||||
|
VMBridge::class.java.getDeclaredField("instance").apply {
|
||||||
|
isAccessible = true
|
||||||
|
}.get(null) as VMBridge
|
||||||
|
}
|
||||||
|
|
||||||
|
val contextLocal: ThreadLocal<Any> by lazy {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
instance::class.java.getDeclaredField("contextLocal").apply {
|
||||||
|
isAccessible = true
|
||||||
|
}.get(null) as ThreadLocal<Any>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user