zijunchiang
|
87aa979abd
|
合并自建歌曲源支线(selfhost-source)+ 版本号按约定修正为 1.6.0
版本号:
- 1.5.11 越界(规则 3 规定 patch ≤ 10)→ 修正为 1.6.0(进位到 minor、patch 归 0)
- versionCode 43 不变:43 从未对外发布,本次修正只是把命名拉回约定
合并支线(原在工作区根目录、App 仓库之外,DESIGN.md 自称"支线"):
- selfhost-source/server/ 自建歌曲源服务器(纯 Python 标准库、零 pip 依赖)
- selfhost-source/deploy/、DESIGN.md、LIBRARY.md、build_inventory.py
- selfhost-source/inventory/ 小清单(playlists.csv、song-stats.md 等)
- ⚠️ inventory 生成物(songs-*.csv / singers-top.csv,共 84MB)不入库,
已写入 .gitignore;需要时用 build_inventory.py 依据 muse.db 重新生成
同时入库的 App 侧支线改动(此前为工作树未提交状态):
- Whoami.kt(whoami 设备心跳)、DeviceIdentity.kt(设备身份伪装)
- PushDownload.kt / PushDownloadClient.kt(点歌推送)
- AdVideoFilter 带 query/fragment 的地址闸门;KTV_BRIDGE_API 11 → 13
- 修 ktv_api.js 的 global.KtvBridgeApiVersion(12 → 13,与注释及 Kotlin 两处校验一致)
- 对应单测:WhoamiTest / DeviceIdentityTest / PushDownloadTest / AdVideoFilterTest(共 39 例)
文档:
- DESIGN.md 的"在 App 仓库之外"约定改为"已并入仓库统一版本化"
- AGENTS.md 版本号行更新为 1.6.0/43,并新增支线并入说明
验证:assembleRelease + testReleaseUnitTest 全过(39 例)
|
2026-10-14 02:06:28 +08:00 |
|
zijunchiang
|
e060ae5ebe
|
源列表只保留 zk.ktv1.top:6677, 删除其余全部源
实测(同一出口 IP, 编号 7009126, 三节点同时刻对照):
zk.ktv1.top:6677 → i.php 200(与 e.ac19.cn 逐字段一致) + do.php 真源 7009126.ts / 7303235.ts ✅
e.ac19.cn → 同款 i.php + 真源 7009126.ts ✅
124.223.20.123:58→ 同款 i.php, 但 do.php 返回 0021.ts(非本曲) ❌
三者同一后台; 昨日 e.ac19.cn/124.223.20.123:58 的 HTTP 403 空响应为临时额度/风控, 今日已恢复
改动:
- SongUrlResolver.DEFAULT_HOSTS 与 ktv_api.js CONFIG.HOSTS(含 HOST) 均只留 http://zk.ktv1.top:6677
- KTV_BRIDGE_API 11 → 12 (ktv_api.js 两处 + KtvJsBridge.kt 两处校验)
- ensureConfig(): api.php 失败也写负缓存(该节点无 api.php, 否则每首歌白请求一次)
- MainActivity 日志不再写死旧源名, 改用 SongUrlResolver.lastHost
- AdVideoFilter.AD_HOSTS 不变(管"返回地址"的 host, 与"取址入口"无关)
版本号按规则 3: 1.5.9(41) → 1.5.10(42); 本地打包(未发布)
验证: assembleRelease + 单元测试(9 例全过); 解包确认 APK 内 ktv_api.js 只含新源且标记为 12
|
2026-10-09 21:42:27 +08:00 |
|
zijunchiang
|
4576a89f9a
|
按 HAR 抓包对齐原版协议: 补 api.php 取配置 + 识别业务码 403 + 按试用配额限源
依据用户提供的 HAR(e.ac19.cn, Reqable): 原版客户端是三步
1) POST /api.php action=get_config&device_id=<mac> (表单 + Dalvik UA)
2) GET /i.php 参数与 App 完全一致
3) GET /music/do.php 参数与 App 完全一致, 返回真源 CDN 地址
发现并修复三处差异:
1. 我们从未调 api.php(原版取址前必调)
→ KtvJsBridge.rawCleanPost(): 干净 POST + Content-Type 表单 + Dalvik UA
(UA 由本机 Build.VERSION.RELEASE/MODEL/ID 拼出, 与 HAR 同形态)
→ SongUrlResolver.ensureConfig(): 缓存 30 分钟, 桥不可用则跳过
→ SongApiClient 注入 POST 通道
2. 风控拒绝的真实形态是 HTTP 200 + 业务码 403
{"code":403,"msg":"请求被拒绝:检测到异常行为"} —— 不是 HTTP 403
→ i.php / do.php 两处业务码 403 都置 sawForbidden(诊断不再误判为"没有地址")
3. 无视试用配额: api.php 给出 trial_times=2 / trial_duration=5
→ 一次取址最多尝试 trial_times 个源(原先 9 个源全扫会把额度瞬间打光)
请求头分工与 HAR 一致: i.php/do.php 只有 Host+Accept 的干净 GET;
仅 api.php 带表单 Content-Type 与 Dalvik UA。
日志: 远程一条 SOURCE-CONFIG trial_enabled=… trial_times=…; 原始 JSON/卡密提示只写本地。
本机实测: api.php 返回与 HAR 相同配置; do.php 返回 code=403 检测到异常行为
(本出口 IP 处于风控/未授权: network_verification=true + 卡密机制, 额度按 IP 计)。
版本号按规则 3: 1.5.8(40) → 1.5.9(41); 按要求未打包
验证: compileReleaseKotlin + 单元测试(9 例全过)
|
2026-10-08 03:56:59 +08:00 |
|
zijunchiang
|
745a180258
|
精简远程日志: 每次事件一条汇总, 细节只留本地
- MiliLog 新增只写本地通道 local/localWarn/localError
(队列改为 Entry(local, remote), remote 为 null 即只写本地、跳过 HTTP)
- 本地日志文件改为纯文本 yyyy-MM-dd HH:mm:ss.SSS LEVEL text, 单行上限 4000
(远程仍按 900 截断) —— 本地是"详细记录", 要人能直接读
- 逐源/逐次日志全部改本地:
· SongUrlResolver: i.php/do.php 异常、token 失败、do.php code、HTTP 4xx/5xx、
响应非 JSON、冷却跳过、403 提示、广告示例 URL
· KtvJsBridge: JS 控制台转发(量最大)、单次 HTTP-<status>、NET-ERR
- 结果: 每次取址远程恰好一条
成功 NATIVE-SONG 取址成功… / 失败 NATIVE-SONG 全部源失败… / 全广告 SOURCE-BLOCKED…
(改前 9 个源逐个上报, 一次取址 9+ 条)
- 保留: JS-BRIDGE…(状态变化)、点歌失败…(每首一行)、SONG-AD…(广告拦截) 仍上报远程
- AGENTS: 写日志约定新增第 5 条(远程=结论、本地=过程), JS 日志规则改为只进本地
版本号按规则 3: 1.5.7(39) → 1.5.8(40); 按要求未打包
验证: compileReleaseKotlin + 单元测试(9 例全过)
|
2026-10-08 03:42:03 +08:00 |
|
zijunchiang
|
97ca1ae28e
|
V1.5.5: 加入源 a.ktv8.my;"所有源失效"改为明确弹窗提示
一、加入歌曲源 a.ktv8.my(置于 gz.ac16.vip 之后)
实测(代理修复后,DNS 直连真实 IP 20~42ms):
- 真实 IP 154.219.102.181 —— 与 w345.my **同一台服务器**(同一后台的兄弟入口);
- GET / → 200(917B,主机面板默认占位页);
- i.php → 200,170B,与其它 5 个入口响应**逐字节一致**:
code=200 / msg=ok / ordertype=0 / end_date=2026-10-05 14:58:21 / retry=5 / heart_time=5400,token 正常下发;
- 自身媒体路径 /<编号>.ts 与 /ad_files/my_ad_video.ts 均 404(138B,与 e.ac19.cn 的 404 逐字节相同)
→ 它不托管广告媒体;
- ⚠️ do.php 取址行为未能定性: 测试期间本机出口 IP(与门店设备同一个 188.253.112.231)
的配额被反复探测耗尽,表现为"成功一两次后长时间静默丢包"(与代码里记录的
trial_times:2 × trial_duration:5 吻合)。故置于次位,由运行时升降序自动调整。
KTV_BRIDGE_API 10 → 11(ktv_api.js 两处标记 + KtvJsBridge 两处校验)。
二、"所有源都取不到地址"给出明确提示
新增 Eac19WindowGuard.onAllFailed ← noteAllSourcesFailed(),由 SongUrlResolver.resolve()
在**本轮彻底失败**时调用(403 / 只返回广告 / 网络异常都算),弹窗文案:
「远程源已全部失效,等待新版本修复」
- 与既有的 onBlocked(风控 403 → "换公网 IP")常常同时触发,因此在 onBlocked 处理器里
用 lastAllFailedAt() 做 60 秒去重,不会同时弹两个框;两者均 15 分钟节流。
- 以前这里只有一句"无法生成下载地址"的 toast,用户看起来像界面没反应。
版本 1.5.4(36) → 1.5.5(37);签名证书不变。测试 9 例全通过。
|
2026-10-07 07:53:48 +08:00 |
|
zijunchiang
|
c1fd0fa319
|
V1.5.4: 新增可用源 gz.ac16.vip;修复"点歌失败不入下载列表";下载列表删除按状态区分
一、新增歌曲源 gz.ac16.vip(置于源列表首位)
实测(DoH 取真实 IP 154.222.26.244,绕开本机网关 fake-IP 干扰):
- i.php: HTTP 200,下发 token,end_date=2027-10-05(比现用镜像源的 2026-10-05 更晚);
- music/do.php: 对 7303235/7200188/8614960 分别返回各自不同的真源地址
(http://txog.ktvsky.com/.../<编号>.ts?token=... / http://download.origjoy.com/.../<编号>.ts?sign=...),
文件名均等于点歌编号;
- 这些地址能真实下载:HTTP 206 video/MP2T,逐曲内容各不相同,
TS 流同步字节按 188 字节对齐(41/43 包同步);
- 对照:其余 7 个入口对每首歌(含不存在的编号)统一返回同一段
https://wb66.cn/f/d/7b7c9/10188555.ts(广告/测试资源),主源 w.w345.my/i.php 直接 403。
因此把 gz.ac16.vip 加入源列表:ktv_api.js 的 CONFIG.HOSTS 与
SongUrlResolver.DEFAULT_HOSTS 各一处(均置于首位)。
⚠️ 边界:该域名**自己**的 /<编号>.ts 永远是同一段广告,所以它继续留在
AdVideoFilter.AD_HOSTS(媒体层拦截);广告黑名单只作用于"返回地址的 host",
不作用于"取址入口的 host",两者不可混淆,否则会误杀唯一可用源。
KTV_BRIDGE_API 9 → 10(ktv_api.js 两处标记 + KtvJsBridge 两处校验),保证设备热更新到新脚本。
二、修复"点歌失败时歌曲不出现在下载列表"
根因:DownloadTask 只在 onDownloadStart 里创建,所以"取址失败(无法生成下载地址)"这类
还没真正开始下载的失败**不会留下任何记录**,下载列表里什么都没有。
修复:downloadSong() 在**发起下载之前**就登记任务(状态「排队中」),
失败时由既有回调就地改成「失败:原因」,条目保留在列表里,可看原因、可重试、可清理。
三、下载列表删除功能(按状态区分语义)
- 有本地文件 → 「删除文件」(confirmDeleteLocalFile,同时清掉下载记录);
- 失败/进行中(无文件)→ 「移出下载列表」(confirmRemoveDownloadEntry):
只删记录 + stateDatabase.removeDownload,**绝不动任何歌曲文件**
(历史上"顺手清缓存"删光公放曲库的教训见 AGENTS);
- 「更多」菜单在下载列表内额外提供「移出列表」;
- SongListAdapter 的「更多」图标由装饰改为**可点/可聚焦**(遥控器可达,不再只能长按)。
版本 1.5.3(35) → 1.5.4(36);签名证书不变。测试 9 例全通过。
|
2026-10-04 00:04:20 +08:00 |
|
zijunchiang
|
96bad3bd68
|
V1.5.3: 远程日志不再静默失效(+本地日志文件);补 403 受限识别
现场两个问题分别定位:
一、"歌曲源全部失效" —— 已确认是**源站对该出口 IP 的授权/风控**,非客户端可修
- 实测(本机与设备同一出口 188.253.112.231):
· 主源 http://w.w345.my/i.php → **HTTP 403 Forbidden**(nginx 拦截页);
· 镜像源(desktok.top / mm.kk456.top / w.w345.my:66 / z.w345.my / b.sktv.top / w345.my)
返回 200,但对**每一首歌(含不存在的编号 1111111/9999999)都返回同一个广告/测试资源**
https://wb66.cn/f/d/7b7c9/10188555.ts;
· 换全新随机设备身份(mac/sn)结果完全相同 → 限制在**出口 IP** 层面,与设备无关;
· 7 host × ls=1~3 × (720/1080 × h265 0/1) 全组合探测:0 个真源;
· 旧的试用领取接口 api.php 已 **404**,App 无法自行领取窗口。
- 结论:源站侧无有效授权(i.php 的 ordertype=0、mwsLogin 的 license_box_count=0 /
license_duration=0 均指向此)。**需更换公网 IP 或与歌曲源服务方确认续订**。
我们拒绝这些地址是正确行为(否则就是 1.4.10 修过的"广告被当成歌曲播放")。
二、"手动更新数据库后远程日志失效" —— 数据库没问题,日志客户端有隐患,两者都已处理
- 实测新曲库 day=261003(md5 300AFD957BF332F02C17ED94A9F4E4CF,431,530,367 字节,
解压 1.01GB、69.5 万首): 用 App 的**真实 SQL**(SONG_SELECT/推荐/中文搜索/拼音搜索/
歌手检索/歌单/语种/歌手列表)逐条跑通 → **结构完全兼容,更新曲库不会破坏 App**。
- 日志侧修复(原来确实可能"静默失效"):
· MiliLog.init 改为**可重复调用并自动重启发送线程**(旧实现 `if (started.getAndSet(true)) return`,
线程一旦退出就永久不再发送且无任何迹象);
· sendLoop 整体加 catch(Throwable) 兜底,任何异常都不允许发送线程退出;
· 新增**本地日志文件** <存储根>/logs/ktv.log(>1MB 滚动为 .prev),
远程日志不可用时现场可直接取文件;启动打 APP-INIT 行给出路径;
· MiliLog.init 提前到 KtvApplication.onCreate,早于 MainActivity,早期崩溃也有日志。
- 补 403 识别: KtvJsBridge 的 403 判定从仅 /music/do.php 扩展到 /i.php 也认
(主源 i.php 就是 403);SongUrlResolver 记录本轮是否见过 403,全部失败且见过 403 时
同样走 Eac19WindowGuard 提示换 IP。
AdVideoFilter: 仅保留高区分度标记 wb66,去掉过于宽松的 demo(避免误伤真源 URL 里的随机 token)。
版本 1.5.2(34) → 1.5.3(35);签名证书不变。测试 9 例全通过。
|
2026-10-03 21:03:07 +08:00 |
|
zijunchiang
|
eadc2d4de1
|
V1.5.0: 修复"公放切歌失效"——上一版清空歌曲缓存把公放曲库删了(新设备回归)
现象: 新设备公放时按「切歌」没有任何反应。
根因(我上一版引入的回归):
- 1.4.10 为了让"被当成歌曲缓存下来的广告视频"消失,在启动时做了一次
**整目录清理**(删 cloud-song 下所有 .ts/.ls/.mp4/.mkv)。
- 但 MuseDatabase.songDirectories() **只有 cloudSongsDir 一个目录**,
SongOkDownloadManager.getLocalFile() 也落在那里 —— 那等于**把公放曲库整个删掉**。
- 后果链: 库空 → playRandomLocalSong() 里 allLocalSongs 为空 →
RandomPlayQueue.next() 返回 null → 状态栏"本地暂无可播放歌曲"、currentSong 保持 null
→ 按「切歌」时 advancePlayback 落进「取消随机续播、进入空闲态」分支 → 按钮无反应。
(RandomPlayQueue.next 本身有"只剩一首也能播"的兜底,队列逻辑没问题。)
修复:
- 删除整目录清理,改为 purgeInjectedAdDuplicatesOnce(): **只删能证明是注入广告的文件** ——
广告对每首歌下发同一个视频,特征是"多首不同歌曲的本地文件内容完全相同"
(按文件大小 + 头部 4MB 指纹分组,只清理 size>=2 的完全相同组)。
正常歌曲内容各不相同,绝不会被误删;公放曲库得以保留。
同时按用户要求,复查"老设备适配期间改动对新设备的影响",另修正两处:
- SongApiClient.getSongDownloadUrl 的主线程分支: 1.4.7 起会先走 resolveNatively()
(最多 7 个源 × 8 秒超时的阻塞网络请求)—— 一旦有主线程调用者就会卡死 UI 到 ANR。
现在主线程只做本地 DB 兜底,网络取址一律留在后台线程。
- KtvJsBridge 裸 Socket 的 HTTPS 分支: 1.4.6 改用了无参 createSocket(),拿到的 SSLSocket
**不带主机名**(影响 SNI 与证书主机名校验,共享 CDN/反代多证书时会握手失败)。
改为先 connect 普通 socket(保留显式连接超时)再 createSocket(plain, host, port, true)。
- buildDownloadUrl 的文件名比对只在点歌编号"可信"时启用: 只有从 song.filename 推出的编号才
参与比对;song.id 可能是数据库主键,拿它比对会误杀正常地址(真源被判"文件名不符")。
(另已实测 7 个源: 可用的 5 个真源 URL 文件名全部严格等于点歌编号,规则不会误杀。)
顺带记录用户给出的版本号约定(已写入 AGENTS.md):
versionName = <major>.<minor>.<patch>,patch 0~10、minor 1~10;
1.4.10 之后是 1.5.0,1.10.1 之后是 2.1.0;versionCode 始终 +1。
版本 1.4.10(30) → 1.5.0(31);签名证书不变。测试 8 例全通过。
|
2026-09-26 21:25:37 +08:00 |
|
zijunchiang
|
669766ba60
|
V1.4.10: 修复广告过滤失效(gz.ac16.vip 广告被当成歌曲下载播放)
现场事故(新设备 SM-S9180 / Android 16,WebView 正常 → 走 JS 取址):
277 JS [KtvApi] URL OK: 7303235 ls=1 via http://w.w345.my
278 点歌编号=7303235 标题=情歌(HD) 请求源=http://w.w345.my 状态=成功 urlHost=gz.ac16.vip
279 PLAY id=7303235 title=情歌(HD) src=local
即 gz.ac16.vip 的广告视频被当成歌曲下载、缓存并播放。
三处漏洞叠加:
1. ktv_api.js 的 isDemoUrl 不认识广告域名;且"ts 文件名与编号比对"用的是
/\/(\d+)\.ts/,只匹配纯数字名 —— my_ad_video.ts 这类非数字名字直接放行;
2. SongOkDownloadManager.buildDownloadUrl 的**成功分支完全没有广告校验**
(AdVideoFilter.isAdVideo 全项目只在"缓存地址"与"原生解析"两处被调用);
3. SongApiClient.fetchFromDb 只做 isLikelyDemoUrl(纯 IP / r2.dev),挡不住广告域名。
且**内容校验挡不住广告**: 该广告通过 SongFileValidator(≥6MB/≥60s/TS 包完整),
被 isDownloaded() 一直复用,广告会反复播。
修复(单一闸门,全链路把关):
- AdVideoFilter 新增 isTrustedSongUrl(url, musicno):非 http(s)、命中广告特征
(路径 /ad_files/、my_ad_video、**域名 *.ac16.vip**)、或 URL 里 .ts 文件名不等于
点歌编号 → 一律不可信。真源恒为 /<musicno>.ts(已实测 txog.ktvsky.com 与
download.origjoy.com 两个 CDN 域共 6 首全部相符)。
- 该闸门接入全部出入口: JS isDemoUrl(同步加固 + KTV_BRIDGE_API 8→9)、
SongUrlResolver.isValidSongUrl、SongApiClient.fetchFromDb、
以及最终兜底 SongOkDownloadManager.buildDownloadUrl(成功/缓存两条分支)。
- 判定不可信时**同时删除已落盘的歌曲文件**(purgeCachedSongFile),
否则 isDownloaded() 会一直复用那个广告文件。
- 新增 SongOkDownloadManager.purgeLegacySongCacheOnce(): 升级后一次性清理历史遗留的
可疑歌曲缓存(marker: filesDir/song_cache_purged_v1)。只是让歌曲重新下载,不动用户数据。
- 新增回归测试 AdVideoFilterTest(6 例,用日志里的真实 URL 形态,含两个真源 CDN 域);
该测试当场抓到本修复自身的一个 bug:正则把扩展名也捕获进去导致真源被误杀,已修正。
版本 1.4.9(29) 到 1.4.10(30);签名证书不变。
|
2026-09-26 21:17:04 +08:00 |
|
zijunchiang
|
b3b17210f1
|
V1.4.9: 修复启动被误判为"断网恢复"(并补齐 WebView 失效原因取证)
现场日志(1.4.8)显示启动瞬间就出现:
20:46:59.636 NET-UP 设备网络已恢复, 重建歌曲源会话
20:46:59.647 NET-REFRESH reason=net-up retried=0
20:46:59.980 BOOT
即 NET-UP 出现在 BOOT 之前 —— 启动被当成了"断网后恢复"。
根因: KtvNetworkMonitor.start() 末尾无条件 onChange(initial),
注册 ConnectivityManager 回调时系统会回报一次初始状态,上层把它当成了状态跃迁。
而启动流程(onCreate)已经做过 startRemoteServer()/updateMobileQrOverlay()/
refreshLogDeviceContext()/DeviceMac 解析,所以这次"恢复"是纯粹重复劳动,且有副作用:
- SongApiClient.resetSourceSession() 白清掉刚建立的歌曲源会话;
- Eac19WindowGuard.noteUrlOk() 语义错误(还没取到任何地址就标记窗口正常,
可能掩盖真正的歌曲源窗口到期提示);
- 状态栏被写成"网络已恢复"(启动瞬间就显示,用户可见的错误文案)。
修复:
- KtvNetworkMonitor.start/refresh 的回调签名改为 (online, initial),
initial=true 表示"注册时的初始状态";
- MainActivity.onNetworkStateChanged(online, initial): 初始状态**只更新断网角标**,
不做任何恢复动作;恢复动作(重建会话/重启点歌服务/刷新二维码/重试下载)
只在真正的 offline→online 跃迁时执行。
同时补齐 WebView 失效原因的取证(旧日志只有"KtvApi 未导出"这一句,无法定因):
- wrapHtml 注入特性探测: 上报 WEBVIEW ua=... asyncFunction=yes|no promise=...
(用 new Function 探测,避免探测语句自身引起语法错误)。Android 5.x 的 WebView
可能是 Chrome 39~4x,不支持 async/await 会让**整个 ktv_api.js 语法报错** →
window.KtvApi 未定义、桥永远不就绪,这条日志可直接确证;
- window.onerror 上报脚本解析/运行错误(JS-ERROR ... @line:col),语法错误也会触发;
- 合并重复的失败行: "JS-BRIDGE 初始化返回 false" 与
"JS-BRIDGE 判定本机 WebView 不可用" 只保留后者(含原因)。
版本 1.4.8(28) 到 1.4.9(29);签名证书不变。ktv_api.js 未改动,KTV_BRIDGE_API 仍为 8。
|
2026-09-26 21:00:21 +08:00 |
|
zijunchiang
|
906fa210e4
|
V1.4.8: 按现场日志复盘优化取址等待与远程日志(含 15 秒白等修复)
依据用户提供的 1.4.7 现场日志(21 行,5 次点歌全部成功)复盘出四点:
1. 【行为,最大】每次点歌白等 15 秒。
日志里每首歌前都有一条「JS-BRIDGE 15s 未就绪」,紧跟着「NATIVE-SONG 取址成功 用时=3xx~7xxms」——
即取址本身只要 0.3~0.7 秒,但用户从按下点歌到开始下载要等 15.7 秒。
原因: 桥一旦不可用,SongApiClient 每次都仍等满 15 秒。
修复: KtvJsBridge 新增「已判定不可用」状态(markUnavailable/isUnavailable),
首次判定后后续点歌直接走原生通道;桥万一恢复(热更新重载成功)自动清除该状态。
2. 【日志去噪】持久状态只记一次。
原来「15s 未就绪」按每次点歌一条 WARN 输出,一天几百条且无新增信息。
现在只在状态首次变化时记一条 ERROR。
3. 【可定位】补上"为什么不可用"。
旧日志里没有任何 KtvApi 未导出/WebView 初始化失败的行,说明 init 回调根本没触发。
KtvJsBridge.init 增加 20 秒看门狗: onPageFinished 未回调时明确上报
「初始化超时:onPageFinished 未回调或 KtvApi 未导出」;
init 异常与 KtvApi 未导出也统一收敛到 markUnavailable。
4. 【上下文完整】启动关键行不再有 undefined。
旧日志的 STORAGE 行 serverip/clientip/user/traceid 全是 undefined(记在设备上下文之前),
而且 AppPaths.storageFacts() 会真实建目录、写探针、读 /proc/mounts —— 却跑在主线程。
现在把 STORAGE 移入 reportDeviceBootInfo() 的后台线程、在 refreshLogDeviceContext()
与 MiliLog.awaitServerIp() 之后上报: 上下文完整且不再阻塞主线程。
5. 【新增状态样本】每 6 小时一条 HEALTH(版本/在线/可用空间/曲库是否存在及大小/根目录)。
日志此前只有事件没有状态样本,无法排查"随时间变化"的问题(空间变少、曲库丢失、长期离线)。
6. 【字段一致】「请求源=」不再为空: 走原生兜底时 JS 的 reportSongResult 不会触发,
现在 SongUrlResolver 记录 lastHost,SongApiClient.lastSongSourceHost() 依次回退。
AGENTS.md 增补「写日志的五条约定」(只记一次/上下文完整/不做重活/可自我定位/状态样本+事件)。
版本 1.4.7(27) 到 1.4.8(28);签名证书不变。ktv_api.js 未改动,KTV_BRIDGE_API 仍为 8。
|
2026-09-26 20:38:52 +08:00 |
|
zijunchiang
|
1687f87eca
|
V1.4.7: 新增原生取址兜底 —— 修复 WebView 不可用的老盒子无法点歌
证据(用户导出的 1.4.6 远程日志):
- 两条失败记录只有 Kotlin 侧那一行
「点歌编号=... 详情=无法生成下载地址」,
而 JS ...(本版新加的 console 桥)、RAW-ERR、NET-ERR、点歌失败 一条都没有。
- 即 JS 侧从未执行 —— 隐藏 WebView 在这台盒子上从未就绪
(每条失败前正好有约 15s 的「等桥就绪」超时,时间吻合)。
- 于是 SongApiClient 只能回退 DB 的 cloud_url,取不到就报「无法生成下载地址」。
根因: 取址完全依赖 WebView 里的 ktv_api.js;GMUI2.0/Android 5.1.1 上
WebView 不可用(无法导出 window.KtvApi),桥永远 not ready。
修复:
- 新增 SongUrlResolver: 用 Kotlin 复刻 ktv_api.js 的协议
(GET <host>/i.php 取 token → GET <host>/music/do.php 取地址),
镜像多源排序(失败源沉底)与 isDemoUrl 校验(wb66/demo/纯IP/r2.dev/ts编号不一致)。
- 走与 JS 完全相同的裸 Socket「干净请求」通道(KtvJsBridge.rawCleanGet),
请求特征一致故风控表现一致,且不依赖 WebView 是否就绪。
- SongApiClient.getSongDownloadUrl 三处回退点改为:
桥未就绪 / JS 未取到地址 / 主线程调用 → 先走原生解析,再回退 DB。
- 补齐桥的生命周期可观测性: WebView 初始化失败、KtvApi 未导出、
15s 未就绪 均写入远程日志(以前只在 logcat,这就是本轮一开始没有线索的原因)。
已用 App 的真实身份( mac=800b510bf4fd / sn=2d7bb4a7d7f7 / VER=2.0 )
从本机复现协议: i.php 200 → do.php code=200 且返回
http://txog.ktvsky.com/.../480p/7303235.ts (与点歌编号一致),
证明参数顺序与签名算法正确。
版本 1.4.6(26) 到 1.4.7(27);签名证书不变。ktv_api.js 未改动,KTV_BRIDGE_API 仍为 8。
|
2026-09-26 17:08:38 +08:00 |
|
zijunchiang
|
0cffd08789
|
V1.4.6: 点歌取址失败——修裸 Socket 通道两处缺陷 + 打通 JS 侧日志
现象: 曲库正常后点歌失败「详情=无法生成下载地址」,且远程日志里没有任何线索。
排查(本地复现源站协议):
- 用同一套签名从本机请求 i.php / music/do.php 全部正常,能拿到这首歌的真实地址;
换 App 注入的版本号(VER=2.0/VN=4.1.3.03281430) 也正常 → 源站没问题,是设备侧。
- 根因在 KtvJsBridge.rawRequest() 这条裸 Socket 通道(点歌取址正是走它):
1. 连接阶段没有超时: `Socket(host, port)` 不受 soTimeout 约束,遇路由黑洞按系统
TCP 超时(~2min)干等;7 个源叠加远超 JS_REQUEST_TIMEOUT_MS(180s) →
桥返回空 → 「无法生成下载地址」,而日志里连失败都没有(请求还没失败);
2. HTTPS 分支用 SSLSocketFactory.getDefault(),既不受
HttpsURLConnection.setDefaultSSLSocketFactory 影响,也不受
network_security_config 约束 → 老设备缺 ISRG 新根时握手必失败。
修复:
- rawRequest 连接改为显式 connect(timeout): Socket().connect(InetSocketAddress, t);
HTTPS 改用 TlsCompat.sslSocketFactory()(系统信任 ∪ 内置根证书)。
- TlsCompat 暴露 sslSocketFactory(),并按 API 分级: 裸通道始终用增强工厂,
HttpsURLConnection 默认工厂仅在 API<24 接管。
- 裸通道失败上报 RAW-ERR(原来只在 logcat)。
- 打通 JS 侧日志: wrapHtml 注入 console 桥,把 ktv_api.js 的 _log()/console.log
转发到 android.log → MiliLog("JS ...");点歌失败时可通过
「JS ...」逐步过程 + 「点歌失败 ... 详情=」汇总原因定位。
版本 1.4.5(25) 到 1.4.6(26);签名证书不变。ktv_api.js 未改动,KTV_BRIDGE_API 仍为 8。
|
2026-09-26 16:59:43 +08:00 |
|
zijunchiang
|
62216b0fb8
|
V1.4.3: 修复老系统(API<24)不认 Let's Encrypt 新根,导致无法下载曲库
根因(用远程日志的 STORAGE 行 + 本地复现 Muse 协议定位):
- 曲库服务 mconn.cherryonline.cn 与点歌登录 mws.cherryonline.cn 都用 Let's Encrypt 新链
leaf ← YR2 ← Root YR ← ISRG Root X1。
- res/xml/network_security_config.xml 里虽然已把这三个根加为信任锚,但
**该配置只在 Android 7.0(API 24)+ 被系统读取**;Android 5.1.1 上被完全忽略,
系统信任库又没有 ISRG 新根 → SSLHandshakeException: Trust anchor for certification
path not found → login 失败 → fetchVersion()=null → 根本不触发下载,
界面只剩兜底文案「未找到可用曲库」,而失败只写 logcat,远程日志里看不到。
- 这也解释了为什么只有这台老设备出问题(其它设备都 >= API 24)。
修复:
- 新增 TlsCompat: 在 API<24 上把「系统默认信任 ∪ 内置根证书」的复合 X509TrustManager
装到全局 HttpsURLConnection.setDefaultSSLSocketFactory,覆盖 App 内所有 HttpsURLConnection
(曲库同步/JS 热更新下载/更新检查/头像下载)。主机名校验保持系统默认,不放行任意证书。
- WebView 的 TLS 无法用 SSLContext 覆盖(JS 会直连 mws): 在 KtvJsBridge 的
onReceivedSslError 里不盲目 proceed,先用 TlsCompat.isHostTrustedByOurStore(host)
对同一主机独立校验,通过才放行。
- TlsCompat.install 在 KtvApplication.attachBaseContext 调用(早于任何网络请求)。
- 补齐可观测性: 曲库同步每一步失败改为 MiliLog 上报
(MUSE-DB login/sync ...、DB-SYNC download failed/空间不足/成功);
fetchRemoteVersion 失败时设置明确的 bootstrapError,不再只显示兜底文案。
- AGENTS.md 增补「老系统 TLS 根证书」条目。
版本 1.4.2(22) → 1.4.3(23);签名证书不变。
|
2026-09-26 16:06:27 +08:00 |
|
zijunchiang
|
40d1fec719
|
V1.4.1: 修复 Android 5.1.1 设备一打开就闪退(API 守卫缺失)
根因(极米 H1S / MStar 6A928 / GMUI2.0 / Android 5.1.1):
- MainActivity 把 API 23 才有的 android.media.AudioDeviceCallback 写成了
**字段初始化器**(private val micDeviceCallback = object : ...)。
字段初始化在 Activity 构造期执行,早于 onCreate 里的 SDK_INT 判断,
于是下面的 registerMicInputWatcher() 版本守卫形同虚设 ——
构造 MainActivity 即抛 NoClassDefFoundError,表现为一打开就闪退。
- 顺带修复 36 处同类未守卫的高版本 API 调用(lint NewApi 全量清零):
* AudioDeviceCallback / AudioDeviceInfo.isSource/getType (API 23)
→ 改为可空字段 + 仅在 SDK_INT>=23 分支按需创建(@TargetApi)
* HashMap.putIfAbsent ×5 (API 24) → MutableMap.getOrPut
* Comparator.comparing ×3 + comparingInt ×1 (API 24) → Kotlin compareBy
(同时移除 java.util.function 导入)
* ConcurrentHashMap.newKeySet ×2 (API 24) → Collections.newSetFromMap
(连带 16 处 KeySetView.add/remove/contains)
* ConcurrentHashMap.computeIfAbsent (API 24) → ConcurrentHashMap.putIfAbsent(API 1)
* URLConnection.getContentLengthLong ×5 (API 24) → 新增 Compat.contentLengthCompat()
* ByteArrayOutputStream.toString(Charset) (API 33) → String(bytes, UTF_8)
防回归:
- app/build.gradle 打开 lint 闸门: checkReleaseBuilds true + abortOnError true +
fatal += NewApi(并 disable ExpiredTargetSdkVersion,避免它挤占闸门)。
已实测: 植入一个 NewApi 违规后 assembleRelease 直接 FAILED。
- AGENTS.md 新增「低版本设备闪退(API 守卫)」条目,记录根因、替代写法与自查命令。
版本号 1.4.0(16) → 1.4.1(17);签名证书不变,可覆盖安装。
|
2026-09-26 13:59:51 +08:00 |
|
zijunchiang
|
942ec527f1
|
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, 仅本地打包测试。
|
2026-09-21 03:34:29 +08:00 |
|
zijunchiang
|
485235e96e
|
V1.3.1: 设备MAC改取当前网卡 + 播放界面当前/下一首实时刷新 + 日志IP与启动信息调整
1) 设备 MAC: 新增 DeviceMac 解析「当前在用网卡」硬件地址(优先 wlan/eth/en,
忽略虚拟/蜂窝/VPN; 直接读 NetworkInterface.getHardwareAddress, 规避 Android 6+
WifiInfo 恒返回 02:00:00:00:00:00 的限制), 读出无效值或失败时回退历史抓包值。
结果按进程缓存, 网络切换时由 remoteNetworkMonitor 重解析。
消费方: KtvJsBridge.DEVICE_MAC(JS 端 mac)、whoami 心跳 _mac/androidid/_cid。
2) 播放界面: 抽出 refreshNowPlayingLabels() 作为「当前播放/下一首」的唯一刷新入口,
只在文本变化时写 View, 并挂到 lyricTicker(500ms), 使播放界面左上角、首页播放条、
旧版播放区与顶部状态栏在本地点歌/扫码点歌/自动切歌后都能实时同步;
顶部状态栏的「下首」改为按当前曲目位置取其后一首(原先固定取队列第 2 首)。
3) 远程日志: serverip 改为主机 IP(公网出口 IP, 查询后缓存 5 分钟、失败 1 分钟后重试,
后台线程执行), 新增 clientip 字段承载设备网卡获取到的局域网 IP; 启动上报改为
采集 10 项: 包名、版本号、安装时间、品牌、型号、设备名、Android 版本、CPU 架构、
内核版本、屏幕分辨率。
4) 版本号 1.3.0 -> 1.3.1(versionCode 6 -> 7)。本次不发布 Release, 仅本地打包测试。
|
2026-09-14 02:59:21 +08:00 |
|
zijunchiang
|
ba31caaa38
|
全量迁移到 Gitea: App更新检查/JS热更新端点上链 Gitea, 文档同步
- AppUpdateManager: releases/latest 改用 gitea.kasugano.cn API
- KtvJsBridge: JS 热更新改用 Gitea raw(/raw/branch/master/...)
- MainActivity/SongApiClient/DatabaseBootstrapper/MuseDbSync: 文案与注释更新
- README.md/README.en.md: 标注本仓库地址(Gitea), 保留上游框架归属链接
- AGENTS.md: 运行时端点迁移状态
|
2026-09-11 23:08:46 +08:00 |
|
zijunchiang
|
2d3751064f
|
歌曲源: 新增6源(desktok.top/mm.kk456.top/w.w345.my:66/z.w345.my/b.sktv.top/w345.my) + 多源自动排序
- ktv_api.js: HOSTS 扩为 7 源(同后台多入口), 缓存标记 7→8
- 自动排序: 解析失败的源 demote 到队尾, 成功源 promote 到队首(sourceOrder 运行时可变)
- JS→Kotlin 上报实际命中源(reportSongResult), 远程日志请求源不再写死
- SongApiClient/KtvJsBridge: lastSongSourceHost 供日志使用
|
2026-09-04 23:06:05 +08:00 |
|
zijunchiang
|
0c26f6da68
|
麦动KTV: 移除Gitee数据库(改官方Muse同步), 歌曲源切w.w345.my, 远程日志+广告屏蔽
- DatabaseBootstrapper: 删除 Gitee 分片/manifest/blob 下载逻辑, 曲库只走官方 MuseDbSync
- build.gradle: 移除 GITEE_DATABASE_* 构建参数
- MainActivity: 重置数据库文案改为官方同步
- ktv_api.js: 歌曲源仅 w.w345.my, 缓存标记 7, 广告/错号资源拦截+重试
- 远程日志: 点歌记录 编号/标题/请求源/状态
|
2026-09-04 21:23:21 +08:00 |
|