Merge pull request #1 from gedoor/master

update
pull/784/head^2
litcc 4 years ago committed by GitHub
commit b4c0f1f018
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 127
      app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
  2. 64
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  3. 10
      app/src/main/res/layout/item_bookshelf_list.xml
  4. 6
      app/src/main/res/menu/book_source.xml
  5. 1
      app/src/main/res/values-zh-rHK/strings.xml
  6. 1
      app/src/main/res/values-zh-rTW/strings.xml
  7. 1
      app/src/main/res/values-zh/strings.xml
  8. 1
      app/src/main/res/values/strings.xml

@ -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
if (nowSpeak == 0) { ReadAloud.httpTTS?.let {
downloadAudio() val fileName = md5SpeakFileName(it.url, AppConfig.ttsSpeechRate.toString(), contentList[nowSpeak])
} else { if (nowSpeak == 0) {
val file = getSpeakFile(nowSpeak) downloadAudio()
if (file.exists()) { } else {
playAudio(FileInputStream(file).fd) val file = getSpeakFileAsMd5(fileName)
if (file.exists()) {
playAudio(FileInputStream(file).fd)
} else {
downloadAudio()
}
} }
} }
} }
@ -68,28 +80,54 @@ 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) { ReadAloud.httpTTS?.let {
if (isActive) { contentList.forEachWithIndex { index, item ->
ReadAloud.httpTTS?.let { if (isActive) {
AnalyzeUrl( val fileName =
it.url, md5SpeakFileName(it.url, AppConfig.ttsSpeechRate.toString(), item)
speakText = contentList[index],
speakSpeed = AppConfig.ttsSpeechRate if (hasSpeakFile(fileName)) { //已经下载好的语音缓存
).getByteArray().let { bytes -> if (index == nowSpeak) {
if (isActive) { val file = getSpeakFileAsMd5(fileName)
val file = getSpeakFile(index)
file.writeBytes(bytes) @Suppress("BlockingMethodInNonBlockingContext")
if (index == nowSpeak) { val fis = FileInputStream(file)
@Suppress("BlockingMethodInNonBlockingContext") playAudio(fis.fd)
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 { 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()

@ -130,6 +130,11 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
sortCheck(Sort.Update) sortCheck(Sort.Update)
initLiveDataBookSource(searchView.query?.toString()) initLiveDataBookSource(searchView.query?.toString())
} }
R.id.menu_sort_enable -> {
item.isChecked = true
sortCheck(Sort.Enable)
initLiveDataBookSource(searchView.query?.toString())
}
R.id.menu_enabled_group -> { R.id.menu_enabled_group -> {
searchView.setQuery(getString(R.string.enabled), true) searchView.setQuery(getString(R.string.enabled), true)
} }
@ -187,29 +192,44 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
else -> { else -> {
App.db.bookSourceDao.liveDataSearch("%$searchKey%") App.db.bookSourceDao.liveDataSearch("%$searchKey%")
} }
} }.apply {
bookSourceLiveDate?.observe(this, { data -> observe(this@BookSourceActivity, { data ->
val sourceList = val sourceList =
if (sortAscending) when (sort) { if (sortAscending) when (sort) {
Sort.Weight -> data.sortedBy { it.weight } Sort.Weight -> data.sortedBy { it.weight }
Sort.Name -> data.sortedWith { o1, o2 -> Sort.Name -> data.sortedWith { o1, o2 ->
o1.bookSourceName.cnCompare(o2.bookSourceName) o1.bookSourceName.cnCompare(o2.bookSourceName)
}
Sort.Url -> data.sortedBy { it.bookSourceUrl }
Sort.Update -> data.sortedByDescending { it.lastUpdateTime }
Sort.Enable -> data.sortedWith { o1, o2 ->
var sort = -o1.enabled.compareTo(o2.enabled)
if (sort == 0) {
sort = o1.bookSourceName.cnCompare(o2.bookSourceName)
}
sort
}
else -> data
} }
Sort.Url -> data.sortedBy { it.bookSourceUrl } else when (sort) {
Sort.Update -> data.sortedByDescending { it.lastUpdateTime } Sort.Weight -> data.sortedByDescending { it.weight }
else -> data Sort.Name -> data.sortedWith { o1, o2 ->
} o2.bookSourceName.cnCompare(o1.bookSourceName)
else when (sort) { }
Sort.Weight -> data.sortedByDescending { it.weight } Sort.Url -> data.sortedByDescending { it.bookSourceUrl }
Sort.Name -> data.sortedWith { o1, o2 -> Sort.Update -> data.sortedBy { it.lastUpdateTime }
o2.bookSourceName.cnCompare(o1.bookSourceName) Sort.Enable -> data.sortedWith { o1, o2 ->
var sort = o1.enabled.compareTo(o2.enabled)
if (sort == 0) {
sort = o1.bookSourceName.cnCompare(o2.bookSourceName)
}
sort
}
else -> data.reversed()
} }
Sort.Url -> data.sortedByDescending { it.bookSourceUrl } adapter.setItems(sourceList, adapter.diffItemCallback)
Sort.Update -> data.sortedBy { it.lastUpdateTime } })
else -> data.reversed() }
}
adapter.setItems(sourceList, adapter.diffItemCallback)
})
} }
private fun showHelp() { private fun showHelp() {
@ -483,6 +503,6 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
} }
enum class Sort { enum class Sort {
Default, Name, Url, Weight, Update Default, Name, Url, Weight, Update, Enable
} }
} }

@ -11,8 +11,8 @@
<io.legado.app.ui.widget.image.CoverImageView <io.legado.app.ui.widget.image.CoverImageView
android:id="@+id/iv_cover" android:id="@+id/iv_cover"
android:layout_width="60dp" android:layout_width="66dp"
android:layout_height="80dp" android:layout_height="90dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="12dp" android:layout_marginBottom="12dp"
@ -97,7 +97,7 @@
android:textSize="13sp" android:textSize="13sp"
app:layout_constraintBottom_toTopOf="@+id/tv_read" app:layout_constraintBottom_toTopOf="@+id/tv_read"
app:layout_constraintLeft_toRightOf="@+id/iv_author" app:layout_constraintLeft_toRightOf="@+id/iv_author"
app:layout_constraintRight_toLeftOf="@id/fl_has_new" app:layout_constraintRight_toRightOf="@id/fl_has_new"
app:layout_constraintTop_toBottomOf="@+id/tv_name" app:layout_constraintTop_toBottomOf="@+id/tv_name"
tools:ignore="RtlSymmetry" /> tools:ignore="RtlSymmetry" />
@ -126,7 +126,7 @@
android:textSize="13sp" android:textSize="13sp"
app:layout_constraintBottom_toTopOf="@id/tv_last" app:layout_constraintBottom_toTopOf="@id/tv_last"
app:layout_constraintLeft_toRightOf="@+id/iv_read" app:layout_constraintLeft_toRightOf="@+id/iv_read"
app:layout_constraintRight_toLeftOf="@id/fl_has_new" app:layout_constraintRight_toRightOf="@+id/fl_has_new"
app:layout_constraintTop_toBottomOf="@+id/tv_author" /> app:layout_constraintTop_toBottomOf="@+id/tv_author" />
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
@ -154,7 +154,7 @@
android:textSize="13sp" android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/iv_last" app:layout_constraintLeft_toRightOf="@+id/iv_last"
app:layout_constraintRight_toLeftOf="@id/fl_has_new" app:layout_constraintRight_toRightOf="@id/fl_has_new"
app:layout_constraintTop_toBottomOf="@+id/tv_read" /> app:layout_constraintTop_toBottomOf="@+id/tv_read" />
<View <View

@ -50,6 +50,12 @@
android:id="@+id/menu_sort_time" android:id="@+id/menu_sort_time"
android:checkable="true" android:checkable="true"
android:title="@string/sort_by_lastUpdateTime" /> android:title="@string/sort_by_lastUpdateTime" />
<item
android:id="@+id/menu_sort_enable"
android:checkable="true"
android:title="@string/sort_enable" />
</group> </group>
</menu> </menu>

@ -799,5 +799,6 @@
<string name="import_bookshelf">导入书单</string> <string name="import_bookshelf">导入书单</string>
<string name="pre_download">预下载</string> <string name="pre_download">预下载</string>
<string name="pre_download_s">预先下载10章正文</string> <string name="pre_download_s">预先下载10章正文</string>
<string name="sort_enable">启用排序</string>
</resources> </resources>

@ -800,5 +800,6 @@
<string name="import_bookshelf">导入书单</string> <string name="import_bookshelf">导入书单</string>
<string name="pre_download">预下载</string> <string name="pre_download">预下载</string>
<string name="pre_download_s">预先下载10章正文</string> <string name="pre_download_s">预先下载10章正文</string>
<string name="sort_enable">启用排序</string>
</resources> </resources>

@ -803,5 +803,6 @@
<string name="import_bookshelf">导入书单</string> <string name="import_bookshelf">导入书单</string>
<string name="pre_download">预下载</string> <string name="pre_download">预下载</string>
<string name="pre_download_s">预先下载10章正文</string> <string name="pre_download_s">预先下载10章正文</string>
<string name="sort_enable">启用排序</string>
</resources> </resources>

@ -806,5 +806,6 @@
<string name="import_bookshelf">导入书单</string> <string name="import_bookshelf">导入书单</string>
<string name="pre_download">预下载</string> <string name="pre_download">预下载</string>
<string name="pre_download_s">预先下载10章正文</string> <string name="pre_download_s">预先下载10章正文</string>
<string name="sort_enable">启用排序</string>
</resources> </resources>

Loading…
Cancel
Save