reelsort 0.2.6 → 0.2.8
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 +360 -321
- package/dist/index.js +284 -251
- package/dist/index.mjs +251 -218
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -23,13 +23,16 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
mod
|
|
24
24
|
));
|
|
25
25
|
|
|
26
|
-
// src/
|
|
26
|
+
// src/cli.ts
|
|
27
27
|
var import_termkit19 = require("termkit");
|
|
28
28
|
|
|
29
|
+
// src/program.ts
|
|
30
|
+
var import_termkit18 = require("termkit");
|
|
31
|
+
|
|
29
32
|
// src/actions/add.ts
|
|
30
33
|
var import_fs3 = require("fs");
|
|
31
34
|
var import_path3 = require("path");
|
|
32
|
-
var
|
|
35
|
+
var import_termkit = require("termkit");
|
|
33
36
|
|
|
34
37
|
// src/config.ts
|
|
35
38
|
var import_fs = require("fs");
|
|
@@ -286,60 +289,20 @@ var searchTv = async (title, apiKey) => {
|
|
|
286
289
|
}
|
|
287
290
|
};
|
|
288
291
|
|
|
289
|
-
// src/refs/spinner.ts
|
|
290
|
-
var import_termkit = require("termkit");
|
|
291
|
-
var Spinner = class {
|
|
292
|
-
spinner;
|
|
293
|
-
constructor() {
|
|
294
|
-
this.spinner = new import_termkit.Spinner();
|
|
295
|
-
}
|
|
296
|
-
get text() {
|
|
297
|
-
return this.spinner.text;
|
|
298
|
-
}
|
|
299
|
-
set text(t) {
|
|
300
|
-
this.spinner.text = t;
|
|
301
|
-
}
|
|
302
|
-
start(s) {
|
|
303
|
-
if (s) this.spinner.text = s;
|
|
304
|
-
this.spinner.start();
|
|
305
|
-
return this;
|
|
306
|
-
}
|
|
307
|
-
info(s) {
|
|
308
|
-
this.spinner.info(s);
|
|
309
|
-
return this;
|
|
310
|
-
}
|
|
311
|
-
warn(s) {
|
|
312
|
-
this.spinner.warn(s);
|
|
313
|
-
return this;
|
|
314
|
-
}
|
|
315
|
-
fail(s) {
|
|
316
|
-
this.spinner.fail(s);
|
|
317
|
-
return this;
|
|
318
|
-
}
|
|
319
|
-
succeed(s) {
|
|
320
|
-
this.spinner.succeed(s);
|
|
321
|
-
return this;
|
|
322
|
-
}
|
|
323
|
-
stop() {
|
|
324
|
-
this.spinner.stop();
|
|
325
|
-
return this;
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
var spinner_default = new Spinner();
|
|
329
|
-
|
|
330
292
|
// src/actions/add.ts
|
|
293
|
+
var spinner = new import_termkit.Spinner();
|
|
331
294
|
var add = async ({ name }) => {
|
|
332
295
|
const config = getConfig();
|
|
333
296
|
if (!config.tmdbApiKey) throw new Error("TMDb API key required \u2014 run: reelsort config set tmdb-key <key>");
|
|
334
297
|
const destRoot = config.dest.tv;
|
|
335
298
|
if (!destRoot) throw new Error("no TV destination configured \u2014 run: reelsort config set dest tv <dir>");
|
|
336
|
-
|
|
299
|
+
spinner.update(`searching TMDb for "${name}"`).start();
|
|
337
300
|
const results = await searchTv(name, config.tmdbApiKey);
|
|
338
|
-
|
|
301
|
+
spinner.stop();
|
|
339
302
|
if (results.length === 0) throw new Error(`no TMDb results for "${name}"`);
|
|
340
303
|
let picked = results.length === 1 ? results[0] : null;
|
|
341
304
|
if (!picked) {
|
|
342
|
-
const select = new
|
|
305
|
+
const select = new import_termkit.Select();
|
|
343
306
|
const items = results.map((r) => ({
|
|
344
307
|
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
345
308
|
description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
|
|
@@ -353,15 +316,16 @@ var add = async ({ name }) => {
|
|
|
353
316
|
const showPath = (0, import_path3.resolve)(destRoot, folderName);
|
|
354
317
|
(0, import_fs3.mkdirSync)(showPath, { recursive: true });
|
|
355
318
|
upsertShow(showPath, picked.id, picked.title);
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
319
|
+
spinner.start();
|
|
320
|
+
spinner.succeed(`added ${import_termkit.Color.green.encoder(folderName)}`);
|
|
321
|
+
spinner.stop();
|
|
359
322
|
};
|
|
360
323
|
var add_default = add;
|
|
361
324
|
|
|
362
325
|
// src/actions/clean.ts
|
|
363
326
|
var import_fs4 = require("fs");
|
|
364
|
-
var
|
|
327
|
+
var import_termkit2 = require("termkit");
|
|
328
|
+
var spinner2 = new import_termkit2.Spinner();
|
|
365
329
|
var parseOlderThan = (s) => {
|
|
366
330
|
const match = s.match(/^(\d+)([dhm])$/);
|
|
367
331
|
if (!match) return null;
|
|
@@ -371,11 +335,11 @@ var parseOlderThan = (s) => {
|
|
|
371
335
|
return ms;
|
|
372
336
|
};
|
|
373
337
|
var clean = async ({ dryRun, olderThan }) => {
|
|
374
|
-
|
|
338
|
+
spinner2.start();
|
|
375
339
|
const imports = getCleanableImports();
|
|
376
340
|
if (imports.length === 0) {
|
|
377
|
-
|
|
378
|
-
|
|
341
|
+
spinner2.info("nothing to clean");
|
|
342
|
+
spinner2.stop();
|
|
379
343
|
return;
|
|
380
344
|
}
|
|
381
345
|
const cutoffMs = olderThan ? parseOlderThan(olderThan) : null;
|
|
@@ -394,30 +358,30 @@ var clean = async ({ dryRun, olderThan }) => {
|
|
|
394
358
|
continue;
|
|
395
359
|
}
|
|
396
360
|
if (dryRun) {
|
|
397
|
-
|
|
361
|
+
spinner2.succeed(`[dry] would remove ${import_termkit2.Color.white.encoder(imp.sourcePath)}`);
|
|
398
362
|
cleaned++;
|
|
399
363
|
continue;
|
|
400
364
|
}
|
|
401
365
|
try {
|
|
402
366
|
(0, import_fs4.rmSync)(imp.sourcePath, { recursive: true, force: true });
|
|
403
367
|
deleteImport(imp.id);
|
|
404
|
-
|
|
368
|
+
spinner2.succeed(`removed ${import_termkit2.Color.white.encoder(imp.sourcePath)}`);
|
|
405
369
|
cleaned++;
|
|
406
370
|
} catch {
|
|
407
|
-
|
|
371
|
+
spinner2.warn(`locked or inaccessible, skipped: ${import_termkit2.Color.white.encoder(imp.sourcePath)}`);
|
|
408
372
|
skipped++;
|
|
409
373
|
}
|
|
410
374
|
}
|
|
411
|
-
|
|
412
|
-
if (skipped)
|
|
413
|
-
|
|
375
|
+
spinner2.succeed(`cleaned ${cleaned} items`);
|
|
376
|
+
if (skipped) spinner2.info(`skipped ${skipped} items`);
|
|
377
|
+
spinner2.stop();
|
|
414
378
|
};
|
|
415
379
|
var clean_default = clean;
|
|
416
380
|
|
|
417
381
|
// src/actions/config.ts
|
|
418
382
|
var import_fs5 = require("fs");
|
|
419
383
|
var import_path4 = require("path");
|
|
420
|
-
var
|
|
384
|
+
var import_termkit3 = require("termkit");
|
|
421
385
|
|
|
422
386
|
// src/helpers/formatEpisode.ts
|
|
423
387
|
var DEFAULT_EPISODE_FORMAT = "{s}x{ee} - {title}";
|
|
@@ -436,6 +400,7 @@ var formatEpisode = (season, episode, format = DEFAULT_EPISODE_FORMAT, double =
|
|
|
436
400
|
};
|
|
437
401
|
|
|
438
402
|
// src/actions/config.ts
|
|
403
|
+
var spinner3 = new import_termkit3.Spinner();
|
|
439
404
|
var DEST_TYPES = ["movie", "tv", "ps3", "book"];
|
|
440
405
|
var getSourceEntries = (sources) => {
|
|
441
406
|
const entries = [];
|
|
@@ -459,27 +424,27 @@ var sourceAdd = async ({ dir }) => {
|
|
|
459
424
|
const resolved = (0, import_path4.resolve)(dir);
|
|
460
425
|
const config = getConfig();
|
|
461
426
|
if (config.sources.includes(resolved)) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
427
|
+
spinner3.start();
|
|
428
|
+
spinner3.info(`source already configured: ${import_termkit3.Color.white.encoder(resolved)}`);
|
|
429
|
+
spinner3.stop();
|
|
465
430
|
return;
|
|
466
431
|
}
|
|
467
432
|
config.sources.push(resolved);
|
|
468
433
|
saveConfig(config);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
434
|
+
spinner3.start();
|
|
435
|
+
spinner3.succeed(`added source: ${import_termkit3.Color.white.encoder(resolved)}`);
|
|
436
|
+
spinner3.stop();
|
|
472
437
|
};
|
|
473
438
|
var sourceRemove = async ({ dir }) => {
|
|
474
439
|
const config = getConfig();
|
|
475
440
|
if (!dir) {
|
|
476
441
|
if (config.sources.length === 0) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
442
|
+
spinner3.start();
|
|
443
|
+
spinner3.warn("no sources configured");
|
|
444
|
+
spinner3.stop();
|
|
480
445
|
return;
|
|
481
446
|
}
|
|
482
|
-
const select = new
|
|
447
|
+
const select = new import_termkit3.Select();
|
|
483
448
|
const picked = await select.ask("Which source do you want to remove?", config.sources.map((s) => ({ label: s, value: s })));
|
|
484
449
|
if (!picked) return;
|
|
485
450
|
dir = picked.value;
|
|
@@ -487,16 +452,16 @@ var sourceRemove = async ({ dir }) => {
|
|
|
487
452
|
const resolved = (0, import_path4.resolve)(dir);
|
|
488
453
|
const index = config.sources.indexOf(resolved);
|
|
489
454
|
if (index === -1) {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
455
|
+
spinner3.start();
|
|
456
|
+
spinner3.warn(`source not found: ${import_termkit3.Color.white.encoder(resolved)}`);
|
|
457
|
+
spinner3.stop();
|
|
493
458
|
return;
|
|
494
459
|
}
|
|
495
460
|
config.sources.splice(index, 1);
|
|
496
461
|
saveConfig(config);
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
462
|
+
spinner3.start();
|
|
463
|
+
spinner3.succeed(`removed source: ${import_termkit3.Color.white.encoder(resolved)}`);
|
|
464
|
+
spinner3.stop();
|
|
500
465
|
};
|
|
501
466
|
var destAdd = async ({ type, dir }) => {
|
|
502
467
|
if (!DEST_TYPES.includes(type)) {
|
|
@@ -506,21 +471,21 @@ var destAdd = async ({ type, dir }) => {
|
|
|
506
471
|
const config = getConfig();
|
|
507
472
|
config.dest[type] = resolved;
|
|
508
473
|
saveConfig(config);
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
474
|
+
spinner3.start();
|
|
475
|
+
spinner3.succeed(`set ${type} destination: ${import_termkit3.Color.green.encoder(resolved)}`);
|
|
476
|
+
spinner3.stop();
|
|
512
477
|
};
|
|
513
478
|
var destRemove = async ({ type }) => {
|
|
514
479
|
const config = getConfig();
|
|
515
480
|
if (!type) {
|
|
516
481
|
const configured = DEST_TYPES.filter((t) => config.dest[t]);
|
|
517
482
|
if (configured.length === 0) {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
483
|
+
spinner3.start();
|
|
484
|
+
spinner3.warn("no destinations configured");
|
|
485
|
+
spinner3.stop();
|
|
521
486
|
return;
|
|
522
487
|
}
|
|
523
|
-
const select = new
|
|
488
|
+
const select = new import_termkit3.Select();
|
|
524
489
|
const picked = await select.ask("Which destination do you want to remove?", configured.map((t) => ({ label: `${t.padEnd(6)} ${config.dest[t]}`, value: t })));
|
|
525
490
|
if (!picked) return;
|
|
526
491
|
type = picked.value;
|
|
@@ -529,16 +494,16 @@ var destRemove = async ({ type }) => {
|
|
|
529
494
|
throw new Error(`unknown type '${type}', expected: ${DEST_TYPES.join(", ")}`);
|
|
530
495
|
}
|
|
531
496
|
if (!config.dest[type]) {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
497
|
+
spinner3.start();
|
|
498
|
+
spinner3.warn(`no ${type} destination configured`);
|
|
499
|
+
spinner3.stop();
|
|
535
500
|
return;
|
|
536
501
|
}
|
|
537
502
|
delete config.dest[type];
|
|
538
503
|
saveConfig(config);
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
504
|
+
spinner3.start();
|
|
505
|
+
spinner3.succeed(`removed ${type} destination`);
|
|
506
|
+
spinner3.stop();
|
|
542
507
|
};
|
|
543
508
|
var ignore = async ({ name }) => {
|
|
544
509
|
const config = getConfig();
|
|
@@ -547,12 +512,12 @@ var ignore = async ({ name }) => {
|
|
|
547
512
|
if (names.length === 0) {
|
|
548
513
|
const entries = getSourceEntries(config.sources);
|
|
549
514
|
if (entries.length === 0) {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
515
|
+
spinner3.start();
|
|
516
|
+
spinner3.warn("no files found in configured sources");
|
|
517
|
+
spinner3.stop();
|
|
553
518
|
return;
|
|
554
519
|
}
|
|
555
|
-
const ms = new
|
|
520
|
+
const ms = new import_termkit3.MultiSelect({ allowSkip: true, search: true, maxHeight: 20 });
|
|
556
521
|
const items = entries.map((e) => ({ label: e }));
|
|
557
522
|
const picked = await ms.ask("Select files to ignore during scan:", items);
|
|
558
523
|
if (!picked || picked.length === 0) return;
|
|
@@ -566,67 +531,67 @@ var ignore = async ({ name }) => {
|
|
|
566
531
|
}
|
|
567
532
|
}
|
|
568
533
|
if (added.length === 0) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
534
|
+
spinner3.start();
|
|
535
|
+
spinner3.info("all selected files are already ignored");
|
|
536
|
+
spinner3.stop();
|
|
572
537
|
return;
|
|
573
538
|
}
|
|
574
539
|
saveConfig(config);
|
|
575
|
-
|
|
576
|
-
for (const n of added)
|
|
577
|
-
|
|
540
|
+
spinner3.start();
|
|
541
|
+
for (const n of added) spinner3.succeed(`ignoring: ${import_termkit3.Color.white.encoder(n)}`);
|
|
542
|
+
spinner3.stop();
|
|
578
543
|
};
|
|
579
544
|
var ignoreRemove = async ({ name }) => {
|
|
580
545
|
const config = getConfig();
|
|
581
546
|
const list2 = config.ignore ?? [];
|
|
582
547
|
if (!name) {
|
|
583
548
|
if (list2.length === 0) {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
549
|
+
spinner3.start();
|
|
550
|
+
spinner3.warn("ignore list is empty");
|
|
551
|
+
spinner3.stop();
|
|
587
552
|
return;
|
|
588
553
|
}
|
|
589
|
-
const ms = new
|
|
554
|
+
const ms = new import_termkit3.MultiSelect({ allowSkip: true, search: true, maxHeight: 20 });
|
|
590
555
|
const items = list2.map((n) => ({ label: n }));
|
|
591
556
|
const picked = await ms.ask("Select files to remove from ignore list:", items);
|
|
592
557
|
if (!picked || picked.length === 0) return;
|
|
593
558
|
const toRemove = new Set(picked.map((p) => p.label));
|
|
594
559
|
config.ignore = list2.filter((n) => !toRemove.has(n));
|
|
595
560
|
saveConfig(config);
|
|
596
|
-
|
|
597
|
-
for (const n of [...toRemove])
|
|
598
|
-
|
|
561
|
+
spinner3.start();
|
|
562
|
+
for (const n of [...toRemove]) spinner3.succeed(`removed from ignore list: ${import_termkit3.Color.white.encoder(n)}`);
|
|
563
|
+
spinner3.stop();
|
|
599
564
|
return;
|
|
600
565
|
}
|
|
601
566
|
const index = list2.indexOf(name);
|
|
602
567
|
if (index === -1) {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
568
|
+
spinner3.start();
|
|
569
|
+
spinner3.warn(`not in ignore list: ${import_termkit3.Color.white.encoder(name)}`);
|
|
570
|
+
spinner3.stop();
|
|
606
571
|
return;
|
|
607
572
|
}
|
|
608
573
|
config.ignore.splice(index, 1);
|
|
609
574
|
saveConfig(config);
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
575
|
+
spinner3.start();
|
|
576
|
+
spinner3.succeed(`removed from ignore list: ${import_termkit3.Color.white.encoder(name)}`);
|
|
577
|
+
spinner3.stop();
|
|
613
578
|
};
|
|
614
579
|
var configSet = async ({ key, subkey, value }) => {
|
|
615
580
|
const config = getConfig();
|
|
616
581
|
if (key === "language") {
|
|
617
582
|
config.language = subkey;
|
|
618
583
|
saveConfig(config);
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
584
|
+
spinner3.start();
|
|
585
|
+
spinner3.succeed(`set subtitle language: ${import_termkit3.Color.green.encoder(subkey)}`);
|
|
586
|
+
spinner3.stop();
|
|
622
587
|
return;
|
|
623
588
|
}
|
|
624
589
|
if (key === "tmdb-key") {
|
|
625
590
|
config.tmdbApiKey = subkey;
|
|
626
591
|
saveConfig(config);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
592
|
+
spinner3.start();
|
|
593
|
+
spinner3.succeed(`set TMDb API key`);
|
|
594
|
+
spinner3.stop();
|
|
630
595
|
return;
|
|
631
596
|
}
|
|
632
597
|
if (key === "format") {
|
|
@@ -646,9 +611,9 @@ var configSet = async ({ key, subkey, value }) => {
|
|
|
646
611
|
throw new Error(`unknown format key '${subkey}', expected: movie, episode, season`);
|
|
647
612
|
}
|
|
648
613
|
saveConfig(config);
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
614
|
+
spinner3.start();
|
|
615
|
+
spinner3.succeed(`set ${subkey} format: ${import_termkit3.Color.green.encoder(value ?? subkey)}`);
|
|
616
|
+
spinner3.stop();
|
|
652
617
|
return;
|
|
653
618
|
}
|
|
654
619
|
throw new Error(`unknown key '${key}', expected: language, tmdb-key, format`);
|
|
@@ -659,7 +624,7 @@ var configShow = async () => {
|
|
|
659
624
|
if (config.sources.length === 0) {
|
|
660
625
|
console.log(" (none)");
|
|
661
626
|
} else {
|
|
662
|
-
for (const s of config.sources) console.log(` ${
|
|
627
|
+
for (const s of config.sources) console.log(` ${import_termkit3.Color.white.encoder(s)}`);
|
|
663
628
|
}
|
|
664
629
|
console.log("\nDestinations:");
|
|
665
630
|
const entries = DEST_TYPES.map((t) => ({ type: t, path: config.dest[t] })).filter((e) => e.path);
|
|
@@ -667,20 +632,20 @@ var configShow = async () => {
|
|
|
667
632
|
console.log(" (none)");
|
|
668
633
|
} else {
|
|
669
634
|
for (const { type, path } of entries) {
|
|
670
|
-
console.log(` ${type.padEnd(6)} ${
|
|
635
|
+
console.log(` ${type.padEnd(6)} ${import_termkit3.Color.green.encoder(path)}`);
|
|
671
636
|
}
|
|
672
637
|
}
|
|
673
638
|
console.log(`
|
|
674
|
-
Subtitle language: ${
|
|
675
|
-
console.log(`TMDb API key: ${config.tmdbApiKey ?
|
|
676
|
-
console.log(`Movie format: ${
|
|
677
|
-
console.log(`Episode format: ${
|
|
678
|
-
console.log(`Season folder: ${
|
|
639
|
+
Subtitle language: ${import_termkit3.Color.green.encoder(config.language ?? "eng (default)")}`);
|
|
640
|
+
console.log(`TMDb API key: ${config.tmdbApiKey ? import_termkit3.Color.green.encoder("configured") : import_termkit3.Color.red.encoder("not set")}`);
|
|
641
|
+
console.log(`Movie format: ${import_termkit3.Color.green.encoder(config.format?.movie ?? DEFAULT_MOVIE_FORMAT + " (default)")}`);
|
|
642
|
+
console.log(`Episode format: ${import_termkit3.Color.green.encoder(config.format?.episode ?? DEFAULT_EPISODE_FORMAT + " (default)")}`);
|
|
643
|
+
console.log(`Season folder: ${import_termkit3.Color.green.encoder(config.format?.season ?? DEFAULT_SEASON_FORMAT + " (default)")}`);
|
|
679
644
|
console.log("\nIgnored files:");
|
|
680
645
|
if (!config.ignore || config.ignore.length === 0) {
|
|
681
646
|
console.log(" (none)");
|
|
682
647
|
} else {
|
|
683
|
-
for (const name of config.ignore) console.log(` ${
|
|
648
|
+
for (const name of config.ignore) console.log(` ${import_termkit3.Color.white.encoder(name)}`);
|
|
684
649
|
}
|
|
685
650
|
console.log();
|
|
686
651
|
};
|
|
@@ -688,12 +653,13 @@ Subtitle language: ${import_termkit4.Color.green.encoder(config.language ?? "eng
|
|
|
688
653
|
// src/actions/differences.ts
|
|
689
654
|
var import_fs6 = require("fs");
|
|
690
655
|
var import_path5 = require("path");
|
|
691
|
-
var
|
|
656
|
+
var import_termkit4 = require("termkit");
|
|
657
|
+
var spinner4 = new import_termkit4.Spinner();
|
|
692
658
|
var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore: ignore2 }) => {
|
|
693
659
|
let dir1 = rawDir1;
|
|
694
660
|
let dir2 = rawDir2;
|
|
695
|
-
|
|
696
|
-
|
|
661
|
+
spinner4.update(`checking differences between ${import_termkit4.Color.white.encoder(dir1)} and ${import_termkit4.Color.white.encoder(dir2)}`);
|
|
662
|
+
spinner4.start();
|
|
697
663
|
dir1 = (0, import_path5.resolve)(dir1);
|
|
698
664
|
dir2 = (0, import_path5.resolve)(dir2);
|
|
699
665
|
if (!(0, import_fs6.existsSync)(dir1)) throw new Error(`dir1 ${dir1} does not exist`);
|
|
@@ -728,27 +694,28 @@ var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore: ignore2 }
|
|
|
728
694
|
removed.push(l);
|
|
729
695
|
}
|
|
730
696
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
for (const i of added) console.log(`${
|
|
736
|
-
for (const i of removed) console.log(`${
|
|
697
|
+
spinner4.succeed(`checked differences between ${import_termkit4.Color.white.encoder(dir1)} and ${import_termkit4.Color.white.encoder(dir2)}`);
|
|
698
|
+
spinner4.succeed(`found ${added.length} added files`);
|
|
699
|
+
spinner4.succeed(`found ${removed.length} removed files`);
|
|
700
|
+
spinner4.stop();
|
|
701
|
+
for (const i of added) console.log(`${import_termkit4.Color.green.encoder("added")} ${i}`);
|
|
702
|
+
for (const i of removed) console.log(`${import_termkit4.Color.red.encoder("removed")} ${i}`);
|
|
737
703
|
};
|
|
738
704
|
var differences_default = differences;
|
|
739
705
|
|
|
740
706
|
// src/actions/ended.ts
|
|
741
|
-
var
|
|
707
|
+
var import_termkit5 = require("termkit");
|
|
708
|
+
var spinner5 = new import_termkit5.Spinner();
|
|
742
709
|
var ended = async ({ remove }) => {
|
|
743
710
|
const shows2 = getShows();
|
|
744
711
|
const candidates = shows2.filter((s) => remove ? s.ended : !s.ended);
|
|
745
712
|
if (candidates.length === 0) {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
713
|
+
spinner5.start();
|
|
714
|
+
spinner5.info(remove ? "no ended shows to restore" : "no active shows to mark as ended");
|
|
715
|
+
spinner5.stop();
|
|
749
716
|
return;
|
|
750
717
|
}
|
|
751
|
-
const select = new
|
|
718
|
+
const select = new import_termkit5.Select();
|
|
752
719
|
const items = candidates.map((s) => ({
|
|
753
720
|
label: s.path.split("/").pop() ?? s.path,
|
|
754
721
|
path: s.path
|
|
@@ -757,19 +724,19 @@ var ended = async ({ remove }) => {
|
|
|
757
724
|
const picked = await select.ask(prompt, items);
|
|
758
725
|
if (!picked) return;
|
|
759
726
|
setShowEnded(picked.path, !remove);
|
|
760
|
-
|
|
727
|
+
spinner5.start();
|
|
761
728
|
if (remove) {
|
|
762
|
-
|
|
729
|
+
spinner5.succeed(`marked as active: ${import_termkit5.Color.green.encoder(picked.label)}`);
|
|
763
730
|
} else {
|
|
764
|
-
|
|
731
|
+
spinner5.succeed(`marked as ended: ${import_termkit5.Color.green.encoder(picked.label)}`);
|
|
765
732
|
}
|
|
766
|
-
|
|
733
|
+
spinner5.stop();
|
|
767
734
|
};
|
|
768
735
|
var ended_default = ended;
|
|
769
736
|
|
|
770
737
|
// src/actions/history.ts
|
|
771
738
|
var import_path6 = require("path");
|
|
772
|
-
var
|
|
739
|
+
var import_termkit6 = require("termkit");
|
|
773
740
|
var history = async ({ limit, imports }) => {
|
|
774
741
|
if (imports) {
|
|
775
742
|
const sessions = getImportHistory(limit ?? 10);
|
|
@@ -781,11 +748,11 @@ var history = async ({ limit, imports }) => {
|
|
|
781
748
|
const date = new Date(session.sessionId);
|
|
782
749
|
const label = isNaN(date.getTime()) ? session.sessionId : date.toLocaleString();
|
|
783
750
|
console.log(`
|
|
784
|
-
${
|
|
751
|
+
${import_termkit6.Color.yellow.encoder(label)} (${session.records.length} item${session.records.length !== 1 ? "s" : ""})`);
|
|
785
752
|
for (const r of session.records) {
|
|
786
753
|
const src = (0, import_path6.basename)(r.sourcePath);
|
|
787
|
-
const dest =
|
|
788
|
-
const mode = r.mode !== "move" ? ` ${
|
|
754
|
+
const dest = import_termkit6.Color.green.encoder(r.destinationPath);
|
|
755
|
+
const mode = r.mode !== "move" ? ` ${import_termkit6.Color.white.encoder(`[${r.mode}]`)}` : "";
|
|
789
756
|
console.log(` ${src} \u2192 ${dest}${mode}`);
|
|
790
757
|
}
|
|
791
758
|
}
|
|
@@ -800,11 +767,11 @@ ${import_termkit7.Color.yellow.encoder(label)} (${session.records.length} item$
|
|
|
800
767
|
const label = isNaN(date.getTime()) ? session.sessionId : date.toLocaleString();
|
|
801
768
|
const folders = session.records.filter((r) => (0, import_path6.extname)(r.newPath) === "");
|
|
802
769
|
console.log(`
|
|
803
|
-
${
|
|
770
|
+
${import_termkit6.Color.yellow.encoder(label)} (${folders.length} item${folders.length !== 1 ? "s" : ""})`);
|
|
804
771
|
for (const r of folders) {
|
|
805
772
|
const oldName = (0, import_path6.basename)(r.oldPath);
|
|
806
773
|
const newName = (0, import_path6.basename)(r.newPath);
|
|
807
|
-
console.log(` ${
|
|
774
|
+
console.log(` ${import_termkit6.Color.white.encoder(oldName)} \u2192 ${import_termkit6.Color.green.encoder(newName)}`);
|
|
808
775
|
}
|
|
809
776
|
}
|
|
810
777
|
}
|
|
@@ -815,7 +782,8 @@ var history_default = history;
|
|
|
815
782
|
// src/actions/link.ts
|
|
816
783
|
var import_fs7 = require("fs");
|
|
817
784
|
var import_path7 = require("path");
|
|
818
|
-
var
|
|
785
|
+
var import_termkit7 = require("termkit");
|
|
786
|
+
var spinner6 = new import_termkit7.Spinner();
|
|
819
787
|
var parseShowTitle = (folderName) => {
|
|
820
788
|
const withoutYear = folderName.replace(/\s*\(\d{4}\)\s*$/, "").trim();
|
|
821
789
|
return withoutYear || folderName;
|
|
@@ -842,49 +810,49 @@ var link = async ({ force }) => {
|
|
|
842
810
|
skipped++;
|
|
843
811
|
continue;
|
|
844
812
|
}
|
|
845
|
-
|
|
813
|
+
spinner6.update(`linking ${import_termkit7.Color.white.encoder(show)}`).start();
|
|
846
814
|
const title = parseShowTitle(show);
|
|
847
815
|
const results = await searchTv(title, config.tmdbApiKey);
|
|
848
816
|
if (results.length === 0) {
|
|
849
|
-
|
|
817
|
+
spinner6.warn(`not found in TMDb: ${show}`);
|
|
850
818
|
notFound++;
|
|
851
819
|
continue;
|
|
852
820
|
}
|
|
853
821
|
if (results.length === 1) {
|
|
854
822
|
upsertShow(showPath, results[0].id, results[0].title);
|
|
855
|
-
|
|
823
|
+
spinner6.succeed(`${show} \u2192 ${import_termkit7.Color.green.encoder(results[0].title)} (${results[0].year})`);
|
|
856
824
|
linked++;
|
|
857
825
|
continue;
|
|
858
826
|
}
|
|
859
|
-
|
|
860
|
-
const select = new
|
|
827
|
+
spinner6.stop();
|
|
828
|
+
const select = new import_termkit7.Select();
|
|
861
829
|
const items = results.map((r) => ({
|
|
862
830
|
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
863
831
|
description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
|
|
864
832
|
...r
|
|
865
833
|
}));
|
|
866
834
|
const picked = await select.ask(`Multiple shows found for "${show}":`, items);
|
|
867
|
-
|
|
835
|
+
spinner6.start();
|
|
868
836
|
if (picked) {
|
|
869
837
|
upsertShow(showPath, picked.id, picked.title);
|
|
870
|
-
|
|
838
|
+
spinner6.succeed(`${show} \u2192 ${import_termkit7.Color.green.encoder(picked.title)} (${picked.year})`);
|
|
871
839
|
linked++;
|
|
872
840
|
} else {
|
|
873
|
-
|
|
841
|
+
spinner6.info(`skipped: ${show}`);
|
|
874
842
|
skipped++;
|
|
875
843
|
}
|
|
876
844
|
}
|
|
877
|
-
|
|
878
|
-
if (notFound)
|
|
879
|
-
if (skipped)
|
|
880
|
-
|
|
845
|
+
spinner6.succeed(`linked ${linked} show${linked !== 1 ? "s" : ""}`);
|
|
846
|
+
if (notFound) spinner6.warn(`not found in TMDb: ${notFound}`);
|
|
847
|
+
if (skipped) spinner6.info(`skipped ${skipped} (already linked \u2014 use --force to re-link)`);
|
|
848
|
+
spinner6.stop();
|
|
881
849
|
};
|
|
882
850
|
var link_default = link;
|
|
883
851
|
|
|
884
852
|
// src/actions/list.ts
|
|
885
853
|
var import_fs9 = require("fs");
|
|
886
854
|
var import_path9 = require("path");
|
|
887
|
-
var
|
|
855
|
+
var import_termkit8 = require("termkit");
|
|
888
856
|
|
|
889
857
|
// src/helpers/dirSize.ts
|
|
890
858
|
var import_fs8 = require("fs");
|
|
@@ -1024,7 +992,7 @@ var list = async ({ type, missingSubs, codec: codecFilter, resolution: resFilter
|
|
|
1024
992
|
const destRoot = config.dest[t];
|
|
1025
993
|
if (!(0, import_fs9.existsSync)(destRoot)) {
|
|
1026
994
|
console.log(`
|
|
1027
|
-
${t.toUpperCase()} ${
|
|
995
|
+
${t.toUpperCase()} ${import_termkit8.Color.white.encoder(destRoot)} (not found)`);
|
|
1028
996
|
continue;
|
|
1029
997
|
}
|
|
1030
998
|
const folders = (0, import_fs9.readdirSync)(destRoot).filter((f) => {
|
|
@@ -1055,8 +1023,8 @@ ${t.toUpperCase()} ${import_termkit9.Color.white.encoder(destRoot)} (not found
|
|
|
1055
1023
|
return yearDiff !== 0 ? yearDiff : a.title.localeCompare(b.title);
|
|
1056
1024
|
});
|
|
1057
1025
|
console.log(`
|
|
1058
|
-
${
|
|
1059
|
-
new
|
|
1026
|
+
${import_termkit8.Color.yellow.encoder(t.toUpperCase())} ${import_termkit8.Color.white.encoder(destRoot)}`);
|
|
1027
|
+
new import_termkit8.Table(
|
|
1060
1028
|
filtered.map((e) => ({
|
|
1061
1029
|
title: e.title,
|
|
1062
1030
|
year: e.year,
|
|
@@ -1073,7 +1041,7 @@ ${import_termkit9.Color.yellow.encoder(t.toUpperCase())} ${import_termkit9.Colo
|
|
|
1073
1041
|
{ key: "resolution", title: "Res", value: (v) => v ?? "\u2014" },
|
|
1074
1042
|
{ key: "codec", title: "Codec", value: (v) => v ?? "\u2014" },
|
|
1075
1043
|
{ key: "size", title: "Size" },
|
|
1076
|
-
{ key: "sub", title: "Sub", value: (v) => v ?
|
|
1044
|
+
{ key: "sub", title: "Sub", value: (v) => v ? import_termkit8.Color.green.encoder("\u2713") : import_termkit8.Color.red.encoder("\u2717") }
|
|
1077
1045
|
]
|
|
1078
1046
|
}
|
|
1079
1047
|
).print();
|
|
@@ -1086,7 +1054,7 @@ var list_default = list;
|
|
|
1086
1054
|
// src/actions/missing.ts
|
|
1087
1055
|
var import_fs10 = require("fs");
|
|
1088
1056
|
var import_path10 = require("path");
|
|
1089
|
-
var
|
|
1057
|
+
var import_termkit9 = require("termkit");
|
|
1090
1058
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1091
1059
|
var parseSeasonNumber = (folderName) => {
|
|
1092
1060
|
const match = folderName.match(/(?:season|s)\s*0*(\d+)/i);
|
|
@@ -1148,7 +1116,7 @@ var missing = async ({ show: showFilter }) => {
|
|
|
1148
1116
|
if (showMissing.length > 0) {
|
|
1149
1117
|
totalMissing += showMissing.length;
|
|
1150
1118
|
console.log(`
|
|
1151
|
-
${
|
|
1119
|
+
${import_termkit9.Color.yellow.encoder(show)}`);
|
|
1152
1120
|
for (const line of showMissing) console.log(line);
|
|
1153
1121
|
}
|
|
1154
1122
|
}
|
|
@@ -1169,7 +1137,7 @@ var missing_default = missing;
|
|
|
1169
1137
|
var import_child_process = require("child_process");
|
|
1170
1138
|
var import_fs11 = require("fs");
|
|
1171
1139
|
var import_path11 = require("path");
|
|
1172
|
-
var
|
|
1140
|
+
var import_termkit10 = require("termkit");
|
|
1173
1141
|
|
|
1174
1142
|
// src/refs/verbose.ts
|
|
1175
1143
|
var _verbose = false;
|
|
@@ -1179,6 +1147,7 @@ var setVerbose = (v) => {
|
|
|
1179
1147
|
var isVerbose = () => _verbose;
|
|
1180
1148
|
|
|
1181
1149
|
// src/actions/probe.ts
|
|
1150
|
+
var spinner7 = new import_termkit10.Spinner();
|
|
1182
1151
|
var DEST_TYPES3 = ["movie", "tv", "ps3"];
|
|
1183
1152
|
var CODEC_MAP2 = {
|
|
1184
1153
|
hevc: "x265",
|
|
@@ -1241,10 +1210,10 @@ var walkVideoFiles = (dir, depth = 0, maxDepth = 3) => {
|
|
|
1241
1210
|
return results;
|
|
1242
1211
|
};
|
|
1243
1212
|
var probe = async ({ type, force }) => {
|
|
1244
|
-
|
|
1213
|
+
spinner7.start();
|
|
1245
1214
|
if (!isFfprobeAvailable()) {
|
|
1246
|
-
|
|
1247
|
-
|
|
1215
|
+
spinner7.fail("ffprobe not found \u2014 install ffmpeg to use this command");
|
|
1216
|
+
spinner7.stop();
|
|
1248
1217
|
return;
|
|
1249
1218
|
}
|
|
1250
1219
|
const config = getConfig();
|
|
@@ -1254,30 +1223,30 @@ var probe = async ({ type, force }) => {
|
|
|
1254
1223
|
for (const t of types) {
|
|
1255
1224
|
const destRoot = config.dest[t];
|
|
1256
1225
|
if (!(0, import_fs11.existsSync)(destRoot)) continue;
|
|
1257
|
-
|
|
1226
|
+
spinner7.update(`scanning ${import_termkit10.Color.white.encoder(destRoot)}`);
|
|
1258
1227
|
const files = walkVideoFiles(destRoot);
|
|
1259
1228
|
for (const filePath of files) {
|
|
1260
1229
|
if (!force && getMediaInfo(filePath)) {
|
|
1261
|
-
if (isVerbose())
|
|
1230
|
+
if (isVerbose()) spinner7.info(`already probed: ${filePath}`);
|
|
1262
1231
|
skipped++;
|
|
1263
1232
|
continue;
|
|
1264
1233
|
}
|
|
1265
|
-
|
|
1234
|
+
spinner7.update(`probing ${import_termkit10.Color.white.encoder(filePath)}`);
|
|
1266
1235
|
const result = runFfprobe(filePath);
|
|
1267
1236
|
if (!result) {
|
|
1268
|
-
if (isVerbose())
|
|
1237
|
+
if (isVerbose()) spinner7.warn(`ffprobe failed: ${filePath}`);
|
|
1269
1238
|
failed++;
|
|
1270
1239
|
continue;
|
|
1271
1240
|
}
|
|
1272
1241
|
upsertMediaInfo(filePath, result.codec, result.resolution, result.width, result.height, result.duration);
|
|
1273
|
-
if (isVerbose())
|
|
1242
|
+
if (isVerbose()) spinner7.succeed(`${result.resolution ?? "?"} ${result.codec ?? "?"} ${filePath}`);
|
|
1274
1243
|
probed++;
|
|
1275
1244
|
}
|
|
1276
1245
|
}
|
|
1277
|
-
|
|
1278
|
-
if (skipped)
|
|
1279
|
-
if (failed)
|
|
1280
|
-
|
|
1246
|
+
spinner7.succeed(`probed ${probed} files`);
|
|
1247
|
+
if (skipped) spinner7.info(`skipped ${skipped} already indexed`);
|
|
1248
|
+
if (failed) spinner7.warn(`failed ${failed} files`);
|
|
1249
|
+
spinner7.stop();
|
|
1281
1250
|
};
|
|
1282
1251
|
var probe_default = probe;
|
|
1283
1252
|
|
|
@@ -1285,7 +1254,7 @@ var probe_default = probe;
|
|
|
1285
1254
|
var import_fs12 = require("fs");
|
|
1286
1255
|
var import_path12 = require("path");
|
|
1287
1256
|
var import_rimraf = require("rimraf");
|
|
1288
|
-
var
|
|
1257
|
+
var import_termkit11 = require("termkit");
|
|
1289
1258
|
|
|
1290
1259
|
// src/helpers/findSubtitle.ts
|
|
1291
1260
|
var LANGUAGE_ALIASES = {
|
|
@@ -1340,21 +1309,22 @@ var titleCase_default = (s) => {
|
|
|
1340
1309
|
};
|
|
1341
1310
|
|
|
1342
1311
|
// src/actions/rename.ts
|
|
1312
|
+
var spinner8 = new import_termkit11.Spinner();
|
|
1343
1313
|
var rename = async ({ dir: inputDir, type }) => {
|
|
1344
1314
|
const dir = (0, import_path12.resolve)(inputDir);
|
|
1345
1315
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
1346
1316
|
const config = getConfig();
|
|
1347
1317
|
const language = config.language ?? "eng";
|
|
1348
1318
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1349
|
-
|
|
1350
|
-
|
|
1319
|
+
spinner8.update(`renaming in ${import_termkit11.Color.white.encoder(dir)}`);
|
|
1320
|
+
spinner8.start();
|
|
1351
1321
|
if (!(0, import_fs12.existsSync)(dir)) throw new Error(`dir ${dir} does not exist`);
|
|
1352
1322
|
const list2 = (0, import_fs12.readdirSync)(dir);
|
|
1353
1323
|
let renamed = 0, removed = 0, skipped = 0;
|
|
1354
1324
|
for (const [index, entry] of list2.entries()) {
|
|
1355
|
-
|
|
1325
|
+
spinner8.update(`renaming in ${import_termkit11.Color.white.encoder(dir)} ${index + 1}/${list2.length}`);
|
|
1356
1326
|
if (!(0, import_fs12.lstatSync)((0, import_path12.resolve)(dir, entry)).isDirectory()) {
|
|
1357
|
-
if (isVerbose())
|
|
1327
|
+
if (isVerbose()) spinner8.info(`skipped ${entry}`);
|
|
1358
1328
|
skipped++;
|
|
1359
1329
|
continue;
|
|
1360
1330
|
}
|
|
@@ -1364,7 +1334,7 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
1364
1334
|
const nameMatch = entry.match(/(?<=\[).+?(?=\])/);
|
|
1365
1335
|
const id = entry.split("-")[0];
|
|
1366
1336
|
if (!nameMatch || !id) {
|
|
1367
|
-
if (isVerbose())
|
|
1337
|
+
if (isVerbose()) spinner8.info(`skipped ${entry}`);
|
|
1368
1338
|
skipped++;
|
|
1369
1339
|
continue;
|
|
1370
1340
|
}
|
|
@@ -1372,19 +1342,19 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
1372
1342
|
const ps3New = (0, import_path12.resolve)(dir, `${nameMatch[0]} [${id}]`);
|
|
1373
1343
|
(0, import_fs12.renameSync)(ps3Old, ps3New);
|
|
1374
1344
|
recordRename(sessionId, ps3Old, ps3New);
|
|
1375
|
-
|
|
1345
|
+
spinner8.succeed(`${nameMatch[0]} [${id}]`);
|
|
1376
1346
|
renamed++;
|
|
1377
1347
|
continue;
|
|
1378
1348
|
}
|
|
1379
1349
|
const yearMatch = entry.match(/\([^\d]*(\d+)[^\d]*\)/);
|
|
1380
1350
|
if (!yearMatch) {
|
|
1381
|
-
if (isVerbose())
|
|
1351
|
+
if (isVerbose()) spinner8.info(`skipped ${entry}`);
|
|
1382
1352
|
skipped++;
|
|
1383
1353
|
continue;
|
|
1384
1354
|
}
|
|
1385
1355
|
const year = yearMatch[0];
|
|
1386
1356
|
if (year.length !== 6) {
|
|
1387
|
-
if (isVerbose())
|
|
1357
|
+
if (isVerbose()) spinner8.info(`skipped ${entry}`);
|
|
1388
1358
|
skipped++;
|
|
1389
1359
|
continue;
|
|
1390
1360
|
}
|
|
@@ -1395,20 +1365,20 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
1395
1365
|
return videoExtensions_default.includes(ext2) && title.split(" ").reduce((a, w) => f.toLowerCase().includes(w.toLowerCase()) ? a : false, true);
|
|
1396
1366
|
});
|
|
1397
1367
|
if (!video) {
|
|
1398
|
-
if (isVerbose())
|
|
1368
|
+
if (isVerbose()) spinner8.info(`skipped ${entry}`);
|
|
1399
1369
|
skipped++;
|
|
1400
1370
|
continue;
|
|
1401
1371
|
}
|
|
1402
1372
|
const ext = video.match(/([^.]+$)/)?.[0];
|
|
1403
1373
|
if (!ext) {
|
|
1404
|
-
if (isVerbose())
|
|
1374
|
+
if (isVerbose()) spinner8.info(`skipped ${entry}`);
|
|
1405
1375
|
skipped++;
|
|
1406
1376
|
continue;
|
|
1407
1377
|
}
|
|
1408
1378
|
const yearNum = parseInt(year.replace(/\D/g, ""));
|
|
1409
1379
|
const formatted = formatMovieName(movieFormat, title, yearNum);
|
|
1410
1380
|
if (entry === formatted && video === `${formatted}.${ext}`) {
|
|
1411
|
-
if (isVerbose())
|
|
1381
|
+
if (isVerbose()) spinner8.info(`skipped ${entry}`);
|
|
1412
1382
|
skipped++;
|
|
1413
1383
|
continue;
|
|
1414
1384
|
}
|
|
@@ -1431,25 +1401,26 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
1431
1401
|
(0, import_fs12.renameSync)(folderOld, folderNew);
|
|
1432
1402
|
recordRename(sessionId, fileOld, fileNew);
|
|
1433
1403
|
recordRename(sessionId, folderOld, folderNew);
|
|
1434
|
-
|
|
1404
|
+
spinner8.succeed(formatted);
|
|
1435
1405
|
renamed++;
|
|
1436
1406
|
}
|
|
1437
|
-
|
|
1438
|
-
if (removed)
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1407
|
+
spinner8.succeed(`renamed ${renamed} files`);
|
|
1408
|
+
if (removed) spinner8.info(`removed ${removed} files`);
|
|
1409
|
+
spinner8.info(`skipped ${skipped} files`);
|
|
1410
|
+
spinner8.succeed(`done in ${import_termkit11.Color.green.encoder(dir)}`);
|
|
1411
|
+
spinner8.stop();
|
|
1442
1412
|
};
|
|
1443
1413
|
var rename_default = rename;
|
|
1444
1414
|
|
|
1445
1415
|
// src/actions/reset.ts
|
|
1446
1416
|
var import_fs13 = require("fs");
|
|
1447
1417
|
var import_path13 = require("path");
|
|
1448
|
-
var
|
|
1418
|
+
var import_termkit12 = require("termkit");
|
|
1419
|
+
var spinner9 = new import_termkit12.Spinner();
|
|
1449
1420
|
var reset = async ({ dir: inputDir, double }) => {
|
|
1450
1421
|
let dir = inputDir;
|
|
1451
|
-
|
|
1452
|
-
|
|
1422
|
+
spinner9.update(`resetting episodes in ${import_termkit12.Color.white.encoder(dir)}`);
|
|
1423
|
+
spinner9.start();
|
|
1453
1424
|
dir = (0, import_path13.resolve)(dir);
|
|
1454
1425
|
if (!(0, import_fs13.existsSync)(dir)) throw new Error(`dir ${dir} does not exist`);
|
|
1455
1426
|
const list2 = (0, import_fs13.readdirSync)(dir).sort();
|
|
@@ -1465,7 +1436,7 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1465
1436
|
if (!seasonNum) throw new Error(`unable to identify season number`);
|
|
1466
1437
|
const parentFolder = (0, import_path13.basename)((0, import_path13.dirname)(dir));
|
|
1467
1438
|
const showTitle = parentFolder.match(/^(.+?)\s*(?:\(\d{4}\))?$/)?.[1]?.trim() || void 0;
|
|
1468
|
-
|
|
1439
|
+
spinner9.info(`identified as season ${seasonNum}${showTitle ? ` of ${showTitle}` : ""}`);
|
|
1469
1440
|
const sublist = list2.filter((f) => {
|
|
1470
1441
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1471
1442
|
return videoExtensions_default.includes(ext) && f.split(" ").reduce((a, w) => f.toLowerCase().includes(w.toLowerCase()) ? a : false, true);
|
|
@@ -1477,7 +1448,7 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1477
1448
|
const episodeFormat = getConfig().format?.episode;
|
|
1478
1449
|
let renamed = 0, skipped = other.length;
|
|
1479
1450
|
for (const [index, i] of sublist.entries()) {
|
|
1480
|
-
|
|
1451
|
+
spinner9.update(`resetting episodes in ${import_termkit12.Color.white.encoder(dir)} ${index}/${list2.length}`);
|
|
1481
1452
|
const ext = i.match(/([^.]+$)/)?.[0];
|
|
1482
1453
|
const episode = double ? index * 2 + 1 : index + 1;
|
|
1483
1454
|
const name = `${formatEpisode(seasonNum, episode, episodeFormat, double, showTitle)}.${ext}`;
|
|
@@ -1488,17 +1459,17 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1488
1459
|
(0, import_fs13.renameSync)((0, import_path13.resolve)(dir, i), (0, import_path13.resolve)(dir, name));
|
|
1489
1460
|
renamed++;
|
|
1490
1461
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1462
|
+
spinner9.succeed(`renamed ${renamed} files`);
|
|
1463
|
+
spinner9.info(`skipped ${skipped} files`);
|
|
1464
|
+
spinner9.succeed(`done in ${import_termkit12.Color.green.encoder(dir)}`);
|
|
1465
|
+
spinner9.stop();
|
|
1495
1466
|
};
|
|
1496
1467
|
var reset_default = reset;
|
|
1497
1468
|
|
|
1498
1469
|
// src/actions/scan.ts
|
|
1499
1470
|
var import_fs15 = require("fs");
|
|
1500
1471
|
var import_path15 = require("path");
|
|
1501
|
-
var
|
|
1472
|
+
var import_termkit13 = require("termkit");
|
|
1502
1473
|
|
|
1503
1474
|
// src/helpers/detectEdition.ts
|
|
1504
1475
|
var EDITIONS = [
|
|
@@ -1601,6 +1572,7 @@ var parseDownloadName = (rawName) => {
|
|
|
1601
1572
|
var bookExtensions_default = ["epub", "mobi", "azw3", "azw"];
|
|
1602
1573
|
|
|
1603
1574
|
// src/actions/scan.ts
|
|
1575
|
+
var spinner10 = new import_termkit13.Spinner();
|
|
1604
1576
|
var sameDev = (a, b) => {
|
|
1605
1577
|
try {
|
|
1606
1578
|
let bExisting = b;
|
|
@@ -1634,6 +1606,24 @@ var containsBook = (dir, depth = 2) => (0, import_fs15.readdirSync)(dir).some((f
|
|
|
1634
1606
|
}
|
|
1635
1607
|
return false;
|
|
1636
1608
|
});
|
|
1609
|
+
var containsPdf = (dir) => {
|
|
1610
|
+
try {
|
|
1611
|
+
return (0, import_fs15.readdirSync)(dir).some((f) => /\.pdf$/i.test(f));
|
|
1612
|
+
} catch {
|
|
1613
|
+
return false;
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
var countVideos = (dir) => {
|
|
1617
|
+
try {
|
|
1618
|
+
return (0, import_fs15.readdirSync)(dir).filter((f) => {
|
|
1619
|
+
if (/\bsample\b/i.test(f)) return false;
|
|
1620
|
+
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1621
|
+
return !!(ext && videoExtensions_default.includes(ext));
|
|
1622
|
+
}).length;
|
|
1623
|
+
} catch {
|
|
1624
|
+
return 0;
|
|
1625
|
+
}
|
|
1626
|
+
};
|
|
1637
1627
|
var isTvEpisodeName = (name) => /S\d{2,3}E\d{2,3}/i.test(name) || /\d+x\d{2,3}/i.test(name);
|
|
1638
1628
|
var isSeasonDirName = (name) => !isTvEpisodeName(name) && /(?:^|[.\s_-])(?:season|s)\s*0*\d+(?:[.\s_-]|$)/i.test(name);
|
|
1639
1629
|
var gatherEntries = (source) => {
|
|
@@ -1784,13 +1774,17 @@ var classifyMovieConfidence = (entry) => {
|
|
|
1784
1774
|
return "ambiguous";
|
|
1785
1775
|
};
|
|
1786
1776
|
var typeColor = {
|
|
1787
|
-
movie: (s) =>
|
|
1788
|
-
tv: (s) =>
|
|
1789
|
-
book: (s) =>
|
|
1790
|
-
ps3: (s) =>
|
|
1791
|
-
};
|
|
1792
|
-
var typeGlyph = (t) => typeColor[t]("
|
|
1793
|
-
var
|
|
1777
|
+
movie: (s) => import_termkit13.Color.cyan.encoder(s),
|
|
1778
|
+
tv: (s) => import_termkit13.Color.green.encoder(s),
|
|
1779
|
+
book: (s) => import_termkit13.Color.yellow.encoder(s),
|
|
1780
|
+
ps3: (s) => import_termkit13.Color.magenta.encoder(s)
|
|
1781
|
+
};
|
|
1782
|
+
var typeGlyph = (t) => typeColor[t]("?");
|
|
1783
|
+
var checkGlyph = (t) => typeColor[t]("\u2714");
|
|
1784
|
+
var greyGlyph = import_termkit13.Color.white.faint.encoder("\u25CF");
|
|
1785
|
+
var warnGlyph = import_termkit13.Color.yellow.encoder("\u26A0");
|
|
1786
|
+
var typeTag = (t) => isVerbose() ? import_termkit13.Color.white.faint.encoder(` (${t})`) : "";
|
|
1787
|
+
var sortByEntry = (arr) => arr.sort((a, b) => a.entry.localeCompare(b.entry, void 0, { sensitivity: "base" }));
|
|
1794
1788
|
var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactive }) => {
|
|
1795
1789
|
const config = getConfig();
|
|
1796
1790
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1798,27 +1792,28 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1798
1792
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1799
1793
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
1800
1794
|
const specialsFolder = config.specialsFolder ?? "Specials";
|
|
1795
|
+
const dryTag = dryRun ? import_termkit13.Color.white.faint.encoder(" [dry]") : "";
|
|
1801
1796
|
const lookupMovie = async (parsed) => {
|
|
1802
1797
|
let tmdbId;
|
|
1803
1798
|
let resolvedTitle = parsed.title;
|
|
1804
1799
|
let resolvedYear = parsed.year;
|
|
1805
1800
|
if (config.tmdbApiKey) {
|
|
1806
|
-
|
|
1801
|
+
spinner10.update(`TMDb: ${parsed.title}`);
|
|
1807
1802
|
const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
|
|
1808
1803
|
if (results.length === 1) {
|
|
1809
1804
|
tmdbId = results[0].id;
|
|
1810
1805
|
resolvedTitle = results[0].title;
|
|
1811
1806
|
resolvedYear = results[0].year ?? parsed.year;
|
|
1812
1807
|
} else if (results.length > 1) {
|
|
1813
|
-
|
|
1814
|
-
const select = new
|
|
1808
|
+
spinner10.stop();
|
|
1809
|
+
const select = new import_termkit13.Select();
|
|
1815
1810
|
const items = results.map((r) => ({
|
|
1816
1811
|
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
1817
1812
|
description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
|
|
1818
1813
|
...r
|
|
1819
1814
|
}));
|
|
1820
1815
|
const picked = await select.ask(`Multiple movies found for "${parsed.title}":`, items);
|
|
1821
|
-
|
|
1816
|
+
spinner10.start();
|
|
1822
1817
|
if (picked) {
|
|
1823
1818
|
tmdbId = picked.id;
|
|
1824
1819
|
resolvedTitle = picked.title;
|
|
@@ -1833,12 +1828,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1833
1828
|
const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
|
|
1834
1829
|
const destFolder = (0, import_path15.resolve)(destRoot, folderName);
|
|
1835
1830
|
if ((0, import_fs15.existsSync)(destFolder)) {
|
|
1836
|
-
|
|
1831
|
+
spinner10.log(`${typeColor.movie(folderName)}${typeTag("movie")}`, greyGlyph);
|
|
1837
1832
|
return false;
|
|
1838
1833
|
}
|
|
1839
1834
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
1840
1835
|
if (!videoFile) {
|
|
1841
|
-
|
|
1836
|
+
spinner10.log(`${entry}${typeTag("movie")}`, warnGlyph);
|
|
1842
1837
|
return false;
|
|
1843
1838
|
}
|
|
1844
1839
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
@@ -1859,7 +1854,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1859
1854
|
(0, import_fs15.linkSync)(videoSourcePath, destVideoPath);
|
|
1860
1855
|
mode = "hardlink";
|
|
1861
1856
|
} catch {
|
|
1862
|
-
|
|
1857
|
+
spinner10.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
1863
1858
|
(0, import_fs15.cpSync)(videoSourcePath, destVideoPath);
|
|
1864
1859
|
mode = "copy";
|
|
1865
1860
|
}
|
|
@@ -1885,27 +1880,33 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1885
1880
|
recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
|
|
1886
1881
|
}
|
|
1887
1882
|
}
|
|
1888
|
-
|
|
1883
|
+
spinner10.log(`${typeColor.movie(folderName)}${typeTag("movie")}${dryTag}`, checkGlyph("movie"));
|
|
1889
1884
|
return true;
|
|
1890
1885
|
};
|
|
1891
|
-
|
|
1886
|
+
spinner10.start();
|
|
1892
1887
|
if (config.sources.length === 0) throw new Error("no sources configured \u2014 run: reelsort config add source <dir>");
|
|
1893
1888
|
let imported = 0, skipped = 0;
|
|
1894
1889
|
const pendingMovies = [];
|
|
1895
1890
|
const pendingTv = [];
|
|
1891
|
+
const pendingBooks = [];
|
|
1892
|
+
const pendingAnime = [];
|
|
1896
1893
|
const ignoreSet = new Set(config.ignore ?? []);
|
|
1897
1894
|
const seenIgnored = /* @__PURE__ */ new Set();
|
|
1898
1895
|
for (const source of config.sources) {
|
|
1899
1896
|
if (!(0, import_fs15.existsSync)(source)) {
|
|
1900
|
-
|
|
1897
|
+
spinner10.warn(`source not found: ${import_termkit13.Color.white.encoder(source)}`);
|
|
1901
1898
|
continue;
|
|
1902
1899
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1900
|
+
spinner10.update(`scanning ${import_termkit13.Color.white.encoder(source)}`);
|
|
1901
|
+
const entries = gatherEntries(source).sort(
|
|
1902
|
+
(a, b) => a.entry.localeCompare(b.entry, void 0, { sensitivity: "base" })
|
|
1903
|
+
);
|
|
1904
|
+
for (const { entry, entryPath, isDir } of entries) {
|
|
1905
|
+
spinner10.update(`scanning: ${entry}`);
|
|
1906
1906
|
if (ignoreSet.has(entry)) {
|
|
1907
1907
|
seenIgnored.add(entry);
|
|
1908
|
-
if (isVerbose())
|
|
1908
|
+
if (isVerbose()) spinner10.log(entry, greyGlyph);
|
|
1909
|
+
skipped++;
|
|
1909
1910
|
continue;
|
|
1910
1911
|
}
|
|
1911
1912
|
const ext = entry.match(/([^.]+$)/)?.[0];
|
|
@@ -1925,7 +1926,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1925
1926
|
}
|
|
1926
1927
|
const destRoot = config.dest[detectedType];
|
|
1927
1928
|
if (!destRoot) {
|
|
1928
|
-
if (isVerbose())
|
|
1929
|
+
if (isVerbose()) spinner10.log(`${entry}${typeTag(detectedType)}`, greyGlyph);
|
|
1929
1930
|
skipped++;
|
|
1930
1931
|
continue;
|
|
1931
1932
|
}
|
|
@@ -1939,7 +1940,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1939
1940
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
1940
1941
|
const destPath = (0, import_path15.resolve)(destRoot, destName);
|
|
1941
1942
|
if ((0, import_fs15.existsSync)(destPath)) {
|
|
1942
|
-
|
|
1943
|
+
spinner10.log(`${typeColor.ps3(destName)}${typeTag("ps3")}`, greyGlyph);
|
|
1943
1944
|
skipped++;
|
|
1944
1945
|
continue;
|
|
1945
1946
|
}
|
|
@@ -1947,14 +1948,14 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1947
1948
|
moveFolder(entryPath, destPath);
|
|
1948
1949
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
1949
1950
|
}
|
|
1950
|
-
|
|
1951
|
+
spinner10.log(`${typeColor.ps3(destName)}${typeTag("ps3")}${dryTag}`, checkGlyph("ps3"));
|
|
1951
1952
|
imported++;
|
|
1952
1953
|
continue;
|
|
1953
1954
|
}
|
|
1954
1955
|
if (detectedType === "book") {
|
|
1955
1956
|
const destPath = (0, import_path15.resolve)(destRoot, entry);
|
|
1956
1957
|
if ((0, import_fs15.existsSync)(destPath)) {
|
|
1957
|
-
|
|
1958
|
+
spinner10.log(`${typeColor.book(entry)}${typeTag("book")}`, greyGlyph);
|
|
1958
1959
|
skipped++;
|
|
1959
1960
|
continue;
|
|
1960
1961
|
}
|
|
@@ -1972,20 +1973,36 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1972
1973
|
}
|
|
1973
1974
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
1974
1975
|
}
|
|
1975
|
-
|
|
1976
|
+
spinner10.log(`${typeColor.book(entry)}${typeTag("book")}${dryTag}`, checkGlyph("book"));
|
|
1976
1977
|
imported++;
|
|
1977
1978
|
continue;
|
|
1978
1979
|
}
|
|
1980
|
+
if (detectedType === "movie" && isDir) {
|
|
1981
|
+
const videoCount = countVideos(entryPath);
|
|
1982
|
+
if (videoCount === 0) {
|
|
1983
|
+
if (containsPdf(entryPath)) {
|
|
1984
|
+
pendingBooks.push({ entry, entryPath });
|
|
1985
|
+
} else {
|
|
1986
|
+
if (isVerbose()) spinner10.log(entry, greyGlyph);
|
|
1987
|
+
skipped++;
|
|
1988
|
+
}
|
|
1989
|
+
continue;
|
|
1990
|
+
}
|
|
1991
|
+
if (videoCount >= 2) {
|
|
1992
|
+
pendingAnime.push({ entry, entryPath, videoCount });
|
|
1993
|
+
continue;
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1979
1996
|
const parsed = parseDownloadName(entry);
|
|
1980
1997
|
if (!parsed) {
|
|
1981
|
-
if (isVerbose())
|
|
1998
|
+
if (isVerbose()) spinner10.log(entry, greyGlyph);
|
|
1982
1999
|
skipped++;
|
|
1983
2000
|
continue;
|
|
1984
2001
|
}
|
|
1985
2002
|
if (detectedType === "movie") {
|
|
1986
2003
|
const confidence = classifyMovieConfidence(entry);
|
|
1987
2004
|
if (confidence === "skip") {
|
|
1988
|
-
if (isVerbose())
|
|
2005
|
+
if (isVerbose()) spinner10.log(entry, greyGlyph);
|
|
1989
2006
|
skipped++;
|
|
1990
2007
|
continue;
|
|
1991
2008
|
}
|
|
@@ -1999,22 +2016,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1999
2016
|
let resolvedYear = parsed.year;
|
|
2000
2017
|
if (config.tmdbApiKey) {
|
|
2001
2018
|
if (detectedType === "tv") {
|
|
2002
|
-
|
|
2019
|
+
spinner10.update(`TMDb: ${parsed.title}`);
|
|
2003
2020
|
const results = await searchTv(parsed.title, config.tmdbApiKey);
|
|
2004
2021
|
if (results.length === 1) {
|
|
2005
2022
|
tmdbId = results[0].id;
|
|
2006
2023
|
resolvedTitle = results[0].title;
|
|
2007
2024
|
resolvedYear = results[0].year ?? parsed.year;
|
|
2008
2025
|
} else if (results.length > 1) {
|
|
2009
|
-
|
|
2010
|
-
const select = new
|
|
2026
|
+
spinner10.stop();
|
|
2027
|
+
const select = new import_termkit13.Select();
|
|
2011
2028
|
const items = results.map((r) => ({
|
|
2012
2029
|
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
2013
2030
|
description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
|
|
2014
2031
|
...r
|
|
2015
2032
|
}));
|
|
2016
2033
|
const picked = await select.ask(`Multiple shows found for "${parsed.title}":`, items);
|
|
2017
|
-
|
|
2034
|
+
spinner10.start();
|
|
2018
2035
|
if (picked) {
|
|
2019
2036
|
tmdbId = picked.id;
|
|
2020
2037
|
resolvedTitle = picked.title;
|
|
@@ -2030,7 +2047,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2030
2047
|
}
|
|
2031
2048
|
if (detectedType === "tv") {
|
|
2032
2049
|
if (parsed.season === void 0) {
|
|
2033
|
-
if (isVerbose())
|
|
2050
|
+
if (isVerbose()) spinner10.log(entry, greyGlyph);
|
|
2034
2051
|
skipped++;
|
|
2035
2052
|
continue;
|
|
2036
2053
|
}
|
|
@@ -2058,12 +2075,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2058
2075
|
const seasonPath = (0, import_path15.resolve)(showPath, seasonFolderName);
|
|
2059
2076
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
2060
2077
|
if (!videoFile) {
|
|
2061
|
-
|
|
2078
|
+
spinner10.log(`${entry}${typeTag("tv")}`, warnGlyph);
|
|
2062
2079
|
skipped++;
|
|
2063
2080
|
continue;
|
|
2064
2081
|
}
|
|
2065
2082
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
2066
|
-
if (tmdbId && config.tmdbApiKey)
|
|
2083
|
+
if (tmdbId && config.tmdbApiKey) spinner10.update(`TMDb: episode name for ${resolvedTitle}`);
|
|
2067
2084
|
const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
2068
2085
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
|
|
2069
2086
|
const destVideoName = `${episodeName}.${videoExt}`;
|
|
@@ -2073,17 +2090,17 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2073
2090
|
const isRepack = /\brepack\d*\b|\bproper\b/i.test(entry);
|
|
2074
2091
|
let shouldReplace = force || isRepack;
|
|
2075
2092
|
if (!shouldReplace && interactive) {
|
|
2076
|
-
|
|
2077
|
-
const select = new
|
|
2093
|
+
spinner10.stop();
|
|
2094
|
+
const select = new import_termkit13.Select();
|
|
2078
2095
|
const picked = await select.ask(`Already exists \u2014 replace?`, [
|
|
2079
2096
|
{ label: `${showFolderName} / ${seasonFolderName} / ${episodeName}`, value: "replace" },
|
|
2080
2097
|
{ label: "Skip", value: "skip" }
|
|
2081
2098
|
]);
|
|
2082
|
-
|
|
2099
|
+
spinner10.start();
|
|
2083
2100
|
shouldReplace = picked?.value === "replace";
|
|
2084
2101
|
}
|
|
2085
2102
|
if (!shouldReplace) {
|
|
2086
|
-
|
|
2103
|
+
spinner10.log(`${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}`, greyGlyph);
|
|
2087
2104
|
skipped++;
|
|
2088
2105
|
continue;
|
|
2089
2106
|
}
|
|
@@ -2107,7 +2124,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2107
2124
|
(0, import_fs15.linkSync)(videoSourcePath, destVideoPath);
|
|
2108
2125
|
mode = "hardlink";
|
|
2109
2126
|
} catch {
|
|
2110
|
-
|
|
2127
|
+
spinner10.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
2111
2128
|
(0, import_fs15.cpSync)(videoSourcePath, destVideoPath);
|
|
2112
2129
|
mode = "copy";
|
|
2113
2130
|
}
|
|
@@ -2124,7 +2141,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2124
2141
|
}
|
|
2125
2142
|
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
|
|
2126
2143
|
}
|
|
2127
|
-
|
|
2144
|
+
spinner10.log(`${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}${dryTag}`, checkGlyph("tv"));
|
|
2128
2145
|
imported++;
|
|
2129
2146
|
continue;
|
|
2130
2147
|
}
|
|
@@ -2135,25 +2152,27 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2135
2152
|
}
|
|
2136
2153
|
}
|
|
2137
2154
|
}
|
|
2155
|
+
let uncertainMovies = 0;
|
|
2138
2156
|
if (pendingMovies.length > 0) {
|
|
2139
|
-
spinner_default.warn(`${pendingMovies.length} uncertain movie match${pendingMovies.length > 1 ? "es" : ""} skipped \u2014 use -i to review or -f to import all`);
|
|
2140
|
-
for (const p of pendingMovies) spinner_default.info(` ${typeGlyph("movie")} ${p.entry.replace(/\/$/, "")}${typeTag("movie")}`);
|
|
2141
2157
|
let toProcess = [];
|
|
2142
2158
|
if (interactive) {
|
|
2143
|
-
|
|
2144
|
-
const ms = new
|
|
2159
|
+
spinner10.stop();
|
|
2160
|
+
const ms = new import_termkit13.MultiSelect({ allowSkip: true, search: true, maxHeight: 20 });
|
|
2145
2161
|
const items = pendingMovies.map((p) => ({
|
|
2146
2162
|
label: p.entry.replace(/\/$/, ""),
|
|
2147
2163
|
description: p.parsed.year ? `parsed: ${p.parsed.title} \xB7 ${p.parsed.year}` : `parsed: ${p.parsed.title}`,
|
|
2148
2164
|
...p
|
|
2149
2165
|
}));
|
|
2150
2166
|
toProcess = await ms.ask("Select entries to import as movies:", items) ?? [];
|
|
2151
|
-
|
|
2152
|
-
skipped += pendingMovies.length - toProcess.length;
|
|
2167
|
+
spinner10.start();
|
|
2153
2168
|
} else if (force) {
|
|
2154
2169
|
toProcess = pendingMovies;
|
|
2155
|
-
}
|
|
2156
|
-
|
|
2170
|
+
}
|
|
2171
|
+
const toSkip = pendingMovies.filter((p) => !toProcess.includes(p));
|
|
2172
|
+
uncertainMovies = toSkip.length;
|
|
2173
|
+
sortByEntry(toSkip);
|
|
2174
|
+
for (const p of toSkip) {
|
|
2175
|
+
spinner10.log(`${typeColor.movie(p.entry.replace(/\/$/, ""))}${typeTag("movie")}`, typeGlyph("movie"));
|
|
2157
2176
|
}
|
|
2158
2177
|
for (const p of toProcess) {
|
|
2159
2178
|
const { tmdbId, resolvedTitle, resolvedYear } = await lookupMovie(p.parsed);
|
|
@@ -2165,9 +2184,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2165
2184
|
}
|
|
2166
2185
|
}
|
|
2167
2186
|
if (pendingTv.length > 0) {
|
|
2168
|
-
|
|
2169
|
-
for (const p of pendingTv)
|
|
2170
|
-
|
|
2187
|
+
pendingTv.sort((a, b) => a.resolvedTitle.localeCompare(b.resolvedTitle, void 0, { sensitivity: "base" }));
|
|
2188
|
+
for (const p of pendingTv) {
|
|
2189
|
+
spinner10.log(`${typeColor.tv(p.resolvedTitle)} \u2014 ${p.entry.replace(/\/$/, "")}${typeTag("tv")}`, typeGlyph("tv"));
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
if (pendingBooks.length > 0) {
|
|
2193
|
+
sortByEntry(pendingBooks);
|
|
2194
|
+
for (const p of pendingBooks) {
|
|
2195
|
+
spinner10.log(`${typeColor.book(p.entry)}${typeTag("book")}`, typeGlyph("book"));
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
if (pendingAnime.length > 0) {
|
|
2199
|
+
sortByEntry(pendingAnime);
|
|
2200
|
+
for (const p of pendingAnime) {
|
|
2201
|
+
spinner10.log(`${typeColor.tv(p.entry)} (${p.videoCount} video${p.videoCount > 1 ? "s" : ""})${typeTag("tv")}`, typeGlyph("tv"));
|
|
2202
|
+
}
|
|
2171
2203
|
}
|
|
2172
2204
|
if (ignoreSet.size > 0) {
|
|
2173
2205
|
const stale = [...ignoreSet].filter((name) => !seenIgnored.has(name));
|
|
@@ -2175,18 +2207,23 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2175
2207
|
const updated = config.ignore.filter((name) => !stale.includes(name));
|
|
2176
2208
|
config.ignore = updated;
|
|
2177
2209
|
saveConfig(config);
|
|
2178
|
-
for (const name of stale)
|
|
2210
|
+
for (const name of stale) spinner10.info(`removed from ignore list (not found): ${import_termkit13.Color.white.encoder(name)}`);
|
|
2179
2211
|
}
|
|
2180
2212
|
}
|
|
2181
|
-
|
|
2182
|
-
if (skipped)
|
|
2183
|
-
|
|
2213
|
+
const summaryParts = [`${dryRun ? "would import" : "imported"} ${imported}`];
|
|
2214
|
+
if (skipped) summaryParts.push(`skipped ${skipped}`);
|
|
2215
|
+
if (uncertainMovies) summaryParts.push(`uncertain movie ${uncertainMovies}`);
|
|
2216
|
+
if (pendingTv.length) summaryParts.push(`uncertain tv ${pendingTv.length}`);
|
|
2217
|
+
if (pendingBooks.length) summaryParts.push(`uncertain book ${pendingBooks.length}`);
|
|
2218
|
+
if (pendingAnime.length) summaryParts.push(`uncertain anime ${pendingAnime.length}`);
|
|
2219
|
+
spinner10.succeed(summaryParts.join(", "));
|
|
2220
|
+
spinner10.stop();
|
|
2184
2221
|
};
|
|
2185
2222
|
var scan_default = scan;
|
|
2186
2223
|
|
|
2187
2224
|
// src/actions/shows.ts
|
|
2188
2225
|
var import_fs16 = require("fs");
|
|
2189
|
-
var
|
|
2226
|
+
var import_termkit14 = require("termkit");
|
|
2190
2227
|
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
2191
2228
|
var shows = async () => {
|
|
2192
2229
|
const config = getConfig();
|
|
@@ -2198,8 +2235,8 @@ var shows = async () => {
|
|
|
2198
2235
|
return;
|
|
2199
2236
|
}
|
|
2200
2237
|
console.log(`
|
|
2201
|
-
${
|
|
2202
|
-
new
|
|
2238
|
+
${import_termkit14.Color.yellow.encoder("SHOWS")}${destRoot ? ` ${import_termkit14.Color.white.encoder(destRoot)}` : ""} (${allShows.length} registered)`);
|
|
2239
|
+
new import_termkit14.Table(
|
|
2203
2240
|
allShows.map((show) => ({
|
|
2204
2241
|
name: show.path.split("/").pop() ?? show.path,
|
|
2205
2242
|
size: (0, import_fs16.existsSync)(show.path) ? formatSize(dirSize(show.path)) : "\u2014",
|
|
@@ -2218,9 +2255,9 @@ ${import_termkit15.Color.yellow.encoder("SHOWS")}${destRoot ? ` ${import_termki
|
|
|
2218
2255
|
if (v) {
|
|
2219
2256
|
const url = `https://www.themoviedb.org/tv/${v}`;
|
|
2220
2257
|
const text = process.stdout.isTTY ? hyperlink(url, "\u2713 tmdb") : "\u2713 tmdb";
|
|
2221
|
-
return
|
|
2258
|
+
return import_termkit14.Color.green.encoder(text);
|
|
2222
2259
|
}
|
|
2223
|
-
return
|
|
2260
|
+
return import_termkit14.Color.red.encoder("\u2717");
|
|
2224
2261
|
}
|
|
2225
2262
|
},
|
|
2226
2263
|
{ key: "ended", title: "Status", value: (v) => v ? dim("ended") : "active" }
|
|
@@ -2237,13 +2274,13 @@ var shows_default = shows;
|
|
|
2237
2274
|
// src/actions/stats.ts
|
|
2238
2275
|
var import_fs17 = require("fs");
|
|
2239
2276
|
var import_path16 = require("path");
|
|
2240
|
-
var
|
|
2241
|
-
var
|
|
2277
|
+
var import_termkit15 = require("termkit");
|
|
2278
|
+
var countVideos2 = (dir) => {
|
|
2242
2279
|
let count = 0;
|
|
2243
2280
|
try {
|
|
2244
2281
|
for (const entry of (0, import_fs17.readdirSync)(dir, { withFileTypes: true })) {
|
|
2245
2282
|
if (entry.isDirectory()) {
|
|
2246
|
-
count +=
|
|
2283
|
+
count += countVideos2((0, import_path16.resolve)(dir, entry.name));
|
|
2247
2284
|
} else {
|
|
2248
2285
|
const ext = entry.name.match(/([^.]+$)/)?.[0]?.toLowerCase();
|
|
2249
2286
|
if (ext && videoExtensions_default.includes(ext)) count++;
|
|
@@ -2277,7 +2314,7 @@ var stats = async () => {
|
|
|
2277
2314
|
const tvDest = config.dest.tv;
|
|
2278
2315
|
if (tvDest && (0, import_fs17.existsSync)(tvDest)) {
|
|
2279
2316
|
rows.push({ category: "Shows", count: shows2.length, size: formatSize(dirSize(tvDest)) });
|
|
2280
|
-
rows.push({ category: "Episodes", count:
|
|
2317
|
+
rows.push({ category: "Episodes", count: countVideos2(tvDest) });
|
|
2281
2318
|
}
|
|
2282
2319
|
const ps3Dest = config.dest.ps3;
|
|
2283
2320
|
if (ps3Dest && (0, import_fs17.existsSync)(ps3Dest)) {
|
|
@@ -2285,7 +2322,7 @@ var stats = async () => {
|
|
|
2285
2322
|
}
|
|
2286
2323
|
if (rows.length === 0) return;
|
|
2287
2324
|
console.log();
|
|
2288
|
-
new
|
|
2325
|
+
new import_termkit15.Table(rows, {
|
|
2289
2326
|
title: "LIBRARY STATISTICS",
|
|
2290
2327
|
separator: " ",
|
|
2291
2328
|
columns: [
|
|
@@ -2300,14 +2337,15 @@ var stats_default = stats;
|
|
|
2300
2337
|
|
|
2301
2338
|
// src/actions/undo.ts
|
|
2302
2339
|
var import_fs18 = require("fs");
|
|
2303
|
-
var
|
|
2340
|
+
var import_termkit16 = require("termkit");
|
|
2341
|
+
var spinner11 = new import_termkit16.Spinner();
|
|
2304
2342
|
var undo = async () => {
|
|
2305
|
-
|
|
2343
|
+
spinner11.start();
|
|
2306
2344
|
const renameRecords = getLastSession();
|
|
2307
2345
|
const importRecords = getLastImportSession();
|
|
2308
2346
|
if (renameRecords.length === 0 && importRecords.length === 0) {
|
|
2309
|
-
|
|
2310
|
-
|
|
2347
|
+
spinner11.info("nothing to undo");
|
|
2348
|
+
spinner11.stop();
|
|
2311
2349
|
return;
|
|
2312
2350
|
}
|
|
2313
2351
|
const useImports = importRecords.length > 0 && (renameRecords.length === 0 || importRecords[0].sessionId > renameRecords[0].sessionId);
|
|
@@ -2315,40 +2353,40 @@ var undo = async () => {
|
|
|
2315
2353
|
let undone2 = 0;
|
|
2316
2354
|
for (const record of renameRecords) {
|
|
2317
2355
|
(0, import_fs18.renameSync)(record.newPath, record.oldPath);
|
|
2318
|
-
|
|
2356
|
+
spinner11.succeed(`${import_termkit16.Color.green.encoder(record.newPath)} \u2192 ${import_termkit16.Color.white.encoder(record.oldPath)}`);
|
|
2319
2357
|
undone2++;
|
|
2320
2358
|
}
|
|
2321
2359
|
deleteSession(renameRecords[0].sessionId);
|
|
2322
|
-
|
|
2323
|
-
|
|
2360
|
+
spinner11.succeed(`undid ${undone2} renames`);
|
|
2361
|
+
spinner11.stop();
|
|
2324
2362
|
return;
|
|
2325
2363
|
}
|
|
2326
2364
|
let undone = 0;
|
|
2327
2365
|
let skipped = 0;
|
|
2328
2366
|
for (const record of importRecords) {
|
|
2329
2367
|
if (record.mode !== "move") {
|
|
2330
|
-
|
|
2368
|
+
spinner11.info(`skipped ${record.destinationPath} (${record.mode} \u2014 source file unchanged)`);
|
|
2331
2369
|
skipped++;
|
|
2332
2370
|
continue;
|
|
2333
2371
|
}
|
|
2334
2372
|
if (record.type === "tv") {
|
|
2335
|
-
|
|
2373
|
+
spinner11.info(`skipped TV import \u2014 season folder cannot be cleanly reversed: ${record.destinationPath}`);
|
|
2336
2374
|
skipped++;
|
|
2337
2375
|
continue;
|
|
2338
2376
|
}
|
|
2339
2377
|
if (!(0, import_fs18.existsSync)(record.destinationPath)) {
|
|
2340
|
-
|
|
2378
|
+
spinner11.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
|
|
2341
2379
|
skipped++;
|
|
2342
2380
|
continue;
|
|
2343
2381
|
}
|
|
2344
2382
|
(0, import_fs18.renameSync)(record.destinationPath, record.sourcePath);
|
|
2345
|
-
|
|
2383
|
+
spinner11.succeed(`${import_termkit16.Color.green.encoder(record.destinationPath)} \u2192 ${import_termkit16.Color.white.encoder(record.sourcePath)}`);
|
|
2346
2384
|
undone++;
|
|
2347
2385
|
}
|
|
2348
2386
|
deleteImportSession(importRecords[0].sessionId);
|
|
2349
|
-
if (undone > 0)
|
|
2350
|
-
if (skipped > 0)
|
|
2351
|
-
|
|
2387
|
+
if (undone > 0) spinner11.succeed(`undid ${undone} import${undone !== 1 ? "s" : ""}`);
|
|
2388
|
+
if (skipped > 0) spinner11.info(`skipped ${skipped} item${skipped !== 1 ? "s" : ""} (TV or non-move mode)`);
|
|
2389
|
+
spinner11.stop();
|
|
2352
2390
|
};
|
|
2353
2391
|
var undo_default = undo;
|
|
2354
2392
|
|
|
@@ -2356,7 +2394,8 @@ var undo_default = undo;
|
|
|
2356
2394
|
var import_chokidar = __toESM(require("chokidar"));
|
|
2357
2395
|
var import_fs19 = require("fs");
|
|
2358
2396
|
var import_path17 = require("path");
|
|
2359
|
-
var
|
|
2397
|
+
var import_termkit17 = require("termkit");
|
|
2398
|
+
var spinner12 = new import_termkit17.Spinner();
|
|
2360
2399
|
var sameDev2 = (a, b) => {
|
|
2361
2400
|
try {
|
|
2362
2401
|
let bExisting = b;
|
|
@@ -2496,7 +2535,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2496
2535
|
}
|
|
2497
2536
|
const destRoot = config.dest[detectedType];
|
|
2498
2537
|
if (!destRoot) {
|
|
2499
|
-
if (isVerbose())
|
|
2538
|
+
if (isVerbose()) spinner12.info(`no ${detectedType} destination configured, skipped: ${entry}`);
|
|
2500
2539
|
return;
|
|
2501
2540
|
}
|
|
2502
2541
|
if (detectedType === "ps3") {
|
|
@@ -2506,18 +2545,18 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2506
2545
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
2507
2546
|
const destPath = (0, import_path17.resolve)(destRoot, destName);
|
|
2508
2547
|
if ((0, import_fs19.existsSync)(destPath)) {
|
|
2509
|
-
|
|
2548
|
+
spinner12.warn(`already exists: ${destName}`);
|
|
2510
2549
|
return;
|
|
2511
2550
|
}
|
|
2512
2551
|
moveItem(entryPath, destPath);
|
|
2513
2552
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
2514
|
-
|
|
2553
|
+
spinner12.succeed(`imported ${import_termkit17.Color.green.encoder(destName)}`);
|
|
2515
2554
|
return;
|
|
2516
2555
|
}
|
|
2517
2556
|
if (detectedType === "book") {
|
|
2518
2557
|
const destPath = (0, import_path17.resolve)(destRoot, entry);
|
|
2519
2558
|
if ((0, import_fs19.existsSync)(destPath)) {
|
|
2520
|
-
|
|
2559
|
+
spinner12.warn(`already exists: ${entry}`);
|
|
2521
2560
|
return;
|
|
2522
2561
|
}
|
|
2523
2562
|
if (isDir || isBookDir) {
|
|
@@ -2532,17 +2571,17 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2532
2571
|
}
|
|
2533
2572
|
}
|
|
2534
2573
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
2535
|
-
|
|
2574
|
+
spinner12.succeed(`imported ${import_termkit17.Color.green.encoder(entry)}`);
|
|
2536
2575
|
return;
|
|
2537
2576
|
}
|
|
2538
2577
|
const parsed = parseDownloadName(entry);
|
|
2539
2578
|
if (!parsed) {
|
|
2540
|
-
if (isVerbose())
|
|
2579
|
+
if (isVerbose()) spinner12.info(`could not parse: ${entry}`);
|
|
2541
2580
|
return;
|
|
2542
2581
|
}
|
|
2543
2582
|
if (detectedType === "tv") {
|
|
2544
2583
|
if (parsed.season === void 0) {
|
|
2545
|
-
if (isVerbose())
|
|
2584
|
+
if (isVerbose()) spinner12.info(`could not detect season from: ${entry}`);
|
|
2546
2585
|
return;
|
|
2547
2586
|
}
|
|
2548
2587
|
const registeredShow = getShowByTitle(parsed.title);
|
|
@@ -2556,14 +2595,14 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2556
2595
|
showPath = (0, import_path17.resolve)(destRoot, showFolderName);
|
|
2557
2596
|
upsertShow(showPath, null, parsed.title);
|
|
2558
2597
|
} else {
|
|
2559
|
-
if (isVerbose())
|
|
2598
|
+
if (isVerbose()) spinner12.info(`not registered, skipped: ${parsed.title} \u2014 run: reelsort add "${parsed.title}"`);
|
|
2560
2599
|
return;
|
|
2561
2600
|
}
|
|
2562
2601
|
const seasonFolderName = findSeasonFolder2(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
2563
2602
|
const seasonPath = (0, import_path17.resolve)(showPath, seasonFolderName);
|
|
2564
2603
|
const videoFile2 = isDir ? findVideo2(entryPath) : entry;
|
|
2565
2604
|
if (!videoFile2) {
|
|
2566
|
-
if (isVerbose())
|
|
2605
|
+
if (isVerbose()) spinner12.info(`no video found in: ${entry}`);
|
|
2567
2606
|
return;
|
|
2568
2607
|
}
|
|
2569
2608
|
const videoExt2 = videoFile2.match(/([^.]+$)/)?.[0];
|
|
@@ -2573,7 +2612,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2573
2612
|
const destVideoPath = (0, import_path17.resolve)(seasonPath, destVideoName2);
|
|
2574
2613
|
const videoSourcePath2 = isDir ? (0, import_path17.resolve)(entryPath, videoFile2) : entryPath;
|
|
2575
2614
|
if ((0, import_fs19.existsSync)(destVideoPath)) {
|
|
2576
|
-
|
|
2615
|
+
spinner12.warn(`already exists: ${episodeName}`);
|
|
2577
2616
|
return;
|
|
2578
2617
|
}
|
|
2579
2618
|
const dirFiles2 = isDir ? (0, import_fs19.readdirSync)(entryPath) : [];
|
|
@@ -2589,7 +2628,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2589
2628
|
(0, import_fs19.linkSync)(videoSourcePath2, destVideoPath);
|
|
2590
2629
|
mode = "hardlink";
|
|
2591
2630
|
} catch {
|
|
2592
|
-
|
|
2631
|
+
spinner12.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
2593
2632
|
(0, import_fs19.cpSync)(videoSourcePath2, destVideoPath);
|
|
2594
2633
|
mode = "copy";
|
|
2595
2634
|
}
|
|
@@ -2605,19 +2644,19 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2605
2644
|
if (isDir) (0, import_fs19.rmSync)(entryPath, { recursive: true, force: true });
|
|
2606
2645
|
}
|
|
2607
2646
|
recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
|
|
2608
|
-
|
|
2647
|
+
spinner12.succeed(`imported ${import_termkit17.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
2609
2648
|
return;
|
|
2610
2649
|
}
|
|
2611
2650
|
const edition = detectEdition(entry);
|
|
2612
2651
|
const folderName = formatMovieName(movieFormat, parsed.title, parsed.year, edition);
|
|
2613
2652
|
const destFolder = (0, import_path17.resolve)(destRoot, folderName);
|
|
2614
2653
|
if ((0, import_fs19.existsSync)(destFolder)) {
|
|
2615
|
-
|
|
2654
|
+
spinner12.warn(`already exists: ${folderName}`);
|
|
2616
2655
|
return;
|
|
2617
2656
|
}
|
|
2618
2657
|
const videoFile = isDir ? findVideo2(entryPath) : entry;
|
|
2619
2658
|
if (!videoFile) {
|
|
2620
|
-
if (isVerbose())
|
|
2659
|
+
if (isVerbose()) spinner12.info(`no video found in: ${entry}`);
|
|
2621
2660
|
return;
|
|
2622
2661
|
}
|
|
2623
2662
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
@@ -2637,7 +2676,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2637
2676
|
(0, import_fs19.linkSync)(videoSourcePath, destVideoPath);
|
|
2638
2677
|
mode = "hardlink";
|
|
2639
2678
|
} catch {
|
|
2640
|
-
|
|
2679
|
+
spinner12.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
2641
2680
|
(0, import_fs19.cpSync)(videoSourcePath, destVideoPath);
|
|
2642
2681
|
mode = "copy";
|
|
2643
2682
|
}
|
|
@@ -2662,7 +2701,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2662
2701
|
}
|
|
2663
2702
|
recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
|
|
2664
2703
|
}
|
|
2665
|
-
|
|
2704
|
+
spinner12.succeed(`imported ${import_termkit17.Color.green.encoder(folderName)}`);
|
|
2666
2705
|
};
|
|
2667
2706
|
var watch = async ({ hardlink = false, auto = false }) => {
|
|
2668
2707
|
const config = getConfig();
|
|
@@ -2681,7 +2720,7 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2681
2720
|
await processItem(entry, hardlink, language, auto);
|
|
2682
2721
|
}
|
|
2683
2722
|
} catch (err) {
|
|
2684
|
-
|
|
2723
|
+
spinner12.fail(`error processing ${path}: ${err.message}`);
|
|
2685
2724
|
}
|
|
2686
2725
|
}, 5e3)
|
|
2687
2726
|
);
|
|
@@ -2693,16 +2732,16 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2693
2732
|
});
|
|
2694
2733
|
watcher.on("addDir", handle);
|
|
2695
2734
|
watcher.on("add", handle);
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
for (const s of config.sources)
|
|
2699
|
-
|
|
2735
|
+
spinner12.start();
|
|
2736
|
+
spinner12.succeed(`watching ${config.sources.length} source${config.sources.length !== 1 ? "s" : ""}`);
|
|
2737
|
+
for (const s of config.sources) spinner12.info(` ${import_termkit17.Color.white.encoder(s)}`);
|
|
2738
|
+
spinner12.stop();
|
|
2700
2739
|
process.stdin.resume();
|
|
2701
2740
|
};
|
|
2702
2741
|
var watch_default = watch;
|
|
2703
2742
|
|
|
2704
2743
|
// package.json
|
|
2705
|
-
var version = "0.2.
|
|
2744
|
+
var version = "0.2.8";
|
|
2706
2745
|
|
|
2707
2746
|
// src/program.ts
|
|
2708
2747
|
var toCamel = (s) => s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
@@ -2711,8 +2750,8 @@ var adapt = (fn) => (options) => {
|
|
|
2711
2750
|
setVerbose(!!camel.verbose);
|
|
2712
2751
|
return fn(camel);
|
|
2713
2752
|
};
|
|
2714
|
-
var { command, option } =
|
|
2715
|
-
var program =
|
|
2753
|
+
var { command, option } = import_termkit18.Program;
|
|
2754
|
+
var program = import_termkit18.Program.command("reelsort").version(version).description("a cli to manage media").commands([
|
|
2716
2755
|
command("config").description("manage configuration").commands([
|
|
2717
2756
|
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))]),
|
|
2718
2757
|
command("dest").description("manage destinations").commands([command("add", "<type> <dir>").description("set a destination (movie, tv, ps3, book)").action(adapt(destAdd)), command("remove", "[type]").description("remove a destination (movie, tv, ps3, book)").action(adapt(destRemove))]),
|
|
@@ -2750,8 +2789,8 @@ var run = async (args) => {
|
|
|
2750
2789
|
try {
|
|
2751
2790
|
await program_default.parse(args);
|
|
2752
2791
|
} catch (err) {
|
|
2753
|
-
|
|
2754
|
-
|
|
2792
|
+
import_termkit19.Spinner.current?.fail(err.message);
|
|
2793
|
+
import_termkit19.Spinner.current?.stop();
|
|
2755
2794
|
}
|
|
2756
2795
|
process.exit();
|
|
2757
2796
|
};
|