reelsort 0.2.7 → 0.2.9
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 +358 -359
- package/dist/index.js +282 -290
- package/dist/index.mjs +252 -260
- 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;
|
|
@@ -1733,46 +1705,49 @@ var gatherEntries = (source) => {
|
|
|
1733
1705
|
}
|
|
1734
1706
|
return result;
|
|
1735
1707
|
};
|
|
1736
|
-
var
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1708
|
+
var nTitle = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1709
|
+
var showDirIndexCache = /* @__PURE__ */ new Map();
|
|
1710
|
+
var getShowDirIndex = (destRoot) => {
|
|
1711
|
+
if (showDirIndexCache.has(destRoot)) return showDirIndexCache.get(destRoot);
|
|
1712
|
+
const idx = /* @__PURE__ */ new Map();
|
|
1713
|
+
showDirIndexCache.set(destRoot, idx);
|
|
1714
|
+
if (!(0, import_fs15.existsSync)(destRoot)) return idx;
|
|
1715
|
+
let dirs;
|
|
1716
|
+
try {
|
|
1717
|
+
dirs = (0, import_fs15.readdirSync)(destRoot);
|
|
1718
|
+
} catch {
|
|
1719
|
+
return idx;
|
|
1720
|
+
}
|
|
1721
|
+
for (const dir of dirs) {
|
|
1741
1722
|
try {
|
|
1742
|
-
|
|
1723
|
+
if (!(0, import_fs15.lstatSync)((0, import_path15.resolve)(destRoot, dir)).isDirectory()) continue;
|
|
1743
1724
|
} catch {
|
|
1744
|
-
|
|
1725
|
+
continue;
|
|
1745
1726
|
}
|
|
1746
|
-
|
|
1747
|
-
};
|
|
1748
|
-
var findShowFolderByContent = (destRoot, title) => {
|
|
1749
|
-
if (!(0, import_fs15.existsSync)(destRoot)) return null;
|
|
1750
|
-
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1751
|
-
const target = normalize(title);
|
|
1752
|
-
const matchesTitle = (name) => {
|
|
1753
|
-
if (!isTvEpisodeName(name)) return false;
|
|
1754
|
-
const p = parseDownloadName(name);
|
|
1755
|
-
return !!p && normalize(p.title) === target;
|
|
1756
|
-
};
|
|
1757
|
-
for (const folder of (0, import_fs15.readdirSync)(destRoot)) {
|
|
1727
|
+
idx.set(nTitle(dir), dir);
|
|
1758
1728
|
try {
|
|
1759
|
-
const
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1729
|
+
const children = (0, import_fs15.readdirSync)((0, import_path15.resolve)(destRoot, dir));
|
|
1730
|
+
const tryFile = (name) => {
|
|
1731
|
+
if (!isTvEpisodeName(name)) return;
|
|
1732
|
+
const p = parseDownloadName(name);
|
|
1733
|
+
if (p) {
|
|
1734
|
+
const k = nTitle(p.title);
|
|
1735
|
+
if (!idx.has(k)) idx.set(k, dir);
|
|
1736
|
+
}
|
|
1737
|
+
};
|
|
1738
|
+
children.forEach(tryFile);
|
|
1739
|
+
children.forEach((child) => {
|
|
1740
|
+
if (!isSeasonDirName(child)) return;
|
|
1765
1741
|
try {
|
|
1766
|
-
const
|
|
1767
|
-
if (
|
|
1768
|
-
if ((0, import_fs15.readdirSync)(seasonPath).some(matchesTitle)) return folder;
|
|
1742
|
+
const sp = (0, import_path15.resolve)(destRoot, dir, child);
|
|
1743
|
+
if ((0, import_fs15.lstatSync)(sp).isDirectory()) (0, import_fs15.readdirSync)(sp).forEach(tryFile);
|
|
1769
1744
|
} catch {
|
|
1770
1745
|
}
|
|
1771
|
-
}
|
|
1746
|
+
});
|
|
1772
1747
|
} catch {
|
|
1773
1748
|
}
|
|
1774
1749
|
}
|
|
1775
|
-
return
|
|
1750
|
+
return idx;
|
|
1776
1751
|
};
|
|
1777
1752
|
var findSeasonFolder = (showPath, season, specialsFolder) => {
|
|
1778
1753
|
if (!(0, import_fs15.existsSync)(showPath)) return null;
|
|
@@ -1802,13 +1777,17 @@ var classifyMovieConfidence = (entry) => {
|
|
|
1802
1777
|
return "ambiguous";
|
|
1803
1778
|
};
|
|
1804
1779
|
var typeColor = {
|
|
1805
|
-
movie: (s) =>
|
|
1806
|
-
tv: (s) =>
|
|
1807
|
-
book: (s) =>
|
|
1808
|
-
ps3: (s) =>
|
|
1809
|
-
};
|
|
1810
|
-
var typeGlyph = (t) => typeColor[t]("
|
|
1811
|
-
var
|
|
1780
|
+
movie: (s) => import_termkit13.Color.yellow.encoder(s),
|
|
1781
|
+
tv: (s) => import_termkit13.Color.blue.encoder(s),
|
|
1782
|
+
book: (s) => import_termkit13.Color.white.encoder(s),
|
|
1783
|
+
ps3: (s) => import_termkit13.Color.magenta.encoder(s)
|
|
1784
|
+
};
|
|
1785
|
+
var typeGlyph = (t) => typeColor[t]("?");
|
|
1786
|
+
var checkGlyph = (t) => typeColor[t]("\u2714");
|
|
1787
|
+
var greyGlyph = import_termkit13.Color.white.faint.encoder("\u25CF");
|
|
1788
|
+
var warnGlyph = import_termkit13.Color.yellow.encoder("\u26A0");
|
|
1789
|
+
var typeTag = (t) => isVerbose() ? import_termkit13.Color.white.faint.encoder(` (${t})`) : "";
|
|
1790
|
+
var sortByEntry = (arr) => arr.sort((a, b) => a.entry.localeCompare(b.entry, void 0, { sensitivity: "base" }));
|
|
1812
1791
|
var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactive }) => {
|
|
1813
1792
|
const config = getConfig();
|
|
1814
1793
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1816,27 +1795,28 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1816
1795
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1817
1796
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
1818
1797
|
const specialsFolder = config.specialsFolder ?? "Specials";
|
|
1798
|
+
const dryTag = dryRun ? import_termkit13.Color.white.faint.encoder(" [dry]") : "";
|
|
1819
1799
|
const lookupMovie = async (parsed) => {
|
|
1820
1800
|
let tmdbId;
|
|
1821
1801
|
let resolvedTitle = parsed.title;
|
|
1822
1802
|
let resolvedYear = parsed.year;
|
|
1823
1803
|
if (config.tmdbApiKey) {
|
|
1824
|
-
|
|
1804
|
+
spinner10.update(`TMDb: ${parsed.title}`);
|
|
1825
1805
|
const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
|
|
1826
1806
|
if (results.length === 1) {
|
|
1827
1807
|
tmdbId = results[0].id;
|
|
1828
1808
|
resolvedTitle = results[0].title;
|
|
1829
1809
|
resolvedYear = results[0].year ?? parsed.year;
|
|
1830
1810
|
} else if (results.length > 1) {
|
|
1831
|
-
|
|
1832
|
-
const select = new
|
|
1811
|
+
spinner10.stop();
|
|
1812
|
+
const select = new import_termkit13.Select();
|
|
1833
1813
|
const items = results.map((r) => ({
|
|
1834
1814
|
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
1835
1815
|
description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
|
|
1836
1816
|
...r
|
|
1837
1817
|
}));
|
|
1838
1818
|
const picked = await select.ask(`Multiple movies found for "${parsed.title}":`, items);
|
|
1839
|
-
|
|
1819
|
+
spinner10.start();
|
|
1840
1820
|
if (picked) {
|
|
1841
1821
|
tmdbId = picked.id;
|
|
1842
1822
|
resolvedTitle = picked.title;
|
|
@@ -1851,12 +1831,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1851
1831
|
const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
|
|
1852
1832
|
const destFolder = (0, import_path15.resolve)(destRoot, folderName);
|
|
1853
1833
|
if ((0, import_fs15.existsSync)(destFolder)) {
|
|
1854
|
-
|
|
1834
|
+
spinner10.log(`${typeColor.movie(folderName)}${typeTag("movie")}`, greyGlyph);
|
|
1855
1835
|
return false;
|
|
1856
1836
|
}
|
|
1857
1837
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
1858
1838
|
if (!videoFile) {
|
|
1859
|
-
|
|
1839
|
+
spinner10.log(`${entry}${typeTag("movie")}`, warnGlyph);
|
|
1860
1840
|
return false;
|
|
1861
1841
|
}
|
|
1862
1842
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
@@ -1877,7 +1857,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1877
1857
|
(0, import_fs15.linkSync)(videoSourcePath, destVideoPath);
|
|
1878
1858
|
mode = "hardlink";
|
|
1879
1859
|
} catch {
|
|
1880
|
-
|
|
1860
|
+
spinner10.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
1881
1861
|
(0, import_fs15.cpSync)(videoSourcePath, destVideoPath);
|
|
1882
1862
|
mode = "copy";
|
|
1883
1863
|
}
|
|
@@ -1903,10 +1883,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1903
1883
|
recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
|
|
1904
1884
|
}
|
|
1905
1885
|
}
|
|
1906
|
-
|
|
1886
|
+
spinner10.log(`${typeColor.movie(folderName)}${typeTag("movie")}${dryTag}`, checkGlyph("movie"));
|
|
1907
1887
|
return true;
|
|
1908
1888
|
};
|
|
1909
|
-
|
|
1889
|
+
spinner10.start();
|
|
1910
1890
|
if (config.sources.length === 0) throw new Error("no sources configured \u2014 run: reelsort config add source <dir>");
|
|
1911
1891
|
let imported = 0, skipped = 0;
|
|
1912
1892
|
const pendingMovies = [];
|
|
@@ -1917,15 +1897,19 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1917
1897
|
const seenIgnored = /* @__PURE__ */ new Set();
|
|
1918
1898
|
for (const source of config.sources) {
|
|
1919
1899
|
if (!(0, import_fs15.existsSync)(source)) {
|
|
1920
|
-
|
|
1900
|
+
spinner10.warn(`source not found: ${import_termkit13.Color.white.encoder(source)}`);
|
|
1921
1901
|
continue;
|
|
1922
1902
|
}
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1903
|
+
spinner10.update(`scanning ${import_termkit13.Color.white.encoder(source)}`);
|
|
1904
|
+
const entries = gatherEntries(source).sort(
|
|
1905
|
+
(a, b) => a.entry.localeCompare(b.entry, void 0, { sensitivity: "base" })
|
|
1906
|
+
);
|
|
1907
|
+
for (const { entry, entryPath, isDir } of entries) {
|
|
1908
|
+
spinner10.update(`scanning: ${entry}`);
|
|
1926
1909
|
if (ignoreSet.has(entry)) {
|
|
1927
1910
|
seenIgnored.add(entry);
|
|
1928
|
-
if (isVerbose())
|
|
1911
|
+
if (isVerbose()) spinner10.log(entry, greyGlyph);
|
|
1912
|
+
skipped++;
|
|
1929
1913
|
continue;
|
|
1930
1914
|
}
|
|
1931
1915
|
const ext = entry.match(/([^.]+$)/)?.[0];
|
|
@@ -1945,7 +1929,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1945
1929
|
}
|
|
1946
1930
|
const destRoot = config.dest[detectedType];
|
|
1947
1931
|
if (!destRoot) {
|
|
1948
|
-
if (isVerbose())
|
|
1932
|
+
if (isVerbose()) spinner10.log(`${entry}${typeTag(detectedType)}`, greyGlyph);
|
|
1949
1933
|
skipped++;
|
|
1950
1934
|
continue;
|
|
1951
1935
|
}
|
|
@@ -1959,7 +1943,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1959
1943
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
1960
1944
|
const destPath = (0, import_path15.resolve)(destRoot, destName);
|
|
1961
1945
|
if ((0, import_fs15.existsSync)(destPath)) {
|
|
1962
|
-
|
|
1946
|
+
spinner10.log(`${typeColor.ps3(destName)}${typeTag("ps3")}`, greyGlyph);
|
|
1963
1947
|
skipped++;
|
|
1964
1948
|
continue;
|
|
1965
1949
|
}
|
|
@@ -1967,14 +1951,14 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1967
1951
|
moveFolder(entryPath, destPath);
|
|
1968
1952
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
1969
1953
|
}
|
|
1970
|
-
|
|
1954
|
+
spinner10.log(`${typeColor.ps3(destName)}${typeTag("ps3")}${dryTag}`, checkGlyph("ps3"));
|
|
1971
1955
|
imported++;
|
|
1972
1956
|
continue;
|
|
1973
1957
|
}
|
|
1974
1958
|
if (detectedType === "book") {
|
|
1975
1959
|
const destPath = (0, import_path15.resolve)(destRoot, entry);
|
|
1976
1960
|
if ((0, import_fs15.existsSync)(destPath)) {
|
|
1977
|
-
|
|
1961
|
+
spinner10.log(`${typeColor.book(entry)}${typeTag("book")}`, greyGlyph);
|
|
1978
1962
|
skipped++;
|
|
1979
1963
|
continue;
|
|
1980
1964
|
}
|
|
@@ -1992,7 +1976,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1992
1976
|
}
|
|
1993
1977
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
1994
1978
|
}
|
|
1995
|
-
|
|
1979
|
+
spinner10.log(`${typeColor.book(entry)}${typeTag("book")}${dryTag}`, checkGlyph("book"));
|
|
1996
1980
|
imported++;
|
|
1997
1981
|
continue;
|
|
1998
1982
|
}
|
|
@@ -2001,10 +1985,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2001
1985
|
if (videoCount === 0) {
|
|
2002
1986
|
if (containsPdf(entryPath)) {
|
|
2003
1987
|
pendingBooks.push({ entry, entryPath });
|
|
2004
|
-
} else
|
|
2005
|
-
|
|
1988
|
+
} else {
|
|
1989
|
+
if (isVerbose()) spinner10.log(entry, greyGlyph);
|
|
1990
|
+
skipped++;
|
|
2006
1991
|
}
|
|
2007
|
-
skipped++;
|
|
2008
1992
|
continue;
|
|
2009
1993
|
}
|
|
2010
1994
|
if (videoCount >= 2) {
|
|
@@ -2014,14 +1998,14 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2014
1998
|
}
|
|
2015
1999
|
const parsed = parseDownloadName(entry);
|
|
2016
2000
|
if (!parsed) {
|
|
2017
|
-
if (isVerbose())
|
|
2001
|
+
if (isVerbose()) spinner10.log(entry, greyGlyph);
|
|
2018
2002
|
skipped++;
|
|
2019
2003
|
continue;
|
|
2020
2004
|
}
|
|
2021
2005
|
if (detectedType === "movie") {
|
|
2022
2006
|
const confidence = classifyMovieConfidence(entry);
|
|
2023
2007
|
if (confidence === "skip") {
|
|
2024
|
-
if (isVerbose())
|
|
2008
|
+
if (isVerbose()) spinner10.log(entry, greyGlyph);
|
|
2025
2009
|
skipped++;
|
|
2026
2010
|
continue;
|
|
2027
2011
|
}
|
|
@@ -2035,22 +2019,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2035
2019
|
let resolvedYear = parsed.year;
|
|
2036
2020
|
if (config.tmdbApiKey) {
|
|
2037
2021
|
if (detectedType === "tv") {
|
|
2038
|
-
|
|
2022
|
+
spinner10.update(`TMDb: ${parsed.title}`);
|
|
2039
2023
|
const results = await searchTv(parsed.title, config.tmdbApiKey);
|
|
2040
2024
|
if (results.length === 1) {
|
|
2041
2025
|
tmdbId = results[0].id;
|
|
2042
2026
|
resolvedTitle = results[0].title;
|
|
2043
2027
|
resolvedYear = results[0].year ?? parsed.year;
|
|
2044
2028
|
} else if (results.length > 1) {
|
|
2045
|
-
|
|
2046
|
-
const select = new
|
|
2029
|
+
spinner10.stop();
|
|
2030
|
+
const select = new import_termkit13.Select();
|
|
2047
2031
|
const items = results.map((r) => ({
|
|
2048
2032
|
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
2049
2033
|
description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
|
|
2050
2034
|
...r
|
|
2051
2035
|
}));
|
|
2052
2036
|
const picked = await select.ask(`Multiple shows found for "${parsed.title}":`, items);
|
|
2053
|
-
|
|
2037
|
+
spinner10.start();
|
|
2054
2038
|
if (picked) {
|
|
2055
2039
|
tmdbId = picked.id;
|
|
2056
2040
|
resolvedTitle = picked.title;
|
|
@@ -2066,7 +2050,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2066
2050
|
}
|
|
2067
2051
|
if (detectedType === "tv") {
|
|
2068
2052
|
if (parsed.season === void 0) {
|
|
2069
|
-
if (isVerbose())
|
|
2053
|
+
if (isVerbose()) spinner10.log(entry, greyGlyph);
|
|
2070
2054
|
skipped++;
|
|
2071
2055
|
continue;
|
|
2072
2056
|
}
|
|
@@ -2077,7 +2061,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2077
2061
|
showPath = registeredShow.path;
|
|
2078
2062
|
showFolderName = showPath.split("/").pop() ?? registeredShow.path;
|
|
2079
2063
|
} else {
|
|
2080
|
-
const existingFolder =
|
|
2064
|
+
const existingFolder = getShowDirIndex(destRoot).get(nTitle(resolvedTitle)) ?? null;
|
|
2081
2065
|
if (existingFolder) {
|
|
2082
2066
|
showFolderName = existingFolder;
|
|
2083
2067
|
showPath = (0, import_path15.resolve)(destRoot, existingFolder);
|
|
@@ -2085,6 +2069,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2085
2069
|
showFolderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear);
|
|
2086
2070
|
showPath = (0, import_path15.resolve)(destRoot, showFolderName);
|
|
2087
2071
|
if (!dryRun) upsertShow(showPath, tmdbId ?? null, resolvedTitle);
|
|
2072
|
+
getShowDirIndex(destRoot).set(nTitle(resolvedTitle), showFolderName);
|
|
2088
2073
|
} else {
|
|
2089
2074
|
pendingTv.push({ entry, entryPath, isDir, parsed, resolvedTitle, destRoot });
|
|
2090
2075
|
continue;
|
|
@@ -2094,12 +2079,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2094
2079
|
const seasonPath = (0, import_path15.resolve)(showPath, seasonFolderName);
|
|
2095
2080
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
2096
2081
|
if (!videoFile) {
|
|
2097
|
-
|
|
2082
|
+
spinner10.log(`${entry}${typeTag("tv")}`, warnGlyph);
|
|
2098
2083
|
skipped++;
|
|
2099
2084
|
continue;
|
|
2100
2085
|
}
|
|
2101
2086
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
2102
|
-
if (tmdbId && config.tmdbApiKey)
|
|
2087
|
+
if (tmdbId && config.tmdbApiKey) spinner10.update(`TMDb: episode name for ${resolvedTitle}`);
|
|
2103
2088
|
const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
2104
2089
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
|
|
2105
2090
|
const destVideoName = `${episodeName}.${videoExt}`;
|
|
@@ -2109,17 +2094,17 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2109
2094
|
const isRepack = /\brepack\d*\b|\bproper\b/i.test(entry);
|
|
2110
2095
|
let shouldReplace = force || isRepack;
|
|
2111
2096
|
if (!shouldReplace && interactive) {
|
|
2112
|
-
|
|
2113
|
-
const select = new
|
|
2097
|
+
spinner10.stop();
|
|
2098
|
+
const select = new import_termkit13.Select();
|
|
2114
2099
|
const picked = await select.ask(`Already exists \u2014 replace?`, [
|
|
2115
2100
|
{ label: `${showFolderName} / ${seasonFolderName} / ${episodeName}`, value: "replace" },
|
|
2116
2101
|
{ label: "Skip", value: "skip" }
|
|
2117
2102
|
]);
|
|
2118
|
-
|
|
2103
|
+
spinner10.start();
|
|
2119
2104
|
shouldReplace = picked?.value === "replace";
|
|
2120
2105
|
}
|
|
2121
2106
|
if (!shouldReplace) {
|
|
2122
|
-
|
|
2107
|
+
spinner10.log(`${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}`, greyGlyph);
|
|
2123
2108
|
skipped++;
|
|
2124
2109
|
continue;
|
|
2125
2110
|
}
|
|
@@ -2143,7 +2128,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2143
2128
|
(0, import_fs15.linkSync)(videoSourcePath, destVideoPath);
|
|
2144
2129
|
mode = "hardlink";
|
|
2145
2130
|
} catch {
|
|
2146
|
-
|
|
2131
|
+
spinner10.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
2147
2132
|
(0, import_fs15.cpSync)(videoSourcePath, destVideoPath);
|
|
2148
2133
|
mode = "copy";
|
|
2149
2134
|
}
|
|
@@ -2160,7 +2145,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2160
2145
|
}
|
|
2161
2146
|
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
|
|
2162
2147
|
}
|
|
2163
|
-
|
|
2148
|
+
spinner10.log(`${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}${dryTag}`, checkGlyph("tv"));
|
|
2164
2149
|
imported++;
|
|
2165
2150
|
continue;
|
|
2166
2151
|
}
|
|
@@ -2171,25 +2156,27 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2171
2156
|
}
|
|
2172
2157
|
}
|
|
2173
2158
|
}
|
|
2159
|
+
let uncertainMovies = 0;
|
|
2174
2160
|
if (pendingMovies.length > 0) {
|
|
2175
|
-
spinner_default.warn(`${pendingMovies.length} uncertain movie match${pendingMovies.length > 1 ? "es" : ""} skipped \u2014 use -i to review or -f to import all`);
|
|
2176
|
-
for (const p of pendingMovies) spinner_default.info(` ${typeGlyph("movie")} ${p.entry.replace(/\/$/, "")}${typeTag("movie")}`);
|
|
2177
2161
|
let toProcess = [];
|
|
2178
2162
|
if (interactive) {
|
|
2179
|
-
|
|
2180
|
-
const ms = new
|
|
2163
|
+
spinner10.stop();
|
|
2164
|
+
const ms = new import_termkit13.MultiSelect({ allowSkip: true, search: true, maxHeight: 20 });
|
|
2181
2165
|
const items = pendingMovies.map((p) => ({
|
|
2182
2166
|
label: p.entry.replace(/\/$/, ""),
|
|
2183
2167
|
description: p.parsed.year ? `parsed: ${p.parsed.title} \xB7 ${p.parsed.year}` : `parsed: ${p.parsed.title}`,
|
|
2184
2168
|
...p
|
|
2185
2169
|
}));
|
|
2186
2170
|
toProcess = await ms.ask("Select entries to import as movies:", items) ?? [];
|
|
2187
|
-
|
|
2188
|
-
skipped += pendingMovies.length - toProcess.length;
|
|
2171
|
+
spinner10.start();
|
|
2189
2172
|
} else if (force) {
|
|
2190
2173
|
toProcess = pendingMovies;
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2174
|
+
}
|
|
2175
|
+
const toSkip = pendingMovies.filter((p) => !toProcess.includes(p));
|
|
2176
|
+
uncertainMovies = toSkip.length;
|
|
2177
|
+
sortByEntry(toSkip);
|
|
2178
|
+
for (const p of toSkip) {
|
|
2179
|
+
spinner10.log(`${typeColor.movie(p.entry.replace(/\/$/, ""))}${typeTag("movie")}`, typeGlyph("movie"));
|
|
2193
2180
|
}
|
|
2194
2181
|
for (const p of toProcess) {
|
|
2195
2182
|
const { tmdbId, resolvedTitle, resolvedYear } = await lookupMovie(p.parsed);
|
|
@@ -2201,18 +2188,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2201
2188
|
}
|
|
2202
2189
|
}
|
|
2203
2190
|
if (pendingTv.length > 0) {
|
|
2204
|
-
|
|
2205
|
-
for (const p of pendingTv)
|
|
2206
|
-
|
|
2191
|
+
pendingTv.sort((a, b) => a.resolvedTitle.localeCompare(b.resolvedTitle, void 0, { sensitivity: "base" }));
|
|
2192
|
+
for (const p of pendingTv) {
|
|
2193
|
+
spinner10.log(`${typeColor.tv(p.resolvedTitle)} \u2014 ${p.entry.replace(/\/$/, "")}${typeTag("tv")}`, typeGlyph("tv"));
|
|
2194
|
+
}
|
|
2207
2195
|
}
|
|
2208
2196
|
if (pendingBooks.length > 0) {
|
|
2209
|
-
|
|
2210
|
-
for (const p of pendingBooks)
|
|
2197
|
+
sortByEntry(pendingBooks);
|
|
2198
|
+
for (const p of pendingBooks) {
|
|
2199
|
+
spinner10.log(`${typeColor.book(p.entry)}${typeTag("book")}`, typeGlyph("book"));
|
|
2200
|
+
}
|
|
2211
2201
|
}
|
|
2212
2202
|
if (pendingAnime.length > 0) {
|
|
2213
|
-
|
|
2214
|
-
for (const p of pendingAnime)
|
|
2215
|
-
|
|
2203
|
+
sortByEntry(pendingAnime);
|
|
2204
|
+
for (const p of pendingAnime) {
|
|
2205
|
+
spinner10.log(`${typeColor.tv(p.entry)} (${p.videoCount} video${p.videoCount > 1 ? "s" : ""})${typeTag("tv")}`, typeGlyph("tv"));
|
|
2206
|
+
}
|
|
2216
2207
|
}
|
|
2217
2208
|
if (ignoreSet.size > 0) {
|
|
2218
2209
|
const stale = [...ignoreSet].filter((name) => !seenIgnored.has(name));
|
|
@@ -2220,18 +2211,23 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2220
2211
|
const updated = config.ignore.filter((name) => !stale.includes(name));
|
|
2221
2212
|
config.ignore = updated;
|
|
2222
2213
|
saveConfig(config);
|
|
2223
|
-
for (const name of stale)
|
|
2214
|
+
for (const name of stale) spinner10.info(`removed from ignore list (not found): ${import_termkit13.Color.white.encoder(name)}`);
|
|
2224
2215
|
}
|
|
2225
2216
|
}
|
|
2226
|
-
|
|
2227
|
-
if (skipped)
|
|
2228
|
-
|
|
2217
|
+
const summaryParts = [`${dryRun ? "would import" : "imported"} ${imported}`];
|
|
2218
|
+
if (skipped) summaryParts.push(`skipped ${skipped}`);
|
|
2219
|
+
if (uncertainMovies) summaryParts.push(`uncertain movie ${uncertainMovies}`);
|
|
2220
|
+
if (pendingTv.length) summaryParts.push(`uncertain tv ${pendingTv.length}`);
|
|
2221
|
+
if (pendingBooks.length) summaryParts.push(`uncertain book ${pendingBooks.length}`);
|
|
2222
|
+
if (pendingAnime.length) summaryParts.push(`uncertain anime ${pendingAnime.length}`);
|
|
2223
|
+
spinner10.succeed(summaryParts.join(", "));
|
|
2224
|
+
spinner10.stop();
|
|
2229
2225
|
};
|
|
2230
2226
|
var scan_default = scan;
|
|
2231
2227
|
|
|
2232
2228
|
// src/actions/shows.ts
|
|
2233
2229
|
var import_fs16 = require("fs");
|
|
2234
|
-
var
|
|
2230
|
+
var import_termkit14 = require("termkit");
|
|
2235
2231
|
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
2236
2232
|
var shows = async () => {
|
|
2237
2233
|
const config = getConfig();
|
|
@@ -2243,8 +2239,8 @@ var shows = async () => {
|
|
|
2243
2239
|
return;
|
|
2244
2240
|
}
|
|
2245
2241
|
console.log(`
|
|
2246
|
-
${
|
|
2247
|
-
new
|
|
2242
|
+
${import_termkit14.Color.yellow.encoder("SHOWS")}${destRoot ? ` ${import_termkit14.Color.white.encoder(destRoot)}` : ""} (${allShows.length} registered)`);
|
|
2243
|
+
new import_termkit14.Table(
|
|
2248
2244
|
allShows.map((show) => ({
|
|
2249
2245
|
name: show.path.split("/").pop() ?? show.path,
|
|
2250
2246
|
size: (0, import_fs16.existsSync)(show.path) ? formatSize(dirSize(show.path)) : "\u2014",
|
|
@@ -2263,9 +2259,9 @@ ${import_termkit15.Color.yellow.encoder("SHOWS")}${destRoot ? ` ${import_termki
|
|
|
2263
2259
|
if (v) {
|
|
2264
2260
|
const url = `https://www.themoviedb.org/tv/${v}`;
|
|
2265
2261
|
const text = process.stdout.isTTY ? hyperlink(url, "\u2713 tmdb") : "\u2713 tmdb";
|
|
2266
|
-
return
|
|
2262
|
+
return import_termkit14.Color.green.encoder(text);
|
|
2267
2263
|
}
|
|
2268
|
-
return
|
|
2264
|
+
return import_termkit14.Color.red.encoder("\u2717");
|
|
2269
2265
|
}
|
|
2270
2266
|
},
|
|
2271
2267
|
{ key: "ended", title: "Status", value: (v) => v ? dim("ended") : "active" }
|
|
@@ -2282,7 +2278,7 @@ var shows_default = shows;
|
|
|
2282
2278
|
// src/actions/stats.ts
|
|
2283
2279
|
var import_fs17 = require("fs");
|
|
2284
2280
|
var import_path16 = require("path");
|
|
2285
|
-
var
|
|
2281
|
+
var import_termkit15 = require("termkit");
|
|
2286
2282
|
var countVideos2 = (dir) => {
|
|
2287
2283
|
let count = 0;
|
|
2288
2284
|
try {
|
|
@@ -2330,7 +2326,7 @@ var stats = async () => {
|
|
|
2330
2326
|
}
|
|
2331
2327
|
if (rows.length === 0) return;
|
|
2332
2328
|
console.log();
|
|
2333
|
-
new
|
|
2329
|
+
new import_termkit15.Table(rows, {
|
|
2334
2330
|
title: "LIBRARY STATISTICS",
|
|
2335
2331
|
separator: " ",
|
|
2336
2332
|
columns: [
|
|
@@ -2345,14 +2341,15 @@ var stats_default = stats;
|
|
|
2345
2341
|
|
|
2346
2342
|
// src/actions/undo.ts
|
|
2347
2343
|
var import_fs18 = require("fs");
|
|
2348
|
-
var
|
|
2344
|
+
var import_termkit16 = require("termkit");
|
|
2345
|
+
var spinner11 = new import_termkit16.Spinner();
|
|
2349
2346
|
var undo = async () => {
|
|
2350
|
-
|
|
2347
|
+
spinner11.start();
|
|
2351
2348
|
const renameRecords = getLastSession();
|
|
2352
2349
|
const importRecords = getLastImportSession();
|
|
2353
2350
|
if (renameRecords.length === 0 && importRecords.length === 0) {
|
|
2354
|
-
|
|
2355
|
-
|
|
2351
|
+
spinner11.info("nothing to undo");
|
|
2352
|
+
spinner11.stop();
|
|
2356
2353
|
return;
|
|
2357
2354
|
}
|
|
2358
2355
|
const useImports = importRecords.length > 0 && (renameRecords.length === 0 || importRecords[0].sessionId > renameRecords[0].sessionId);
|
|
@@ -2360,40 +2357,40 @@ var undo = async () => {
|
|
|
2360
2357
|
let undone2 = 0;
|
|
2361
2358
|
for (const record of renameRecords) {
|
|
2362
2359
|
(0, import_fs18.renameSync)(record.newPath, record.oldPath);
|
|
2363
|
-
|
|
2360
|
+
spinner11.succeed(`${import_termkit16.Color.green.encoder(record.newPath)} \u2192 ${import_termkit16.Color.white.encoder(record.oldPath)}`);
|
|
2364
2361
|
undone2++;
|
|
2365
2362
|
}
|
|
2366
2363
|
deleteSession(renameRecords[0].sessionId);
|
|
2367
|
-
|
|
2368
|
-
|
|
2364
|
+
spinner11.succeed(`undid ${undone2} renames`);
|
|
2365
|
+
spinner11.stop();
|
|
2369
2366
|
return;
|
|
2370
2367
|
}
|
|
2371
2368
|
let undone = 0;
|
|
2372
2369
|
let skipped = 0;
|
|
2373
2370
|
for (const record of importRecords) {
|
|
2374
2371
|
if (record.mode !== "move") {
|
|
2375
|
-
|
|
2372
|
+
spinner11.info(`skipped ${record.destinationPath} (${record.mode} \u2014 source file unchanged)`);
|
|
2376
2373
|
skipped++;
|
|
2377
2374
|
continue;
|
|
2378
2375
|
}
|
|
2379
2376
|
if (record.type === "tv") {
|
|
2380
|
-
|
|
2377
|
+
spinner11.info(`skipped TV import \u2014 season folder cannot be cleanly reversed: ${record.destinationPath}`);
|
|
2381
2378
|
skipped++;
|
|
2382
2379
|
continue;
|
|
2383
2380
|
}
|
|
2384
2381
|
if (!(0, import_fs18.existsSync)(record.destinationPath)) {
|
|
2385
|
-
|
|
2382
|
+
spinner11.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
|
|
2386
2383
|
skipped++;
|
|
2387
2384
|
continue;
|
|
2388
2385
|
}
|
|
2389
2386
|
(0, import_fs18.renameSync)(record.destinationPath, record.sourcePath);
|
|
2390
|
-
|
|
2387
|
+
spinner11.succeed(`${import_termkit16.Color.green.encoder(record.destinationPath)} \u2192 ${import_termkit16.Color.white.encoder(record.sourcePath)}`);
|
|
2391
2388
|
undone++;
|
|
2392
2389
|
}
|
|
2393
2390
|
deleteImportSession(importRecords[0].sessionId);
|
|
2394
|
-
if (undone > 0)
|
|
2395
|
-
if (skipped > 0)
|
|
2396
|
-
|
|
2391
|
+
if (undone > 0) spinner11.succeed(`undid ${undone} import${undone !== 1 ? "s" : ""}`);
|
|
2392
|
+
if (skipped > 0) spinner11.info(`skipped ${skipped} item${skipped !== 1 ? "s" : ""} (TV or non-move mode)`);
|
|
2393
|
+
spinner11.stop();
|
|
2397
2394
|
};
|
|
2398
2395
|
var undo_default = undo;
|
|
2399
2396
|
|
|
@@ -2401,7 +2398,8 @@ var undo_default = undo;
|
|
|
2401
2398
|
var import_chokidar = __toESM(require("chokidar"));
|
|
2402
2399
|
var import_fs19 = require("fs");
|
|
2403
2400
|
var import_path17 = require("path");
|
|
2404
|
-
var
|
|
2401
|
+
var import_termkit17 = require("termkit");
|
|
2402
|
+
var spinner12 = new import_termkit17.Spinner();
|
|
2405
2403
|
var sameDev2 = (a, b) => {
|
|
2406
2404
|
try {
|
|
2407
2405
|
let bExisting = b;
|
|
@@ -2541,7 +2539,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2541
2539
|
}
|
|
2542
2540
|
const destRoot = config.dest[detectedType];
|
|
2543
2541
|
if (!destRoot) {
|
|
2544
|
-
if (isVerbose())
|
|
2542
|
+
if (isVerbose()) spinner12.info(`no ${detectedType} destination configured, skipped: ${entry}`);
|
|
2545
2543
|
return;
|
|
2546
2544
|
}
|
|
2547
2545
|
if (detectedType === "ps3") {
|
|
@@ -2551,18 +2549,18 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2551
2549
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
2552
2550
|
const destPath = (0, import_path17.resolve)(destRoot, destName);
|
|
2553
2551
|
if ((0, import_fs19.existsSync)(destPath)) {
|
|
2554
|
-
|
|
2552
|
+
spinner12.warn(`already exists: ${destName}`);
|
|
2555
2553
|
return;
|
|
2556
2554
|
}
|
|
2557
2555
|
moveItem(entryPath, destPath);
|
|
2558
2556
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
2559
|
-
|
|
2557
|
+
spinner12.succeed(`imported ${import_termkit17.Color.green.encoder(destName)}`);
|
|
2560
2558
|
return;
|
|
2561
2559
|
}
|
|
2562
2560
|
if (detectedType === "book") {
|
|
2563
2561
|
const destPath = (0, import_path17.resolve)(destRoot, entry);
|
|
2564
2562
|
if ((0, import_fs19.existsSync)(destPath)) {
|
|
2565
|
-
|
|
2563
|
+
spinner12.warn(`already exists: ${entry}`);
|
|
2566
2564
|
return;
|
|
2567
2565
|
}
|
|
2568
2566
|
if (isDir || isBookDir) {
|
|
@@ -2577,17 +2575,17 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2577
2575
|
}
|
|
2578
2576
|
}
|
|
2579
2577
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
2580
|
-
|
|
2578
|
+
spinner12.succeed(`imported ${import_termkit17.Color.green.encoder(entry)}`);
|
|
2581
2579
|
return;
|
|
2582
2580
|
}
|
|
2583
2581
|
const parsed = parseDownloadName(entry);
|
|
2584
2582
|
if (!parsed) {
|
|
2585
|
-
if (isVerbose())
|
|
2583
|
+
if (isVerbose()) spinner12.info(`could not parse: ${entry}`);
|
|
2586
2584
|
return;
|
|
2587
2585
|
}
|
|
2588
2586
|
if (detectedType === "tv") {
|
|
2589
2587
|
if (parsed.season === void 0) {
|
|
2590
|
-
if (isVerbose())
|
|
2588
|
+
if (isVerbose()) spinner12.info(`could not detect season from: ${entry}`);
|
|
2591
2589
|
return;
|
|
2592
2590
|
}
|
|
2593
2591
|
const registeredShow = getShowByTitle(parsed.title);
|
|
@@ -2601,14 +2599,14 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2601
2599
|
showPath = (0, import_path17.resolve)(destRoot, showFolderName);
|
|
2602
2600
|
upsertShow(showPath, null, parsed.title);
|
|
2603
2601
|
} else {
|
|
2604
|
-
if (isVerbose())
|
|
2602
|
+
if (isVerbose()) spinner12.info(`not registered, skipped: ${parsed.title} \u2014 run: reelsort add "${parsed.title}"`);
|
|
2605
2603
|
return;
|
|
2606
2604
|
}
|
|
2607
2605
|
const seasonFolderName = findSeasonFolder2(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
2608
2606
|
const seasonPath = (0, import_path17.resolve)(showPath, seasonFolderName);
|
|
2609
2607
|
const videoFile2 = isDir ? findVideo2(entryPath) : entry;
|
|
2610
2608
|
if (!videoFile2) {
|
|
2611
|
-
if (isVerbose())
|
|
2609
|
+
if (isVerbose()) spinner12.info(`no video found in: ${entry}`);
|
|
2612
2610
|
return;
|
|
2613
2611
|
}
|
|
2614
2612
|
const videoExt2 = videoFile2.match(/([^.]+$)/)?.[0];
|
|
@@ -2618,7 +2616,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2618
2616
|
const destVideoPath = (0, import_path17.resolve)(seasonPath, destVideoName2);
|
|
2619
2617
|
const videoSourcePath2 = isDir ? (0, import_path17.resolve)(entryPath, videoFile2) : entryPath;
|
|
2620
2618
|
if ((0, import_fs19.existsSync)(destVideoPath)) {
|
|
2621
|
-
|
|
2619
|
+
spinner12.warn(`already exists: ${episodeName}`);
|
|
2622
2620
|
return;
|
|
2623
2621
|
}
|
|
2624
2622
|
const dirFiles2 = isDir ? (0, import_fs19.readdirSync)(entryPath) : [];
|
|
@@ -2634,7 +2632,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2634
2632
|
(0, import_fs19.linkSync)(videoSourcePath2, destVideoPath);
|
|
2635
2633
|
mode = "hardlink";
|
|
2636
2634
|
} catch {
|
|
2637
|
-
|
|
2635
|
+
spinner12.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
2638
2636
|
(0, import_fs19.cpSync)(videoSourcePath2, destVideoPath);
|
|
2639
2637
|
mode = "copy";
|
|
2640
2638
|
}
|
|
@@ -2650,19 +2648,19 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2650
2648
|
if (isDir) (0, import_fs19.rmSync)(entryPath, { recursive: true, force: true });
|
|
2651
2649
|
}
|
|
2652
2650
|
recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
|
|
2653
|
-
|
|
2651
|
+
spinner12.succeed(`imported ${import_termkit17.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
2654
2652
|
return;
|
|
2655
2653
|
}
|
|
2656
2654
|
const edition = detectEdition(entry);
|
|
2657
2655
|
const folderName = formatMovieName(movieFormat, parsed.title, parsed.year, edition);
|
|
2658
2656
|
const destFolder = (0, import_path17.resolve)(destRoot, folderName);
|
|
2659
2657
|
if ((0, import_fs19.existsSync)(destFolder)) {
|
|
2660
|
-
|
|
2658
|
+
spinner12.warn(`already exists: ${folderName}`);
|
|
2661
2659
|
return;
|
|
2662
2660
|
}
|
|
2663
2661
|
const videoFile = isDir ? findVideo2(entryPath) : entry;
|
|
2664
2662
|
if (!videoFile) {
|
|
2665
|
-
if (isVerbose())
|
|
2663
|
+
if (isVerbose()) spinner12.info(`no video found in: ${entry}`);
|
|
2666
2664
|
return;
|
|
2667
2665
|
}
|
|
2668
2666
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
@@ -2682,7 +2680,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2682
2680
|
(0, import_fs19.linkSync)(videoSourcePath, destVideoPath);
|
|
2683
2681
|
mode = "hardlink";
|
|
2684
2682
|
} catch {
|
|
2685
|
-
|
|
2683
|
+
spinner12.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
2686
2684
|
(0, import_fs19.cpSync)(videoSourcePath, destVideoPath);
|
|
2687
2685
|
mode = "copy";
|
|
2688
2686
|
}
|
|
@@ -2707,7 +2705,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2707
2705
|
}
|
|
2708
2706
|
recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
|
|
2709
2707
|
}
|
|
2710
|
-
|
|
2708
|
+
spinner12.succeed(`imported ${import_termkit17.Color.green.encoder(folderName)}`);
|
|
2711
2709
|
};
|
|
2712
2710
|
var watch = async ({ hardlink = false, auto = false }) => {
|
|
2713
2711
|
const config = getConfig();
|
|
@@ -2726,7 +2724,7 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2726
2724
|
await processItem(entry, hardlink, language, auto);
|
|
2727
2725
|
}
|
|
2728
2726
|
} catch (err) {
|
|
2729
|
-
|
|
2727
|
+
spinner12.fail(`error processing ${path}: ${err.message}`);
|
|
2730
2728
|
}
|
|
2731
2729
|
}, 5e3)
|
|
2732
2730
|
);
|
|
@@ -2738,16 +2736,16 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2738
2736
|
});
|
|
2739
2737
|
watcher.on("addDir", handle);
|
|
2740
2738
|
watcher.on("add", handle);
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
for (const s of config.sources)
|
|
2744
|
-
|
|
2739
|
+
spinner12.start();
|
|
2740
|
+
spinner12.succeed(`watching ${config.sources.length} source${config.sources.length !== 1 ? "s" : ""}`);
|
|
2741
|
+
for (const s of config.sources) spinner12.info(` ${import_termkit17.Color.white.encoder(s)}`);
|
|
2742
|
+
spinner12.stop();
|
|
2745
2743
|
process.stdin.resume();
|
|
2746
2744
|
};
|
|
2747
2745
|
var watch_default = watch;
|
|
2748
2746
|
|
|
2749
2747
|
// package.json
|
|
2750
|
-
var version = "0.2.
|
|
2748
|
+
var version = "0.2.9";
|
|
2751
2749
|
|
|
2752
2750
|
// src/program.ts
|
|
2753
2751
|
var toCamel = (s) => s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
@@ -2756,8 +2754,8 @@ var adapt = (fn) => (options) => {
|
|
|
2756
2754
|
setVerbose(!!camel.verbose);
|
|
2757
2755
|
return fn(camel);
|
|
2758
2756
|
};
|
|
2759
|
-
var { command, option } =
|
|
2760
|
-
var program =
|
|
2757
|
+
var { command, option } = import_termkit18.Program;
|
|
2758
|
+
var program = import_termkit18.Program.command("reelsort").version(version).description("a cli to manage media").commands([
|
|
2761
2759
|
command("config").description("manage configuration").commands([
|
|
2762
2760
|
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))]),
|
|
2763
2761
|
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))]),
|
|
@@ -2786,17 +2784,18 @@ var program_default = program;
|
|
|
2786
2784
|
|
|
2787
2785
|
// src/cli.ts
|
|
2788
2786
|
if (!process.stdout.isTTY) {
|
|
2789
|
-
const { FORCE_COLOR, MSYSTEM, WT_SESSION, TERM } = process.env;
|
|
2790
|
-
if (FORCE_COLOR || MSYSTEM || WT_SESSION || TERM?.startsWith("xterm")) {
|
|
2787
|
+
const { FORCE_COLOR, MSYSTEM, WT_SESSION, TERM, PSModulePath } = process.env;
|
|
2788
|
+
if (FORCE_COLOR || MSYSTEM || WT_SESSION || TERM?.startsWith("xterm") || PSModulePath) {
|
|
2791
2789
|
Object.defineProperty(process.stdout, "isTTY", { value: true, configurable: true });
|
|
2790
|
+
Object.defineProperty(process.stdin, "isTTY", { value: true, configurable: true });
|
|
2792
2791
|
}
|
|
2793
2792
|
}
|
|
2794
2793
|
var run = async (args) => {
|
|
2795
2794
|
try {
|
|
2796
2795
|
await program_default.parse(args);
|
|
2797
2796
|
} catch (err) {
|
|
2798
|
-
|
|
2799
|
-
|
|
2797
|
+
import_termkit19.Spinner.current?.fail(err.message);
|
|
2798
|
+
import_termkit19.Spinner.current?.stop();
|
|
2800
2799
|
}
|
|
2801
2800
|
process.exit();
|
|
2802
2801
|
};
|