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:
MeowOS committed 2026-08-31 17:40:04 +08:00
1 parent d394c73a7c
commit c041facd89
13 files changed
+447 -65

No files matched your search

+52
View File
@@ -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;
}
+51
View File
@@ -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;
}
+82
View File
@@ -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;
}