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:
1 parent
05a19a9ea3
commit
ab4da09e75
45 files changed
+1926
-147
No files matched your search
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user