V1.4.0: 修复断网重连后无法下载 + 网络状态检测维护
根因(断网重连后再也下载不了、日志却显示"歌曲源获取成功"):
SongApiClient.clearTokenCache() 一直是**空实现**。JS 侧 window.api.tokens 与
_apiInitPromise 因此被一直复用; 而歌曲源按「出口 IP + device」识别、试用窗口
也记在出口 IP 上(见 Eac19WindowGuard), 断网/重连(尤其换出口 IP)后旧 token 已失效 ——
取址这一步看起来成功, 真正下载却 403/失败。
1) 网络状态检测(新增 KtvNetworkMonitor)
走 ConnectivityManager 回调(不轮询)维护全局 online, 状态变化才回调主线程;
全局可读 online 供下载层判断失败是否由断网引起。
2) 播放页右上角「网络未连接」提示
全屏播放页控制层 header 内、标题右侧新增红色徽标(不遮挡右上角二维码),
断网显示、恢复自动隐藏。
3) 网络恢复/换网后的统一维护 refreshNetworkState()
- 重建歌曲源会话: clearTokenCache() 真正下发 KtvJsBridge.resetApiSession()
(置空 window.api 与 _apiInitPromise, 下次取址重新构造 API 实例、回到默认源顺序
并重新登录拿新 token);
- 复位 Eac19WindowGuard 窗口守卫; 重解析网卡 MAC + 刷新日志上下文;
- 重启本地点歌服务并刷新二维码;
- 自动重试断网期间失败的下载(最多 8 首)。
断网恢复走该路径(带提示), 换网换 IP 也走同一路径(静默)。
4) 下载层网络失败跟踪(SongOkDownloadManager)
结合 KtvNetworkMonitor.online 与异常类型(UnknownHost/Connect/SocketTimeout/
NoRouteToHost/SSL/EOF)及 HTTP 0 判定网络类失败, 记入 networkFailed;
成功后清除、手动取消不入集合; retryNetworkFailed() 供恢复时调用。
5) 顺带修掉一个隐患: 离线时 localIp() 为空会让 remoteNetworkMonitor 每 5s 误判
"IP 变化"而反复重启本地点歌服务; 现已加空值短路, 离线只等网络回调。
版本号 1.3.9 -> 1.4.0(versionCode 15 -> 16)。按约定本次不发布 Release, 仅本地打包测试。
This commit is contained in:
@@ -47,6 +47,22 @@ class KtvJsBridge(context: Context) {
|
||||
// sn 沿用抓包注册值, 保证 mac 变化后仍能对上原有账号体系。
|
||||
private val DEVICE_MAC: String get() = DeviceMac.effective()
|
||||
private const val DEVICE_SN = "2d7bb4a7d7f7"
|
||||
|
||||
/**
|
||||
* 断网重连后重置歌曲源会话。
|
||||
*
|
||||
* 源站按「出口 IP + device」识别,试用窗口也记在出口 IP 上(见 Eac19WindowGuard)。
|
||||
* 断网/重连(尤其换了出口 IP)后旧 token 已失效,继续复用会表现为
|
||||
* **「取址看起来成功、实际下载 403/失败」**。这里把整个 API 实例与初始化 Promise
|
||||
* 都清掉:下一次取址会重新 new KtvApi(回到默认源顺序)并重新登录拿新 token。
|
||||
*/
|
||||
private const val RESET_SESSION_JS = """(function(){
|
||||
try {
|
||||
window.api = null;
|
||||
window._apiInitPromise = null;
|
||||
} catch (e) {}
|
||||
return 'ok';
|
||||
})()"""
|
||||
}
|
||||
|
||||
private val appContext = context.applicationContext
|
||||
@@ -119,6 +135,23 @@ class KtvJsBridge(context: Context) {
|
||||
|
||||
fun isDestroyed(): Boolean = destroyed
|
||||
|
||||
/**
|
||||
* 重置 JS 侧歌曲源会话(断网恢复 / 下载重试时调用)。
|
||||
*
|
||||
* 置空 `window.api` 与 `window._apiInitPromise`,下一次取址会重新构造 API 实例
|
||||
* (恢复默认源顺序)并重新登录,避免继续使用失效 token。
|
||||
*/
|
||||
fun resetApiSession() {
|
||||
val view = webView
|
||||
if (!ready || destroyed || view == null) return
|
||||
mainHandler.post {
|
||||
runCatching {
|
||||
view.evaluateJavascript(RESET_SESSION_JS, null)
|
||||
Log.i(TAG, "reset source api session")
|
||||
}.onFailure { Log.w(TAG, "reset source api session failed", it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun getSongUrl(
|
||||
musicNo: String,
|
||||
resolution: String,
|
||||
|
||||
Reference in New Issue
Block a user