|  |  |  | @ -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() | 
			
		
	
	
		
			
				
					|  |  |  | 
 |