Files
MeowOS-For-R3P2/patch_v2/ncm_app/app/app.c
T
AskaEth cd7329d141 feat(ncm): MUSIC_U 临期主动续期 — 启动查龄超阈值后台静默刷新/eapi 会话锚点/cookie 合并+冷却
用户定案:失效前用当前 cookie 换新登录态(用户无感);仅刷新失败才退扫码重登。

- core/ncm_req.c|h:eapi Cookie 头补齐会话锚点 MUSIC_U/MUSIC_A(对齐 request.rs,
  补 §36.5 遗留——登录刷新/song_url 会员档依赖);新增 ncm_req_cookie_merge
  (patch 覆盖 base 同名 cookie,MUSIC_A/__remember_me 等不丢)。
- core/ncm_login.c|h:ncm_login_refresh(eapi /api/login/token/refresh 空体,
  code200+set-cookie→merge;code!=200→NCM_ERR_AUTH 退扫码;网络/TLS→负码退避)。
- app/logic/account.c:account.json 增 last_refresh_ts + 拆分 cvip/svip;
  account_cookie_maybe_refresh(年龄阈值 12h 可配 ncm.cookie_refresh_h,单飞/boot,
  失败冷却 30min 跨 boot 持久化,AUTH 置回登录页标志)+ mark_fresh/need_relogin。
- app/ui/home.c:home_worker 先续期再拉歌单;app/ui/login.c:803 落盘后 mark_fresh;
  app/app.c:UI tick 消费回登录标志 → logout+切登录页。
- 新增测试:test_ncm_core 的 cookie_merge + eapi_musicu_hdr;bench/cookie_refresh_sim
  (端点语义,0 网络);bench/account_cookie_sim(临期/未临期/退避/失效/冷却,0 网络)。

验证:host+qemu 核心测试 PASS;build_ncm_sims.sh 全家桶 PASS;build_ncm.sh 0 warning。
真机只读探针 2 次(设备 10.78.193.50,不写回):refresh 走 eapi 域
interface.music.163.com 在当前网络被 TLS reset(alert 80)→ rc=-5 瞬断退避,
session.enc/deviceId md5 前后一致(登录态零损伤)。新 ncm md5 c960244b。
2026-09-02 14:30:04 +08:00

222 lines
9.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* app.c — dev.player.ncm 应用装配层:main + 组装 + 事件分发。
*
* 职责(对齐 hello_app/app.c):main 只做初始化/建页/跑事件循环/清理;把 meow_ui_run 的平铺输入
* 回调(key/tap/swipe/tick/touch)路由到当前活动页。页面切换统一走 meow_view_page_push/pop/replace。
* 播放线程(player_manager)+ 媒体会话 IPC 线程(ipc_media)在 main 起动、退出时停。
*
* 铁律(APPS.md §3.4.1):物理键禁映射——on_key 不处理任何键语义;返回=左缘左滑/返回钮/request_exit。
* 左缘返回:播放页→歌单→主页→退出(登录页→退出)。
*/
#include "ncm_app_internal.h"
#include <signal.h>
#include <ucontext.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
ncm_app_state_t g_st;
ncm_play_run_t g_play = { .mu = PTHREAD_MUTEX_INITIALIZER,
.state = NCM_PC_STOPPED, .cur_song = -1 };
volatile int g_app_quit = 0;
volatile int g_net_ready = 0;
int g_host_mock = 0;
static meow_view *g_login_view, *g_home_view, *g_pl_view, *g_plr_view, *g_set_view,
*g_search_view;
static void on_sigterm(int s) { (void)s; meow_ui_request_exit(); }
/* SIGSEGV/SIGBUS/SIGILL 定位处理器(真机复现调试用):**必须 async-signal-safe**(只 write+_exit,
* 手写十六进制格式化,不用 snprintf/sprintf——后者内部取锁,在信号上下文可能自锁死)。把
* si_addr + 中断点 pc + ra/sp/fp(MIPS O32:mcontext_t.pc / gregs[31 ra][29 sp][30 fp])
* 写 stderr(amsd 捕获)+ 固定文件,配合 nm/链接布局定位崩溃函数。 */
static void fatal_sig_report(int sig, siginfo_t *si, void *ctx)
{
ucontext_t *uc = (ucontext_t *)ctx;
unsigned long long v = 0;
unsigned char b[192];
int o = 0;
const char *tag = (sig == SIGSEGV) ? "SIGSEGV" : (sig == SIGBUS ? "SIGBUS" : "SIGILL");
#define FATAL_PUTS(s) do { const char *_p = (s); while (*_p) b[o++] = (unsigned char)*_p++; } while (0)
#define FATAL_HEX(x) do { unsigned long long _v = (unsigned long long)(x); \
int _sh; \
for (_sh = 28; _sh >= 0; _sh -= 4) b[o++] = \
(unsigned char)"0123456789abcdef"[(_v >> _sh) & 15]; } while (0)
#define FATAL_STR(h, x) do { FATAL_PUTS(h); FATAL_HEX(x); } while (0)
FATAL_PUTS("\nFATAL "); FATAL_PUTS(tag);
FATAL_PUTS(" sig="); FATAL_HEX(sig);
v = (unsigned long long)(uintptr_t)si->si_addr; FATAL_STR(" addr=", v);
v = uc->uc_mcontext.pc; FATAL_STR(" pc=", v);
v = uc->uc_mcontext.gregs[31]; FATAL_STR(" ra=", v);
v = uc->uc_mcontext.gregs[29]; FATAL_STR(" sp=", v);
v = uc->uc_mcontext.gregs[30]; FATAL_STR(" fp=", v);
FATAL_PUTS("\n");
#undef FATAL_PUTS
#undef FATAL_HEX
#undef FATAL_STR
(void)write(2, b, (size_t)o);
int fd = open("/usr/data/mnt/sd_0/MeowOS/logs/dev.player.ncm/crash_report.txt",
O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd >= 0) { (void)write(fd, b, (size_t)o); close(fd); }
_exit(128 + sig);
}
static void fatal_sig_install(void)
{
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_sigaction = fatal_sig_report;
sa.sa_flags = SA_SIGINFO; /* 不置 SA_NODEFER:防信号内重入 */
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
}
/* ============================================================ */
/* 事件分发:meow_ui_run 平铺回调 → 当前页 */
/* ============================================================ */
static void app_on_key(int keycode, int is_down)
{
(void)keycode; (void)is_down; /* 物理键系统语义;App 不映射(§3.4.1) */
}
static void app_on_tap(int x, int y)
{
if (login_is_active()) { login_on_tap(x, y); return; }
if (player_is_active()) { player_on_tap(x, y); return; }
if (settings_is_active()) { settings_on_tap(x, y); return; }
if (playlist_is_active()) { playlist_on_tap(x, y); return; }
if (search_is_active()) { search_on_tap(x, y); return; }
home_on_tap(x, y);
}
static void app_on_swipe(int dir, int dx, int dy)
{
(void)dx;
if (dir == MEOW_SWIPE_LEFT) {
/* 搜索历史模式:左滑删单条(§23);未消费才走上/下一层返回语义。 */
if (search_is_active() && search_swipe_left()) return;
if (player_is_active()) { meow_view_page_pop(); return; }
if (settings_is_active()) { meow_view_page_pop(); return; }
if (playlist_is_active()) { meow_view_page_pop(); return; }
if (search_is_active()) { meow_view_page_pop(); return; }
/* home / login = 根 → 退出 */
meow_log(MEOW_LOG_INFO, "exit:swipe");
meow_ui_request_exit();
return;
}
if (home_is_active()) { home_swipe(dir, dy); return; }
if (playlist_is_active()) { playlist_swipe(dir, dy); return; }
if (search_is_active()) { search_swipe(dir, dy); return; }
if (player_is_active()) { player_on_swipe(dir, dy); return; }
}
static void app_on_touch_down(int x, int y)
{
if (player_is_active()) player_touch_down(x, y); /* §22 中部横滑页切换跟踪 */
else if (home_is_active()) home_touch_down(x, y);
else if (playlist_is_active()) playlist_touch_down(x, y);
else if (settings_is_active()) settings_touch_down(x, y);
else if (search_is_active()) search_touch_down(x, y);
}
static void app_on_touch_move(int x, int y)
{
if (player_is_active()) player_touch_move(x, y);
else if (home_is_active()) home_touch_move(x, y);
else if (playlist_is_active()) playlist_touch_move(x, y);
else if (settings_is_active()) settings_touch_move(x, y);
else if (search_is_active()) search_touch_move(x, y);
}
static void app_on_swipe_progress(int x_start, int dx)
{
meow_ui_swipe_hint(x_start + dx);
}
static void app_on_tick(void)
{
uint64_t now = meow_ui_time_ms();
/* §NEW cookie 续期判会话失效 → 清登录态回登录页(worker 在后台续期发现 301/-460 时置标志;
* 在此 UI 线程执行 logout+切页——meow_view_page_replace 非线程安全,只能 UI 线程做) */
if (account_cookie_need_relogin()) {
account_cookie_relogin_consume();
meow_log(MEOW_LOG_WARN, "account: cookie invalid -> re-login");
appstate_logout();
meow_view_page_replace(ACT_LOGIN);
}
autoplay_maybe(); /* 真机联调 hook:/tmp/ncm_autoplay 存在则自动播已缓存曲目(一次性) */
/* autoplay 已在后台线程起播缓存曲 → 在此(UI 线程)打开播放页,复现用户点曲播放路径 */
{
int ai = autoplay_ui_consume();
if (ai >= 0) player_play_song(ai);
}
if (login_is_active()) { login_tick(now); return; }
if (player_is_active()) { player_tick(now); return; }
if (search_is_active()) { search_tick(now); return; }
if (home_is_active()) { home_tick(now); }
if (playlist_is_active()) { playlist_tick(now); }
chrome_tick();
}
/* ============================================================ */
/* main */
/* ============================================================ */
int main(void)
{
meow_log(MEOW_LOG_INFO, "start");
meow_ui_opts o = { APP_W, APP_H };
if (meow_ui_init(&o) != 0) { meow_log(MEOW_LOG_ERROR, "init failed"); return 1; }
/* 主题侦听:登记真实回调(旧代码 pass NULL 无效),系统明暗切换 → ncm_on_theme 实时换肤。
* 初始同步一次 g_dark(listener 只在变化时被调,初次状态需显式对齐)。 */
g_dark = (meow_theme_get_mode() == MEOW_THEME_DARK);
meow_theme_add_listener(ncm_on_theme);
signal(SIGTERM, on_sigterm);
fatal_sig_install(); /* SIGSEGV/SIGBUS/SIGILL 写崩溃地址定位(真机复现调试) */
/* 应用数据/核心库上下文(cookie/deviceId/cache/设置) */
appstate_init();
/* M2.82-联调修复:设备无系统 CA 库(真机 rc=-5 TLS 错误根因)——App 自带 Mozilla CA bundle,
* 显式指给 core HTTP 层(保持 verify=1,不降级为不校验) */
ncm_http_set_verify_default(1, "/usr/data/mnt/sd_0/MeowAPPs/dev.player.ncm/cacert.pem");
/* 播放 + 媒体会话线程(UI 不卡;无 launcher socket 时 ipc 优雅跳过) */
player_manager_start();
ipc_media_start();
g_net_ready = 1;
/* 建六页 view */
g_login_view = login_view_create();
g_home_view = home_view_create();
g_pl_view = playlist_view_create();
g_plr_view = player_view_create();
g_set_view = settings_view_create();
g_search_view = search_view_create();
/* 进入根页:已登录→home,否则→login */
meow_view_page_push(g_st.logged_in ? ACT_HOME : ACT_LOGIN);
meow_ui_events ev = {
app_on_key, app_on_tap, app_on_swipe, app_on_tick, app_on_swipe_progress,
app_on_touch_down, app_on_touch_move,
};
meow_ui_run(&ev);
/* 清理:停线程 → 释放页 */
g_app_quit = 1;
player_manager_stop_thread();
ipc_media_stop();
meow_view_free(g_set_view);
meow_view_free(g_plr_view);
meow_view_free(g_pl_view);
meow_view_free(g_home_view);
meow_view_free(g_login_view);
meow_view_free(g_search_view);
meow_ui_shutdown();
meow_log(MEOW_LOG_INFO, "exit");
return 0;
}