|
|
@ -12,11 +12,18 @@ import io.legado.app.service.help.ReadAloud |
|
|
|
import io.legado.app.service.help.ReadBook |
|
|
|
import io.legado.app.service.help.ReadBook |
|
|
|
import io.legado.app.utils.FileUtils |
|
|
|
import io.legado.app.utils.FileUtils |
|
|
|
import io.legado.app.utils.LogUtils |
|
|
|
import io.legado.app.utils.LogUtils |
|
|
|
|
|
|
|
import io.legado.app.utils.MD5Utils |
|
|
|
import io.legado.app.utils.postEvent |
|
|
|
import io.legado.app.utils.postEvent |
|
|
|
|
|
|
|
import kotlinx.coroutines.delay |
|
|
|
import kotlinx.coroutines.isActive |
|
|
|
import kotlinx.coroutines.isActive |
|
|
|
|
|
|
|
import kotlinx.coroutines.launch |
|
|
|
|
|
|
|
import org.jetbrains.anko.collections.forEachWithIndex |
|
|
|
import java.io.File |
|
|
|
import java.io.File |
|
|
|
import java.io.FileDescriptor |
|
|
|
import java.io.FileDescriptor |
|
|
|
import java.io.FileInputStream |
|
|
|
import java.io.FileInputStream |
|
|
|
|
|
|
|
import java.net.ConnectException |
|
|
|
|
|
|
|
import java.net.SocketTimeoutException |
|
|
|
|
|
|
|
import java.util.* |
|
|
|
|
|
|
|
|
|
|
|
class HttpReadAloudService : BaseReadAloudService(), |
|
|
|
class HttpReadAloudService : BaseReadAloudService(), |
|
|
|
MediaPlayer.OnPreparedListener, |
|
|
|
MediaPlayer.OnPreparedListener, |
|
|
@ -55,12 +62,17 @@ class HttpReadAloudService : BaseReadAloudService(), |
|
|
|
|
|
|
|
|
|
|
|
override fun play() { |
|
|
|
override fun play() { |
|
|
|
if (contentList.isEmpty()) return |
|
|
|
if (contentList.isEmpty()) return |
|
|
|
|
|
|
|
ReadAloud.httpTTS?.let { |
|
|
|
|
|
|
|
val fileName = md5SpeakFileName(it.url, AppConfig.ttsSpeechRate.toString(), contentList[nowSpeak]) |
|
|
|
if (nowSpeak == 0) { |
|
|
|
if (nowSpeak == 0) { |
|
|
|
downloadAudio() |
|
|
|
downloadAudio() |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
val file = getSpeakFile(nowSpeak) |
|
|
|
val file = getSpeakFileAsMd5(fileName) |
|
|
|
if (file.exists()) { |
|
|
|
if (file.exists()) { |
|
|
|
playAudio(FileInputStream(file).fd) |
|
|
|
playAudio(FileInputStream(file).fd) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
downloadAudio() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -68,18 +80,36 @@ class HttpReadAloudService : BaseReadAloudService(), |
|
|
|
private fun downloadAudio() { |
|
|
|
private fun downloadAudio() { |
|
|
|
task?.cancel() |
|
|
|
task?.cancel() |
|
|
|
task = execute { |
|
|
|
task = execute { |
|
|
|
FileUtils.deleteFile(ttsFolder) |
|
|
|
removeCacheFile() |
|
|
|
for (index in 0 until contentList.size) { |
|
|
|
|
|
|
|
if (isActive) { |
|
|
|
|
|
|
|
ReadAloud.httpTTS?.let { |
|
|
|
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( |
|
|
|
AnalyzeUrl( |
|
|
|
it.url, |
|
|
|
it.url, |
|
|
|
speakText = contentList[index], |
|
|
|
speakText = item, |
|
|
|
speakSpeed = AppConfig.ttsSpeechRate |
|
|
|
speakSpeed = AppConfig.ttsSpeechRate |
|
|
|
).getByteArray().let { bytes -> |
|
|
|
).getByteArray().let { bytes -> |
|
|
|
if (isActive) { |
|
|
|
if (isActive) { |
|
|
|
val file = getSpeakFile(index) |
|
|
|
val file = getSpeakFileAsMd5(fileName) |
|
|
|
|
|
|
|
//val file = getSpeakFile(index) |
|
|
|
file.writeBytes(bytes) |
|
|
|
file.writeBytes(bytes) |
|
|
|
|
|
|
|
|
|
|
|
if (index == nowSpeak) { |
|
|
|
if (index == nowSpeak) { |
|
|
|
@Suppress("BlockingMethodInNonBlockingContext") |
|
|
|
@Suppress("BlockingMethodInNonBlockingContext") |
|
|
|
val fis = FileInputStream(file) |
|
|
|
val fis = FileInputStream(file) |
|
|
@ -87,9 +117,17 @@ class HttpReadAloudService : BaseReadAloudService(), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} 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 { |
|
|
|
private fun speakFilePath() = ttsFolder + File.separator |
|
|
|
return FileUtils.createFileIfNotExist("${ttsFolder}${File.separator}${index}.mp3") |
|
|
|
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) { |
|
|
|
override fun pauseReadAloud(pause: Boolean) { |
|
|
|
super.pauseReadAloud(pause) |
|
|
|
super.pauseReadAloud(pause) |
|
|
|
mediaPlayer.pause() |
|
|
|
mediaPlayer.pause() |
|
|
|