接入自建歌曲源服务器:源列表自建优先 + App 令牌 + 媒体地址允许清单 + 推送默认目标(V1.6.1/44,未发布)
源列表(自建源优先、官方源兜底) - SongUrlResolver.DEFAULT_HOSTS 与 ktv_api.js 的 CONFIG.HOSTS/HOST 改为 https://mdmuse.kasugano.cn + http://zk.ktv1.top:6677; - 新增设置项 KtvStore.sourceHosts(一行一个可编辑)与纯 JVM SourceHosts(解析/归一化/ 自建源判定),App 与 JS 都以它为准(JS 走既有配置注入:KtvJsBridge 覆写 window.KtvApiConfig)。 App 令牌(自适应,不破坏官方源/老服务端) - 新增纯 JVM AppToken/AppTokenSession(取/缓存/404-405 记忆不支持/401 清缓存重试一次) 与胶水 AppTokenClient(同一条干净裸 Socket、8 秒超时、失败只写 MiliLog.local*); - /music/do.php 与 /push_download 前先对当前源 GET /token,取到即带 X-App-Token (expires_in 提前 30 秒过期);官方源 404/405 记住后不再尝试;401 只重试一次; - /i.php、/music/do.php 的参数与签名算法一个字未改(令牌只加一个请求头); - JS 侧同步:android.appToken(host)/invalidateAppToken(host) + 401 立即重试一次。 媒体地址闸门(自建源适配) - 新增设置项 KtvStore.allowedMediaHosts(默认 md.kasugano.cn + 192.168.100.232); - AdVideoFilter.isTrustedSongUrl(url, musicno, allowedHosts) 只对命中主机放行"非域名" 限制(裸 IP);黑名单 wb66/demo/ac16.vip/ad_files/my_ad_video/pub-*.r2.dev/ss 与 ".ts 文件名必须等于编号"一律不变且优先级更高(命中清单也拒); - 四处调用点同口径:KtvJsBridge.isTrustedDownloadUrl、SongApiClient.isLikelyDemoUrl、 JS isDemoUrl、下载兜底。 点歌推送默认目标 - PushDownload.effectiveServerUrl:显式配置优先;留空且首源是自建源则默认用它; off/none/关闭 等显式关闭;pushToken 逻辑不变(服务端接受 X-App-Token 或 push_token)。 版本与测试 - versionName 1.6.1 / versionCode 44;KTV_BRIDGE_API 13 -> 14(JS 两处 + KtvJsBridge 两处); - 设置页「数据设置」新增「歌曲源」「媒体地址允许清单」两项; - 新增单测 AppTokenTest(11)/SourceHostsTest(7)/MediaHostAllowlistTest(8),共 65 例全过。
This commit is contained in:
@@ -26,9 +26,12 @@ object SongApiClient {
|
||||
bridge = KtvJsBridge(context)
|
||||
// 原生兜底取址通道:复用桥的「干净请求」裸 Socket(与 JS 同一条路,风控表现一致),
|
||||
// 但**不依赖 WebView 是否可用** —— 老盒子上 WebView 不可用时唯一能取到地址的路径。
|
||||
SongUrlResolver.install { url -> bridge?.rawCleanGet(url) }
|
||||
// 第二参是自建源的 App 令牌(官方源传 null,请求形态与改造前逐字节一致)。
|
||||
SongUrlResolver.install { url, appToken -> bridge?.rawCleanGet(url, appToken) }
|
||||
// api.php 取配置走 POST(原版形态);桥不可用时为 null,此时跳过配置拉取。
|
||||
SongUrlResolver.installPost { url, form -> bridge?.rawCleanPost(url, form) }
|
||||
// App 令牌(自建源 `GET /token`)也走同一条干净 GET:与取址共用缓存与"该源不支持"记忆。
|
||||
AppTokenClient.install { url -> bridge?.rawCleanGet(url, null, AppToken.TIMEOUT_MS) }
|
||||
bridge?.init { ok ->
|
||||
ready = ok
|
||||
// 失败原因由 KtvJsBridge.markUnavailable() 统一上报(只记一次),此处不再重复记日志。
|
||||
@@ -161,11 +164,15 @@ object SongApiClient {
|
||||
* ⚠️ host 一律用 [AdVideoFilter.hostOf](**已剥离 query/fragment,且 URI 解析失败会退化**):
|
||||
* 旧实现直接 `java.net.URI(url).host`,query 里出现 `|`/空格这类字符时 `URI()` 抛异常,
|
||||
* 被 `?: return true` 当成 demo —— 合法真源地址会因此被拒("取址失败"的隐蔽来源)。
|
||||
*
|
||||
* ⚠️ 裸 IP 不再一律判 demo:命中媒体地址允许清单([AdVideoFilter.isAllowedMediaHost])时
|
||||
* 放行 —— 直连内网自建源时 DB 里的 `cloud_url` 就是裸 IP。
|
||||
*/
|
||||
private fun isLikelyDemoUrl(url: String): Boolean {
|
||||
val host = AdVideoFilter.hostOf(url) ?: return true
|
||||
// 纯 IP 地址 → 高概率 demo
|
||||
if (host.matches(Regex("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"))) return true
|
||||
// 纯 IP 地址 → 高概率 demo(**允许清单里的主机除外**)
|
||||
if (host.matches(Regex("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$")) &&
|
||||
!AdVideoFilter.isAllowedMediaHost(host)) return true
|
||||
// Cloudflare R2 demo bucket (pub-*.r2.dev)
|
||||
if (host.matches(Regex("^pub-[a-z0-9]+\\.r2\\.dev$"))) return true
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user