fix(sdk+launcher): 图标不渲染真根因(agent-219) — meow_ui_img.c scale_buf cover 采样把乘写成除(÷缩放比→采到源图左上角透明区,全丢); 改乘+居中裁剪偏移修正; 宿主旧测试用纯色图掩盖了该bug。+ main_menu App退出/结构变化重绘时 fb_pan_buf0_force 无条件pan回buffer0(防App翻页留buffer1致菜单残帧)

meow_ui.a→3e0ee8f7 launcher→9d0efc2f hello→9609ea6e files→90f5cf07 ncm→be8be221(均已上卡); 真机截图证据 .tmp/meowbug/fix1.png+fix_b2_after.png
This commit is contained in:
MeowOS committed 2026-08-31 15:42:24 +08:00
1 parent 05a19a9ea3
commit ab4da09e75
45 files changed
+1926 -147

No files matched your search

+98 -46
View File
@@ -100,6 +100,38 @@ static int obj_bool(const ncm_json_t *j, int tok, const char *key) {
return (strcmp(s, "true") == 0);
}
/* 解析一处「歌曲对象数组」(song_detail / cloudsearch / playlist_track_all 共用)。
* 元素字段:id/name/ar[0].name/al.name/al.picUrl/dt/fee。id 可为数字或字符串(均以文本拷贝,
* 兼容 >2^31 的歌曲 id)。返回 NCM_OK,*out_n=写入数。 */
static int parse_songs_array(const ncm_json_t *j, int arr, ncm_song_t *out, int max, int *out_n)
{
*out_n = 0;
if (arr < 0 || ncm_json_type(j, arr) != JSMN_ARRAY) return NCM_ERR_NOTFOUND;
int cnt = ncm_json_arr_len(j, arr);
if (cnt > max) cnt = max;
memset(out, 0, sizeof(*out) * (size_t)(cnt > 0 ? cnt : 0));
for (int i = 0; i < cnt; i++) {
int e = ncm_json_arr_get(j, arr, i);
if (e < 0) break;
obj_str(j, e, "id", out[i].id, sizeof out[i].id);
obj_str(j, e, "name", out[i].name, sizeof out[i].name);
out[i].duration_ms = obj_int(j, e, "dt");
out[i].fee = (int)obj_int(j, e, "fee");
int ar = ncm_json_obj_get(j, e, "ar");
if (ar >= 0 && ncm_json_type(j, ar) == JSMN_ARRAY) {
int a0 = ncm_json_arr_get(j, ar, 0);
if (a0 >= 0) obj_str(j, a0, "name", out[i].art_name, sizeof out[i].art_name);
}
int al = ncm_json_obj_get(j, e, "al");
if (al >= 0) {
obj_str(j, al, "name", out[i].album_name, sizeof out[i].album_name);
obj_str(j, al, "picUrl", out[i].al_pic, sizeof out[i].al_pic); /* 专辑封面(黑胶) */
}
}
*out_n = cnt;
return NCM_OK;
}
int ncm_api_user_playlist(const char *device_id, const char *cookie, const char *uid,
int limit, int offset, ncm_playlist_t *out, int max, int *out_n) {
if (!out_n) return NCM_ERR_ARG;
@@ -195,28 +227,9 @@ int ncm_api_song_detail(const char *device_id, const char *cookie, const int64_t
if (rc != NCM_OK) return rc;
int arr = ncm_json_path(&c.j, "songs");
if (arr < 0 || ncm_json_type(&c.j, arr) != JSMN_ARRAY) { api_free(&c); return NCM_ERR_NOTFOUND; }
int cnt = ncm_json_arr_len(&c.j, arr);
if (cnt > max) cnt = max;
memset(out, 0, sizeof(*out) * (size_t)(cnt > 0 ? cnt : 0));
for (int i = 0; i < cnt; i++) {
int e = ncm_json_arr_get(&c.j, arr, i);
if (e < 0) break;
obj_str(&c.j, e, "id", out[i].id, sizeof out[i].id);
obj_str(&c.j, e, "name", out[i].name, sizeof out[i].name);
out[i].duration_ms = obj_int(&c.j, e, "dt");
out[i].fee = (int)obj_int(&c.j, e, "fee");
int ar = ncm_json_obj_get(&c.j, e, "ar");
if (ar >= 0 && ncm_json_type(&c.j, ar) == JSMN_ARRAY) {
int a0 = ncm_json_arr_get(&c.j, ar, 0);
if (a0 >= 0) obj_str(&c.j, a0, "name", out[i].art_name, sizeof out[i].art_name);
}
int al = ncm_json_obj_get(&c.j, e, "al");
if (al >= 0) obj_str(&c.j, al, "name", out[i].album_name, sizeof out[i].album_name);
}
*out_n = cnt;
int rc2 = parse_songs_array(&c.j, arr, out, max, out_n);
api_free(&c);
return NCM_OK;
return rc2;
}
int ncm_api_song_url(const char *device_id, const char *cookie, const char *id,
@@ -305,30 +318,69 @@ int ncm_api_cloudsearch(const char *device_id, const char *cookie, const char *k
if (rc != NCM_OK) return rc;
int arr = ncm_json_path(&c.j, "result.songs");
if (arr < 0 || ncm_json_type(&c.j, arr) != JSMN_ARRAY) { api_free(&c); return NCM_ERR_NOTFOUND; }
int cnt = ncm_json_arr_len(&c.j, arr);
if (cnt > max) cnt = max;
memset(out, 0, sizeof(*out) * (size_t)(cnt > 0 ? cnt : 0));
for (int i = 0; i < cnt; i++) {
int e = ncm_json_arr_get(&c.j, arr, i);
if (e < 0) break;
obj_str(&c.j, e, "name", out[i].name, sizeof out[i].name);
out[i].duration_ms = obj_int(&c.j, e, "dt");
out[i].fee = (int)obj_int(&c.j, e, "fee");
/* id 用 i64:网易云歌曲 id 可 >2^31 */
int vt = ncm_json_obj_get(&c.j, e, "id");
int rc2;
int64_t idv = ncm_json_get_i64_at(&c.j, vt, &rc2);
snprintf(out[i].id, sizeof out[i].id, "%lld", (long long)idv);
int ar = ncm_json_obj_get(&c.j, e, "ar");
if (ar >= 0 && ncm_json_type(&c.j, ar) == JSMN_ARRAY) {
int a0 = ncm_json_arr_get(&c.j, ar, 0);
if (a0 >= 0) obj_str(&c.j, a0, "name", out[i].art_name, sizeof out[i].art_name);
}
int al = ncm_json_obj_get(&c.j, e, "al");
if (al >= 0) obj_str(&c.j, al, "name", out[i].album_name, sizeof out[i].album_name);
}
*out_n = cnt;
int rc2 = parse_songs_array(&c.j, arr, out, max, out_n);
api_free(&c);
return NCM_OK;
return rc2;
}
/* 从一段 track/all 响应 JSON 串解析歌曲数组 + total(离线样本/单元测试用,不打网络)。
* 数组取「songs → playlist.tracks → data.songs」首个命中;total 取「total → count →
* playlist.trackCount → musicCount」,全缺则 0。 */
int ncm_api_parse_track_all(const char *json, size_t len, ncm_song_t *out, int max,
int *out_n, int64_t *total) {
if (out_n) *out_n = 0;
if (total) *total = 0;
if (!json || len == 0 || !out || max <= 0) return NCM_ERR_ARG;
jsmn_parser p; jsmn_init(&p);
int n = jsmn_parse(&p, json, len, NULL, 0);
if (n <= 0) return NCM_ERR_JSON;
jsmntok_t *toks = (jsmntok_t *)malloc(sizeof(jsmntok_t) * (size_t)n);
int *parent = (int *)malloc(sizeof(int) * (size_t)n);
if (!toks || !parent) { free(toks); free(parent); return NCM_ERR_OOM; }
ncm_json_t j;
int rc = ncm_json_parse(&j, json, len, toks, n, parent);
if (rc < 0) { free(toks); free(parent); return NCM_ERR_JSON; }
/* 定位歌曲数组(多路径容错)。 */
int arr = ncm_json_path(&j, "songs");
if (arr < 0) arr = ncm_json_path(&j, "playlist.tracks");
if (arr < 0) arr = ncm_json_path(&j, "data.songs");
if (arr < 0 || ncm_json_type(&j, arr) != JSMN_ARRAY) { free(toks); free(parent); return NCM_ERR_NOTFOUND; }
long v = 0;
if (ncm_json_path_int(&j, "total", &v) != NCM_OK && ncm_json_path_int(&j, "count", &v) != NCM_OK &&
ncm_json_path_int(&j, "playlist.trackCount", &v) != NCM_OK && ncm_json_path_int(&j, "musicCount", &v) != NCM_OK)
v = 0;
if (total) *total = (int64_t)v;
int on = 0;
rc = parse_songs_array(&j, arr, out, max, &on);
if (out_n) *out_n = on;
free(toks); free(parent);
return rc;
}
int ncm_api_playlist_track_all(const char *device_id, const char *cookie, const char *playlist_id,
int offset, int limit, ncm_song_t *out, int max, int *out_n,
int64_t *total) {
if (out_n) *out_n = 0;
if (limit <= 0) limit = 50;
if (limit > max) limit = max;
if (offset < 0) offset = 0;
char json[256];
snprintf(json, sizeof json, "{\"id\":\"%s\",\"offset\":%d,\"limit\":%d,\"n\":%d,\"s\":8}",
playlist_id, offset, limit, limit);
api_json_t c; memset(&c, 0, sizeof c);
int rc = api_post(device_id, cookie, "/api/playlist/track/all", json, NCM_CRYPT_EAPI,
NCM_RESP_MAX, &c);
if (rc != NCM_OK) return rc;
int64_t tot = 0;
rc = ncm_api_parse_track_all(c.resp.body, c.resp.body_len, out, max, out_n, &tot);
if (rc == NCM_OK && total) *total = tot;
api_free(&c);
return rc;
}
+15
View File
@@ -38,6 +38,7 @@ typedef struct ncm_song {
char name[256];
char art_name[256]; /* 首个歌手名 */
char album_name[256];
char al_pic[512]; /* 专辑封面 picUrl(song_detail/cloudsearch/playlist_track_all 解析;播放页黑胶封面) */
long duration_ms;
int fee;
} ncm_song_t;
@@ -83,6 +84,20 @@ int ncm_api_lyric(const char *device_id, const char *cookie, const char *id,
int ncm_api_cloudsearch(const char *device_id, const char *cookie, const char *keyword,
ncm_song_t *out, int max, int *out_n);
/* 歌单曲目服务端分页(eapi /api/playlist/track/all,REPORT §11 二期)。
* 相对 /v6/playlist/detail 的「全量 trackIds(5191 首 ≈900KB)」改进:本端点按
* offset/limit 直接返回该页的歌曲对象(含 al.picUrl,无需再 song_detail 二次请求)。
* 首屏 offset=0 limit=50;滚动到底继续 offset+=limit。out 填 ncm_song_t。
* *total 返回歌单总曲数(0=未知;调用方可退化为 playlist.trackCount)。 */
int ncm_api_playlist_track_all(const char *device_id, const char *cookie, const char *playlist_id,
int offset, int limit, ncm_song_t *out, int max, int *out_n,
int64_t *total);
/* 离线解析:从一段 track/all 响应 JSON 串解析歌曲数组(含 al.picUrl)+ total。
* 网络版内部同一解析器;供「宿主离线样本重放」与单元测试用,不打网络。 */
int ncm_api_parse_track_all(const char *json, size_t len, ncm_song_t *out, int max,
int *out_n, int64_t *total);
#ifdef __cplusplus
}
#endif