playlist-admin 2.15.67 → 2.15.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/cli/index.js +143 -3
  2. package/package.json +1 -1
package/cli/index.js CHANGED
@@ -11,10 +11,16 @@ import { fileURLToPath } from 'node:url';
11
11
 
12
12
  const HELP = `playlist-admin CLI
13
13
 
14
- Runs the same engine as the GUI app (shared Dart code). Requires a built
15
- release: flutter build windows --release
14
+ Runs the same engine as the GUI app (shared Dart code).
16
15
 
17
16
  Usage:
17
+ playlist-admin -v | --version | version Show CLI + GUI 版本,並檢查有沒有新版
18
+ playlist-admin -h | --help | help 顯示這份說明
19
+ playlist-admin update| upgrade 更新 CLI(npm)並提示 GUI 更新方式
20
+ playlist-admin where 顯示根目錄 / exe / 資料夾位置
21
+ playlist-admin doctor [--fast] [--verbose] [--no-stt]
22
+ 音訊庫健康檢查(覆蓋率/損毀/品質/語言/額度)
23
+
18
24
  playlist-admin pipeline [--step N] Run full pipeline (or from step N)
19
25
  playlist-admin podcast Run podcast pipeline
20
26
  playlist-admin status Show status
@@ -44,9 +50,40 @@ function projectRoot() {
44
50
  while (true) {
45
51
  if (fs.existsSync(path.join(dir, 'pubspec.yaml'))) return dir;
46
52
  const parent = path.dirname(dir);
47
- if (parent === dir) return null;
53
+ if (parent === dir) break;
48
54
  dir = parent;
49
55
  }
56
+ // 從 npm 全域包呼叫時 cwd 向上找不到 pubspec.yaml(npm 包不含 Dart 引擎),
57
+ // 這時要找 GUI 的安裝目錄——它才是真正持有 playlist-admin.exe 的地方。
58
+ // 沒有這段 fallback 的話,使用者每次都得手動設 PA_ROOT 才能跑任何命令。
59
+ return installedRoot();
60
+ }
61
+
62
+ /// GUI 安裝目錄候選(依序找第一個真的持有 exe 的)。
63
+ ///
64
+ /// 安裝版(Inno Setup)的 exe 在**目錄根層**,開發版在 build/windows/...,
65
+ /// 兩種都要認,否則只有開發者能從 npm 呼叫 CLI。
66
+ function installedRoot() {
67
+ const home = process.env.USERPROFILE || process.env.HOME || '';
68
+ const localAppData = process.env.LOCALAPPDATA || '';
69
+ const programFiles = process.env.ProgramFiles || 'C:\\Program Files';
70
+ const programFilesX86 =
71
+ process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)';
72
+ const dirs = [];
73
+ if (localAppData) {
74
+ dirs.push(path.join(localAppData, 'Programs', 'playlist-admin'));
75
+ dirs.push(path.join(localAppData, 'playlist-admin'));
76
+ }
77
+ if (home) {
78
+ dirs.push(path.join(home, 'playlist-admin'));
79
+ dirs.push(path.join(home, 'Desktop', 'playlist-admin'));
80
+ }
81
+ dirs.push(path.join(programFiles, 'playlist-admin'));
82
+ dirs.push(path.join(programFilesX86, 'playlist-admin'));
83
+ for (const d of dirs) {
84
+ if (exePath(d)) return d;
85
+ }
86
+ return null;
50
87
  }
51
88
 
52
89
  /// npm 包自身目錄(全域 npm i -g 時,cwd 向上找不到 pubspec,
@@ -60,7 +97,11 @@ function packageRoot() {
60
97
  }
61
98
 
62
99
  function exePath(root) {
100
+ if (!root) return null;
63
101
  const candidates = [
102
+ // 安裝版(Inno Setup 打包):exe 直接在安裝目錄根層
103
+ path.join(root, 'playlist-admin.exe'),
104
+ // 開發版:flutter build windows --release 的輸出
64
105
  path.join(root, 'build', 'windows', 'x64', 'runner', 'Release', 'playlist-admin.exe'),
65
106
  path.join(root, 'build', 'windows', 'x64', 'runner', 'Debug', 'playlist-admin.exe'),
66
107
  path.join(root, 'build', 'windows', 'arm64', 'runner', 'Release', 'playlist-admin.exe'),
@@ -243,12 +284,86 @@ function runSkill(args) {
243
284
  process.exit(1);
244
285
  }
245
286
 
287
+ /// npm 包自身的版本(package.json)。CI 由 git tag 填入。
288
+ function packageVersion() {
289
+ try {
290
+ const pkg = path.join(packageRoot() || '', 'package.json');
291
+ return JSON.parse(fs.readFileSync(pkg, 'utf8')).version || '?';
292
+ } catch {
293
+ return '?';
294
+ }
295
+ }
296
+
297
+ /// GUI 引擎(playlist-admin.exe)的版本。
298
+ /// exe 的 Windows VersionInfo 沒被 CI 改(仍是 pubspec 的 3.0.0-beta.1),
299
+ /// 所以直接問引擎自己——它知道 build 時注入的 APP_VERSION。
300
+ function guiVersion(exe) {
301
+ if (!exe || !fs.existsSync(exe)) return null;
302
+ const r = spawnSync(exe, [], {
303
+ env: { ...process.env, PA_CLI_ARGS: '["version"]' },
304
+ encoding: 'utf8',
305
+ windowsHide: true,
306
+ timeout: 30000,
307
+ });
308
+ const out = (r.stdout || '').trim();
309
+ const m = out.match(/Engine\s+(\S+)/);
310
+ if (m) return m[1];
311
+ // 引擎起不來就退回修改時間,至少讓使用者看得出新舊
312
+ try {
313
+ const mt = fs.statSync(exe).mtime.toISOString().slice(0, 16).replace('T', ' ');
314
+ return `(exe 最後修改 ${mt})`;
315
+ } catch {
316
+ return null;
317
+ }
318
+ }
319
+
320
+ function cmdVersion() {
321
+ const cli = packageVersion();
322
+ const root = installedRoot() || projectRoot();
323
+ const exe = exePath(root);
324
+ const gv = guiVersion(exe);
325
+ console.log(`CLI ${cli}`);
326
+ console.log(gv ? `GUI ${gv} (${exe})` : 'GUI (找不到安裝中的 GUI)');
327
+ if (gv && !gv.startsWith('(') && gv !== cli) {
328
+ console.log('⚠️ CLI 與 GUI 版本不一致 → 先更新 GUI 再用 CLI');
329
+ }
330
+ const latest = runQuiet('npm', ['view', 'playlist-admin', 'version']);
331
+ if (latest && latest !== cli) {
332
+ console.log(`最新 ${latest} → npm i -g playlist-admin@latest`);
333
+ }
334
+ }
335
+
336
+ function runQuiet(cmd, cmdArgs) {
337
+ try {
338
+ const r = spawnSync(cmd, cmdArgs, { encoding: 'utf8', windowsHide: true });
339
+ return (r.stdout || '').trim().split('\n').pop();
340
+ } catch {
341
+ return null;
342
+ }
343
+ }
344
+
246
345
  async function main() {
247
346
  const args = process.argv.slice(2);
248
347
  if (args.length === 0) {
249
348
  console.log(HELP);
250
349
  return;
251
350
  }
351
+ // 標準旗標
352
+ if (args[0] === '-h' || args[0] === '--help' || args[0] === 'help') {
353
+ console.log(HELP);
354
+ return;
355
+ }
356
+ if (args[0] === '-v' || args[0] === '--version' || args[0] === 'version') {
357
+ cmdVersion();
358
+ return;
359
+ }
360
+ if (args[0] === '--doctor') {
361
+ args[0] = 'doctor';
362
+ }
363
+ if (args[0] === 'update' || args[0] === 'upgrade') {
364
+ cmdUpdate();
365
+ return;
366
+ }
252
367
  if (args[0] === 'rag') {
253
368
  runRag(args.slice(1));
254
369
  return;
@@ -261,6 +376,13 @@ async function main() {
261
376
  runSkill(args.slice(1));
262
377
  return;
263
378
  }
379
+ if (args[0] === 'where' || args[0] === 'doctor-env') {
380
+ const root = installedRoot() || projectRoot();
381
+ console.log(`根目錄: ${root || '(找不到)'}`);
382
+ console.log(`exe : ${exePath(root) || '(找不到)'}`);
383
+ console.log(`資料 : ${path.join(root || '?', 'data')}`);
384
+ return;
385
+ }
264
386
 
265
387
  const root = projectRoot();
266
388
  if (!root) {
@@ -277,6 +399,24 @@ async function main() {
277
399
  forward(exe, args);
278
400
  }
279
401
 
402
+ /// 更新 CLI 與 GUI。
403
+ function cmdUpdate() {
404
+ console.log('更新 CLI (npm)...');
405
+ const r = spawnSync('npm', ['install', '-g', 'playlist-admin@latest'], {
406
+ stdio: 'inherit',
407
+ windowsHide: true,
408
+ });
409
+ if (r.status !== 0) {
410
+ console.error('npm 更新失敗,試試用系統管理員權限重跑');
411
+ process.exit(1);
412
+ }
413
+ console.log('');
414
+ console.log('更新 GUI...');
415
+ console.log('GUI 是獨立安裝檔,請下載最新的 Setup 執行檔:');
416
+ console.log(' https://github.com/thumb2086/playlist-admin/releases/latest');
417
+ console.log('(或直接在 app 設定頁按「檢查更新」)');
418
+ }
419
+
280
420
  main().catch((e) => {
281
421
  console.error(e);
282
422
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playlist-admin",
3
- "version": "2.15.67",
3
+ "version": "2.15.69",
4
4
  "description": "playlist-admin CLI - pipeline, podcast, music + podcast RAG + study RAG (PDF 試題/課本)",
5
5
  "type": "module",
6
6
  "bin": {