diff --git a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt index 2851173ed..e1bb89e61 100644 --- a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt @@ -12,11 +12,18 @@ import io.legado.app.service.help.ReadAloud import io.legado.app.service.help.ReadBook import io.legado.app.utils.FileUtils import io.legado.app.utils.LogUtils +import io.legado.app.utils.MD5Utils import io.legado.app.utils.postEvent +import kotlinx.coroutines.delay import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import org.jetbrains.anko.collections.forEachWithIndex import java.io.File import java.io.FileDescriptor import java.io.FileInputStream +import java.net.ConnectException +import java.net.SocketTimeoutException +import java.util.* class HttpReadAloudService : BaseReadAloudService(), MediaPlayer.OnPreparedListener, @@ -55,12 +62,17 @@ class HttpReadAloudService : BaseReadAloudService(), override fun play() { if (contentList.isEmpty()) return - if (nowSpeak == 0) { - downloadAudio() - } else { - val file = getSpeakFile(nowSpeak) - if (file.exists()) { - playAudio(FileInputStream(file).fd) + ReadAloud.httpTTS?.let { + val fileName = md5SpeakFileName(it.url, AppConfig.ttsSpeechRate.toString(), contentList[nowSpeak]) + if (nowSpeak == 0) { + downloadAudio() + } else { + val file = getSpeakFileAsMd5(fileName) + if (file.exists()) { + playAudio(FileInputStream(file).fd) + } else { + downloadAudio() + } } } } @@ -68,28 +80,54 @@ class HttpReadAloudService : BaseReadAloudService(), private fun downloadAudio() { task?.cancel() task = execute { - FileUtils.deleteFile(ttsFolder) - for (index in 0 until contentList.size) { - if (isActive) { - ReadAloud.httpTTS?.let { - AnalyzeUrl( - it.url, - speakText = contentList[index], - speakSpeed = AppConfig.ttsSpeechRate - ).getByteArray().let { bytes -> - if (isActive) { - val file = getSpeakFile(index) - file.writeBytes(bytes) - if (index == nowSpeak) { - @Suppress("BlockingMethodInNonBlockingContext") - val fis = FileInputStream(file) - playAudio(fis.fd) + removeCacheFile() + ReadAloud.httpTTS?.let { + contentList.forEachWithIndex { index, item -> + if (isActive) { + val fileName = + md5SpeakFileName(it.url, AppConfig.ttsSpeechRate.toString(), item) + + if (hasSpeakFile(fileName)) { //已经下载好的语音缓存 + if (index == nowSpeak) { + val file = getSpeakFileAsMd5(fileName) + + @Suppress("BlockingMethodInNonBlockingContext") + val fis = FileInputStream(file) + playAudio(fis.fd) + } + } else if (hasSpeakCacheFile(fileName)) { //缓存文件还在,可能还没下载完 + return@let + } else { //没有下载并且没有缓存文件 + try { + createSpeakCacheFile(fileName) + AnalyzeUrl( + it.url, + speakText = item, + speakSpeed = AppConfig.ttsSpeechRate + ).getByteArray().let { bytes -> + if (isActive) { + val file = getSpeakFileAsMd5(fileName) + //val file = getSpeakFile(index) + file.writeBytes(bytes) + + if (index == nowSpeak) { + @Suppress("BlockingMethodInNonBlockingContext") + val fis = FileInputStream(file) + playAudio(fis.fd) + } + } } + } catch (e: SocketTimeoutException) { + removeSpeakCacheFile(fileName) + // delay(2000) + // downloadAudio() + } catch (e: ConnectException) { + removeSpeakCacheFile(fileName) + } catch (e: Exception) { + removeSpeakCacheFile(fileName) } } } - } else { - break } } } @@ -110,10 +148,47 @@ class HttpReadAloudService : BaseReadAloudService(), } } - private fun getSpeakFile(index: Int = nowSpeak): File { - return FileUtils.createFileIfNotExist("${ttsFolder}${File.separator}${index}.mp3") + private fun speakFilePath() = ttsFolder + File.separator + private fun md5SpeakFileName(url: String, ttsConfig: String, content: String): String { + return MD5Utils.md5Encode16(textChapter!!.title) + "_" + MD5Utils.md5Encode16("$url-|-$ttsConfig-|-$content") + } + + private fun hasSpeakFile(name: String) = + FileUtils.exist("${speakFilePath()}$name.mp3") + + private fun hasSpeakCacheFile(name: String) = + FileUtils.exist("${speakFilePath()}$name.mp3.cache") + + private fun createSpeakCacheFile(name: String): File = + FileUtils.createFileWithReplace("${speakFilePath()}$name.mp3.cache") + + private fun removeSpeakCacheFile(name: String) { + FileUtils.delete("${speakFilePath()}$name.mp3.cache") + } + + private fun getSpeakFileAsMd5(name: String): File = + FileUtils.createFileIfNotExist("${speakFilePath()}$name.mp3") + + private fun removeCacheFile() { + FileUtils.listDirsAndFiles(speakFilePath())?.forEach { + if (it == null) { + return@forEach + } + if (Regex(""".+\.mp3$""").matches(it.name)) { //mp3缓存文件 + val reg = + """^${MD5Utils.md5Encode16(textChapter!!.title)}_[a-z0-9]{16}\.mp3$""".toRegex() + if (!reg.matches(it.name)) { + FileUtils.deleteFile(it.absolutePath) + } + } else { + if (Date().time - it.lastModified() > 30000) { + FileUtils.deleteFile(it.absolutePath) + } + } + } } + override fun pauseReadAloud(pause: Boolean) { super.pauseReadAloud(pause) mediaPlayer.pause()