This commit is contained in:
Horis
2024-07-30 12:39:04 +08:00
parent 9172659440
commit 452925f419
4 changed files with 42 additions and 34 deletions
@@ -21,6 +21,7 @@ import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.StringUtils
import io.legado.app.utils.SvgUtils
import io.legado.app.utils.UrlUtil
import io.legado.app.utils.createFileIfNotExist
import io.legado.app.utils.exists
import io.legado.app.utils.externalFiles
import io.legado.app.utils.getFile
@@ -29,10 +30,10 @@ import io.legado.app.utils.onEachParallel
import io.legado.app.utils.postEvent
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.withContext
import org.apache.commons.text.similarity.JaccardSimilarity
import splitties.init.appCtx
@@ -55,7 +56,7 @@ object BookHelp {
private const val cacheFolderName = "book_cache"
private const val cacheImageFolderName = "images"
private const val cacheEpubFolderName = "epub"
private val downloadImages = ConcurrentHashMap.newKeySet<String>()
private val downloadImages = ConcurrentHashMap<String, Mutex>()
val cachePath = FileUtils.getPath(downloadDir, cacheFolderName)
@@ -173,15 +174,18 @@ object BookHelp {
src: String,
chapter: BookChapter? = null
) {
while (downloadImages.contains(src)) {
delay(100)
}
if (getImage(book, src).exists()) {
if (isImageExist(book, src)) {
return
}
downloadImages.add(src)
val analyzeUrl = AnalyzeUrl(src, source = bookSource)
val mutex = synchronized(this) {
downloadImages.getOrPut(src) { Mutex() }
}
mutex.lock()
try {
if (isImageExist(book, src)) {
return
}
val analyzeUrl = AnalyzeUrl(src, source = bookSource)
val bytes = analyzeUrl.getByteArrayAwait()
//某些图片被加密,需要进一步解密
ImageUtils.decode(
@@ -193,13 +197,7 @@ object BookHelp {
// throw NoStackTraceException("数据异常")
AppLog.put("${book.name} ${chapter?.title} 图片 $src 下载错误 数据异常")
}
FileUtils.createFileIfNotExist(
downloadDir,
cacheFolderName,
book.getFolderName(),
cacheImageFolderName,
"${MD5Utils.md5Encode16(src)}.${getImageSuffix(src)}"
).writeBytes(it)
writeImage(book, src, it)
}
} catch (e: Exception) {
coroutineContext.ensureActive()
@@ -207,6 +205,7 @@ object BookHelp {
AppLog.put(msg, e)
} finally {
downloadImages.remove(src)
mutex.unlock()
}
}
@@ -219,6 +218,16 @@ object BookHelp {
)
}
@Synchronized
fun writeImage(book: Book, src: String, bytes: ByteArray) {
getImage(book, src).createFileIfNotExist().writeBytes(bytes)
}
@Synchronized
fun isImageExist(book: Book, src: String): Boolean {
return getImage(book, src).exists()
}
fun getImageSuffix(src: String): String {
return UrlUtil.getSuffix(src, "jpg")
}
@@ -94,7 +94,7 @@ object ImageProvider {
): File {
return withContext(IO) {
val vFile = BookHelp.getImage(book, src)
if (!vFile.exists()) {
if (!BookHelp.isImageExist(book, src)) {
val inputStream = when {
book.isEpub -> EpubFile.getImage(book, src)
book.isPdf -> PdfFile.getImage(book, src)
@@ -8,6 +8,7 @@ import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.Timeline
import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.cache.CacheDataSink
@@ -75,7 +76,7 @@ class HttpReadAloudService : BaseReadAloudService(),
private val cache by lazy {
SimpleCache(
File(cacheDir, "httpTTS_cache"),
LeastRecentlyUsedCacheEvictor((50 * 1024 * 1024).toLong()),
LeastRecentlyUsedCacheEvictor(128 * 1024 * 1024),
StandaloneDatabaseProvider(appCtx)
)
}
@@ -182,15 +183,7 @@ class HttpReadAloudService : BaseReadAloudService(),
val file = getSpeakFileAsMd5(fileName)
val mediaItem = MediaItem.fromUri(Uri.fromFile(file))
launch(Main) {
if (exoPlayer.playbackState == Player.STATE_ENDED) {
exoPlayer.stop()
exoPlayer.clearMediaItems()
}
exoPlayer.addMediaItem(mediaItem)
if (!exoPlayer.isPlaying) {
exoPlayer.playWhenReady = !pause
exoPlayer.prepare()
}
}
}
preDownloadAudios(httpTts)
@@ -256,15 +249,7 @@ class HttpReadAloudService : BaseReadAloudService(),
downloaderChannel.send(downloader)
val mediaSource = createMediaSource(dataSourceFactory, fileName)
launch(Main) {
if (exoPlayer.playbackState == Player.STATE_ENDED) {
exoPlayer.stop()
exoPlayer.clearMediaItems()
}
exoPlayer.addMediaSource(mediaSource)
if (!exoPlayer.isPlaying) {
exoPlayer.playWhenReady = !pause
exoPlayer.prepare()
}
}
}
preDownloadAudiosStream(httpTts, downloaderChannel)
@@ -540,10 +525,24 @@ class HttpReadAloudService : BaseReadAloudService(),
// 结束
playErrorNo = 0
updateNextPos()
exoPlayer.stop()
exoPlayer.clearMediaItems()
}
}
}
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
when (reason) {
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED -> {
if (!timeline.isEmpty && exoPlayer.playbackState == Player.STATE_IDLE) {
exoPlayer.prepare()
}
}
else -> {}
}
}
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED) return
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
@@ -35,7 +35,7 @@ fun File.listFileDocs(filter: FileDocFilter? = null): ArrayList<FileDoc> {
fun File.createFileIfNotExist(): File {
if (!exists()) {
parentFile?.createFileIfNotExist()
parentFile?.createFolderIfNotExist()
createNewFile()
}
return this