fix: resolve emulator crash - thread safety and icon fallback
- NativeSearch: move onResult/onError callbacks outside withContext(Dispatchers.IO) to prevent CalledFromWrongThreadException - SearchStrategy: change callback types to suspend to enable proper coroutine chaining - SearchCoordinator: remove leaked CoroutineScope, rely on suspend callback chaining for fallback flow - Resources: add mipmap-hdpi/mdpi/xhdpi/xxhdpi icon fallbacks for API < 26 devices
This commit is contained in:
84
app/src/main/java/com/videoapp/tv/engine/NativeSearch.kt
Normal file
84
app/src/main/java/com/videoapp/tv/engine/NativeSearch.kt
Normal file
@@ -0,0 +1,84 @@
|
||||
package com.videoapp.tv.engine
|
||||
|
||||
import com.videoapp.tv.data.SearchResult
|
||||
import com.videoapp.tv.data.SiteConfig
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jsoup.Connection
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
class NativeSearch : SearchStrategy {
|
||||
|
||||
override fun getName() = "NativeSearch"
|
||||
|
||||
override suspend fun search(
|
||||
keyword: String,
|
||||
config: SiteConfig,
|
||||
onResult: suspend (List<SearchResult>) -> Unit,
|
||||
onError: suspend (String) -> Unit
|
||||
) {
|
||||
try {
|
||||
val results = withContext(Dispatchers.IO) {
|
||||
val url = config.getFullSearchUrl()
|
||||
val connection: Connection = Jsoup.connect(url)
|
||||
.data(config.keywordParam, keyword)
|
||||
.timeout(15000)
|
||||
|
||||
config.extraParams.forEach { (key, value) ->
|
||||
connection.data(key, value)
|
||||
}
|
||||
|
||||
val method = config.searchMethod.uppercase()
|
||||
val doc = if (method == "GET") {
|
||||
connection.get()
|
||||
} else {
|
||||
connection.post()
|
||||
}
|
||||
|
||||
val items = doc.select(config.resultSelector)
|
||||
val resultsList = mutableListOf<SearchResult>()
|
||||
|
||||
for (item in items) {
|
||||
try {
|
||||
val titleEl = item.selectFirst(config.titleSelector) ?: continue
|
||||
val linkEl = item.selectFirst(config.linkSelector)
|
||||
val coverEl = item.selectFirst(config.coverSelector)
|
||||
val categoryEl = item.selectFirst(config.categorySelector)
|
||||
val dateEl = item.selectFirst(config.dateSelector)
|
||||
|
||||
val title = titleEl.text().trim()
|
||||
val detailUrl = linkEl?.attr("href")?.trim() ?: ""
|
||||
val coverUrl = coverEl?.attr("src")?.trim() ?: ""
|
||||
val category = categoryEl?.text()?.trim() ?: ""
|
||||
val date = dateEl?.text()?.trim() ?: ""
|
||||
|
||||
if (title.isNotEmpty() && detailUrl.isNotEmpty()) {
|
||||
resultsList.add(
|
||||
SearchResult(
|
||||
title = title,
|
||||
coverUrl = if (coverUrl.startsWith("http")) coverUrl
|
||||
else config.baseUrl.trimEnd('/') + "/" + coverUrl.trimStart('/'),
|
||||
detailUrl = if (detailUrl.startsWith("http")) detailUrl
|
||||
else config.baseUrl.trimEnd('/') + "/" + detailUrl.trimStart('/'),
|
||||
category = category,
|
||||
date = date
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// skip malformed items
|
||||
}
|
||||
}
|
||||
resultsList
|
||||
}
|
||||
|
||||
if (results.isEmpty()) {
|
||||
onError("未找到结果")
|
||||
} else {
|
||||
onResult(results)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
onError("NativeSearch 失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user