reelsort 0.1.2 → 0.2.0

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.
package/dist/cli.js CHANGED
@@ -24,13 +24,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/program.ts
27
- var import_termkit = require("termkit");
27
+ var import_termkit19 = require("termkit");
28
28
 
29
29
  // src/actions/add.ts
30
- var import_cosmetic = __toESM(require("cosmetic"));
31
30
  var import_fs3 = require("fs");
32
31
  var import_path3 = require("path");
33
- var import_termpulse2 = require("termpulse");
32
+ var import_termkit2 = require("termkit");
34
33
 
35
34
  // src/config.ts
36
35
  var import_fs = require("fs");
@@ -277,28 +276,21 @@ var searchTv = async (title, apiKey) => {
277
276
  };
278
277
 
279
278
  // src/refs/spinner.ts
280
- var import_termpulse = require("termpulse");
279
+ var import_termkit = require("termkit");
281
280
  var Spinner = class {
282
281
  spinner;
283
- _isSpinning = false;
284
- _text = "";
285
282
  constructor() {
286
- this.spinner = new import_termpulse.Spinner();
283
+ this.spinner = new import_termkit.Spinner();
287
284
  }
288
285
  get text() {
289
- return this._text;
286
+ return this.spinner.text;
290
287
  }
291
288
  set text(t) {
292
- this._text = t;
293
- if (this._isSpinning) this.spinner.message(t);
294
- }
295
- get isSpinning() {
296
- return this._isSpinning;
289
+ this.spinner.text = t;
297
290
  }
298
291
  start(s) {
299
- if (s) this._text = s;
292
+ if (s) this.spinner.text = s;
300
293
  this.spinner.start();
301
- this._isSpinning = true;
302
294
  return this;
303
295
  }
304
296
  info(s) {
@@ -319,8 +311,6 @@ var Spinner = class {
319
311
  }
320
312
  stop() {
321
313
  this.spinner.stop();
322
- this._isSpinning = false;
323
- process.stdin.resume();
324
314
  return this;
325
315
  }
326
316
  };
@@ -338,7 +328,7 @@ var add = async ({ name }) => {
338
328
  if (results.length === 0) throw new Error(`no TMDb results for "${name}"`);
339
329
  let picked = results.length === 1 ? results[0] : null;
340
330
  if (!picked) {
341
- const select = new import_termpulse2.Select();
331
+ const select = new import_termkit2.Select();
342
332
  const items = results.map((r) => ({
343
333
  label: r.year ? `${r.title} (${r.year})` : r.title,
344
334
  description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
@@ -353,14 +343,14 @@ var add = async ({ name }) => {
353
343
  (0, import_fs3.mkdirSync)(showPath, { recursive: true });
354
344
  upsertShow(showPath, picked.id, picked.title);
355
345
  spinner_default.start();
356
- spinner_default.succeed(`added ${import_cosmetic.default.cyan.encoder(folderName)}`);
346
+ spinner_default.succeed(`added ${import_termkit2.Color.cyan.encoder(folderName)}`);
357
347
  spinner_default.stop();
358
348
  };
359
349
  var add_default = add;
360
350
 
361
351
  // src/actions/clean.ts
362
- var import_cosmetic2 = __toESM(require("cosmetic"));
363
352
  var import_fs4 = require("fs");
353
+ var import_termkit3 = require("termkit");
364
354
  var parseOlderThan = (s) => {
365
355
  const match = s.match(/^(\d+)([dhm])$/);
366
356
  if (!match) return null;
@@ -393,17 +383,17 @@ var clean = async ({ dryRun, olderThan }) => {
393
383
  continue;
394
384
  }
395
385
  if (dryRun) {
396
- spinner_default.succeed(`[dry] would remove ${import_cosmetic2.default.blue.encoder(imp.sourcePath)}`);
386
+ spinner_default.succeed(`[dry] would remove ${import_termkit3.Color.blue.encoder(imp.sourcePath)}`);
397
387
  cleaned++;
398
388
  continue;
399
389
  }
400
390
  try {
401
391
  (0, import_fs4.rmSync)(imp.sourcePath, { recursive: true, force: true });
402
392
  deleteImport(imp.id);
403
- spinner_default.succeed(`removed ${import_cosmetic2.default.blue.encoder(imp.sourcePath)}`);
393
+ spinner_default.succeed(`removed ${import_termkit3.Color.blue.encoder(imp.sourcePath)}`);
404
394
  cleaned++;
405
395
  } catch {
406
- spinner_default.warn(`locked or inaccessible, skipped: ${import_cosmetic2.default.blue.encoder(imp.sourcePath)}`);
396
+ spinner_default.warn(`locked or inaccessible, skipped: ${import_termkit3.Color.blue.encoder(imp.sourcePath)}`);
407
397
  skipped++;
408
398
  }
409
399
  }
@@ -414,8 +404,8 @@ var clean = async ({ dryRun, olderThan }) => {
414
404
  var clean_default = clean;
415
405
 
416
406
  // src/actions/config.ts
417
- var import_cosmetic3 = __toESM(require("cosmetic"));
418
407
  var import_path4 = require("path");
408
+ var import_termkit4 = require("termkit");
419
409
 
420
410
  // src/helpers/formatEpisode.ts
421
411
  var DEFAULT_EPISODE_FORMAT = "{s}x{ee}";
@@ -435,37 +425,64 @@ var formatEpisode = (season, episode, format = DEFAULT_EPISODE_FORMAT, double =
435
425
 
436
426
  // src/actions/config.ts
437
427
  var DEST_TYPES = ["movie", "tv", "ps3"];
438
- var configAdd = async ({ key, value }) => {
439
- if (key !== "source") throw new Error(`unknown key '${key}', expected: source`);
440
- const dir = (0, import_path4.resolve)(value);
428
+ var sourceAdd = async ({ dir }) => {
429
+ const resolved = (0, import_path4.resolve)(dir);
441
430
  const config = getConfig();
442
- if (config.sources.includes(dir)) {
431
+ if (config.sources.includes(resolved)) {
443
432
  spinner_default.start();
444
- spinner_default.info(`source already configured: ${import_cosmetic3.default.blue.encoder(dir)}`);
433
+ spinner_default.info(`source already configured: ${import_termkit4.Color.blue.encoder(resolved)}`);
445
434
  spinner_default.stop();
446
435
  return;
447
436
  }
448
- config.sources.push(dir);
437
+ config.sources.push(resolved);
449
438
  saveConfig(config);
450
439
  spinner_default.start();
451
- spinner_default.succeed(`added source: ${import_cosmetic3.default.blue.encoder(dir)}`);
440
+ spinner_default.succeed(`added source: ${import_termkit4.Color.blue.encoder(resolved)}`);
452
441
  spinner_default.stop();
453
442
  };
454
- var configRemove = async ({ key, value }) => {
455
- if (key !== "source") throw new Error(`unknown key '${key}', expected: source`);
456
- const dir = (0, import_path4.resolve)(value);
443
+ var sourceRemove = async ({ dir }) => {
444
+ const resolved = (0, import_path4.resolve)(dir);
457
445
  const config = getConfig();
458
- const index = config.sources.indexOf(dir);
446
+ const index = config.sources.indexOf(resolved);
459
447
  if (index === -1) {
460
448
  spinner_default.start();
461
- spinner_default.warn(`source not found: ${import_cosmetic3.default.blue.encoder(dir)}`);
449
+ spinner_default.warn(`source not found: ${import_termkit4.Color.blue.encoder(resolved)}`);
462
450
  spinner_default.stop();
463
451
  return;
464
452
  }
465
453
  config.sources.splice(index, 1);
466
454
  saveConfig(config);
467
455
  spinner_default.start();
468
- spinner_default.succeed(`removed source: ${import_cosmetic3.default.blue.encoder(dir)}`);
456
+ spinner_default.succeed(`removed source: ${import_termkit4.Color.blue.encoder(resolved)}`);
457
+ spinner_default.stop();
458
+ };
459
+ var destAdd = async ({ type, dir }) => {
460
+ if (!DEST_TYPES.includes(type)) {
461
+ throw new Error(`unknown type '${type}', expected: ${DEST_TYPES.join(", ")}`);
462
+ }
463
+ const resolved = (0, import_path4.resolve)(dir);
464
+ const config = getConfig();
465
+ config.dest[type] = resolved;
466
+ saveConfig(config);
467
+ spinner_default.start();
468
+ spinner_default.succeed(`set ${type} destination: ${import_termkit4.Color.cyan.encoder(resolved)}`);
469
+ spinner_default.stop();
470
+ };
471
+ var destRemove = async ({ type }) => {
472
+ if (!DEST_TYPES.includes(type)) {
473
+ throw new Error(`unknown type '${type}', expected: ${DEST_TYPES.join(", ")}`);
474
+ }
475
+ const config = getConfig();
476
+ if (!config.dest[type]) {
477
+ spinner_default.start();
478
+ spinner_default.warn(`no ${type} destination configured`);
479
+ spinner_default.stop();
480
+ return;
481
+ }
482
+ delete config.dest[type];
483
+ saveConfig(config);
484
+ spinner_default.start();
485
+ spinner_default.succeed(`removed ${type} destination`);
469
486
  spinner_default.stop();
470
487
  };
471
488
  var configSet = async ({ key, subkey, value }) => {
@@ -474,7 +491,7 @@ var configSet = async ({ key, subkey, value }) => {
474
491
  config.language = subkey;
475
492
  saveConfig(config);
476
493
  spinner_default.start();
477
- spinner_default.succeed(`set subtitle language: ${import_cosmetic3.default.cyan.encoder(subkey)}`);
494
+ spinner_default.succeed(`set subtitle language: ${import_termkit4.Color.cyan.encoder(subkey)}`);
478
495
  spinner_default.stop();
479
496
  return;
480
497
  }
@@ -504,21 +521,11 @@ var configSet = async ({ key, subkey, value }) => {
504
521
  }
505
522
  saveConfig(config);
506
523
  spinner_default.start();
507
- spinner_default.succeed(`set ${subkey} format: ${import_cosmetic3.default.cyan.encoder(value ?? subkey)}`);
524
+ spinner_default.succeed(`set ${subkey} format: ${import_termkit4.Color.cyan.encoder(value ?? subkey)}`);
508
525
  spinner_default.stop();
509
526
  return;
510
527
  }
511
- if (key !== "dest") throw new Error(`unknown key '${key}', expected: dest, language, tmdb-key, format`);
512
- if (!DEST_TYPES.includes(subkey)) {
513
- throw new Error(`unknown type '${subkey}', expected: ${DEST_TYPES.join(", ")}`);
514
- }
515
- if (!value) throw new Error(`missing path for dest ${subkey}`);
516
- const dir = (0, import_path4.resolve)(value);
517
- config.dest[subkey] = dir;
518
- saveConfig(config);
519
- spinner_default.start();
520
- spinner_default.succeed(`set ${subkey} destination: ${import_cosmetic3.default.cyan.encoder(dir)}`);
521
- spinner_default.stop();
528
+ throw new Error(`unknown key '${key}', expected: language, tmdb-key, format`);
522
529
  };
523
530
  var configShow = async () => {
524
531
  const config = getConfig();
@@ -526,7 +533,7 @@ var configShow = async () => {
526
533
  if (config.sources.length === 0) {
527
534
  console.log(" (none)");
528
535
  } else {
529
- for (const s of config.sources) console.log(` ${import_cosmetic3.default.blue.encoder(s)}`);
536
+ for (const s of config.sources) console.log(` ${import_termkit4.Color.blue.encoder(s)}`);
530
537
  }
531
538
  console.log("\nDestinations:");
532
539
  const entries = DEST_TYPES.map((t) => ({ type: t, path: config.dest[t] })).filter((e) => e.path);
@@ -534,26 +541,26 @@ var configShow = async () => {
534
541
  console.log(" (none)");
535
542
  } else {
536
543
  for (const { type, path } of entries) {
537
- console.log(` ${type.padEnd(6)} ${import_cosmetic3.default.cyan.encoder(path)}`);
544
+ console.log(` ${type.padEnd(6)} ${import_termkit4.Color.cyan.encoder(path)}`);
538
545
  }
539
546
  }
540
547
  console.log(`
541
- Subtitle language: ${import_cosmetic3.default.cyan.encoder(config.language ?? "eng (default)")}`);
542
- console.log(`TMDb API key: ${config.tmdbApiKey ? import_cosmetic3.default.green.encoder("configured") : import_cosmetic3.default.red.encoder("not set")}`);
543
- console.log(`Movie format: ${import_cosmetic3.default.cyan.encoder(config.format?.movie ?? DEFAULT_MOVIE_FORMAT + " (default)")}`);
544
- console.log(`Episode format: ${import_cosmetic3.default.cyan.encoder(config.format?.episode ?? DEFAULT_EPISODE_FORMAT + " (default)")}`);
545
- console.log(`Season folder: ${import_cosmetic3.default.cyan.encoder(config.format?.season ?? DEFAULT_SEASON_FORMAT + " (default)")}`);
548
+ Subtitle language: ${import_termkit4.Color.cyan.encoder(config.language ?? "eng (default)")}`);
549
+ console.log(`TMDb API key: ${config.tmdbApiKey ? import_termkit4.Color.green.encoder("configured") : import_termkit4.Color.red.encoder("not set")}`);
550
+ console.log(`Movie format: ${import_termkit4.Color.cyan.encoder(config.format?.movie ?? DEFAULT_MOVIE_FORMAT + " (default)")}`);
551
+ console.log(`Episode format: ${import_termkit4.Color.cyan.encoder(config.format?.episode ?? DEFAULT_EPISODE_FORMAT + " (default)")}`);
552
+ console.log(`Season folder: ${import_termkit4.Color.cyan.encoder(config.format?.season ?? DEFAULT_SEASON_FORMAT + " (default)")}`);
546
553
  console.log();
547
554
  };
548
555
 
549
556
  // src/actions/differences.ts
550
- var import_cosmetic4 = __toESM(require("cosmetic"));
551
557
  var import_fs5 = require("fs");
552
558
  var import_path5 = require("path");
559
+ var import_termkit5 = require("termkit");
553
560
  var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
554
561
  let dir1 = rawDir1;
555
562
  let dir2 = rawDir2;
556
- spinner_default.text = `checking differences between ${import_cosmetic4.default.blue.encoder(dir1)} and ${import_cosmetic4.default.blue.encoder(dir2)}`;
563
+ spinner_default.text = `checking differences between ${import_termkit5.Color.blue.encoder(dir1)} and ${import_termkit5.Color.blue.encoder(dir2)}`;
557
564
  spinner_default.start();
558
565
  dir1 = (0, import_path5.resolve)(dir1);
559
566
  dir2 = (0, import_path5.resolve)(dir2);
@@ -589,18 +596,17 @@ var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
589
596
  removed.push(l);
590
597
  }
591
598
  }
592
- spinner_default.succeed(`checked differences between ${import_cosmetic4.default.blue.encoder(dir1)} and ${import_cosmetic4.default.blue.encoder(dir2)}`);
599
+ spinner_default.succeed(`checked differences between ${import_termkit5.Color.blue.encoder(dir1)} and ${import_termkit5.Color.blue.encoder(dir2)}`);
593
600
  spinner_default.succeed(`found ${added.length} added files`);
594
601
  spinner_default.succeed(`found ${removed.length} removed files`);
595
602
  spinner_default.stop();
596
- for (const i of added) console.log(`${import_cosmetic4.default.green.encoder("added")} ${i}`);
597
- for (const i of removed) console.log(`${import_cosmetic4.default.red.encoder("removed")} ${i}`);
603
+ for (const i of added) console.log(`${import_termkit5.Color.green.encoder("added")} ${i}`);
604
+ for (const i of removed) console.log(`${import_termkit5.Color.red.encoder("removed")} ${i}`);
598
605
  };
599
606
  var differences_default = differences;
600
607
 
601
608
  // src/actions/ended.ts
602
- var import_cosmetic5 = __toESM(require("cosmetic"));
603
- var import_termpulse3 = require("termpulse");
609
+ var import_termkit6 = require("termkit");
604
610
  var ended = async ({ remove }) => {
605
611
  const shows2 = getShows();
606
612
  const candidates = shows2.filter((s) => remove ? s.ended : !s.ended);
@@ -610,7 +616,7 @@ var ended = async ({ remove }) => {
610
616
  spinner_default.stop();
611
617
  return;
612
618
  }
613
- const select = new import_termpulse3.Select();
619
+ const select = new import_termkit6.Select();
614
620
  const items = candidates.map((s) => ({
615
621
  label: s.path.split("/").pop() ?? s.path,
616
622
  path: s.path
@@ -621,17 +627,17 @@ var ended = async ({ remove }) => {
621
627
  setShowEnded(picked.path, !remove);
622
628
  spinner_default.start();
623
629
  if (remove) {
624
- spinner_default.succeed(`marked as active: ${import_cosmetic5.default.cyan.encoder(picked.label)}`);
630
+ spinner_default.succeed(`marked as active: ${import_termkit6.Color.cyan.encoder(picked.label)}`);
625
631
  } else {
626
- spinner_default.succeed(`marked as ended: ${import_cosmetic5.default.cyan.encoder(picked.label)}`);
632
+ spinner_default.succeed(`marked as ended: ${import_termkit6.Color.cyan.encoder(picked.label)}`);
627
633
  }
628
634
  spinner_default.stop();
629
635
  };
630
636
  var ended_default = ended;
631
637
 
632
638
  // src/actions/history.ts
633
- var import_cosmetic6 = __toESM(require("cosmetic"));
634
639
  var import_path6 = require("path");
640
+ var import_termkit7 = require("termkit");
635
641
  var history = async ({ limit, imports }) => {
636
642
  if (imports) {
637
643
  const sessions = getImportHistory(limit ?? 10);
@@ -643,11 +649,11 @@ var history = async ({ limit, imports }) => {
643
649
  const date = new Date(session.sessionId);
644
650
  const label = isNaN(date.getTime()) ? session.sessionId : date.toLocaleString();
645
651
  console.log(`
646
- ${import_cosmetic6.default.yellow.encoder(label)} (${session.records.length} item${session.records.length !== 1 ? "s" : ""})`);
652
+ ${import_termkit7.Color.yellow.encoder(label)} (${session.records.length} item${session.records.length !== 1 ? "s" : ""})`);
647
653
  for (const r of session.records) {
648
654
  const src = (0, import_path6.basename)(r.sourcePath);
649
- const dest = import_cosmetic6.default.cyan.encoder(r.destinationPath);
650
- const mode = r.mode !== "move" ? ` ${import_cosmetic6.default.blue.encoder(`[${r.mode}]`)}` : "";
655
+ const dest = import_termkit7.Color.cyan.encoder(r.destinationPath);
656
+ const mode = r.mode !== "move" ? ` ${import_termkit7.Color.blue.encoder(`[${r.mode}]`)}` : "";
651
657
  console.log(` ${src} \u2192 ${dest}${mode}`);
652
658
  }
653
659
  }
@@ -662,11 +668,11 @@ ${import_cosmetic6.default.yellow.encoder(label)} (${session.records.length} it
662
668
  const label = isNaN(date.getTime()) ? session.sessionId : date.toLocaleString();
663
669
  const folders = session.records.filter((r) => (0, import_path6.extname)(r.newPath) === "");
664
670
  console.log(`
665
- ${import_cosmetic6.default.yellow.encoder(label)} (${folders.length} item${folders.length !== 1 ? "s" : ""})`);
671
+ ${import_termkit7.Color.yellow.encoder(label)} (${folders.length} item${folders.length !== 1 ? "s" : ""})`);
666
672
  for (const r of folders) {
667
673
  const oldName = (0, import_path6.basename)(r.oldPath);
668
674
  const newName = (0, import_path6.basename)(r.newPath);
669
- console.log(` ${import_cosmetic6.default.blue.encoder(oldName)} \u2192 ${import_cosmetic6.default.cyan.encoder(newName)}`);
675
+ console.log(` ${import_termkit7.Color.blue.encoder(oldName)} \u2192 ${import_termkit7.Color.cyan.encoder(newName)}`);
670
676
  }
671
677
  }
672
678
  }
@@ -675,10 +681,9 @@ ${import_cosmetic6.default.yellow.encoder(label)} (${folders.length} item${fold
675
681
  var history_default = history;
676
682
 
677
683
  // src/actions/link.ts
678
- var import_cosmetic7 = __toESM(require("cosmetic"));
679
684
  var import_fs6 = require("fs");
680
685
  var import_path7 = require("path");
681
- var import_termpulse4 = require("termpulse");
686
+ var import_termkit8 = require("termkit");
682
687
  var parseShowTitle = (folderName) => {
683
688
  const withoutYear = folderName.replace(/\s*\(\d{4}\)\s*$/, "").trim();
684
689
  return withoutYear || folderName;
@@ -705,7 +710,7 @@ var link = async ({ force }) => {
705
710
  skipped++;
706
711
  continue;
707
712
  }
708
- spinner_default.start(`linking ${import_cosmetic7.default.blue.encoder(show)}`);
713
+ spinner_default.start(`linking ${import_termkit8.Color.blue.encoder(show)}`);
709
714
  const title = parseShowTitle(show);
710
715
  const results = await searchTv(title, config.tmdbApiKey);
711
716
  if (results.length === 0) {
@@ -715,12 +720,12 @@ var link = async ({ force }) => {
715
720
  }
716
721
  if (results.length === 1) {
717
722
  upsertShow(showPath, results[0].id, results[0].title);
718
- spinner_default.succeed(`${show} \u2192 ${import_cosmetic7.default.cyan.encoder(results[0].title)} (${results[0].year})`);
723
+ spinner_default.succeed(`${show} \u2192 ${import_termkit8.Color.cyan.encoder(results[0].title)} (${results[0].year})`);
719
724
  linked++;
720
725
  continue;
721
726
  }
722
727
  spinner_default.stop();
723
- const select = new import_termpulse4.Select();
728
+ const select = new import_termkit8.Select();
724
729
  const items = results.map((r) => ({
725
730
  label: r.year ? `${r.title} (${r.year})` : r.title,
726
731
  description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
@@ -730,7 +735,7 @@ var link = async ({ force }) => {
730
735
  spinner_default.start();
731
736
  if (picked) {
732
737
  upsertShow(showPath, picked.id, picked.title);
733
- spinner_default.succeed(`${show} \u2192 ${import_cosmetic7.default.cyan.encoder(picked.title)} (${picked.year})`);
738
+ spinner_default.succeed(`${show} \u2192 ${import_termkit8.Color.cyan.encoder(picked.title)} (${picked.year})`);
734
739
  linked++;
735
740
  } else {
736
741
  spinner_default.info(`skipped: ${show}`);
@@ -745,9 +750,9 @@ var link = async ({ force }) => {
745
750
  var link_default = link;
746
751
 
747
752
  // src/actions/list.ts
748
- var import_cosmetic8 = __toESM(require("cosmetic"));
749
753
  var import_fs8 = require("fs");
750
754
  var import_path9 = require("path");
755
+ var import_termkit9 = require("termkit");
751
756
 
752
757
  // src/helpers/dirSize.ts
753
758
  var import_fs7 = require("fs");
@@ -879,7 +884,6 @@ var parseLibraryFolder = (name) => {
879
884
  if (match) return { title: match[1].trim(), year: parseInt(match[2]) };
880
885
  return { title: name };
881
886
  };
882
- var col = (s, width) => s.padEnd(width).substring(0, width);
883
887
  var list = async ({ type, missingSubs, codec: codecFilter, resolution: resFilter, sort }) => {
884
888
  const config = getConfig();
885
889
  const types = (type ? [type] : DEST_TYPES2).filter((t) => config.dest[t]);
@@ -888,7 +892,7 @@ var list = async ({ type, missingSubs, codec: codecFilter, resolution: resFilter
888
892
  const destRoot = config.dest[t];
889
893
  if (!(0, import_fs8.existsSync)(destRoot)) {
890
894
  console.log(`
891
- ${t.toUpperCase()} ${import_cosmetic8.default.blue.encoder(destRoot)} (not found)`);
895
+ ${t.toUpperCase()} ${import_termkit9.Color.blue.encoder(destRoot)} (not found)`);
892
896
  continue;
893
897
  }
894
898
  const folders = (0, import_fs8.readdirSync)(destRoot).filter((f) => {
@@ -918,18 +922,29 @@ ${t.toUpperCase()} ${import_cosmetic8.default.blue.encoder(destRoot)} (not fou
918
922
  const yearDiff = (b.year ?? 0) - (a.year ?? 0);
919
923
  return yearDiff !== 0 ? yearDiff : a.title.localeCompare(b.title);
920
924
  });
921
- const titleW = Math.min(50, Math.max(10, ...filtered.map((e) => e.title.length)) + 2);
922
- const divider = "\u2500".repeat(titleW + 44);
923
925
  console.log(`
924
- ${import_cosmetic8.default.yellow.encoder(t.toUpperCase())} ${import_cosmetic8.default.blue.encoder(destRoot)}`);
925
- console.log(divider);
926
- console.log(`${"Title".padEnd(titleW)} ${"Year".padEnd(6)} ${"Res".padEnd(6)} ${"Codec".padEnd(6)} ${"Size".padEnd(10)} Sub`);
927
- console.log(divider);
928
- for (const e of filtered) {
929
- const sub = e.hasSub ? import_cosmetic8.default.green.encoder("\u2713") : import_cosmetic8.default.red.encoder("\u2717");
930
- console.log(`${col(e.title, titleW)} ${col(e.year?.toString() ?? "\u2014", 6)} ${col(e.resolution ?? "\u2014", 6)} ${col(e.codec ?? "\u2014", 6)} ${col(e.size, 10)} ${sub}`);
931
- }
932
- console.log(divider);
926
+ ${import_termkit9.Color.yellow.encoder(t.toUpperCase())} ${import_termkit9.Color.blue.encoder(destRoot)}`);
927
+ new import_termkit9.Table(
928
+ filtered.map((e) => ({
929
+ title: e.title,
930
+ year: e.year,
931
+ resolution: e.resolution,
932
+ codec: e.codec,
933
+ size: e.size,
934
+ sub: e.hasSub
935
+ })),
936
+ {
937
+ separator: " ",
938
+ columns: [
939
+ { key: "title", title: "Title" },
940
+ { key: "year", title: "Year", align: "right", value: (v) => v != null ? String(v) : "\u2014" },
941
+ { key: "resolution", title: "Res", value: (v) => v ?? "\u2014" },
942
+ { key: "codec", title: "Codec", value: (v) => v ?? "\u2014" },
943
+ { key: "size", title: "Size" },
944
+ { key: "sub", title: "Sub", value: (v) => v ? import_termkit9.Color.green.encoder("\u2713") : import_termkit9.Color.red.encoder("\u2717") }
945
+ ]
946
+ }
947
+ ).print();
933
948
  console.log(`${filtered.length} of ${entries.length} item${entries.length !== 1 ? "s" : ""}`);
934
949
  }
935
950
  console.log();
@@ -937,9 +952,9 @@ ${import_cosmetic8.default.yellow.encoder(t.toUpperCase())} ${import_cosmetic8.
937
952
  var list_default = list;
938
953
 
939
954
  // src/actions/missing.ts
940
- var import_cosmetic9 = __toESM(require("cosmetic"));
941
955
  var import_fs9 = require("fs");
942
956
  var import_path10 = require("path");
957
+ var import_termkit10 = require("termkit");
943
958
  var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
944
959
  var parseSeasonNumber = (folderName) => {
945
960
  const match = folderName.match(/(?:season|s)\s*0*(\d+)/i);
@@ -1001,7 +1016,7 @@ var missing = async ({ show: showFilter }) => {
1001
1016
  if (showMissing.length > 0) {
1002
1017
  totalMissing += showMissing.length;
1003
1018
  console.log(`
1004
- ${import_cosmetic9.default.yellow.encoder(show)}`);
1019
+ ${import_termkit10.Color.yellow.encoder(show)}`);
1005
1020
  for (const line of showMissing) console.log(line);
1006
1021
  }
1007
1022
  }
@@ -1020,9 +1035,9 @@ var missing_default = missing;
1020
1035
 
1021
1036
  // src/actions/probe.ts
1022
1037
  var import_child_process = require("child_process");
1023
- var import_cosmetic10 = __toESM(require("cosmetic"));
1024
1038
  var import_fs10 = require("fs");
1025
1039
  var import_path11 = require("path");
1040
+ var import_termkit11 = require("termkit");
1026
1041
  var DEST_TYPES3 = ["movie", "tv", "ps3"];
1027
1042
  var CODEC_MAP2 = {
1028
1043
  hevc: "x265",
@@ -1098,7 +1113,7 @@ var probe = async ({ type, force, verbose }) => {
1098
1113
  for (const t of types) {
1099
1114
  const destRoot = config.dest[t];
1100
1115
  if (!(0, import_fs10.existsSync)(destRoot)) continue;
1101
- spinner_default.text = `scanning ${import_cosmetic10.default.blue.encoder(destRoot)}`;
1116
+ spinner_default.text = `scanning ${import_termkit11.Color.blue.encoder(destRoot)}`;
1102
1117
  const files = walkVideoFiles(destRoot);
1103
1118
  for (const filePath of files) {
1104
1119
  if (!force && getMediaInfo(filePath)) {
@@ -1106,7 +1121,7 @@ var probe = async ({ type, force, verbose }) => {
1106
1121
  skipped++;
1107
1122
  continue;
1108
1123
  }
1109
- spinner_default.text = `probing ${import_cosmetic10.default.blue.encoder(filePath)}`;
1124
+ spinner_default.text = `probing ${import_termkit11.Color.blue.encoder(filePath)}`;
1110
1125
  const result = runFfprobe(filePath);
1111
1126
  if (!result) {
1112
1127
  if (verbose) spinner_default.warn(`ffprobe failed: ${filePath}`);
@@ -1126,10 +1141,10 @@ var probe = async ({ type, force, verbose }) => {
1126
1141
  var probe_default = probe;
1127
1142
 
1128
1143
  // src/actions/rename.ts
1129
- var import_cosmetic11 = __toESM(require("cosmetic"));
1130
1144
  var import_fs11 = require("fs");
1131
1145
  var import_path12 = require("path");
1132
1146
  var import_rimraf = require("rimraf");
1147
+ var import_termkit12 = require("termkit");
1133
1148
 
1134
1149
  // src/helpers/findSubtitle.ts
1135
1150
  var LANGUAGE_ALIASES = {
@@ -1190,13 +1205,13 @@ var rename = async ({ dir: inputDir, type, verbose }) => {
1190
1205
  const config = getConfig();
1191
1206
  const language = config.language ?? "eng";
1192
1207
  const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
1193
- spinner_default.text = `renaming in ${import_cosmetic11.default.blue.encoder(dir)}`;
1208
+ spinner_default.text = `renaming in ${import_termkit12.Color.blue.encoder(dir)}`;
1194
1209
  spinner_default.start();
1195
1210
  if (!(0, import_fs11.existsSync)(dir)) throw new Error(`dir ${dir} does not exist`);
1196
1211
  const list2 = (0, import_fs11.readdirSync)(dir);
1197
1212
  let renamed = 0, removed = 0, skipped = 0;
1198
1213
  for (const [index, entry] of list2.entries()) {
1199
- spinner_default.text = `renaming in ${import_cosmetic11.default.blue.encoder(dir)} ${index + 1}/${list2.length}`;
1214
+ spinner_default.text = `renaming in ${import_termkit12.Color.blue.encoder(dir)} ${index + 1}/${list2.length}`;
1200
1215
  if (!(0, import_fs11.lstatSync)((0, import_path12.resolve)(dir, entry)).isDirectory()) {
1201
1216
  if (verbose) spinner_default.info(`skipped ${entry}`);
1202
1217
  skipped++;
@@ -1281,18 +1296,18 @@ var rename = async ({ dir: inputDir, type, verbose }) => {
1281
1296
  spinner_default.succeed(`renamed ${renamed} files`);
1282
1297
  if (removed) spinner_default.info(`removed ${removed} files`);
1283
1298
  spinner_default.info(`skipped ${skipped} files`);
1284
- spinner_default.succeed(`done in ${import_cosmetic11.default.cyan.encoder(dir)}`);
1299
+ spinner_default.succeed(`done in ${import_termkit12.Color.cyan.encoder(dir)}`);
1285
1300
  spinner_default.stop();
1286
1301
  };
1287
1302
  var rename_default = rename;
1288
1303
 
1289
1304
  // src/actions/reset.ts
1290
- var import_cosmetic12 = __toESM(require("cosmetic"));
1291
1305
  var import_fs12 = require("fs");
1292
1306
  var import_path13 = require("path");
1307
+ var import_termkit13 = require("termkit");
1293
1308
  var reset = async ({ dir: inputDir, double }) => {
1294
1309
  let dir = inputDir;
1295
- spinner_default.text = `resetting episodes in ${import_cosmetic12.default.blue.encoder(dir)}`;
1310
+ spinner_default.text = `resetting episodes in ${import_termkit13.Color.blue.encoder(dir)}`;
1296
1311
  spinner_default.start();
1297
1312
  dir = (0, import_path13.resolve)(dir);
1298
1313
  if (!(0, import_fs12.existsSync)(dir)) throw new Error(`dir ${dir} does not exist`);
@@ -1321,7 +1336,7 @@ var reset = async ({ dir: inputDir, double }) => {
1321
1336
  const episodeFormat = getConfig().format?.episode;
1322
1337
  let renamed = 0, skipped = other.length;
1323
1338
  for (const [index, i] of sublist.entries()) {
1324
- spinner_default.text = `resetting episodes in ${import_cosmetic12.default.blue.encoder(dir)} ${index}/${list2.length}`;
1339
+ spinner_default.text = `resetting episodes in ${import_termkit13.Color.blue.encoder(dir)} ${index}/${list2.length}`;
1325
1340
  const ext = i.match(/([^.]+$)/)?.[0];
1326
1341
  const episode = double ? index * 2 + 1 : index + 1;
1327
1342
  const name = `${formatEpisode(seasonNum, episode, episodeFormat, double, showTitle)}.${ext}`;
@@ -1334,16 +1349,15 @@ var reset = async ({ dir: inputDir, double }) => {
1334
1349
  }
1335
1350
  spinner_default.succeed(`renamed ${renamed} files`);
1336
1351
  spinner_default.info(`skipped ${skipped} files`);
1337
- spinner_default.succeed(`done in ${import_cosmetic12.default.cyan.encoder(dir)}`);
1352
+ spinner_default.succeed(`done in ${import_termkit13.Color.cyan.encoder(dir)}`);
1338
1353
  spinner_default.stop();
1339
1354
  };
1340
1355
  var reset_default = reset;
1341
1356
 
1342
1357
  // src/actions/scan.ts
1343
- var import_cosmetic13 = __toESM(require("cosmetic"));
1344
1358
  var import_fs13 = require("fs");
1345
1359
  var import_path14 = require("path");
1346
- var import_termpulse5 = require("termpulse");
1360
+ var import_termkit14 = require("termkit");
1347
1361
 
1348
1362
  // src/helpers/detectEdition.ts
1349
1363
  var EDITIONS = [
@@ -1450,10 +1464,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1450
1464
  let imported = 0, skipped = 0;
1451
1465
  for (const source of config.sources) {
1452
1466
  if (!(0, import_fs13.existsSync)(source)) {
1453
- spinner_default.warn(`source not found: ${import_cosmetic13.default.blue.encoder(source)}`);
1467
+ spinner_default.warn(`source not found: ${import_termkit14.Color.blue.encoder(source)}`);
1454
1468
  continue;
1455
1469
  }
1456
- spinner_default.text = `scanning ${import_cosmetic13.default.blue.encoder(source)}`;
1470
+ spinner_default.text = `scanning ${import_termkit14.Color.blue.encoder(source)}`;
1457
1471
  for (const entry of (0, import_fs13.readdirSync)(source)) {
1458
1472
  const entryPath = (0, import_path14.resolve)(source, entry);
1459
1473
  const isDir = (0, import_fs13.lstatSync)(entryPath).isDirectory();
@@ -1520,7 +1534,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1520
1534
  resolvedYear = results[0].year ?? parsed.year;
1521
1535
  } else if (results.length > 1) {
1522
1536
  spinner_default.stop();
1523
- const select = new import_termpulse5.Select();
1537
+ const select = new import_termkit14.Select();
1524
1538
  const items = results.map((r) => ({
1525
1539
  label: r.year ? `${r.title} (${r.year})` : r.title,
1526
1540
  description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
@@ -1687,8 +1701,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1687
1701
  var scan_default = scan;
1688
1702
 
1689
1703
  // src/actions/shows.ts
1690
- var import_cosmetic14 = __toESM(require("cosmetic"));
1691
1704
  var import_fs14 = require("fs");
1705
+ var import_termkit15 = require("termkit");
1692
1706
  var dim = (s) => `\x1B[2m${s}\x1B[0m`;
1693
1707
  var shows = async () => {
1694
1708
  const config = getConfig();
@@ -1699,30 +1713,36 @@ var shows = async () => {
1699
1713
  console.log();
1700
1714
  return;
1701
1715
  }
1702
- const names = allShows.map((s) => s.path.split("/").pop() ?? s.path);
1703
- const titleW = Math.min(50, Math.max(10, ...names.map((n) => n.length)) + 2);
1704
- const divider = "\u2500".repeat(titleW + 32);
1705
1716
  console.log(`
1706
- ${import_cosmetic14.default.yellow.encoder("SHOWS")}${destRoot ? ` ${import_cosmetic14.default.blue.encoder(destRoot)}` : ""} (${allShows.length} registered)`);
1707
- console.log(divider);
1708
- console.log(`${"Title".padEnd(titleW)} ${"Size".padEnd(10)} Linked Status`);
1709
- console.log(divider);
1710
- for (const [i, show] of allShows.entries()) {
1711
- const name = names[i];
1712
- const exists = (0, import_fs14.existsSync)(show.path);
1713
- const size = exists ? formatSize(dirSize(show.path)) : "\u2014";
1714
- let linked2;
1715
- if (show.tmdbId) {
1716
- const url = `https://www.themoviedb.org/tv/${show.tmdbId}`;
1717
- const text = process.stdout.isTTY ? hyperlink(url, "\u2713 tmdb") : "\u2713 tmdb";
1718
- linked2 = import_cosmetic14.default.green.encoder(text);
1719
- } else {
1720
- linked2 = import_cosmetic14.default.red.encoder("\u2717 ");
1717
+ ${import_termkit15.Color.yellow.encoder("SHOWS")}${destRoot ? ` ${import_termkit15.Color.blue.encoder(destRoot)}` : ""} (${allShows.length} registered)`);
1718
+ new import_termkit15.Table(
1719
+ allShows.map((show) => ({
1720
+ name: show.path.split("/").pop() ?? show.path,
1721
+ size: (0, import_fs14.existsSync)(show.path) ? formatSize(dirSize(show.path)) : "\u2014",
1722
+ tmdbId: show.tmdbId,
1723
+ ended: show.ended
1724
+ })),
1725
+ {
1726
+ separator: " ",
1727
+ columns: [
1728
+ { key: "name", title: "Title" },
1729
+ { key: "size", title: "Size" },
1730
+ {
1731
+ key: "tmdbId",
1732
+ title: "Linked",
1733
+ value: (v) => {
1734
+ if (v) {
1735
+ const url = `https://www.themoviedb.org/tv/${v}`;
1736
+ const text = process.stdout.isTTY ? hyperlink(url, "\u2713 tmdb") : "\u2713 tmdb";
1737
+ return import_termkit15.Color.green.encoder(text);
1738
+ }
1739
+ return import_termkit15.Color.red.encoder("\u2717");
1740
+ }
1741
+ },
1742
+ { key: "ended", title: "Status", value: (v) => v ? dim("ended") : "active" }
1743
+ ]
1721
1744
  }
1722
- const status = show.ended ? dim("ended") : "active";
1723
- console.log(`${name.padEnd(titleW).substring(0, titleW)} ${size.padEnd(10)} ${linked2} ${status}`);
1724
- }
1725
- console.log(divider);
1745
+ ).print();
1726
1746
  const ended2 = allShows.filter((s) => s.ended).length;
1727
1747
  const linked = allShows.filter((s) => s.tmdbId).length;
1728
1748
  console.log(`${allShows.length} shows \xB7 ${linked} linked \xB7 ${ended2} ended`);
@@ -1731,9 +1751,9 @@ ${import_cosmetic14.default.yellow.encoder("SHOWS")}${destRoot ? ` ${import_cos
1731
1751
  var shows_default = shows;
1732
1752
 
1733
1753
  // src/actions/stats.ts
1734
- var import_cosmetic15 = __toESM(require("cosmetic"));
1735
1754
  var import_fs15 = require("fs");
1736
1755
  var import_path15 = require("path");
1756
+ var import_termkit16 = require("termkit");
1737
1757
  var countVideos = (dir) => {
1738
1758
  let count = 0;
1739
1759
  try {
@@ -1765,36 +1785,38 @@ var countDirs = (dir) => {
1765
1785
  var stats = async () => {
1766
1786
  const config = getConfig();
1767
1787
  const shows2 = getShows();
1768
- const label = (s) => ` ${import_cosmetic15.default.yellow.encoder(s.padEnd(14))}`;
1769
- const val = (n) => String(n);
1770
- console.log("\nLIBRARY STATISTICS\n");
1788
+ const rows = [];
1771
1789
  const movieDest = config.dest.movie;
1772
1790
  if (movieDest && (0, import_fs15.existsSync)(movieDest)) {
1773
- const count = countDirs(movieDest);
1774
- const size = formatSize(dirSize(movieDest));
1775
- console.log(`${label("Movies")}${val(count).padStart(6)} ${size}`);
1791
+ rows.push({ category: "Movies", count: countDirs(movieDest), size: formatSize(dirSize(movieDest)) });
1776
1792
  }
1777
1793
  const tvDest = config.dest.tv;
1778
1794
  if (tvDest && (0, import_fs15.existsSync)(tvDest)) {
1779
- const showCount = shows2.length;
1780
- const episodeCount = countVideos(tvDest);
1781
- const size = formatSize(dirSize(tvDest));
1782
- console.log(`${label("Shows")}${val(showCount).padStart(6)} ${size}`);
1783
- console.log(`${label("Episodes")}${val(episodeCount).padStart(6)}`);
1795
+ rows.push({ category: "Shows", count: shows2.length, size: formatSize(dirSize(tvDest)) });
1796
+ rows.push({ category: "Episodes", count: countVideos(tvDest) });
1784
1797
  }
1785
1798
  const ps3Dest = config.dest.ps3;
1786
1799
  if (ps3Dest && (0, import_fs15.existsSync)(ps3Dest)) {
1787
- const count = countDirs(ps3Dest);
1788
- const size = formatSize(dirSize(ps3Dest));
1789
- console.log(`${label("PS3")}${val(count).padStart(6)} ${size}`);
1800
+ rows.push({ category: "PS3", count: countDirs(ps3Dest), size: formatSize(dirSize(ps3Dest)) });
1790
1801
  }
1802
+ if (rows.length === 0) return;
1803
+ console.log();
1804
+ new import_termkit16.Table(rows, {
1805
+ title: "LIBRARY STATISTICS",
1806
+ separator: " ",
1807
+ columns: [
1808
+ { key: "category", title: "Category" },
1809
+ { key: "count", title: "Count", align: "right", value: (v) => v != null ? String(v) : "" },
1810
+ { key: "size", title: "Size", value: (v) => v ?? "" }
1811
+ ]
1812
+ }).print();
1791
1813
  console.log();
1792
1814
  };
1793
1815
  var stats_default = stats;
1794
1816
 
1795
1817
  // src/actions/undo.ts
1796
- var import_cosmetic16 = __toESM(require("cosmetic"));
1797
1818
  var import_fs16 = require("fs");
1819
+ var import_termkit17 = require("termkit");
1798
1820
  var undo = async () => {
1799
1821
  spinner_default.start();
1800
1822
  const records = getLastSession();
@@ -1806,7 +1828,7 @@ var undo = async () => {
1806
1828
  let undone = 0;
1807
1829
  for (const record of records) {
1808
1830
  (0, import_fs16.renameSync)(record.newPath, record.oldPath);
1809
- spinner_default.succeed(`${import_cosmetic16.default.cyan.encoder(record.newPath)} \u2192 ${import_cosmetic16.default.blue.encoder(record.oldPath)}`);
1831
+ spinner_default.succeed(`${import_termkit17.Color.cyan.encoder(record.newPath)} \u2192 ${import_termkit17.Color.blue.encoder(record.oldPath)}`);
1810
1832
  undone++;
1811
1833
  }
1812
1834
  deleteSession(records[0].sessionId);
@@ -1817,9 +1839,9 @@ var undo_default = undo;
1817
1839
 
1818
1840
  // src/actions/watch.ts
1819
1841
  var import_chokidar = __toESM(require("chokidar"));
1820
- var import_cosmetic17 = __toESM(require("cosmetic"));
1821
1842
  var import_fs17 = require("fs");
1822
1843
  var import_path16 = require("path");
1844
+ var import_termkit18 = require("termkit");
1823
1845
  var sameDev2 = (a, b) => {
1824
1846
  try {
1825
1847
  let bExisting = b;
@@ -1890,7 +1912,7 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
1890
1912
  }
1891
1913
  moveItem(entryPath, destPath);
1892
1914
  recordImport(sessionId, entryPath, destPath, "move");
1893
- spinner_default.succeed(`imported ${import_cosmetic17.default.cyan.encoder(destName)}`);
1915
+ spinner_default.succeed(`imported ${import_termkit18.Color.cyan.encoder(destName)}`);
1894
1916
  return;
1895
1917
  }
1896
1918
  const parsed = parseDownloadName(entry);
@@ -1963,7 +1985,7 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
1963
1985
  if (isDir) (0, import_fs17.rmSync)(entryPath, { recursive: true, force: true });
1964
1986
  }
1965
1987
  recordImport(sessionId, entryPath, seasonPath, mode);
1966
- spinner_default.succeed(`imported ${import_cosmetic17.default.cyan.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
1988
+ spinner_default.succeed(`imported ${import_termkit18.Color.cyan.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
1967
1989
  return;
1968
1990
  }
1969
1991
  const edition = detectEdition(entry);
@@ -2020,7 +2042,7 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
2020
2042
  }
2021
2043
  recordImport(sessionId, entryPath, destFolder, "move");
2022
2044
  }
2023
- spinner_default.succeed(`imported ${import_cosmetic17.default.cyan.encoder(folderName)}`);
2045
+ spinner_default.succeed(`imported ${import_termkit18.Color.cyan.encoder(folderName)}`);
2024
2046
  };
2025
2047
  var watch = async ({ hardlink = false, verbose = false, auto = false }) => {
2026
2048
  const config = getConfig();
@@ -2051,35 +2073,41 @@ var watch = async ({ hardlink = false, verbose = false, auto = false }) => {
2051
2073
  watcher.on("add", handle);
2052
2074
  spinner_default.start();
2053
2075
  spinner_default.succeed(`watching ${config.sources.length} source${config.sources.length !== 1 ? "s" : ""}`);
2054
- for (const s of config.sources) spinner_default.info(` ${import_cosmetic17.default.blue.encoder(s)}`);
2076
+ for (const s of config.sources) spinner_default.info(` ${import_termkit18.Color.blue.encoder(s)}`);
2055
2077
  spinner_default.stop();
2056
2078
  process.stdin.resume();
2057
2079
  };
2058
2080
  var watch_default = watch;
2059
2081
 
2060
2082
  // package.json
2061
- var version = "0.1.2";
2083
+ var version = "0.2.0";
2062
2084
 
2063
2085
  // src/program.ts
2064
2086
  var adapt = (fn) => (options) => fn(options);
2065
- var program = (0, import_termkit.command)("reelsort").version(version).description("a cli to manage media").commands([
2066
- (0, import_termkit.command)("config").description("manage configuration").commands([(0, import_termkit.command)("add", "<key> <value>").description("add a value (e.g. add source <dir>)").action(adapt(configAdd)), (0, import_termkit.command)("remove", "<key> <value>").description("remove a value (e.g. remove source <dir>)").action(adapt(configRemove)), (0, import_termkit.command)("set", "<key> <subkey> [value]").description("set a value (e.g. set dest movie <dir>)").action(adapt(configSet)), (0, import_termkit.command)("show").description("show current configuration").action(adapt(configShow))]),
2067
- (0, import_termkit.command)("rename", "<dir>").description("rename media files in directory").options([(0, import_termkit.option)("t", "type", "<type>", "media type: movie, tv, ps3"), (0, import_termkit.option)("v", "verbose", null, "additional output")]).action(adapt(rename_default)),
2068
- (0, import_termkit.command)("reset", "<dir>").description("reset episode names (sxee)").options([(0, import_termkit.option)("d", "double", null, "episodes are doubles")]).action(adapt(reset_default)),
2069
- (0, import_termkit.command)("probe").description("index library metadata using ffprobe").options([(0, import_termkit.option)("t", "type", "<type>", "media type: movie, tv, ps3"), (0, import_termkit.option)("f", "force", null, "re-probe files already indexed"), (0, import_termkit.option)("v", "verbose", null, "show each file as it is probed")]).action(adapt(probe_default)),
2070
- (0, import_termkit.command)("list").description("list library contents").options([(0, import_termkit.option)("t", "type", "<type>", "media type: movie, tv, ps3"), (0, import_termkit.option)("m", "missing-subs", null, "only show items without subtitles"), (0, import_termkit.option)("c", "codec", "<codec>", "filter by codec (e.g. x265)"), (0, import_termkit.option)("r", "resolution", "<res>", "filter by resolution (e.g. 1080p, 4K)"), (0, import_termkit.option)("s", "sort", "<field>", "sort by: year (default), title")]).action(adapt(list_default)),
2071
- (0, import_termkit.command)("watch").description("watch sources and auto-import new media").options([(0, import_termkit.option)("H", "hardlink", null, "hardlink instead of moving (falls back to copy across filesystems)"), (0, import_termkit.option)("v", "verbose", null, "additional output"), (0, import_termkit.option)("a", "auto", null, "auto-register unrecognised TV shows instead of skipping them")]).action(adapt(watch_default)),
2072
- (0, import_termkit.command)("scan").description("import media from configured sources to destinations").options([(0, import_termkit.option)("t", "type", "<type>", "only process this media type: movie, tv, ps3"), (0, import_termkit.option)("H", "hardlink", null, "hardlink instead of moving (falls back to copy across filesystems)"), (0, import_termkit.option)("n", "dry-run", null, "show what would be imported without doing it"), (0, import_termkit.option)("v", "verbose", null, "additional output"), (0, import_termkit.option)("a", "auto", null, "auto-register unrecognised TV shows instead of skipping them")]).action(adapt(scan_default)),
2073
- (0, import_termkit.command)("clean").description("remove source files that have already been imported").options([(0, import_termkit.option)("n", "dry-run", null, "show what would be removed without doing it"), (0, import_termkit.option)("o", "older-than", "<age>", "only clean imports older than age (e.g. 14d, 6h, 30m)")]).action(adapt(clean_default)),
2074
- (0, import_termkit.command)("undo").description("undo the last rename session").action(adapt(undo_default)),
2075
- (0, import_termkit.command)("history").description("show rename or import history").options([(0, import_termkit.option)("l", "limit", "<n>", "number of sessions to show (default 10)"), (0, import_termkit.option)("i", "imports", null, "show import history instead of rename history")]).action(adapt(history_default)),
2076
- (0, import_termkit.command)("diff", "<dir1> <dir2>").description("compare differences between two directories").options([(0, import_termkit.option)("o", "only", "[ext...]", "check specified extensions"), (0, import_termkit.option)("i", "ignore", "[ext...]", "ignore specified extensions")]).action(adapt(differences_default)),
2077
- (0, import_termkit.command)("missing").description("show missing episodes for TV shows (requires TMDb key)").options([(0, import_termkit.option)("s", "show", "<name>", "check a specific show instead of all")]).action(adapt(missing_default)),
2078
- (0, import_termkit.command)("shows").description("list all registered TV shows with TMDb status and size").action(adapt(shows_default)),
2079
- (0, import_termkit.command)("stats").description("show library statistics").action(adapt(stats_default)),
2080
- (0, import_termkit.command)("link").description("link TV shows to TMDb (retroactively or after manual library setup)").options([(0, import_termkit.option)("f", "force", null, "re-link shows that are already linked")]).action(adapt(link_default)),
2081
- (0, import_termkit.command)("ended").description("mark a show as ended (excluded from reelsort missing)").options([(0, import_termkit.option)("r", "remove", null, "restore an ended show to active")]).action(adapt(ended_default)),
2082
- (0, import_termkit.command)("add", "<name>").description("add a TV show by name \u2014 searches TMDb and registers it for scanning").action(adapt(add_default))
2087
+ var { command, option } = import_termkit19.Program;
2088
+ var program = import_termkit19.Program.command("reelsort").version(version).description("a cli to manage media").commands([
2089
+ command("config").description("manage configuration").commands([
2090
+ command("source").description("manage source directories").commands([command("add", "<dir>").description("add a source directory").action(adapt(sourceAdd)), command("remove", "<dir>").description("remove a source directory").action(adapt(sourceRemove))]),
2091
+ command("dest").description("manage destinations").commands([command("add", "<type> <dir>").description("set a destination (movie, tv, ps3)").action(adapt(destAdd)), command("remove", "<type>").description("remove a destination (movie, tv, ps3)").action(adapt(destRemove))]),
2092
+ command("set", "<key> <subkey> [value]").description("set a value (e.g. set language eng)").action(adapt(configSet)),
2093
+ command("show").description("show current configuration").action(adapt(configShow))
2094
+ ]),
2095
+ command("rename", "<dir>").description("rename media files in directory").options([option("t", "type", "<type>", "media type: movie, tv, ps3"), option("v", "verbose", null, "additional output")]).action(adapt(rename_default)),
2096
+ command("reset", "<dir>").description("reset episode names (sxee)").options([option("d", "double", null, "episodes are doubles")]).action(adapt(reset_default)),
2097
+ command("probe").description("index library metadata using ffprobe").options([option("t", "type", "<type>", "media type: movie, tv, ps3"), option("f", "force", null, "re-probe files already indexed"), option("v", "verbose", null, "show each file as it is probed")]).action(adapt(probe_default)),
2098
+ command("list").description("list library contents").options([option("t", "type", "<type>", "media type: movie, tv, ps3"), option("m", "missing-subs", null, "only show items without subtitles"), option("c", "codec", "<codec>", "filter by codec (e.g. x265)"), option("r", "resolution", "<res>", "filter by resolution (e.g. 1080p, 4K)"), option("s", "sort", "<field>", "sort by: year (default), title")]).action(adapt(list_default)),
2099
+ command("watch").description("watch sources and auto-import new media").options([option("H", "hardlink", null, "hardlink instead of moving (falls back to copy across filesystems)"), option("v", "verbose", null, "additional output"), option("a", "auto", null, "auto-register unrecognised TV shows instead of skipping them")]).action(adapt(watch_default)),
2100
+ command("scan").description("import media from configured sources to destinations").options([option("t", "type", "<type>", "only process this media type: movie, tv, ps3"), option("H", "hardlink", null, "hardlink instead of moving (falls back to copy across filesystems)"), option("n", "dry-run", null, "show what would be imported without doing it"), option("v", "verbose", null, "additional output"), option("a", "auto", null, "auto-register unrecognised TV shows instead of skipping them")]).action(adapt(scan_default)),
2101
+ command("clean").description("remove source files that have already been imported").options([option("n", "dry-run", null, "show what would be removed without doing it"), option("o", "older-than", "<age>", "only clean imports older than age (e.g. 14d, 6h, 30m)")]).action(adapt(clean_default)),
2102
+ command("undo").description("undo the last rename session").action(adapt(undo_default)),
2103
+ command("history").description("show rename or import history").options([option("l", "limit", "<n>", "number of sessions to show (default 10)"), option("i", "imports", null, "show import history instead of rename history")]).action(adapt(history_default)),
2104
+ command("diff", "<dir1> <dir2>").description("compare differences between two directories").options([option("o", "only", "[ext...]", "check specified extensions"), option("i", "ignore", "[ext...]", "ignore specified extensions")]).action(adapt(differences_default)),
2105
+ command("missing").description("show missing episodes for TV shows (requires TMDb key)").options([option("s", "show", "<name>", "check a specific show instead of all")]).action(adapt(missing_default)),
2106
+ command("shows").description("list all registered TV shows with TMDb status and size").action(adapt(shows_default)),
2107
+ command("stats").description("show library statistics").action(adapt(stats_default)),
2108
+ command("link").description("link TV shows to TMDb (retroactively or after manual library setup)").options([option("f", "force", null, "re-link shows that are already linked")]).action(adapt(link_default)),
2109
+ command("ended").description("mark a show as ended (excluded from reelsort missing)").options([option("r", "remove", null, "restore an ended show to active")]).action(adapt(ended_default)),
2110
+ command("add", "<name>").description("add a TV show by name \u2014 searches TMDb and registers it for scanning").action(adapt(add_default))
2083
2111
  ]);
2084
2112
  var program_default = program;
2085
2113