meow_ui.a→3e0ee8f7 launcher→9d0efc2f hello→9609ea6e files→90f5cf07 ncm→be8be221(均已上卡); 真机截图证据 .tmp/meowbug/fix1.png+fix_b2_after.png
186 lines
6.8 KiB
C
186 lines
6.8 KiB
C
/* logic/appstate.c — 应用全局状态:ctx/登录/歌单/歌曲/设置。仅依赖核心库 + SDK meow_config_*。 */
|
||
#include "../ncm_app_internal.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#define NCM_DEFAULT_DATA "/usr/data/mnt/sd_0/MeowData/dev.player.ncm/data"
|
||
|
||
int appstate_init(void)
|
||
{
|
||
const char *dd = getenv("MEOW_DATA_DIR");
|
||
if (!dd || !dd[0]) dd = NCM_DEFAULT_DATA;
|
||
strncpy(g_st.data_dir, dd, sizeof g_st.data_dir - 1);
|
||
g_st.data_dir[sizeof g_st.data_dir - 1] = 0;
|
||
|
||
ncm_ctx_init(&g_st.ctx, g_st.data_dir); /* device_id + cookie 载入 + cache */
|
||
if (g_st.ctx.cache.dir[0])
|
||
strncpy(g_st.cache_dir, g_st.ctx.cache.dir, sizeof g_st.cache_dir - 1);
|
||
else
|
||
ncm_ctx_default_cache_dir(g_st.data_dir, g_st.cache_dir, sizeof g_st.cache_dir);
|
||
|
||
appstate_load_settings();
|
||
g_st.logged_in = (g_st.ctx.cookie[0] != 0);
|
||
g_st.active_playlist = -1;
|
||
g_st.playing_song = -1;
|
||
g_st.qr_size = 0;
|
||
g_st.login_state = NCM_LOGIN_PENDING;
|
||
|
||
/* uid/nickname 从配置恢复(登录时落盘);uid 缺失时不在此阻塞校验——由 home_reload 拉歌单时
|
||
* appstate_ensure_uid 触发 login_status_check(启动快,避免首屏长时间网络阻塞)。 */
|
||
meow_config_get("ncm.uid", g_st.uid, sizeof g_st.uid);
|
||
meow_config_get("ncm.nickname", g_st.nickname, sizeof g_st.nickname);
|
||
|
||
return 0;
|
||
}
|
||
|
||
void appstate_load_settings(void)
|
||
{
|
||
g_st.cache_limit_gb = meow_config_int("ncm.cache_limit_gb", 10);
|
||
if (g_st.cache_limit_gb < 1) g_st.cache_limit_gb = 1;
|
||
if (g_st.cache_limit_gb > 100) g_st.cache_limit_gb = 100;
|
||
|
||
g_st.level = meow_config_int("ncm.level", NCM_LEVEL_HIRES);
|
||
if (g_st.level < NCM_LEVEL_STANDARD || g_st.level > NCM_LEVEL_HIRES)
|
||
g_st.level = NCM_LEVEL_HIRES;
|
||
|
||
ncm_cache_set_limit_gb(&g_st.ctx.cache, g_st.cache_limit_gb);
|
||
g_st.ctx.default_level = g_st.level;
|
||
}
|
||
|
||
void appstate_save_settings(void)
|
||
{
|
||
char b[16];
|
||
snprintf(b, sizeof b, "%d", g_st.cache_limit_gb);
|
||
meow_config_set("ncm.cache_limit_gb", b);
|
||
snprintf(b, sizeof b, "%d", g_st.level);
|
||
meow_config_set("ncm.level", b);
|
||
ncm_cache_set_limit_gb(&g_st.ctx.cache, g_st.cache_limit_gb);
|
||
g_st.ctx.default_level = g_st.level;
|
||
}
|
||
|
||
void appstate_set_logged_in(const char *uid, const char *nickname)
|
||
{
|
||
if (uid && uid[0]) { ncm_strlcpy(g_st.uid, uid, sizeof g_st.uid); meow_config_set("ncm.uid", uid); }
|
||
if (nickname && nickname[0]) {
|
||
ncm_strlcpy(g_st.nickname, nickname, sizeof g_st.nickname);
|
||
meow_config_set("ncm.nickname", nickname);
|
||
}
|
||
g_st.logged_in = 1;
|
||
}
|
||
|
||
void appstate_logout(void)
|
||
{
|
||
ncm_ctx_clear_cookie(&g_st.ctx);
|
||
g_st.logged_in = 0;
|
||
g_st.uid[0] = 0;
|
||
g_st.nickname[0] = 0;
|
||
g_st.active_playlist = -1;
|
||
g_st.playing_song = -1;
|
||
g_st.song_count = 0;
|
||
meow_config_remove("ncm.uid");
|
||
meow_config_remove("ncm.nickname");
|
||
cover_cache_reset();
|
||
}
|
||
|
||
/* 若 uid 缺失(cookie 在但没存 uid),用 login_status_check 校验并取回。返回 0=成功。 */
|
||
int appstate_ensure_uid(void)
|
||
{
|
||
if (!g_st.logged_in || !g_st.ctx.cookie[0]) return NCM_ERR_AUTH;
|
||
if (g_st.uid[0]) return NCM_OK;
|
||
char uid[32], nick[128];
|
||
int rc = ncm_login_status_check(g_st.ctx.device_id, g_st.ctx.cookie, uid, sizeof uid,
|
||
nick, sizeof nick);
|
||
if (rc == NCM_OK) {
|
||
appstate_set_logged_in(uid, nick);
|
||
return NCM_OK;
|
||
}
|
||
/* 会话失效:清 cookie → 未登录 */
|
||
if (rc == NCM_ERR_AUTH) {
|
||
ncm_ctx_clear_cookie(&g_st.ctx);
|
||
g_st.logged_in = 0;
|
||
}
|
||
return rc;
|
||
}
|
||
|
||
int appstate_load_playlists(void)
|
||
{
|
||
if (!g_st.logged_in) return NCM_ERR_AUTH;
|
||
int rc = appstate_ensure_uid();
|
||
if (rc != NCM_OK) return rc;
|
||
memset(g_st.playlists, 0, sizeof g_st.playlists);
|
||
int n = 0;
|
||
rc = ncm_api_user_playlist(g_st.ctx.device_id, g_st.ctx.cookie, g_st.uid,
|
||
NCM_PLAYLIST_MAX, 0, g_st.playlists, NCM_PLAYLIST_MAX, &n);
|
||
g_st.playlist_count = (rc == NCM_OK) ? n : 0;
|
||
meow_log(MEOW_LOG_INFO, "home: playlists rc=%d count=%d uid=%s", rc, g_st.playlist_count, g_st.uid);
|
||
return rc;
|
||
}
|
||
|
||
int appstate_load_playlist_songs_page(int idx, int offset, int limit)
|
||
{
|
||
if (idx < 0 || idx >= g_st.playlist_count) return NCM_ERR_ARG;
|
||
/* 不在此处写 g_st.active_playlist:调用方(playlist_open/UI)已在 UI 线程设定;
|
||
* 若后台 worker 在此改写,会覆盖用户已切换到的新歌单,造成“打开 B 却显示 A”。 */
|
||
const ncm_playlist_t *pl = &g_st.playlists[idx];
|
||
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; }
|
||
|
||
if (offset == 0) g_st.song_count = 0; /* 重头:清空旧歌单(含此前打开的另一歌单) */
|
||
|
||
/* 追加到 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;
|
||
appstate_apply_playlist_cover(idx);
|
||
return NCM_OK;
|
||
}
|
||
|
||
int appstate_load_playlist_songs(int idx)
|
||
{
|
||
/* 兼容入口:整批载入(内部按页循环到 NCM_SONG_MAX 或歌单尾部)。 */
|
||
int rc, loaded = 0;
|
||
do {
|
||
rc = appstate_load_playlist_songs_page(idx, loaded, NCM_PLAYLIST_PAGE);
|
||
if (rc != NCM_OK) return rc;
|
||
int before = loaded;
|
||
loaded = g_st.song_count;
|
||
if (loaded - before < NCM_PLAYLIST_PAGE) break; /* 尾部:已载完 */
|
||
if (loaded >= NCM_SONG_MAX) break;
|
||
} while (1);
|
||
return NCM_OK;
|
||
}
|
||
|
||
void appstate_apply_playlist_cover(int idx)
|
||
{
|
||
if (idx < 0 || idx >= g_st.playlist_count) return;
|
||
ncm_strlcpy(g_st.cover_url, g_st.playlists[idx].cover, sizeof g_st.cover_url);
|
||
}
|
||
|
||
/* 设置播放页当前歌曲 + 封面 URL(player 页 UI 读)。idx=-1 表示清除。 */
|
||
void appstate_set_playing_song(int idx, const char *cover_url)
|
||
{
|
||
g_st.playing_song = idx;
|
||
if (cover_url && cover_url[0])
|
||
ncm_strlcpy(g_st.cover_url, cover_url, sizeof g_st.cover_url);
|
||
}
|
||
|
||
int appstate_current_playing(void)
|
||
{
|
||
if (g_st.playing_song < 0 || g_st.playing_song >= g_st.song_count) return -1;
|
||
return g_st.playing_song;
|
||
}
|