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/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/actions/clean.ts
2
- import cosmetic from "cosmetic";
3
2
  import { existsSync as existsSync2, rmSync } from "fs";
3
+ import { Color } from "termkit";
4
4
 
5
5
  // src/db.ts
6
6
  import Database from "better-sqlite3";
@@ -131,28 +131,21 @@ var getHistory = (limit = 10) => {
131
131
  };
132
132
 
133
133
  // src/refs/spinner.ts
134
- import { Spinner as TermSpinner } from "termpulse";
134
+ import { Spinner as TermSpinner } from "termkit";
135
135
  var Spinner = class {
136
136
  spinner;
137
- _isSpinning = false;
138
- _text = "";
139
137
  constructor() {
140
138
  this.spinner = new TermSpinner();
141
139
  }
142
140
  get text() {
143
- return this._text;
141
+ return this.spinner.text;
144
142
  }
145
143
  set text(t) {
146
- this._text = t;
147
- if (this._isSpinning) this.spinner.message(t);
148
- }
149
- get isSpinning() {
150
- return this._isSpinning;
144
+ this.spinner.text = t;
151
145
  }
152
146
  start(s) {
153
- if (s) this._text = s;
147
+ if (s) this.spinner.text = s;
154
148
  this.spinner.start();
155
- this._isSpinning = true;
156
149
  return this;
157
150
  }
158
151
  info(s) {
@@ -173,8 +166,6 @@ var Spinner = class {
173
166
  }
174
167
  stop() {
175
168
  this.spinner.stop();
176
- this._isSpinning = false;
177
- process.stdin.resume();
178
169
  return this;
179
170
  }
180
171
  };
@@ -213,17 +204,17 @@ var clean = async ({ dryRun, olderThan }) => {
213
204
  continue;
214
205
  }
215
206
  if (dryRun) {
216
- spinner_default.succeed(`[dry] would remove ${cosmetic.blue.encoder(imp.sourcePath)}`);
207
+ spinner_default.succeed(`[dry] would remove ${Color.blue.encoder(imp.sourcePath)}`);
217
208
  cleaned++;
218
209
  continue;
219
210
  }
220
211
  try {
221
212
  rmSync(imp.sourcePath, { recursive: true, force: true });
222
213
  deleteImport(imp.id);
223
- spinner_default.succeed(`removed ${cosmetic.blue.encoder(imp.sourcePath)}`);
214
+ spinner_default.succeed(`removed ${Color.blue.encoder(imp.sourcePath)}`);
224
215
  cleaned++;
225
216
  } catch {
226
- spinner_default.warn(`locked or inaccessible, skipped: ${cosmetic.blue.encoder(imp.sourcePath)}`);
217
+ spinner_default.warn(`locked or inaccessible, skipped: ${Color.blue.encoder(imp.sourcePath)}`);
227
218
  skipped++;
228
219
  }
229
220
  }
@@ -234,8 +225,8 @@ var clean = async ({ dryRun, olderThan }) => {
234
225
  var clean_default = clean;
235
226
 
236
227
  // src/actions/config.ts
237
- import cosmetic2 from "cosmetic";
238
228
  import { resolve } from "path";
229
+ import { Color as Color2 } from "termkit";
239
230
 
240
231
  // src/config.ts
241
232
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
@@ -283,37 +274,64 @@ var formatMovieName = (template, title, year, edition) => {
283
274
 
284
275
  // src/actions/config.ts
285
276
  var DEST_TYPES = ["movie", "tv", "ps3"];
286
- var configAdd = async ({ key, value }) => {
287
- if (key !== "source") throw new Error(`unknown key '${key}', expected: source`);
288
- const dir = resolve(value);
277
+ var sourceAdd = async ({ dir }) => {
278
+ const resolved = resolve(dir);
289
279
  const config = getConfig();
290
- if (config.sources.includes(dir)) {
280
+ if (config.sources.includes(resolved)) {
291
281
  spinner_default.start();
292
- spinner_default.info(`source already configured: ${cosmetic2.blue.encoder(dir)}`);
282
+ spinner_default.info(`source already configured: ${Color2.blue.encoder(resolved)}`);
293
283
  spinner_default.stop();
294
284
  return;
295
285
  }
296
- config.sources.push(dir);
286
+ config.sources.push(resolved);
297
287
  saveConfig(config);
298
288
  spinner_default.start();
299
- spinner_default.succeed(`added source: ${cosmetic2.blue.encoder(dir)}`);
289
+ spinner_default.succeed(`added source: ${Color2.blue.encoder(resolved)}`);
300
290
  spinner_default.stop();
301
291
  };
302
- var configRemove = async ({ key, value }) => {
303
- if (key !== "source") throw new Error(`unknown key '${key}', expected: source`);
304
- const dir = resolve(value);
292
+ var sourceRemove = async ({ dir }) => {
293
+ const resolved = resolve(dir);
305
294
  const config = getConfig();
306
- const index = config.sources.indexOf(dir);
295
+ const index = config.sources.indexOf(resolved);
307
296
  if (index === -1) {
308
297
  spinner_default.start();
309
- spinner_default.warn(`source not found: ${cosmetic2.blue.encoder(dir)}`);
298
+ spinner_default.warn(`source not found: ${Color2.blue.encoder(resolved)}`);
310
299
  spinner_default.stop();
311
300
  return;
312
301
  }
313
302
  config.sources.splice(index, 1);
314
303
  saveConfig(config);
315
304
  spinner_default.start();
316
- spinner_default.succeed(`removed source: ${cosmetic2.blue.encoder(dir)}`);
305
+ spinner_default.succeed(`removed source: ${Color2.blue.encoder(resolved)}`);
306
+ spinner_default.stop();
307
+ };
308
+ var destAdd = async ({ type, dir }) => {
309
+ if (!DEST_TYPES.includes(type)) {
310
+ throw new Error(`unknown type '${type}', expected: ${DEST_TYPES.join(", ")}`);
311
+ }
312
+ const resolved = resolve(dir);
313
+ const config = getConfig();
314
+ config.dest[type] = resolved;
315
+ saveConfig(config);
316
+ spinner_default.start();
317
+ spinner_default.succeed(`set ${type} destination: ${Color2.cyan.encoder(resolved)}`);
318
+ spinner_default.stop();
319
+ };
320
+ var destRemove = async ({ type }) => {
321
+ if (!DEST_TYPES.includes(type)) {
322
+ throw new Error(`unknown type '${type}', expected: ${DEST_TYPES.join(", ")}`);
323
+ }
324
+ const config = getConfig();
325
+ if (!config.dest[type]) {
326
+ spinner_default.start();
327
+ spinner_default.warn(`no ${type} destination configured`);
328
+ spinner_default.stop();
329
+ return;
330
+ }
331
+ delete config.dest[type];
332
+ saveConfig(config);
333
+ spinner_default.start();
334
+ spinner_default.succeed(`removed ${type} destination`);
317
335
  spinner_default.stop();
318
336
  };
319
337
  var configSet = async ({ key, subkey, value }) => {
@@ -322,7 +340,7 @@ var configSet = async ({ key, subkey, value }) => {
322
340
  config.language = subkey;
323
341
  saveConfig(config);
324
342
  spinner_default.start();
325
- spinner_default.succeed(`set subtitle language: ${cosmetic2.cyan.encoder(subkey)}`);
343
+ spinner_default.succeed(`set subtitle language: ${Color2.cyan.encoder(subkey)}`);
326
344
  spinner_default.stop();
327
345
  return;
328
346
  }
@@ -352,21 +370,11 @@ var configSet = async ({ key, subkey, value }) => {
352
370
  }
353
371
  saveConfig(config);
354
372
  spinner_default.start();
355
- spinner_default.succeed(`set ${subkey} format: ${cosmetic2.cyan.encoder(value ?? subkey)}`);
373
+ spinner_default.succeed(`set ${subkey} format: ${Color2.cyan.encoder(value ?? subkey)}`);
356
374
  spinner_default.stop();
357
375
  return;
358
376
  }
359
- if (key !== "dest") throw new Error(`unknown key '${key}', expected: dest, language, tmdb-key, format`);
360
- if (!DEST_TYPES.includes(subkey)) {
361
- throw new Error(`unknown type '${subkey}', expected: ${DEST_TYPES.join(", ")}`);
362
- }
363
- if (!value) throw new Error(`missing path for dest ${subkey}`);
364
- const dir = resolve(value);
365
- config.dest[subkey] = dir;
366
- saveConfig(config);
367
- spinner_default.start();
368
- spinner_default.succeed(`set ${subkey} destination: ${cosmetic2.cyan.encoder(dir)}`);
369
- spinner_default.stop();
377
+ throw new Error(`unknown key '${key}', expected: language, tmdb-key, format`);
370
378
  };
371
379
  var configShow = async () => {
372
380
  const config = getConfig();
@@ -374,7 +382,7 @@ var configShow = async () => {
374
382
  if (config.sources.length === 0) {
375
383
  console.log(" (none)");
376
384
  } else {
377
- for (const s of config.sources) console.log(` ${cosmetic2.blue.encoder(s)}`);
385
+ for (const s of config.sources) console.log(` ${Color2.blue.encoder(s)}`);
378
386
  }
379
387
  console.log("\nDestinations:");
380
388
  const entries = DEST_TYPES.map((t) => ({ type: t, path: config.dest[t] })).filter((e) => e.path);
@@ -382,26 +390,26 @@ var configShow = async () => {
382
390
  console.log(" (none)");
383
391
  } else {
384
392
  for (const { type, path } of entries) {
385
- console.log(` ${type.padEnd(6)} ${cosmetic2.cyan.encoder(path)}`);
393
+ console.log(` ${type.padEnd(6)} ${Color2.cyan.encoder(path)}`);
386
394
  }
387
395
  }
388
396
  console.log(`
389
- Subtitle language: ${cosmetic2.cyan.encoder(config.language ?? "eng (default)")}`);
390
- console.log(`TMDb API key: ${config.tmdbApiKey ? cosmetic2.green.encoder("configured") : cosmetic2.red.encoder("not set")}`);
391
- console.log(`Movie format: ${cosmetic2.cyan.encoder(config.format?.movie ?? DEFAULT_MOVIE_FORMAT + " (default)")}`);
392
- console.log(`Episode format: ${cosmetic2.cyan.encoder(config.format?.episode ?? DEFAULT_EPISODE_FORMAT + " (default)")}`);
393
- console.log(`Season folder: ${cosmetic2.cyan.encoder(config.format?.season ?? DEFAULT_SEASON_FORMAT + " (default)")}`);
397
+ Subtitle language: ${Color2.cyan.encoder(config.language ?? "eng (default)")}`);
398
+ console.log(`TMDb API key: ${config.tmdbApiKey ? Color2.green.encoder("configured") : Color2.red.encoder("not set")}`);
399
+ console.log(`Movie format: ${Color2.cyan.encoder(config.format?.movie ?? DEFAULT_MOVIE_FORMAT + " (default)")}`);
400
+ console.log(`Episode format: ${Color2.cyan.encoder(config.format?.episode ?? DEFAULT_EPISODE_FORMAT + " (default)")}`);
401
+ console.log(`Season folder: ${Color2.cyan.encoder(config.format?.season ?? DEFAULT_SEASON_FORMAT + " (default)")}`);
394
402
  console.log();
395
403
  };
396
404
 
397
405
  // src/actions/differences.ts
398
- import cosmetic3 from "cosmetic";
399
406
  import { existsSync as existsSync4, readdirSync } from "fs";
400
407
  import { resolve as resolve2 } from "path";
408
+ import { Color as Color3 } from "termkit";
401
409
  var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
402
410
  let dir1 = rawDir1;
403
411
  let dir2 = rawDir2;
404
- spinner_default.text = `checking differences between ${cosmetic3.blue.encoder(dir1)} and ${cosmetic3.blue.encoder(dir2)}`;
412
+ spinner_default.text = `checking differences between ${Color3.blue.encoder(dir1)} and ${Color3.blue.encoder(dir2)}`;
405
413
  spinner_default.start();
406
414
  dir1 = resolve2(dir1);
407
415
  dir2 = resolve2(dir2);
@@ -437,18 +445,18 @@ var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
437
445
  removed.push(l);
438
446
  }
439
447
  }
440
- spinner_default.succeed(`checked differences between ${cosmetic3.blue.encoder(dir1)} and ${cosmetic3.blue.encoder(dir2)}`);
448
+ spinner_default.succeed(`checked differences between ${Color3.blue.encoder(dir1)} and ${Color3.blue.encoder(dir2)}`);
441
449
  spinner_default.succeed(`found ${added.length} added files`);
442
450
  spinner_default.succeed(`found ${removed.length} removed files`);
443
451
  spinner_default.stop();
444
- for (const i of added) console.log(`${cosmetic3.green.encoder("added")} ${i}`);
445
- for (const i of removed) console.log(`${cosmetic3.red.encoder("removed")} ${i}`);
452
+ for (const i of added) console.log(`${Color3.green.encoder("added")} ${i}`);
453
+ for (const i of removed) console.log(`${Color3.red.encoder("removed")} ${i}`);
446
454
  };
447
455
  var differences_default = differences;
448
456
 
449
457
  // src/actions/history.ts
450
- import cosmetic4 from "cosmetic";
451
458
  import { basename, extname } from "path";
459
+ import { Color as Color4 } from "termkit";
452
460
  var history = async ({ limit, imports }) => {
453
461
  if (imports) {
454
462
  const sessions = getImportHistory(limit ?? 10);
@@ -460,11 +468,11 @@ var history = async ({ limit, imports }) => {
460
468
  const date = new Date(session.sessionId);
461
469
  const label = isNaN(date.getTime()) ? session.sessionId : date.toLocaleString();
462
470
  console.log(`
463
- ${cosmetic4.yellow.encoder(label)} (${session.records.length} item${session.records.length !== 1 ? "s" : ""})`);
471
+ ${Color4.yellow.encoder(label)} (${session.records.length} item${session.records.length !== 1 ? "s" : ""})`);
464
472
  for (const r of session.records) {
465
473
  const src = basename(r.sourcePath);
466
- const dest = cosmetic4.cyan.encoder(r.destinationPath);
467
- const mode = r.mode !== "move" ? ` ${cosmetic4.blue.encoder(`[${r.mode}]`)}` : "";
474
+ const dest = Color4.cyan.encoder(r.destinationPath);
475
+ const mode = r.mode !== "move" ? ` ${Color4.blue.encoder(`[${r.mode}]`)}` : "";
468
476
  console.log(` ${src} \u2192 ${dest}${mode}`);
469
477
  }
470
478
  }
@@ -479,11 +487,11 @@ ${cosmetic4.yellow.encoder(label)} (${session.records.length} item${session.rec
479
487
  const label = isNaN(date.getTime()) ? session.sessionId : date.toLocaleString();
480
488
  const folders = session.records.filter((r) => extname(r.newPath) === "");
481
489
  console.log(`
482
- ${cosmetic4.yellow.encoder(label)} (${folders.length} item${folders.length !== 1 ? "s" : ""})`);
490
+ ${Color4.yellow.encoder(label)} (${folders.length} item${folders.length !== 1 ? "s" : ""})`);
483
491
  for (const r of folders) {
484
492
  const oldName = basename(r.oldPath);
485
493
  const newName = basename(r.newPath);
486
- console.log(` ${cosmetic4.blue.encoder(oldName)} \u2192 ${cosmetic4.cyan.encoder(newName)}`);
494
+ console.log(` ${Color4.blue.encoder(oldName)} \u2192 ${Color4.cyan.encoder(newName)}`);
487
495
  }
488
496
  }
489
497
  }
@@ -492,9 +500,9 @@ ${cosmetic4.yellow.encoder(label)} (${folders.length} item${folders.length !==
492
500
  var history_default = history;
493
501
 
494
502
  // src/actions/list.ts
495
- import cosmetic5 from "cosmetic";
496
503
  import { existsSync as existsSync5, lstatSync, readdirSync as readdirSync3 } from "fs";
497
504
  import { resolve as resolve4 } from "path";
505
+ import { Color as Color5, Table } from "termkit";
498
506
 
499
507
  // src/helpers/dirSize.ts
500
508
  import { readdirSync as readdirSync2, statSync } from "fs";
@@ -626,7 +634,6 @@ var parseLibraryFolder = (name) => {
626
634
  if (match) return { title: match[1].trim(), year: parseInt(match[2]) };
627
635
  return { title: name };
628
636
  };
629
- var col = (s, width) => s.padEnd(width).substring(0, width);
630
637
  var list = async ({ type, missingSubs, codec: codecFilter, resolution: resFilter, sort }) => {
631
638
  const config = getConfig();
632
639
  const types = (type ? [type] : DEST_TYPES2).filter((t) => config.dest[t]);
@@ -635,7 +642,7 @@ var list = async ({ type, missingSubs, codec: codecFilter, resolution: resFilter
635
642
  const destRoot = config.dest[t];
636
643
  if (!existsSync5(destRoot)) {
637
644
  console.log(`
638
- ${t.toUpperCase()} ${cosmetic5.blue.encoder(destRoot)} (not found)`);
645
+ ${t.toUpperCase()} ${Color5.blue.encoder(destRoot)} (not found)`);
639
646
  continue;
640
647
  }
641
648
  const folders = readdirSync3(destRoot).filter((f) => {
@@ -665,18 +672,29 @@ ${t.toUpperCase()} ${cosmetic5.blue.encoder(destRoot)} (not found)`);
665
672
  const yearDiff = (b.year ?? 0) - (a.year ?? 0);
666
673
  return yearDiff !== 0 ? yearDiff : a.title.localeCompare(b.title);
667
674
  });
668
- const titleW = Math.min(50, Math.max(10, ...filtered.map((e) => e.title.length)) + 2);
669
- const divider = "\u2500".repeat(titleW + 44);
670
675
  console.log(`
671
- ${cosmetic5.yellow.encoder(t.toUpperCase())} ${cosmetic5.blue.encoder(destRoot)}`);
672
- console.log(divider);
673
- console.log(`${"Title".padEnd(titleW)} ${"Year".padEnd(6)} ${"Res".padEnd(6)} ${"Codec".padEnd(6)} ${"Size".padEnd(10)} Sub`);
674
- console.log(divider);
675
- for (const e of filtered) {
676
- const sub = e.hasSub ? cosmetic5.green.encoder("\u2713") : cosmetic5.red.encoder("\u2717");
677
- 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}`);
678
- }
679
- console.log(divider);
676
+ ${Color5.yellow.encoder(t.toUpperCase())} ${Color5.blue.encoder(destRoot)}`);
677
+ new Table(
678
+ filtered.map((e) => ({
679
+ title: e.title,
680
+ year: e.year,
681
+ resolution: e.resolution,
682
+ codec: e.codec,
683
+ size: e.size,
684
+ sub: e.hasSub
685
+ })),
686
+ {
687
+ separator: " ",
688
+ columns: [
689
+ { key: "title", title: "Title" },
690
+ { key: "year", title: "Year", align: "right", value: (v) => v != null ? String(v) : "\u2014" },
691
+ { key: "resolution", title: "Res", value: (v) => v ?? "\u2014" },
692
+ { key: "codec", title: "Codec", value: (v) => v ?? "\u2014" },
693
+ { key: "size", title: "Size" },
694
+ { key: "sub", title: "Sub", value: (v) => v ? Color5.green.encoder("\u2713") : Color5.red.encoder("\u2717") }
695
+ ]
696
+ }
697
+ ).print();
680
698
  console.log(`${filtered.length} of ${entries.length} item${entries.length !== 1 ? "s" : ""}`);
681
699
  }
682
700
  console.log();
@@ -685,9 +703,9 @@ var list_default = list;
685
703
 
686
704
  // src/actions/probe.ts
687
705
  import { spawnSync } from "child_process";
688
- import cosmetic6 from "cosmetic";
689
706
  import { existsSync as existsSync6, lstatSync as lstatSync2, readdirSync as readdirSync4 } from "fs";
690
707
  import { resolve as resolve5 } from "path";
708
+ import { Color as Color6 } from "termkit";
691
709
  var DEST_TYPES3 = ["movie", "tv", "ps3"];
692
710
  var CODEC_MAP2 = {
693
711
  hevc: "x265",
@@ -763,7 +781,7 @@ var probe = async ({ type, force, verbose }) => {
763
781
  for (const t of types) {
764
782
  const destRoot = config.dest[t];
765
783
  if (!existsSync6(destRoot)) continue;
766
- spinner_default.text = `scanning ${cosmetic6.blue.encoder(destRoot)}`;
784
+ spinner_default.text = `scanning ${Color6.blue.encoder(destRoot)}`;
767
785
  const files = walkVideoFiles(destRoot);
768
786
  for (const filePath of files) {
769
787
  if (!force && getMediaInfo(filePath)) {
@@ -771,7 +789,7 @@ var probe = async ({ type, force, verbose }) => {
771
789
  skipped++;
772
790
  continue;
773
791
  }
774
- spinner_default.text = `probing ${cosmetic6.blue.encoder(filePath)}`;
792
+ spinner_default.text = `probing ${Color6.blue.encoder(filePath)}`;
775
793
  const result = runFfprobe(filePath);
776
794
  if (!result) {
777
795
  if (verbose) spinner_default.warn(`ffprobe failed: ${filePath}`);
@@ -791,10 +809,10 @@ var probe = async ({ type, force, verbose }) => {
791
809
  var probe_default = probe;
792
810
 
793
811
  // src/actions/rename.ts
794
- import cosmetic7 from "cosmetic";
795
812
  import { existsSync as existsSync7, lstatSync as lstatSync3, readdirSync as readdirSync5, renameSync } from "fs";
796
813
  import { resolve as resolve6 } from "path";
797
814
  import { rimraf } from "rimraf";
815
+ import { Color as Color7 } from "termkit";
798
816
 
799
817
  // src/helpers/findSubtitle.ts
800
818
  var LANGUAGE_ALIASES = {
@@ -855,13 +873,13 @@ var rename = async ({ dir: inputDir, type, verbose }) => {
855
873
  const config = getConfig();
856
874
  const language = config.language ?? "eng";
857
875
  const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
858
- spinner_default.text = `renaming in ${cosmetic7.blue.encoder(dir)}`;
876
+ spinner_default.text = `renaming in ${Color7.blue.encoder(dir)}`;
859
877
  spinner_default.start();
860
878
  if (!existsSync7(dir)) throw new Error(`dir ${dir} does not exist`);
861
879
  const list2 = readdirSync5(dir);
862
880
  let renamed = 0, removed = 0, skipped = 0;
863
881
  for (const [index, entry] of list2.entries()) {
864
- spinner_default.text = `renaming in ${cosmetic7.blue.encoder(dir)} ${index + 1}/${list2.length}`;
882
+ spinner_default.text = `renaming in ${Color7.blue.encoder(dir)} ${index + 1}/${list2.length}`;
865
883
  if (!lstatSync3(resolve6(dir, entry)).isDirectory()) {
866
884
  if (verbose) spinner_default.info(`skipped ${entry}`);
867
885
  skipped++;
@@ -946,18 +964,18 @@ var rename = async ({ dir: inputDir, type, verbose }) => {
946
964
  spinner_default.succeed(`renamed ${renamed} files`);
947
965
  if (removed) spinner_default.info(`removed ${removed} files`);
948
966
  spinner_default.info(`skipped ${skipped} files`);
949
- spinner_default.succeed(`done in ${cosmetic7.cyan.encoder(dir)}`);
967
+ spinner_default.succeed(`done in ${Color7.cyan.encoder(dir)}`);
950
968
  spinner_default.stop();
951
969
  };
952
970
  var rename_default = rename;
953
971
 
954
972
  // src/actions/reset.ts
955
- import cosmetic8 from "cosmetic";
956
973
  import { existsSync as existsSync8, readdirSync as readdirSync6, renameSync as renameSync2 } from "fs";
957
974
  import { basename as basename2, dirname, resolve as resolve7, sep } from "path";
975
+ import { Color as Color8 } from "termkit";
958
976
  var reset = async ({ dir: inputDir, double }) => {
959
977
  let dir = inputDir;
960
- spinner_default.text = `resetting episodes in ${cosmetic8.blue.encoder(dir)}`;
978
+ spinner_default.text = `resetting episodes in ${Color8.blue.encoder(dir)}`;
961
979
  spinner_default.start();
962
980
  dir = resolve7(dir);
963
981
  if (!existsSync8(dir)) throw new Error(`dir ${dir} does not exist`);
@@ -986,7 +1004,7 @@ var reset = async ({ dir: inputDir, double }) => {
986
1004
  const episodeFormat = getConfig().format?.episode;
987
1005
  let renamed = 0, skipped = other.length;
988
1006
  for (const [index, i] of sublist.entries()) {
989
- spinner_default.text = `resetting episodes in ${cosmetic8.blue.encoder(dir)} ${index}/${list2.length}`;
1007
+ spinner_default.text = `resetting episodes in ${Color8.blue.encoder(dir)} ${index}/${list2.length}`;
990
1008
  const ext = i.match(/([^.]+$)/)?.[0];
991
1009
  const episode = double ? index * 2 + 1 : index + 1;
992
1010
  const name = `${formatEpisode(seasonNum, episode, episodeFormat, double, showTitle)}.${ext}`;
@@ -999,16 +1017,15 @@ var reset = async ({ dir: inputDir, double }) => {
999
1017
  }
1000
1018
  spinner_default.succeed(`renamed ${renamed} files`);
1001
1019
  spinner_default.info(`skipped ${skipped} files`);
1002
- spinner_default.succeed(`done in ${cosmetic8.cyan.encoder(dir)}`);
1020
+ spinner_default.succeed(`done in ${Color8.cyan.encoder(dir)}`);
1003
1021
  spinner_default.stop();
1004
1022
  };
1005
1023
  var reset_default = reset;
1006
1024
 
1007
1025
  // src/actions/scan.ts
1008
- import cosmetic9 from "cosmetic";
1009
1026
  import { cpSync, existsSync as existsSync9, linkSync, lstatSync as lstatSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync7, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync2 } from "fs";
1010
1027
  import { dirname as dirname2, resolve as resolve8 } from "path";
1011
- import { Select } from "termpulse";
1028
+ import { Color as Color9, Select } from "termkit";
1012
1029
 
1013
1030
  // src/helpers/detectEdition.ts
1014
1031
  var EDITIONS = [
@@ -1174,10 +1191,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1174
1191
  let imported = 0, skipped = 0;
1175
1192
  for (const source of config.sources) {
1176
1193
  if (!existsSync9(source)) {
1177
- spinner_default.warn(`source not found: ${cosmetic9.blue.encoder(source)}`);
1194
+ spinner_default.warn(`source not found: ${Color9.blue.encoder(source)}`);
1178
1195
  continue;
1179
1196
  }
1180
- spinner_default.text = `scanning ${cosmetic9.blue.encoder(source)}`;
1197
+ spinner_default.text = `scanning ${Color9.blue.encoder(source)}`;
1181
1198
  for (const entry of readdirSync7(source)) {
1182
1199
  const entryPath = resolve8(source, entry);
1183
1200
  const isDir = lstatSync4(entryPath).isDirectory();
@@ -1411,8 +1428,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
1411
1428
  var scan_default = scan;
1412
1429
 
1413
1430
  // src/actions/undo.ts
1414
- import cosmetic10 from "cosmetic";
1415
1431
  import { renameSync as renameSync4 } from "fs";
1432
+ import { Color as Color10 } from "termkit";
1416
1433
  var undo = async () => {
1417
1434
  spinner_default.start();
1418
1435
  const records = getLastSession();
@@ -1424,7 +1441,7 @@ var undo = async () => {
1424
1441
  let undone = 0;
1425
1442
  for (const record of records) {
1426
1443
  renameSync4(record.newPath, record.oldPath);
1427
- spinner_default.succeed(`${cosmetic10.cyan.encoder(record.newPath)} \u2192 ${cosmetic10.blue.encoder(record.oldPath)}`);
1444
+ spinner_default.succeed(`${Color10.cyan.encoder(record.newPath)} \u2192 ${Color10.blue.encoder(record.oldPath)}`);
1428
1445
  undone++;
1429
1446
  }
1430
1447
  deleteSession(records[0].sessionId);
@@ -1435,9 +1452,9 @@ var undo_default = undo;
1435
1452
 
1436
1453
  // src/actions/watch.ts
1437
1454
  import chokidar from "chokidar";
1438
- import cosmetic11 from "cosmetic";
1439
1455
  import { cpSync as cpSync2, existsSync as existsSync10, linkSync as linkSync2, lstatSync as lstatSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync8, renameSync as renameSync5, rmSync as rmSync3, statSync as statSync3 } from "fs";
1440
1456
  import { basename as basename3, dirname as dirname3, resolve as resolve9 } from "path";
1457
+ import { Color as Color11 } from "termkit";
1441
1458
  var sameDev2 = (a, b) => {
1442
1459
  try {
1443
1460
  let bExisting = b;
@@ -1508,7 +1525,7 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
1508
1525
  }
1509
1526
  moveItem(entryPath, destPath);
1510
1527
  recordImport(sessionId, entryPath, destPath, "move");
1511
- spinner_default.succeed(`imported ${cosmetic11.cyan.encoder(destName)}`);
1528
+ spinner_default.succeed(`imported ${Color11.cyan.encoder(destName)}`);
1512
1529
  return;
1513
1530
  }
1514
1531
  const parsed = parseDownloadName(entry);
@@ -1581,7 +1598,7 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
1581
1598
  if (isDir) rmSync3(entryPath, { recursive: true, force: true });
1582
1599
  }
1583
1600
  recordImport(sessionId, entryPath, seasonPath, mode);
1584
- spinner_default.succeed(`imported ${cosmetic11.cyan.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
1601
+ spinner_default.succeed(`imported ${Color11.cyan.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
1585
1602
  return;
1586
1603
  }
1587
1604
  const edition = detectEdition(entry);
@@ -1638,7 +1655,7 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
1638
1655
  }
1639
1656
  recordImport(sessionId, entryPath, destFolder, "move");
1640
1657
  }
1641
- spinner_default.succeed(`imported ${cosmetic11.cyan.encoder(folderName)}`);
1658
+ spinner_default.succeed(`imported ${Color11.cyan.encoder(folderName)}`);
1642
1659
  };
1643
1660
  var watch = async ({ hardlink = false, verbose = false, auto = false }) => {
1644
1661
  const config = getConfig();
@@ -1669,7 +1686,7 @@ var watch = async ({ hardlink = false, verbose = false, auto = false }) => {
1669
1686
  watcher.on("add", handle);
1670
1687
  spinner_default.start();
1671
1688
  spinner_default.succeed(`watching ${config.sources.length} source${config.sources.length !== 1 ? "s" : ""}`);
1672
- for (const s of config.sources) spinner_default.info(` ${cosmetic11.blue.encoder(s)}`);
1689
+ for (const s of config.sources) spinner_default.info(` ${Color11.blue.encoder(s)}`);
1673
1690
  spinner_default.stop();
1674
1691
  process.stdin.resume();
1675
1692
  };
@@ -1679,12 +1696,12 @@ export {
1679
1696
  DEFAULT_MOVIE_FORMAT,
1680
1697
  DEFAULT_SEASON_FORMAT,
1681
1698
  clean_default as clean,
1682
- configAdd,
1683
- configRemove,
1684
1699
  configSet,
1685
1700
  configShow,
1686
1701
  deleteImport,
1687
1702
  deleteSession,
1703
+ destAdd,
1704
+ destRemove,
1688
1705
  detectEdition,
1689
1706
  differences_default as differences,
1690
1707
  formatEpisode,
@@ -1709,6 +1726,8 @@ export {
1709
1726
  reset_default as reset,
1710
1727
  saveConfig,
1711
1728
  scan_default as scan,
1729
+ sourceAdd,
1730
+ sourceRemove,
1712
1731
  titleCase_default as titleCase,
1713
1732
  undo_default as undo,
1714
1733
  upsertMediaInfo,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reelsort",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "CLI to rename, organize, and manage your movie and TV library — Plex-compatible naming, hardlink support, and automated watch mode.",
5
5
  "keywords": [
6
6
  "media",
@@ -71,11 +71,9 @@
71
71
  "dependencies": {
72
72
  "better-sqlite3": "^12.10.0",
73
73
  "chokidar": "^5.0.0",
74
- "cosmetic": "^1.4.0",
75
74
  "rimraf": "^6.1.3",
76
75
  "roman-numeral": "^0.2.6",
77
- "termkit": "^2.0.2",
78
- "termpulse": "^1.1.3",
76
+ "termkit": "^2.2.0",
79
77
  "to-title-case": "^1.0.0"
80
78
  },
81
79
  "devDependencies": {