fix(ncm): 歌单详情LoadFailed——track/all端点不存在(404)回退detail+分批song_detail已验证链路; 列表误触改按下锁定行+拖动抑制 (agent-227, 真机验证256首出曲) + perf(meowpanel): 屏幕帧整数定点DCT(无FPU软浮点3.96s→0.19s/帧,实测4-5fps)+前端链式轮询防堆积+实测fps显示 (agent-228)
ncm→73422166 已部署; meowpanel→f310a00e 已部署
This commit is contained in:
1 parent
d394c73a7c
commit
c041facd89
13 files changed
+447
-65
No files matched your search
+141
-1
@@ -782,7 +782,147 @@ c3a4aa5cd3da493d5cdb75965aaaa4fe app/ui/playlist.c 610a223232d250d2e04130
|
||||
- **topbar 图标字符潜在同病**:home/player topbar 动作标签 `⟳`(U+21BB)/`⚙`(U+2699)/`⌕`(U+2315) 走
|
||||
`meow_font_class_of`→OTHER 类,若 common 图集无对应字形同样会豆腐块;用户未报(可能在 topbar 用了符号字库槽或未留意),
|
||||
本期未动(避免改非报点区域),后续如需一并确认/替换(如改用 ASCII 或 in-atlas 字符)。
|
||||
- **`song_ids[]` 字段现未被歌单加载使用**(`track_all` 直返歌曲对象),字段保留在 `g_st`(无害,避免结构改动)。
|
||||
- **`song_ids[]` 字段曾在 §12 被闲置**(`track_all` 直返歌曲对象);§13 已回退 detail+song_detail,重新用于
|
||||
暂存 `playlist/detail` 返回的 trackIds 以驱动 `song_detail` 分批(§13 为此更新)。
|
||||
- **歌词竞态**:UI 读 lyric 与 worker 写保留良性竞态(§10.9 FM4 同接受);真机如需彻底消除可后续给 lyric 模块加锁。
|
||||
- **构建方差异**:宿主 `build_ncm_host.sh` 对 `cover.c` 有 glibc-fortify `-Wformat-truncation` 告警(**改动前即存在**,
|
||||
仅宿主 glibc 触发;mipsel musl 无此告警,`build_ncm.sh` 0 warning)。
|
||||
|
||||
---
|
||||
|
||||
## 13. 真机两修:歌单详情 Load Failed 根因(track/all 端点不存在)+ 歌单列表误触(2026-08-31,已真机闭环)
|
||||
|
||||
> 现象(用户真机验收):① 登录/主页 64 歌单正常,但**进任意歌单都 Load Failed**——§12.3 新换的
|
||||
> `/api/playlist/track/all` 分页端点从未真机验过,真机响应字段存疑(§12.8 已自标风险);
|
||||
> ② **偶发误触**:歌单列表点到非触摸坐标的行(列表触摸坐标有偏差/串行)。
|
||||
> 本批先真机用最小请求量抓 track/all 真实响应样本、对齐结论,再修复并用真机验证。仅改 `patch_v2/ncm_app/`,
|
||||
> 未动 sdk/、meow_launcherd/、vendor/;未 commit/push/刷机;未动设备系统/恢复区。
|
||||
|
||||
### 13.1 根因①:`/api/playlist/track/all` 端点根本不存在(404「接口未找到」)
|
||||
|
||||
按 §12.8 的“真机抓真实响应样本对齐”计划:设备侧链路(`bench/probe_track_all.c`,用与 App 相同的
|
||||
`ncm_req_build`+`ncm_http_request`)对小歌单 `18206253175`(Docu/1 首)抓**原始响应体**。为把“端点是否存在/响应字段”
|
||||
与“解析对错”解耦,探针**绕过解析器**直发请求,先打原始 body。三次(风控 ≤3)真实请求结果:
|
||||
|
||||
| 路由 | 加密 | URL | 原始响应 |
|
||||
|---|---|---|---|
|
||||
| `/api/playlist/track/all` | eapi | `https://interface.music.163.com/eapi/playlist/track/all` | `{"code":404,"message":"接口未找到!"}` |
|
||||
| `/api/v6/playlist/track/all` | weapi | `https://music.163.com/weapi/v6/playlist/track/all` | `{"code":404,"message":"接口未找到!"}` |
|
||||
| `/api/playlist/track/all` | weapi | `https://music.163.com/weapi/playlist/track/all` | `{"code":404,"message":"接口未找到!"}` |
|
||||
|
||||
→ **NCM 服务端没有 `track/all` 这条路由**(eapi/weapi、v6/无v6 全 404)。三次请求 body 均为
|
||||
`{"code":404,"message":"接口未找到!"}`(HTTP 200 但业务 code=404),故 `api_post` 判
|
||||
`NCM_ERR_HTTP(-10)` → `playlist_worker` 置 `g_load_err` → 任意歌单都 Load Failed。
|
||||
§12.3 把歌单详情从“验证过的 `/v6/playlist/detail` + `song_detail`(§10.11)”换成基于一条不存在的路由做“服务端分页”,
|
||||
是回归根因;`ncm_api_parse_track_all` 的“多路径容错”救不了**路由不存在**——收到的是 404 业务码,不是歌曲数组。
|
||||
|
||||
### 13.2 根因②:歌单列表误触 = 拖动滚动后 release 被 SDK 判 TAP,命中“释放点+新 scroll”而非按下行
|
||||
|
||||
先验证“触摸坐标与显示是否几何不一致”(任务提示的方向)。设备侧 `bench/probe_input.c`(本地 ioctl,零 NCM 请求):
|
||||
```
|
||||
ABS_MT_POSITION_X min=0 max=480 ABS_MT_POSITION_Y min=0 max=720
|
||||
fb0 xres=480 yres=720 bpp=16
|
||||
```
|
||||
→ 触摸面板与帧缓冲 **1:1**(无缩放/无固定偏移),故“坐标有偏差”不是面板标定/缩放问题,App 侧命中公式
|
||||
(`w->y + w->scroll` 与绘同式)也一致——命中数学本身是对的。
|
||||
|
||||
真正根因是**手势时序**:SDK 抬手判 `adx/ady < 65px`(`SWIPE_THRESH`)为 TAP;而拖动过程中 `touch_move` 已
|
||||
`meow_listview_scroll_by(-dy)` **滚动列表**。于是“小拖动(0..64px)= 列表已滚 + release 仍触 TAP”,
|
||||
`playlist_on_tap`/`home_on_tap` 用 `meow_listview_hit(释放x, 释放y)` = **“释放点 + 更新后的 scroll”**命中,
|
||||
打到的是“已经滚动到手指下方”的那一行,与用户**按下**的行不一致 → “点到非触摸坐标的行”(偶发,手指微动即触发)。
|
||||
(§12.5 宿主离屏 `MEOW_UI_DUMP` 只验静态渲染、不发真实触摸,故未暴露。)
|
||||
|
||||
### 13.3 修法
|
||||
|
||||
- **Load Failed**(`app/logic/appstate.c`):`appstate_load_playlist_songs_page` 回退 §10.11 已验证路径——
|
||||
`/api/v6/playlist/detail`(`n=1`,取前 `NCM_SONG_MAX` 首 trackIds)+ `/api/v3/song/detail` 分批取歌曲对象
|
||||
(`parse_songs_array` 仍带出 `al.picUrl`,保留 §12.1 专辑封面)。`offset==0` 重头整批载入(idn≤NCM_SONG_MAX);
|
||||
`offset>0`(触底“加页”)只要 `NCM_OK` 无操作——detail 一次已取满至上限,无独立分页。
|
||||
- **误触**(`app/ui/home.c` 与 `app/ui/playlist.c`):点行改用**按下行** `g_press_row`(touch_down 命中行,
|
||||
与“红条按压”同源);手指移动量 `g_press_delta > 10`(与按压红条清行同阈值)判为**滚动收尾**,不触发点行。
|
||||
干净点击仍播放按下行(更符合“按哪行播哪行”);拖动手势只滚动不再误播。`search.c` 同款模式,
|
||||
非本次报点,未改(避免扩范围,见 §13.8)。
|
||||
- 仅在 `patch_v2/ncm_app/`;`app/`/`core/` 均未动其它逻辑;`ncm_api_playlist_track_all`/`ncm_api_parse_track_all`
|
||||
保留在 core(现为不可达死代码;`offline_track_all`/`test_track_all_parse` 仍可作离线回归)。
|
||||
|
||||
### 13.4 真机验证记录
|
||||
|
||||
**触摸=fb**(probe_input,本地 ioctl):`ABS_MT[0,480]×[0,720]`,`fb0 480×720 16bpp` → 1:1,排除标定。
|
||||
|
||||
**修复后数据链路**(`bench/probe_fix.c`,复刻修复后 `appstate_load_playlist_songs_page(offset=0)` 的
|
||||
detail→song_detail 完整路径;device_id=`BD6DE5…`, cookie_len=1842):
|
||||
```
|
||||
=== small (Docu/1, 18206253175) ===
|
||||
detail rc=0 elapsed_ms=1175 idn=1 ids[0]=2048962657
|
||||
song off=0 batch=1 rc=0 elapsed_ms=717 bn=1
|
||||
RESULT song_count=1
|
||||
[0] Verve (feat. FJØRA) / Ryan Taubert / Verve (feat. FJØRA)
|
||||
FIX VERIFY OK
|
||||
=== big (我喜欢的音乐, 581857874, 5191) ===
|
||||
detail rc=0 elapsed_ms=34672 idn=256 ids[0]=36190432
|
||||
song off=0/64/128/192 batch=64 rc=0 bn=64 ×4
|
||||
RESULT song_count=256
|
||||
[0] Isla Blanca (Aerosoul Chillout Remix) / Aerosoul / Isla Blanca
|
||||
FIX VERIFY OK
|
||||
```
|
||||
→ 小歌单 1 首、大歌单 256 首(`NCM_SONG_MAX` 截断,§10.9 上限)**真机都出曲目,不再 Load Failed**。
|
||||
大歌单 detail 真机 ~34.7s(5191 首 trackIds ≈900KB),但由 `playlist_worker` 后台跑、UI 显示 LOADING 不冻结(§10.11 P1 已修)。
|
||||
|
||||
**部署二进制冒烟**(`ncm.new`→`mv`,起 8s 再 SIGTERM):
|
||||
```
|
||||
start → home: playlists rc=0 count=120 uid=408996274 → exit
|
||||
```
|
||||
→ 新二进制启动正常、主页加载 120 歌单、干净退出,无崩溃。
|
||||
|
||||
**请求量**:track/all 诊断恰 3 次(风控 ≤3);修复后验证走 detail+song_detail(验收必需)。
|
||||
真实请求总计数:诊断 3(track/all)+ 复验 7(small detail/song 2 + big detail/4×song 5)+ 冒烟 1(user/playlist)= **≈11 次**,
|
||||
均为必要开销。另有一次无效运行(data_dir 误传致 TLS 失败),未发服务器请求。
|
||||
|
||||
### 13.5 真机部署
|
||||
|
||||
`/usr/data/mnt/sd_0/MeowAPPs/dev.player.ncm/ncm`(先 `ncm.new` 再 `mv`;`ncm.bak2`=§12 旧回归二进制,
|
||||
`ncm.bak`=§10.11 前)。**新 md5 `734221661ce547393c03ae3568ca3f36`**(旧 §12 `9c4a79f669967d193cba9fdaa41f8442`)。
|
||||
|
||||
### 13.6 出入清单(只改 `patch_v2/ncm_app/`)
|
||||
|
||||
```
|
||||
修改:
|
||||
app/logic/appstate.c # track_all 404 回归:appstate_load_playlist_songs_page 回退 detail+song_detail
|
||||
app/ui/playlist.c # 误触:点行用按下行 g_press_row + 拖动(g_press_delta>10)抑制
|
||||
app/ui/home.c # 误触(同 playlist.c):点行用按下行 + 拖动抑制
|
||||
新增:
|
||||
bench/probe_track_all.c # track_all 原始响应探针(诊断 404;抓样本用)
|
||||
bench/probe_input.c # 触摸 ABS 范围 + fb0 分辨率探针(本地 ioctl,零 NCM)
|
||||
bench/probe_fix.c # 修复后数据链路真机复验(detail→song_detail)
|
||||
REPORT.md # 本批记录(§13)
|
||||
(未动 sdk/、meow_launcherd/、vendor/;未 commit/push/刷机;未动设备系统/恢复区)
|
||||
```
|
||||
|
||||
### 13.7 md5(本批改动文件 + 产物)
|
||||
|
||||
```
|
||||
734221661ce547393c03ae3568ca3f36 ncm # mipsel 静态 App(新;已部署设备)
|
||||
453eeb5d29809854061d8e4e0300855a app/logic/appstate.c
|
||||
2ce92315d0e27492b36374f512fabaeb app/ui/playlist.c
|
||||
4217763fbf766c77917f86f63abb634c app/ui/home.c
|
||||
279ebbcfca65f9225b93b04213f8fde4 bench/probe_track_all.c 7dc88e448fb10a6da24ad48511023307 bench/probe_input.c
|
||||
5ad3107059bca1f8e14776afdf6abf82 bench/probe_fix.c
|
||||
```
|
||||
|
||||
### 13.8 诚实出入 / 已知限制
|
||||
|
||||
- **`track/all` 不存在使 §12.3 的整体翻页目标落空**:`/api/playlist/track/all`(eapi/weapi、v6/无v6)真机全 404,
|
||||
故“服务端分页 + 第 256 首之后继续加载”无法实现。本期回退为 §10.11 的 detail(全量 trackIds,浏览器首屏 ~34.7s,
|
||||
UI 后台 LOADING 不冻结)+ `NCM_SONG_MAX=256` 截断。若仍要超过 256 首滚动加载,只能改用 detail 已返回的全量
|
||||
trackIds 驱动 song_detail 分页(不依赖 `track/all`),列入后续;本期只恢复到“能出曲目”的可靠基线。
|
||||
- **`ncm_api_playlist_track_all`/`ncm_api_parse_track_all` 现为不可达死代码**(服务端无路由),保留以不破坏
|
||||
`offline_track_all.c`/`test_track_all_parse`(离线样本回归,0 网络)。后续可连同 §12.3 一并清理。
|
||||
- **误触修复未真机触摸复现**:无法经 SSH 驱动真实触摸;修复基于「按下行 + 拖动抑制」的明确逻辑 + 触摸=fb 1:1 的
|
||||
实测佐证(排除标定)。建议用户真机复验:快速横滑列表后抬手,应只滚动不误播;干净点行仍播按下行。
|
||||
- **`search.c` 列表点行同款误触模式**(未报点,本期未改,避免扩范围):`search_on_tap` 仍用 `meow_listview_hit(释放点)`;
|
||||
如需一并修,套用 §13.3 同名改法即可。
|
||||
- **大歌单首屏 ~34.7s**:detail 全量 trackIds(≈900KB)在 MIPS32 上解析慢(§10.11 已记录,宿主 4.7s,~7×);后台 worker
|
||||
避免冻 UI。已恢复到 §10.11 用户接受的基线。
|
||||
- **`al.picUrl` 封面保留**:song_detail 经 `parse_songs_array` 解析出 `al.picUrl`,§12.1 黑胶真专辑封面功能不退。
|
||||
- **构建**:`build_ncm.sh` mipsel 自有代码 `-Wall -Wextra` 0 warning(本批改动文件逐文件复核无告警);vendor 豁免。
|
||||
- **未动 sdk/、meow_launcherd/、vendor/,未 commit/push/刷机,未动设备系统/恢复区**;部署留用户后续验收。
|
||||
@@ -126,25 +126,34 @@ int appstate_load_playlist_songs_page(int idx, int offset, int limit)
|
||||
if (limit <= 0 || limit > NCM_SONG_BATCH) limit = NCM_SONG_BATCH;
|
||||
if (offset < 0) offset = 0;
|
||||
|
||||
/* 二期:改走 /api/playlist/track/all 服务端分页(REPORT §11)。
|
||||
* 相对旧 /v6/playlist/detail 的「全量 trackIds(5191 首 ≈900KB)」本端点直接按页返回歌曲
|
||||
* 对象(含 al.picUrl),无需 detail 瓶颈 + 二次 song_detail。 */
|
||||
ncm_song_t tmp[NCM_SONG_BATCH];
|
||||
int n = 0; int64_t total = 0;
|
||||
int rc = ncm_api_playlist_track_all(g_st.ctx.device_id, g_st.ctx.cookie, pl->id,
|
||||
offset, limit, tmp, NCM_SONG_BATCH, &n, &total);
|
||||
if (rc != NCM_OK) { if (offset == 0) g_st.song_count = 0; return rc; }
|
||||
/* 二期曾改走 /api/playlist/track/all 服务端分页(REPORT §12.3),但真机实测该端点
|
||||
* (eapi/weapi、v6/无v6 四种路由)全部返回 code 404「接口未找到」——NCM 服务端无此路由,
|
||||
* 故回退到 REPORT §10.11 已验证的 /api/v6/playlist/detail(trackIds) + /api/v3/song/detail 分批。
|
||||
* detail 一次取回前 NCM_SONG_MAX 首 trackIds;song_detail 分批取歌曲对象(含 al.picUrl)。
|
||||
* offset==0 重头清空并整批载入;offset>0(触底“加页”)时 detail 已一次取满,无独立分页,
|
||||
* 直接返回 NCM_OK(歌单数 ≤NCM_SONG_MAX 时已全部在列,song_count 不变)。 */
|
||||
if (offset != 0) return NCM_OK;
|
||||
|
||||
if (offset == 0) g_st.song_count = 0; /* 重头:清空旧歌单(含此前打开的另一歌单) */
|
||||
memset(g_st.song_ids, 0, sizeof g_st.song_ids);
|
||||
int idn = 0;
|
||||
int rc = ncm_api_playlist_detail(g_st.ctx.device_id, g_st.ctx.cookie, pl->id,
|
||||
NULL, 0, NULL, 0,
|
||||
g_st.song_ids, NCM_SONG_MAX, &idn);
|
||||
if (rc != NCM_OK) { g_st.song_count = 0; return rc; }
|
||||
|
||||
/* 追加到 g_st.songs[offset..],封顶 NCM_SONG_MAX(内存纪律) */
|
||||
int cap = NCM_SONG_MAX - offset;
|
||||
if (cap <= 0) return NCM_OK;
|
||||
if (n > cap) n = cap;
|
||||
memcpy(&g_st.songs[offset], tmp, sizeof(ncm_song_t) * (size_t)n);
|
||||
int end = offset + n;
|
||||
if (end > NCM_SONG_MAX) end = NCM_SONG_MAX;
|
||||
g_st.song_count = end;
|
||||
memset(g_st.songs, 0, sizeof g_st.songs);
|
||||
int outn = 0;
|
||||
for (int off = 0; off < idn; off += NCM_SONG_BATCH) {
|
||||
int batch = idn - off;
|
||||
if (batch > NCM_SONG_BATCH) batch = NCM_SONG_BATCH;
|
||||
int bn = 0;
|
||||
rc = ncm_api_song_detail(g_st.ctx.device_id, g_st.ctx.cookie, &g_st.song_ids[off],
|
||||
batch, &g_st.songs[off], NCM_SONG_MAX - off, &bn);
|
||||
if (rc != NCM_OK) break;
|
||||
outn += bn;
|
||||
if (bn < batch) break;
|
||||
}
|
||||
g_st.song_count = outn;
|
||||
appstate_apply_playlist_cover(idx);
|
||||
return NCM_OK;
|
||||
}
|
||||
|
||||
@@ -259,9 +259,12 @@ int home_on_tap(int x, int y)
|
||||
}
|
||||
if (g_load_err != 0 || g_st.playlist_count == 0) return 0;
|
||||
if (meow_widget_hit(g_list, x, y)) {
|
||||
int row = meow_listview_hit(g_list, x, y);
|
||||
if (row < 0) { home_drag_reset(); return 1; }
|
||||
/* 误触修复(同 playlist.c):小拖动滚动列表后 release 被 SDK 判成 TAP,旧命中用“释放点 +
|
||||
* 新 scroll”,打到非按下行;改用按下行 g_press_row,移动超阈值(>10)视为滚动不触发。 */
|
||||
int was_drag = (g_press_delta > 10);
|
||||
int row = (g_press_row >= 0) ? g_press_row : meow_listview_hit(g_list, x, y);
|
||||
home_drag_reset();
|
||||
if (row < 0 || was_drag) return 1;
|
||||
playlist_open(tab_global_idx(g_tab, row));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -291,9 +291,14 @@ int playlist_on_tap(int x, int y)
|
||||
}
|
||||
if (g_load_err != 0 || g_st.song_count == 0) return 0;
|
||||
if (meow_widget_hit(g_list, x, y)) {
|
||||
int row = meow_listview_hit(g_list, x, y);
|
||||
if (row < 0) { pl_drag_reset(); return 1; }
|
||||
/* 误触修复:小拖动(<SWIPE_THRESH)会先滚动列表、release 又被 SDK 判成 TAP,旧的
|
||||
* meow_listview_hit(x,y) 用“释放点 + 更新后的 scroll”命中 → 打到“已移动到手指下”的行,
|
||||
* 与按下点不一致(“点到非触摸坐标的行”)。按下行 g_press_row 才是用户真正按的行;
|
||||
* 移动量超过阈值(g_press_delta>10,与按压红条清行同阈值)视为滚动收尾,不触发点行。 */
|
||||
int was_drag = (g_press_delta > 10);
|
||||
int row = (g_press_row >= 0) ? g_press_row : meow_listview_hit(g_list, x, y);
|
||||
pl_drag_reset();
|
||||
if (row < 0 || was_drag) return 1;
|
||||
player_play_song(row);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/* probe_fix.c — 真机验证修复:复刻 appstate_load_playlist_songs_page(offset=0) 的完整数据路径
|
||||
* /api/v6/playlist/detail(trackIds) → /api/v3/song/detail 分批 → 打印曲目。
|
||||
* 与 App 相同的 core 调用(App 端 appstate 修复即按此路径)。请求量:detail 1 + song_detail 1 = 2。
|
||||
* 用法: probe_fix <data_dir> <cacert> <playlist_id>
|
||||
*/
|
||||
#include "ncm.h"
|
||||
#include "ncm_api.h"
|
||||
#include "ncm_common.h"
|
||||
#include "ncm_http.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static ncm_ctx_t ncm_ctx;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *data_dir = argc > 1 ? argv[1] : "/usr/data/mnt/sd_0/MeowData/dev.player.ncm/data";
|
||||
const char *cacert = argc > 2 ? argv[2] : "/usr/data/mnt/sd_0/MeowAPPs/dev.player.ncm/cacert.pem";
|
||||
const char *pid = argc > 3 ? argv[3] : "18206253175";
|
||||
ncm_ctx_init(&ncm_ctx, data_dir);
|
||||
printf("device_id=%s cookie_len=%zu\n", ncm_ctx.device_id, strlen(ncm_ctx.cookie));
|
||||
ncm_http_set_verify_default(1, cacert);
|
||||
|
||||
int64_t ids[256]; int idn = 0;
|
||||
long t0 = (long)ncm_now_ms();
|
||||
int rc = ncm_api_playlist_detail(ncm_ctx.device_id, ncm_ctx.cookie, pid,
|
||||
NULL, 0, NULL, 0, ids, 256, &idn);
|
||||
long dt = (long)ncm_now_ms() - t0;
|
||||
printf("detail rc=%d elapsed_ms=%ld idn=%d ids[0]=%lld\n", rc, dt, idn, idn>0?(long long)ids[0]:-1);
|
||||
if (rc != NCM_OK) { printf("FIX VERIFY FAILED (detail)\n"); return 1; }
|
||||
|
||||
ncm_song_t songs[256]; memset(songs, 0, sizeof songs);
|
||||
int outn = 0;
|
||||
for (int off = 0; off < idn; off += 64) {
|
||||
int batch = idn - off; if (batch > 64) batch = 64;
|
||||
int bn = 0;
|
||||
t0 = (long)ncm_now_ms();
|
||||
rc = ncm_api_song_detail(ncm_ctx.device_id, ncm_ctx.cookie, &ids[off], batch,
|
||||
&songs[off], 256 - off, &bn);
|
||||
printf(" song off=%d batch=%d rc=%d elapsed_ms=%ld bn=%d\n", off, batch, rc,
|
||||
(long)ncm_now_ms() - t0, bn);
|
||||
if (rc != NCM_OK) break;
|
||||
outn += bn;
|
||||
if (bn < batch) break;
|
||||
}
|
||||
printf("RESULT song_count=%d\n", outn);
|
||||
if (outn > 0)
|
||||
printf(" [0] %s / %s / %s\n", songs[0].name, songs[0].art_name, songs[0].album_name);
|
||||
if (rc != NCM_OK || outn == 0) { printf("FIX VERIFY FAILED (songs)\n"); return 1; }
|
||||
printf("FIX VERIFY OK\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* probe_input.c — 读 goodix-ts 触摸 ABS 范围 + fb0 分辨率。
|
||||
* 用于确认「歌单列表触摸坐标有偏差/串行」是否由触摸面板分辨率与帧缓冲不一致引起。
|
||||
* 本地设备操作,零 NCM 请求。 */
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/fb.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
static const char *axis_name(int code)
|
||||
{
|
||||
switch (code) {
|
||||
case ABS_X: return "ABS_X";
|
||||
case ABS_Y: return "ABS_Y";
|
||||
case ABS_MT_POSITION_X: return "ABS_MT_POSITION_X";
|
||||
case ABS_MT_POSITION_Y: return "ABS_MT_POSITION_Y";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fd = open("/dev/input/event1", O_RDONLY | O_NONBLOCK);
|
||||
if (fd < 0) { printf("open event1 failed: %s\n", strerror(errno)); return 1; }
|
||||
int codes[] = { ABS_X, ABS_Y, ABS_MT_POSITION_X, ABS_MT_POSITION_Y };
|
||||
for (unsigned i = 0; i < sizeof codes / sizeof codes[0]; i++) {
|
||||
struct input_absinfo ab; memset(&ab, 0, sizeof ab);
|
||||
if (ioctl(fd, EVIOCGABS(codes[i]), &ab) == 0)
|
||||
printf("%-22s min=%d max=%d value=%d flat=%d fuzz=%d res=%d\n",
|
||||
axis_name(codes[i]), ab.minimum, ab.maximum, ab.value,
|
||||
ab.flat, ab.fuzz, ab.resolution);
|
||||
else
|
||||
printf("%-22s ioctl failed: %s\n", axis_name(codes[i]), strerror(errno));
|
||||
}
|
||||
close(fd);
|
||||
|
||||
/* fb0 分辨率 */
|
||||
fd = open("/dev/fb0", O_RDWR);
|
||||
if (fd >= 0) {
|
||||
struct fb_var_screeninfo vs; memset(&vs, 0, sizeof vs);
|
||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &vs) == 0)
|
||||
printf("fb0 xres=%u yres=%u xres_virtual=%u yres_virtual=%u bpp=%u\n",
|
||||
vs.xres, vs.yres, vs.xres_virtual, vs.yres_virtual, vs.bits_per_pixel);
|
||||
else printf("fb0 vinfo failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
} else printf("open fb0 failed: %s\n", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/* probe_track_all.c — 真机抓 /api/playlist/track/all 原始响应 + 用现有解析器解析。
|
||||
* 目的:确认「Load Failed」根因(eapi 路由 / 响应字段 / code 非 200 / 数组路径)。
|
||||
* 请求量:1(track/all for 一个小歌单)。
|
||||
* 用法: probe_track_all <data_dir> <cacert> <playlist_id> <out_path> [path] [crypto]
|
||||
* data_dir 默认 /usr/data/mnt/sd_0/MeowData/dev.player.ncm/data
|
||||
* cacert 默认 /usr/data/mnt/sd_0/MeowAPPs/dev.player.ncm/cacert.pem
|
||||
* playlist_id 默认 18206253175(Docu/1 首)
|
||||
* out_path 默认 /tmp/track_all_raw.json
|
||||
* path 默认 /api/playlist/track/all(eapi 已证 404,可改 /api/v6/playlist/track/all)
|
||||
* crypto 0=eapi(默认) 1=weapi
|
||||
*/
|
||||
#include "ncm.h"
|
||||
#include "ncm_api.h"
|
||||
#include "ncm_common.h"
|
||||
#include "ncm_http.h"
|
||||
#include "ncm_req.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static ncm_ctx_t ncm_ctx;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *data_dir = argc > 1 ? argv[1] : "/usr/data/mnt/sd_0/MeowData/dev.player.ncm/data";
|
||||
const char *cacert = argc > 2 ? argv[2] : "/usr/data/mnt/sd_0/MeowAPPs/dev.player.ncm/cacert.pem";
|
||||
const char *pid = argc > 3 ? argv[3] : "18206253175";
|
||||
const char *out_path = argc > 4 ? argv[4] : "/tmp/track_all_raw.json";
|
||||
const char *path = argc > 5 ? argv[5] : "/api/playlist/track/all";
|
||||
int crypto_kind = (argc > 6 && atoi(argv[6]) == 1) ? NCM_CRYPT_WEAPI : NCM_CRYPT_EAPI;
|
||||
|
||||
ncm_ctx_init(&ncm_ctx, data_dir);
|
||||
printf("device_id=%s cookie_len=%zu\n", ncm_ctx.device_id, strlen(ncm_ctx.cookie));
|
||||
ncm_http_set_verify_default(1, cacert);
|
||||
|
||||
/* 直接构造 track/all 请求,抓原始 body(不经过解析,避免解析失败掩盖真相)。 */
|
||||
printf("testing path=%s crypto=%s\n", path, crypto_kind == NCM_CRYPT_EAPI ? "eapi" : "weapi");
|
||||
char json[256];
|
||||
snprintf(json, sizeof json, "{\"id\":\"%s\",\"offset\":0,\"limit\":50,\"n\":50,\"s\":8}", pid);
|
||||
char body[NCM_REQ_BODY_MAX], hdrs[NCM_REQ_HDRS_MAX], url[NCM_REQ_URL_MAX];
|
||||
int rc = ncm_req_build(ncm_ctx.device_id, ncm_ctx.cookie, path, json,
|
||||
crypto_kind, body, sizeof body, hdrs, sizeof hdrs, url, sizeof url);
|
||||
printf("req_build rc=%d url=%s\n", rc, url);
|
||||
if (rc != NCM_OK) return 1;
|
||||
|
||||
ncm_http_req_t req;
|
||||
memset(&req, 0, sizeof req);
|
||||
req.url = url; req.method = "POST"; req.body = body; req.body_len = strlen(body);
|
||||
req.extra_hdrs = hdrs; req.verify_ca = NCM_HTTP_VERIFY_DEFAULT; req.timeout_ms = 15000;
|
||||
|
||||
char rbuf[2 * 1024 * 1024];
|
||||
ncm_http_resp_t resp;
|
||||
memset(&resp, 0, sizeof resp);
|
||||
long t0 = (long)ncm_now_ms();
|
||||
rc = ncm_http_request(&req, rbuf, sizeof rbuf, &resp);
|
||||
long dt = (long)ncm_now_ms() - t0;
|
||||
printf("http rc=%d status=%d content_length=%zu body_len=%zu elapsed_ms=%ld\n",
|
||||
rc, resp.status, resp.content_length, resp.body_len, dt);
|
||||
if (rc != NCM_OK) return 1;
|
||||
|
||||
/* 原样落盘(截断到 body_len) */
|
||||
FILE *fp = fopen(out_path, "wb");
|
||||
if (fp) { fwrite(resp.body, 1, resp.body_len, fp); fclose(fp); printf("raw saved to %s (%zu bytes)\n", out_path, resp.body_len); }
|
||||
else printf("cannot write %s\n", out_path);
|
||||
|
||||
/* 打印前 1500 字节便于目检 */
|
||||
size_t show = resp.body_len < 1500 ? resp.body_len : 1500;
|
||||
printf("--- first %zu bytes ---\n", show);
|
||||
fwrite(resp.body, 1, show, stdout);
|
||||
printf("\n--- end ---\n");
|
||||
|
||||
/* 用现有解析器(离线重放,无新请求) */
|
||||
if (resp.body_len > 0) {
|
||||
ncm_song_t songs[50]; int out_n = 0; int64_t total = 0;
|
||||
rc = ncm_api_parse_track_all(resp.body, resp.body_len, songs, 50, &out_n, &total);
|
||||
printf("parse rc=%d out_n=%d total=%lld\n", rc, out_n, (long long)total);
|
||||
if (rc == NCM_OK && out_n > 0)
|
||||
printf(" [0] %s / %s / %s\n", songs[0].name, songs[0].art_name, songs[0].album_name);
|
||||
}
|
||||
printf("PROBE DONE\n");
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
Reference in new issue
Block a user