|
|
@@ -26,13 +26,14 @@ import io.legado.app.utils.printOnDebug
|
|
|
import io.legado.app.utils.servicePendingIntent
|
|
|
import io.legado.app.utils.toastOnUi
|
|
|
import kotlinx.coroutines.CancellationException
|
|
|
-import kotlinx.coroutines.Dispatchers.IO
|
|
|
+import kotlinx.coroutines.Dispatchers.Main
|
|
|
import kotlinx.coroutines.Job
|
|
|
import kotlinx.coroutines.delay
|
|
|
import kotlinx.coroutines.ensureActive
|
|
|
import kotlinx.coroutines.launch
|
|
|
import kotlinx.coroutines.sync.Mutex
|
|
|
import kotlinx.coroutines.sync.withLock
|
|
|
+import kotlinx.coroutines.withContext
|
|
|
import okhttp3.Response
|
|
|
import org.mozilla.javascript.WrappedException
|
|
|
import java.io.File
|
|
|
@@ -68,32 +69,22 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|
|
super.onDestroy()
|
|
|
downloadTask?.cancel()
|
|
|
exoPlayer.release()
|
|
|
+ Coroutine.async {
|
|
|
+ removeCacheFile()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
override fun play() {
|
|
|
pageChanged = false
|
|
|
exoPlayer.stop()
|
|
|
+ exoPlayer.clearMediaItems()
|
|
|
+ if (!requestFocus()) return
|
|
|
if (contentList.isEmpty()) {
|
|
|
AppLog.putDebug("朗读列表为空")
|
|
|
ReadBook.readAloud()
|
|
|
} else {
|
|
|
super.play()
|
|
|
- kotlin.runCatching {
|
|
|
- if (nowSpeak == 0) {
|
|
|
- downloadAudio()
|
|
|
- } else {
|
|
|
- val fileName = md5SpeakFileName(contentList[nowSpeak])
|
|
|
- val file = getSpeakFileAsMd5(fileName)
|
|
|
- if (file.exists()) {
|
|
|
- playAudio(file)
|
|
|
- } else if (!downloadTaskActiveLock.isLocked) {
|
|
|
- downloadAudio()
|
|
|
- }
|
|
|
- }
|
|
|
- }.onFailure {
|
|
|
- toastOnUi("朗读出错:${it.localizedMessage}")
|
|
|
- AppLog.put("朗读出错:${it.localizedMessage}", it)
|
|
|
- }
|
|
|
+ downloadAndPlayAudios()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -105,21 +96,20 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|
|
readAloudNumber += contentList[nowSpeak].length + 1
|
|
|
if (nowSpeak < contentList.lastIndex) {
|
|
|
nowSpeak++
|
|
|
- play()
|
|
|
} else {
|
|
|
nextChapter()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private fun downloadAudio() {
|
|
|
+ private fun downloadAndPlayAudios() {
|
|
|
downloadTask?.cancel()
|
|
|
downloadTask = execute {
|
|
|
downloadTaskActiveLock.withLock {
|
|
|
ensureActive()
|
|
|
- removeCacheFile()
|
|
|
val httpTts = ReadAloud.httpTTS ?: throw NoStackTraceException("tts is null")
|
|
|
contentList.forEachIndexed { index, content ->
|
|
|
ensureActive()
|
|
|
+ if (index < nowSpeak) return@forEachIndexed
|
|
|
val fileName = md5SpeakFileName(content)
|
|
|
val speakText = content.replace(AppPattern.notReadAloudRegex, "")
|
|
|
if (speakText.isEmpty()) {
|
|
|
@@ -141,13 +131,19 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|
|
return@execute
|
|
|
}
|
|
|
}
|
|
|
- if (index == nowSpeak) {
|
|
|
- val file = getSpeakFileAsMd5(fileName)
|
|
|
- playAudio(file)
|
|
|
+ val file = getSpeakFileAsMd5(fileName)
|
|
|
+ val mediaItem = MediaItem.fromUri(Uri.fromFile(file))
|
|
|
+ launch(Main) {
|
|
|
+ exoPlayer.addMediaItem(mediaItem)
|
|
|
+ if (!exoPlayer.isPlaying && nowSpeak == index) {
|
|
|
+ exoPlayer.playWhenReady = true
|
|
|
+ exoPlayer.prepare()
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- }.onError(IO) {
|
|
|
+ }.onError {
|
|
|
+ toastOnUi("朗读出错:${it.localizedMessage}")
|
|
|
AppLog.put("朗读下载出错\n${it.localizedMessage}", it)
|
|
|
}
|
|
|
}
|
|
|
@@ -193,6 +189,7 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|
|
e.printOnDebug()
|
|
|
throw e
|
|
|
}
|
|
|
+
|
|
|
is SocketTimeoutException, is ConnectException -> {
|
|
|
downloadErrorNo++
|
|
|
if (downloadErrorNo > 5) {
|
|
|
@@ -202,6 +199,7 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|
|
throw e
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
else -> {
|
|
|
downloadErrorNo++
|
|
|
val msg = "tts下载错误\n${e.localizedMessage}"
|
|
|
@@ -223,22 +221,6 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|
|
return null
|
|
|
}
|
|
|
|
|
|
- @Synchronized
|
|
|
- private fun playAudio(file: File) {
|
|
|
- if (requestFocus()) {
|
|
|
- launch {
|
|
|
- kotlin.runCatching {
|
|
|
- val mediaItem = MediaItem.fromUri(Uri.fromFile(file))
|
|
|
- exoPlayer.setMediaItem(mediaItem)
|
|
|
- exoPlayer.playWhenReady = true
|
|
|
- exoPlayer.prepare()
|
|
|
- }.onFailure {
|
|
|
- it.printOnDebug()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
private fun md5SpeakFileName(content: String): String {
|
|
|
return MD5Utils.md5Encode16(textChapter?.title ?: "") + "_" +
|
|
|
MD5Utils.md5Encode16("${ReadAloud.httpTTS?.url}-|-$speechRate-|-$content")
|
|
|
@@ -338,7 +320,7 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|
|
downloadTask?.cancel()
|
|
|
exoPlayer.stop()
|
|
|
speechRate = AppConfig.speechRatePlay + 5
|
|
|
- downloadAudio()
|
|
|
+ downloadAndPlayAudios()
|
|
|
}
|
|
|
|
|
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
|
|
@@ -347,15 +329,18 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|
|
Player.STATE_IDLE -> {
|
|
|
// 空闲
|
|
|
}
|
|
|
+
|
|
|
Player.STATE_BUFFERING -> {
|
|
|
// 缓冲中
|
|
|
}
|
|
|
+
|
|
|
Player.STATE_READY -> {
|
|
|
// 准备好
|
|
|
if (pause) return
|
|
|
exoPlayer.play()
|
|
|
upPlayPos()
|
|
|
}
|
|
|
+
|
|
|
Player.STATE_ENDED -> {
|
|
|
// 结束
|
|
|
playErrorNo = 0
|
|
|
@@ -364,6 +349,12 @@ class HttpReadAloudService : BaseReadAloudService(),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
|
|
+ if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED) return
|
|
|
+ upPlayPos()
|
|
|
+ playNext()
|
|
|
+ }
|
|
|
+
|
|
|
override fun onPlayerError(error: PlaybackException) {
|
|
|
super.onPlayerError(error)
|
|
|
AppLog.put("朗读错误\n${contentList[nowSpeak]}", error)
|