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/index.js
CHANGED
|
@@ -78,7 +78,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
78
78
|
|
|
79
79
|
// src/actions/clean.ts
|
|
80
80
|
var import_fs2 = require("fs");
|
|
81
|
-
var
|
|
81
|
+
var import_termkit = require("termkit");
|
|
82
82
|
|
|
83
83
|
// src/db.ts
|
|
84
84
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"));
|
|
@@ -220,48 +220,8 @@ var getHistory = (limit = 10) => {
|
|
|
220
220
|
}));
|
|
221
221
|
};
|
|
222
222
|
|
|
223
|
-
// src/refs/spinner.ts
|
|
224
|
-
var import_termkit = require("termkit");
|
|
225
|
-
var Spinner = class {
|
|
226
|
-
spinner;
|
|
227
|
-
constructor() {
|
|
228
|
-
this.spinner = new import_termkit.Spinner();
|
|
229
|
-
}
|
|
230
|
-
get text() {
|
|
231
|
-
return this.spinner.text;
|
|
232
|
-
}
|
|
233
|
-
set text(t) {
|
|
234
|
-
this.spinner.text = t;
|
|
235
|
-
}
|
|
236
|
-
start(s) {
|
|
237
|
-
if (s) this.spinner.text = s;
|
|
238
|
-
this.spinner.start();
|
|
239
|
-
return this;
|
|
240
|
-
}
|
|
241
|
-
info(s) {
|
|
242
|
-
this.spinner.info(s);
|
|
243
|
-
return this;
|
|
244
|
-
}
|
|
245
|
-
warn(s) {
|
|
246
|
-
this.spinner.warn(s);
|
|
247
|
-
return this;
|
|
248
|
-
}
|
|
249
|
-
fail(s) {
|
|
250
|
-
this.spinner.fail(s);
|
|
251
|
-
return this;
|
|
252
|
-
}
|
|
253
|
-
succeed(s) {
|
|
254
|
-
this.spinner.succeed(s);
|
|
255
|
-
return this;
|
|
256
|
-
}
|
|
257
|
-
stop() {
|
|
258
|
-
this.spinner.stop();
|
|
259
|
-
return this;
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
var spinner_default = new Spinner();
|
|
263
|
-
|
|
264
223
|
// src/actions/clean.ts
|
|
224
|
+
var spinner = new import_termkit.Spinner();
|
|
265
225
|
var parseOlderThan = (s) => {
|
|
266
226
|
const match = s.match(/^(\d+)([dhm])$/);
|
|
267
227
|
if (!match) return null;
|
|
@@ -271,11 +231,11 @@ var parseOlderThan = (s) => {
|
|
|
271
231
|
return ms;
|
|
272
232
|
};
|
|
273
233
|
var clean = async ({ dryRun, olderThan }) => {
|
|
274
|
-
|
|
234
|
+
spinner.start();
|
|
275
235
|
const imports = getCleanableImports();
|
|
276
236
|
if (imports.length === 0) {
|
|
277
|
-
|
|
278
|
-
|
|
237
|
+
spinner.info("nothing to clean");
|
|
238
|
+
spinner.stop();
|
|
279
239
|
return;
|
|
280
240
|
}
|
|
281
241
|
const cutoffMs = olderThan ? parseOlderThan(olderThan) : null;
|
|
@@ -294,29 +254,29 @@ var clean = async ({ dryRun, olderThan }) => {
|
|
|
294
254
|
continue;
|
|
295
255
|
}
|
|
296
256
|
if (dryRun) {
|
|
297
|
-
|
|
257
|
+
spinner.succeed(`[dry] would remove ${import_termkit.Color.white.encoder(imp.sourcePath)}`);
|
|
298
258
|
cleaned++;
|
|
299
259
|
continue;
|
|
300
260
|
}
|
|
301
261
|
try {
|
|
302
262
|
(0, import_fs2.rmSync)(imp.sourcePath, { recursive: true, force: true });
|
|
303
263
|
deleteImport(imp.id);
|
|
304
|
-
|
|
264
|
+
spinner.succeed(`removed ${import_termkit.Color.white.encoder(imp.sourcePath)}`);
|
|
305
265
|
cleaned++;
|
|
306
266
|
} catch {
|
|
307
|
-
|
|
267
|
+
spinner.warn(`locked or inaccessible, skipped: ${import_termkit.Color.white.encoder(imp.sourcePath)}`);
|
|
308
268
|
skipped++;
|
|
309
269
|
}
|
|
310
270
|
}
|
|
311
|
-
|
|
312
|
-
if (skipped)
|
|
313
|
-
|
|
271
|
+
spinner.succeed(`cleaned ${cleaned} items`);
|
|
272
|
+
if (skipped) spinner.info(`skipped ${skipped} items`);
|
|
273
|
+
spinner.stop();
|
|
314
274
|
};
|
|
315
275
|
var clean_default = clean;
|
|
316
276
|
|
|
317
277
|
// src/actions/config.ts
|
|
318
278
|
var import_path3 = require("path");
|
|
319
|
-
var
|
|
279
|
+
var import_termkit2 = require("termkit");
|
|
320
280
|
|
|
321
281
|
// src/config.ts
|
|
322
282
|
var import_fs3 = require("fs");
|
|
@@ -363,32 +323,33 @@ var formatMovieName = (template, title, year, edition) => {
|
|
|
363
323
|
};
|
|
364
324
|
|
|
365
325
|
// src/actions/config.ts
|
|
326
|
+
var spinner2 = new import_termkit2.Spinner();
|
|
366
327
|
var DEST_TYPES = ["movie", "tv", "ps3", "book"];
|
|
367
328
|
var sourceAdd = async ({ dir }) => {
|
|
368
329
|
const resolved = (0, import_path3.resolve)(dir);
|
|
369
330
|
const config = getConfig();
|
|
370
331
|
if (config.sources.includes(resolved)) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
332
|
+
spinner2.start();
|
|
333
|
+
spinner2.info(`source already configured: ${import_termkit2.Color.white.encoder(resolved)}`);
|
|
334
|
+
spinner2.stop();
|
|
374
335
|
return;
|
|
375
336
|
}
|
|
376
337
|
config.sources.push(resolved);
|
|
377
338
|
saveConfig(config);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
339
|
+
spinner2.start();
|
|
340
|
+
spinner2.succeed(`added source: ${import_termkit2.Color.white.encoder(resolved)}`);
|
|
341
|
+
spinner2.stop();
|
|
381
342
|
};
|
|
382
343
|
var sourceRemove = async ({ dir }) => {
|
|
383
344
|
const config = getConfig();
|
|
384
345
|
if (!dir) {
|
|
385
346
|
if (config.sources.length === 0) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
347
|
+
spinner2.start();
|
|
348
|
+
spinner2.warn("no sources configured");
|
|
349
|
+
spinner2.stop();
|
|
389
350
|
return;
|
|
390
351
|
}
|
|
391
|
-
const select = new
|
|
352
|
+
const select = new import_termkit2.Select();
|
|
392
353
|
const picked = await select.ask("Which source do you want to remove?", config.sources.map((s) => ({ label: s, value: s })));
|
|
393
354
|
if (!picked) return;
|
|
394
355
|
dir = picked.value;
|
|
@@ -396,16 +357,16 @@ var sourceRemove = async ({ dir }) => {
|
|
|
396
357
|
const resolved = (0, import_path3.resolve)(dir);
|
|
397
358
|
const index = config.sources.indexOf(resolved);
|
|
398
359
|
if (index === -1) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
360
|
+
spinner2.start();
|
|
361
|
+
spinner2.warn(`source not found: ${import_termkit2.Color.white.encoder(resolved)}`);
|
|
362
|
+
spinner2.stop();
|
|
402
363
|
return;
|
|
403
364
|
}
|
|
404
365
|
config.sources.splice(index, 1);
|
|
405
366
|
saveConfig(config);
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
367
|
+
spinner2.start();
|
|
368
|
+
spinner2.succeed(`removed source: ${import_termkit2.Color.white.encoder(resolved)}`);
|
|
369
|
+
spinner2.stop();
|
|
409
370
|
};
|
|
410
371
|
var destAdd = async ({ type, dir }) => {
|
|
411
372
|
if (!DEST_TYPES.includes(type)) {
|
|
@@ -415,21 +376,21 @@ var destAdd = async ({ type, dir }) => {
|
|
|
415
376
|
const config = getConfig();
|
|
416
377
|
config.dest[type] = resolved;
|
|
417
378
|
saveConfig(config);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
379
|
+
spinner2.start();
|
|
380
|
+
spinner2.succeed(`set ${type} destination: ${import_termkit2.Color.green.encoder(resolved)}`);
|
|
381
|
+
spinner2.stop();
|
|
421
382
|
};
|
|
422
383
|
var destRemove = async ({ type }) => {
|
|
423
384
|
const config = getConfig();
|
|
424
385
|
if (!type) {
|
|
425
386
|
const configured = DEST_TYPES.filter((t) => config.dest[t]);
|
|
426
387
|
if (configured.length === 0) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
388
|
+
spinner2.start();
|
|
389
|
+
spinner2.warn("no destinations configured");
|
|
390
|
+
spinner2.stop();
|
|
430
391
|
return;
|
|
431
392
|
}
|
|
432
|
-
const select = new
|
|
393
|
+
const select = new import_termkit2.Select();
|
|
433
394
|
const picked = await select.ask("Which destination do you want to remove?", configured.map((t) => ({ label: `${t.padEnd(6)} ${config.dest[t]}`, value: t })));
|
|
434
395
|
if (!picked) return;
|
|
435
396
|
type = picked.value;
|
|
@@ -438,33 +399,33 @@ var destRemove = async ({ type }) => {
|
|
|
438
399
|
throw new Error(`unknown type '${type}', expected: ${DEST_TYPES.join(", ")}`);
|
|
439
400
|
}
|
|
440
401
|
if (!config.dest[type]) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
402
|
+
spinner2.start();
|
|
403
|
+
spinner2.warn(`no ${type} destination configured`);
|
|
404
|
+
spinner2.stop();
|
|
444
405
|
return;
|
|
445
406
|
}
|
|
446
407
|
delete config.dest[type];
|
|
447
408
|
saveConfig(config);
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
409
|
+
spinner2.start();
|
|
410
|
+
spinner2.succeed(`removed ${type} destination`);
|
|
411
|
+
spinner2.stop();
|
|
451
412
|
};
|
|
452
413
|
var configSet = async ({ key, subkey, value }) => {
|
|
453
414
|
const config = getConfig();
|
|
454
415
|
if (key === "language") {
|
|
455
416
|
config.language = subkey;
|
|
456
417
|
saveConfig(config);
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
418
|
+
spinner2.start();
|
|
419
|
+
spinner2.succeed(`set subtitle language: ${import_termkit2.Color.green.encoder(subkey)}`);
|
|
420
|
+
spinner2.stop();
|
|
460
421
|
return;
|
|
461
422
|
}
|
|
462
423
|
if (key === "tmdb-key") {
|
|
463
424
|
config.tmdbApiKey = subkey;
|
|
464
425
|
saveConfig(config);
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
426
|
+
spinner2.start();
|
|
427
|
+
spinner2.succeed(`set TMDb API key`);
|
|
428
|
+
spinner2.stop();
|
|
468
429
|
return;
|
|
469
430
|
}
|
|
470
431
|
if (key === "format") {
|
|
@@ -484,9 +445,9 @@ var configSet = async ({ key, subkey, value }) => {
|
|
|
484
445
|
throw new Error(`unknown format key '${subkey}', expected: movie, episode, season`);
|
|
485
446
|
}
|
|
486
447
|
saveConfig(config);
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
448
|
+
spinner2.start();
|
|
449
|
+
spinner2.succeed(`set ${subkey} format: ${import_termkit2.Color.green.encoder(value ?? subkey)}`);
|
|
450
|
+
spinner2.stop();
|
|
490
451
|
return;
|
|
491
452
|
}
|
|
492
453
|
throw new Error(`unknown key '${key}', expected: language, tmdb-key, format`);
|
|
@@ -497,7 +458,7 @@ var configShow = async () => {
|
|
|
497
458
|
if (config.sources.length === 0) {
|
|
498
459
|
console.log(" (none)");
|
|
499
460
|
} else {
|
|
500
|
-
for (const s of config.sources) console.log(` ${
|
|
461
|
+
for (const s of config.sources) console.log(` ${import_termkit2.Color.white.encoder(s)}`);
|
|
501
462
|
}
|
|
502
463
|
console.log("\nDestinations:");
|
|
503
464
|
const entries = DEST_TYPES.map((t) => ({ type: t, path: config.dest[t] })).filter((e) => e.path);
|
|
@@ -505,20 +466,20 @@ var configShow = async () => {
|
|
|
505
466
|
console.log(" (none)");
|
|
506
467
|
} else {
|
|
507
468
|
for (const { type, path } of entries) {
|
|
508
|
-
console.log(` ${type.padEnd(6)} ${
|
|
469
|
+
console.log(` ${type.padEnd(6)} ${import_termkit2.Color.green.encoder(path)}`);
|
|
509
470
|
}
|
|
510
471
|
}
|
|
511
472
|
console.log(`
|
|
512
|
-
Subtitle language: ${
|
|
513
|
-
console.log(`TMDb API key: ${config.tmdbApiKey ?
|
|
514
|
-
console.log(`Movie format: ${
|
|
515
|
-
console.log(`Episode format: ${
|
|
516
|
-
console.log(`Season folder: ${
|
|
473
|
+
Subtitle language: ${import_termkit2.Color.green.encoder(config.language ?? "eng (default)")}`);
|
|
474
|
+
console.log(`TMDb API key: ${config.tmdbApiKey ? import_termkit2.Color.green.encoder("configured") : import_termkit2.Color.red.encoder("not set")}`);
|
|
475
|
+
console.log(`Movie format: ${import_termkit2.Color.green.encoder(config.format?.movie ?? DEFAULT_MOVIE_FORMAT + " (default)")}`);
|
|
476
|
+
console.log(`Episode format: ${import_termkit2.Color.green.encoder(config.format?.episode ?? DEFAULT_EPISODE_FORMAT + " (default)")}`);
|
|
477
|
+
console.log(`Season folder: ${import_termkit2.Color.green.encoder(config.format?.season ?? DEFAULT_SEASON_FORMAT + " (default)")}`);
|
|
517
478
|
console.log("\nIgnored files:");
|
|
518
479
|
if (!config.ignore || config.ignore.length === 0) {
|
|
519
480
|
console.log(" (none)");
|
|
520
481
|
} else {
|
|
521
|
-
for (const name of config.ignore) console.log(` ${
|
|
482
|
+
for (const name of config.ignore) console.log(` ${import_termkit2.Color.white.encoder(name)}`);
|
|
522
483
|
}
|
|
523
484
|
console.log();
|
|
524
485
|
};
|
|
@@ -526,12 +487,13 @@ Subtitle language: ${import_termkit3.Color.green.encoder(config.language ?? "eng
|
|
|
526
487
|
// src/actions/differences.ts
|
|
527
488
|
var import_fs4 = require("fs");
|
|
528
489
|
var import_path4 = require("path");
|
|
529
|
-
var
|
|
490
|
+
var import_termkit3 = require("termkit");
|
|
491
|
+
var spinner3 = new import_termkit3.Spinner();
|
|
530
492
|
var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
|
|
531
493
|
let dir1 = rawDir1;
|
|
532
494
|
let dir2 = rawDir2;
|
|
533
|
-
|
|
534
|
-
|
|
495
|
+
spinner3.update(`checking differences between ${import_termkit3.Color.white.encoder(dir1)} and ${import_termkit3.Color.white.encoder(dir2)}`);
|
|
496
|
+
spinner3.start();
|
|
535
497
|
dir1 = (0, import_path4.resolve)(dir1);
|
|
536
498
|
dir2 = (0, import_path4.resolve)(dir2);
|
|
537
499
|
if (!(0, import_fs4.existsSync)(dir1)) throw new Error(`dir1 ${dir1} does not exist`);
|
|
@@ -566,18 +528,18 @@ var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
|
|
|
566
528
|
removed.push(l);
|
|
567
529
|
}
|
|
568
530
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
for (const i of added) console.log(`${
|
|
574
|
-
for (const i of removed) console.log(`${
|
|
531
|
+
spinner3.succeed(`checked differences between ${import_termkit3.Color.white.encoder(dir1)} and ${import_termkit3.Color.white.encoder(dir2)}`);
|
|
532
|
+
spinner3.succeed(`found ${added.length} added files`);
|
|
533
|
+
spinner3.succeed(`found ${removed.length} removed files`);
|
|
534
|
+
spinner3.stop();
|
|
535
|
+
for (const i of added) console.log(`${import_termkit3.Color.green.encoder("added")} ${i}`);
|
|
536
|
+
for (const i of removed) console.log(`${import_termkit3.Color.red.encoder("removed")} ${i}`);
|
|
575
537
|
};
|
|
576
538
|
var differences_default = differences;
|
|
577
539
|
|
|
578
540
|
// src/actions/history.ts
|
|
579
541
|
var import_path5 = require("path");
|
|
580
|
-
var
|
|
542
|
+
var import_termkit4 = require("termkit");
|
|
581
543
|
var history = async ({ limit, imports }) => {
|
|
582
544
|
if (imports) {
|
|
583
545
|
const sessions = getImportHistory(limit ?? 10);
|
|
@@ -589,11 +551,11 @@ var history = async ({ limit, imports }) => {
|
|
|
589
551
|
const date = new Date(session.sessionId);
|
|
590
552
|
const label = isNaN(date.getTime()) ? session.sessionId : date.toLocaleString();
|
|
591
553
|
console.log(`
|
|
592
|
-
${
|
|
554
|
+
${import_termkit4.Color.yellow.encoder(label)} (${session.records.length} item${session.records.length !== 1 ? "s" : ""})`);
|
|
593
555
|
for (const r of session.records) {
|
|
594
556
|
const src = (0, import_path5.basename)(r.sourcePath);
|
|
595
|
-
const dest =
|
|
596
|
-
const mode = r.mode !== "move" ? ` ${
|
|
557
|
+
const dest = import_termkit4.Color.green.encoder(r.destinationPath);
|
|
558
|
+
const mode = r.mode !== "move" ? ` ${import_termkit4.Color.white.encoder(`[${r.mode}]`)}` : "";
|
|
597
559
|
console.log(` ${src} \u2192 ${dest}${mode}`);
|
|
598
560
|
}
|
|
599
561
|
}
|
|
@@ -608,11 +570,11 @@ ${import_termkit5.Color.yellow.encoder(label)} (${session.records.length} item$
|
|
|
608
570
|
const label = isNaN(date.getTime()) ? session.sessionId : date.toLocaleString();
|
|
609
571
|
const folders = session.records.filter((r) => (0, import_path5.extname)(r.newPath) === "");
|
|
610
572
|
console.log(`
|
|
611
|
-
${
|
|
573
|
+
${import_termkit4.Color.yellow.encoder(label)} (${folders.length} item${folders.length !== 1 ? "s" : ""})`);
|
|
612
574
|
for (const r of folders) {
|
|
613
575
|
const oldName = (0, import_path5.basename)(r.oldPath);
|
|
614
576
|
const newName = (0, import_path5.basename)(r.newPath);
|
|
615
|
-
console.log(` ${
|
|
577
|
+
console.log(` ${import_termkit4.Color.white.encoder(oldName)} \u2192 ${import_termkit4.Color.green.encoder(newName)}`);
|
|
616
578
|
}
|
|
617
579
|
}
|
|
618
580
|
}
|
|
@@ -623,7 +585,7 @@ var history_default = history;
|
|
|
623
585
|
// src/actions/list.ts
|
|
624
586
|
var import_fs6 = require("fs");
|
|
625
587
|
var import_path7 = require("path");
|
|
626
|
-
var
|
|
588
|
+
var import_termkit5 = require("termkit");
|
|
627
589
|
|
|
628
590
|
// src/helpers/dirSize.ts
|
|
629
591
|
var import_fs5 = require("fs");
|
|
@@ -763,7 +725,7 @@ var list = async ({ type, missingSubs, codec: codecFilter, resolution: resFilter
|
|
|
763
725
|
const destRoot = config.dest[t];
|
|
764
726
|
if (!(0, import_fs6.existsSync)(destRoot)) {
|
|
765
727
|
console.log(`
|
|
766
|
-
${t.toUpperCase()} ${
|
|
728
|
+
${t.toUpperCase()} ${import_termkit5.Color.white.encoder(destRoot)} (not found)`);
|
|
767
729
|
continue;
|
|
768
730
|
}
|
|
769
731
|
const folders = (0, import_fs6.readdirSync)(destRoot).filter((f) => {
|
|
@@ -794,8 +756,8 @@ ${t.toUpperCase()} ${import_termkit6.Color.white.encoder(destRoot)} (not found
|
|
|
794
756
|
return yearDiff !== 0 ? yearDiff : a.title.localeCompare(b.title);
|
|
795
757
|
});
|
|
796
758
|
console.log(`
|
|
797
|
-
${
|
|
798
|
-
new
|
|
759
|
+
${import_termkit5.Color.yellow.encoder(t.toUpperCase())} ${import_termkit5.Color.white.encoder(destRoot)}`);
|
|
760
|
+
new import_termkit5.Table(
|
|
799
761
|
filtered.map((e) => ({
|
|
800
762
|
title: e.title,
|
|
801
763
|
year: e.year,
|
|
@@ -812,7 +774,7 @@ ${import_termkit6.Color.yellow.encoder(t.toUpperCase())} ${import_termkit6.Colo
|
|
|
812
774
|
{ key: "resolution", title: "Res", value: (v) => v ?? "\u2014" },
|
|
813
775
|
{ key: "codec", title: "Codec", value: (v) => v ?? "\u2014" },
|
|
814
776
|
{ key: "size", title: "Size" },
|
|
815
|
-
{ key: "sub", title: "Sub", value: (v) => v ?
|
|
777
|
+
{ key: "sub", title: "Sub", value: (v) => v ? import_termkit5.Color.green.encoder("\u2713") : import_termkit5.Color.red.encoder("\u2717") }
|
|
816
778
|
]
|
|
817
779
|
}
|
|
818
780
|
).print();
|
|
@@ -826,13 +788,14 @@ var list_default = list;
|
|
|
826
788
|
var import_child_process = require("child_process");
|
|
827
789
|
var import_fs7 = require("fs");
|
|
828
790
|
var import_path8 = require("path");
|
|
829
|
-
var
|
|
791
|
+
var import_termkit6 = require("termkit");
|
|
830
792
|
|
|
831
793
|
// src/refs/verbose.ts
|
|
832
794
|
var _verbose = false;
|
|
833
795
|
var isVerbose = () => _verbose;
|
|
834
796
|
|
|
835
797
|
// src/actions/probe.ts
|
|
798
|
+
var spinner4 = new import_termkit6.Spinner();
|
|
836
799
|
var DEST_TYPES3 = ["movie", "tv", "ps3"];
|
|
837
800
|
var CODEC_MAP2 = {
|
|
838
801
|
hevc: "x265",
|
|
@@ -895,10 +858,10 @@ var walkVideoFiles = (dir, depth = 0, maxDepth = 3) => {
|
|
|
895
858
|
return results;
|
|
896
859
|
};
|
|
897
860
|
var probe = async ({ type, force }) => {
|
|
898
|
-
|
|
861
|
+
spinner4.start();
|
|
899
862
|
if (!isFfprobeAvailable()) {
|
|
900
|
-
|
|
901
|
-
|
|
863
|
+
spinner4.fail("ffprobe not found \u2014 install ffmpeg to use this command");
|
|
864
|
+
spinner4.stop();
|
|
902
865
|
return;
|
|
903
866
|
}
|
|
904
867
|
const config = getConfig();
|
|
@@ -908,30 +871,30 @@ var probe = async ({ type, force }) => {
|
|
|
908
871
|
for (const t of types) {
|
|
909
872
|
const destRoot = config.dest[t];
|
|
910
873
|
if (!(0, import_fs7.existsSync)(destRoot)) continue;
|
|
911
|
-
|
|
874
|
+
spinner4.update(`scanning ${import_termkit6.Color.white.encoder(destRoot)}`);
|
|
912
875
|
const files = walkVideoFiles(destRoot);
|
|
913
876
|
for (const filePath of files) {
|
|
914
877
|
if (!force && getMediaInfo(filePath)) {
|
|
915
|
-
if (isVerbose())
|
|
878
|
+
if (isVerbose()) spinner4.info(`already probed: ${filePath}`);
|
|
916
879
|
skipped++;
|
|
917
880
|
continue;
|
|
918
881
|
}
|
|
919
|
-
|
|
882
|
+
spinner4.update(`probing ${import_termkit6.Color.white.encoder(filePath)}`);
|
|
920
883
|
const result = runFfprobe(filePath);
|
|
921
884
|
if (!result) {
|
|
922
|
-
if (isVerbose())
|
|
885
|
+
if (isVerbose()) spinner4.warn(`ffprobe failed: ${filePath}`);
|
|
923
886
|
failed++;
|
|
924
887
|
continue;
|
|
925
888
|
}
|
|
926
889
|
upsertMediaInfo(filePath, result.codec, result.resolution, result.width, result.height, result.duration);
|
|
927
|
-
if (isVerbose())
|
|
890
|
+
if (isVerbose()) spinner4.succeed(`${result.resolution ?? "?"} ${result.codec ?? "?"} ${filePath}`);
|
|
928
891
|
probed++;
|
|
929
892
|
}
|
|
930
893
|
}
|
|
931
|
-
|
|
932
|
-
if (skipped)
|
|
933
|
-
if (failed)
|
|
934
|
-
|
|
894
|
+
spinner4.succeed(`probed ${probed} files`);
|
|
895
|
+
if (skipped) spinner4.info(`skipped ${skipped} already indexed`);
|
|
896
|
+
if (failed) spinner4.warn(`failed ${failed} files`);
|
|
897
|
+
spinner4.stop();
|
|
935
898
|
};
|
|
936
899
|
var probe_default = probe;
|
|
937
900
|
|
|
@@ -939,7 +902,7 @@ var probe_default = probe;
|
|
|
939
902
|
var import_fs8 = require("fs");
|
|
940
903
|
var import_path9 = require("path");
|
|
941
904
|
var import_rimraf = require("rimraf");
|
|
942
|
-
var
|
|
905
|
+
var import_termkit7 = require("termkit");
|
|
943
906
|
|
|
944
907
|
// src/helpers/findSubtitle.ts
|
|
945
908
|
var LANGUAGE_ALIASES = {
|
|
@@ -994,21 +957,22 @@ var titleCase_default = (s) => {
|
|
|
994
957
|
};
|
|
995
958
|
|
|
996
959
|
// src/actions/rename.ts
|
|
960
|
+
var spinner5 = new import_termkit7.Spinner();
|
|
997
961
|
var rename = async ({ dir: inputDir, type }) => {
|
|
998
962
|
const dir = (0, import_path9.resolve)(inputDir);
|
|
999
963
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
1000
964
|
const config = getConfig();
|
|
1001
965
|
const language = config.language ?? "eng";
|
|
1002
966
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1003
|
-
|
|
1004
|
-
|
|
967
|
+
spinner5.update(`renaming in ${import_termkit7.Color.white.encoder(dir)}`);
|
|
968
|
+
spinner5.start();
|
|
1005
969
|
if (!(0, import_fs8.existsSync)(dir)) throw new Error(`dir ${dir} does not exist`);
|
|
1006
970
|
const list2 = (0, import_fs8.readdirSync)(dir);
|
|
1007
971
|
let renamed = 0, removed = 0, skipped = 0;
|
|
1008
972
|
for (const [index, entry] of list2.entries()) {
|
|
1009
|
-
|
|
973
|
+
spinner5.update(`renaming in ${import_termkit7.Color.white.encoder(dir)} ${index + 1}/${list2.length}`);
|
|
1010
974
|
if (!(0, import_fs8.lstatSync)((0, import_path9.resolve)(dir, entry)).isDirectory()) {
|
|
1011
|
-
if (isVerbose())
|
|
975
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
1012
976
|
skipped++;
|
|
1013
977
|
continue;
|
|
1014
978
|
}
|
|
@@ -1018,7 +982,7 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
1018
982
|
const nameMatch = entry.match(/(?<=\[).+?(?=\])/);
|
|
1019
983
|
const id = entry.split("-")[0];
|
|
1020
984
|
if (!nameMatch || !id) {
|
|
1021
|
-
if (isVerbose())
|
|
985
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
1022
986
|
skipped++;
|
|
1023
987
|
continue;
|
|
1024
988
|
}
|
|
@@ -1026,19 +990,19 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
1026
990
|
const ps3New = (0, import_path9.resolve)(dir, `${nameMatch[0]} [${id}]`);
|
|
1027
991
|
(0, import_fs8.renameSync)(ps3Old, ps3New);
|
|
1028
992
|
recordRename(sessionId, ps3Old, ps3New);
|
|
1029
|
-
|
|
993
|
+
spinner5.succeed(`${nameMatch[0]} [${id}]`);
|
|
1030
994
|
renamed++;
|
|
1031
995
|
continue;
|
|
1032
996
|
}
|
|
1033
997
|
const yearMatch = entry.match(/\([^\d]*(\d+)[^\d]*\)/);
|
|
1034
998
|
if (!yearMatch) {
|
|
1035
|
-
if (isVerbose())
|
|
999
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
1036
1000
|
skipped++;
|
|
1037
1001
|
continue;
|
|
1038
1002
|
}
|
|
1039
1003
|
const year = yearMatch[0];
|
|
1040
1004
|
if (year.length !== 6) {
|
|
1041
|
-
if (isVerbose())
|
|
1005
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
1042
1006
|
skipped++;
|
|
1043
1007
|
continue;
|
|
1044
1008
|
}
|
|
@@ -1049,20 +1013,20 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
1049
1013
|
return videoExtensions_default.includes(ext2) && title.split(" ").reduce((a, w) => f.toLowerCase().includes(w.toLowerCase()) ? a : false, true);
|
|
1050
1014
|
});
|
|
1051
1015
|
if (!video) {
|
|
1052
|
-
if (isVerbose())
|
|
1016
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
1053
1017
|
skipped++;
|
|
1054
1018
|
continue;
|
|
1055
1019
|
}
|
|
1056
1020
|
const ext = video.match(/([^.]+$)/)?.[0];
|
|
1057
1021
|
if (!ext) {
|
|
1058
|
-
if (isVerbose())
|
|
1022
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
1059
1023
|
skipped++;
|
|
1060
1024
|
continue;
|
|
1061
1025
|
}
|
|
1062
1026
|
const yearNum = parseInt(year.replace(/\D/g, ""));
|
|
1063
1027
|
const formatted = formatMovieName(movieFormat, title, yearNum);
|
|
1064
1028
|
if (entry === formatted && video === `${formatted}.${ext}`) {
|
|
1065
|
-
if (isVerbose())
|
|
1029
|
+
if (isVerbose()) spinner5.info(`skipped ${entry}`);
|
|
1066
1030
|
skipped++;
|
|
1067
1031
|
continue;
|
|
1068
1032
|
}
|
|
@@ -1085,25 +1049,26 @@ var rename = async ({ dir: inputDir, type }) => {
|
|
|
1085
1049
|
(0, import_fs8.renameSync)(folderOld, folderNew);
|
|
1086
1050
|
recordRename(sessionId, fileOld, fileNew);
|
|
1087
1051
|
recordRename(sessionId, folderOld, folderNew);
|
|
1088
|
-
|
|
1052
|
+
spinner5.succeed(formatted);
|
|
1089
1053
|
renamed++;
|
|
1090
1054
|
}
|
|
1091
|
-
|
|
1092
|
-
if (removed)
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1055
|
+
spinner5.succeed(`renamed ${renamed} files`);
|
|
1056
|
+
if (removed) spinner5.info(`removed ${removed} files`);
|
|
1057
|
+
spinner5.info(`skipped ${skipped} files`);
|
|
1058
|
+
spinner5.succeed(`done in ${import_termkit7.Color.green.encoder(dir)}`);
|
|
1059
|
+
spinner5.stop();
|
|
1096
1060
|
};
|
|
1097
1061
|
var rename_default = rename;
|
|
1098
1062
|
|
|
1099
1063
|
// src/actions/reset.ts
|
|
1100
1064
|
var import_fs9 = require("fs");
|
|
1101
1065
|
var import_path10 = require("path");
|
|
1102
|
-
var
|
|
1066
|
+
var import_termkit8 = require("termkit");
|
|
1067
|
+
var spinner6 = new import_termkit8.Spinner();
|
|
1103
1068
|
var reset = async ({ dir: inputDir, double }) => {
|
|
1104
1069
|
let dir = inputDir;
|
|
1105
|
-
|
|
1106
|
-
|
|
1070
|
+
spinner6.update(`resetting episodes in ${import_termkit8.Color.white.encoder(dir)}`);
|
|
1071
|
+
spinner6.start();
|
|
1107
1072
|
dir = (0, import_path10.resolve)(dir);
|
|
1108
1073
|
if (!(0, import_fs9.existsSync)(dir)) throw new Error(`dir ${dir} does not exist`);
|
|
1109
1074
|
const list2 = (0, import_fs9.readdirSync)(dir).sort();
|
|
@@ -1119,7 +1084,7 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1119
1084
|
if (!seasonNum) throw new Error(`unable to identify season number`);
|
|
1120
1085
|
const parentFolder = (0, import_path10.basename)((0, import_path10.dirname)(dir));
|
|
1121
1086
|
const showTitle = parentFolder.match(/^(.+?)\s*(?:\(\d{4}\))?$/)?.[1]?.trim() || void 0;
|
|
1122
|
-
|
|
1087
|
+
spinner6.info(`identified as season ${seasonNum}${showTitle ? ` of ${showTitle}` : ""}`);
|
|
1123
1088
|
const sublist = list2.filter((f) => {
|
|
1124
1089
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1125
1090
|
return videoExtensions_default.includes(ext) && f.split(" ").reduce((a, w) => f.toLowerCase().includes(w.toLowerCase()) ? a : false, true);
|
|
@@ -1131,7 +1096,7 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1131
1096
|
const episodeFormat = getConfig().format?.episode;
|
|
1132
1097
|
let renamed = 0, skipped = other.length;
|
|
1133
1098
|
for (const [index, i] of sublist.entries()) {
|
|
1134
|
-
|
|
1099
|
+
spinner6.update(`resetting episodes in ${import_termkit8.Color.white.encoder(dir)} ${index}/${list2.length}`);
|
|
1135
1100
|
const ext = i.match(/([^.]+$)/)?.[0];
|
|
1136
1101
|
const episode = double ? index * 2 + 1 : index + 1;
|
|
1137
1102
|
const name = `${formatEpisode(seasonNum, episode, episodeFormat, double, showTitle)}.${ext}`;
|
|
@@ -1142,17 +1107,17 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1142
1107
|
(0, import_fs9.renameSync)((0, import_path10.resolve)(dir, i), (0, import_path10.resolve)(dir, name));
|
|
1143
1108
|
renamed++;
|
|
1144
1109
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1110
|
+
spinner6.succeed(`renamed ${renamed} files`);
|
|
1111
|
+
spinner6.info(`skipped ${skipped} files`);
|
|
1112
|
+
spinner6.succeed(`done in ${import_termkit8.Color.green.encoder(dir)}`);
|
|
1113
|
+
spinner6.stop();
|
|
1149
1114
|
};
|
|
1150
1115
|
var reset_default = reset;
|
|
1151
1116
|
|
|
1152
1117
|
// src/actions/scan.ts
|
|
1153
1118
|
var import_fs11 = require("fs");
|
|
1154
1119
|
var import_path12 = require("path");
|
|
1155
|
-
var
|
|
1120
|
+
var import_termkit9 = require("termkit");
|
|
1156
1121
|
|
|
1157
1122
|
// src/helpers/detectEdition.ts
|
|
1158
1123
|
var EDITIONS = [
|
|
@@ -1313,6 +1278,7 @@ var searchTv = async (title, apiKey) => {
|
|
|
1313
1278
|
var bookExtensions_default = ["epub", "mobi", "azw3", "azw"];
|
|
1314
1279
|
|
|
1315
1280
|
// src/actions/scan.ts
|
|
1281
|
+
var spinner7 = new import_termkit9.Spinner();
|
|
1316
1282
|
var sameDev = (a, b) => {
|
|
1317
1283
|
try {
|
|
1318
1284
|
let bExisting = b;
|
|
@@ -1445,46 +1411,49 @@ var gatherEntries = (source) => {
|
|
|
1445
1411
|
}
|
|
1446
1412
|
return result;
|
|
1447
1413
|
};
|
|
1448
|
-
var
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1414
|
+
var nTitle = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1415
|
+
var showDirIndexCache = /* @__PURE__ */ new Map();
|
|
1416
|
+
var getShowDirIndex = (destRoot) => {
|
|
1417
|
+
if (showDirIndexCache.has(destRoot)) return showDirIndexCache.get(destRoot);
|
|
1418
|
+
const idx = /* @__PURE__ */ new Map();
|
|
1419
|
+
showDirIndexCache.set(destRoot, idx);
|
|
1420
|
+
if (!(0, import_fs11.existsSync)(destRoot)) return idx;
|
|
1421
|
+
let dirs;
|
|
1422
|
+
try {
|
|
1423
|
+
dirs = (0, import_fs11.readdirSync)(destRoot);
|
|
1424
|
+
} catch {
|
|
1425
|
+
return idx;
|
|
1426
|
+
}
|
|
1427
|
+
for (const dir of dirs) {
|
|
1453
1428
|
try {
|
|
1454
|
-
|
|
1429
|
+
if (!(0, import_fs11.lstatSync)((0, import_path12.resolve)(destRoot, dir)).isDirectory()) continue;
|
|
1455
1430
|
} catch {
|
|
1456
|
-
|
|
1431
|
+
continue;
|
|
1457
1432
|
}
|
|
1458
|
-
|
|
1459
|
-
};
|
|
1460
|
-
var findShowFolderByContent = (destRoot, title) => {
|
|
1461
|
-
if (!(0, import_fs11.existsSync)(destRoot)) return null;
|
|
1462
|
-
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1463
|
-
const target = normalize(title);
|
|
1464
|
-
const matchesTitle = (name) => {
|
|
1465
|
-
if (!isTvEpisodeName(name)) return false;
|
|
1466
|
-
const p = parseDownloadName(name);
|
|
1467
|
-
return !!p && normalize(p.title) === target;
|
|
1468
|
-
};
|
|
1469
|
-
for (const folder of (0, import_fs11.readdirSync)(destRoot)) {
|
|
1433
|
+
idx.set(nTitle(dir), dir);
|
|
1470
1434
|
try {
|
|
1471
|
-
const
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1435
|
+
const children = (0, import_fs11.readdirSync)((0, import_path12.resolve)(destRoot, dir));
|
|
1436
|
+
const tryFile = (name) => {
|
|
1437
|
+
if (!isTvEpisodeName(name)) return;
|
|
1438
|
+
const p = parseDownloadName(name);
|
|
1439
|
+
if (p) {
|
|
1440
|
+
const k = nTitle(p.title);
|
|
1441
|
+
if (!idx.has(k)) idx.set(k, dir);
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
children.forEach(tryFile);
|
|
1445
|
+
children.forEach((child) => {
|
|
1446
|
+
if (!isSeasonDirName(child)) return;
|
|
1477
1447
|
try {
|
|
1478
|
-
const
|
|
1479
|
-
if (
|
|
1480
|
-
if ((0, import_fs11.readdirSync)(seasonPath).some(matchesTitle)) return folder;
|
|
1448
|
+
const sp = (0, import_path12.resolve)(destRoot, dir, child);
|
|
1449
|
+
if ((0, import_fs11.lstatSync)(sp).isDirectory()) (0, import_fs11.readdirSync)(sp).forEach(tryFile);
|
|
1481
1450
|
} catch {
|
|
1482
1451
|
}
|
|
1483
|
-
}
|
|
1452
|
+
});
|
|
1484
1453
|
} catch {
|
|
1485
1454
|
}
|
|
1486
1455
|
}
|
|
1487
|
-
return
|
|
1456
|
+
return idx;
|
|
1488
1457
|
};
|
|
1489
1458
|
var findSeasonFolder = (showPath, season, specialsFolder) => {
|
|
1490
1459
|
if (!(0, import_fs11.existsSync)(showPath)) return null;
|
|
@@ -1514,13 +1483,17 @@ var classifyMovieConfidence = (entry) => {
|
|
|
1514
1483
|
return "ambiguous";
|
|
1515
1484
|
};
|
|
1516
1485
|
var typeColor = {
|
|
1517
|
-
movie: (s) =>
|
|
1518
|
-
tv: (s) =>
|
|
1519
|
-
book: (s) =>
|
|
1520
|
-
ps3: (s) =>
|
|
1521
|
-
};
|
|
1522
|
-
var typeGlyph = (t) => typeColor[t]("
|
|
1523
|
-
var
|
|
1486
|
+
movie: (s) => import_termkit9.Color.yellow.encoder(s),
|
|
1487
|
+
tv: (s) => import_termkit9.Color.blue.encoder(s),
|
|
1488
|
+
book: (s) => import_termkit9.Color.white.encoder(s),
|
|
1489
|
+
ps3: (s) => import_termkit9.Color.magenta.encoder(s)
|
|
1490
|
+
};
|
|
1491
|
+
var typeGlyph = (t) => typeColor[t]("?");
|
|
1492
|
+
var checkGlyph = (t) => typeColor[t]("\u2714");
|
|
1493
|
+
var greyGlyph = import_termkit9.Color.white.faint.encoder("\u25CF");
|
|
1494
|
+
var warnGlyph = import_termkit9.Color.yellow.encoder("\u26A0");
|
|
1495
|
+
var typeTag = (t) => isVerbose() ? import_termkit9.Color.white.faint.encoder(` (${t})`) : "";
|
|
1496
|
+
var sortByEntry = (arr) => arr.sort((a, b) => a.entry.localeCompare(b.entry, void 0, { sensitivity: "base" }));
|
|
1524
1497
|
var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactive }) => {
|
|
1525
1498
|
const config = getConfig();
|
|
1526
1499
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1528,27 +1501,28 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1528
1501
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1529
1502
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
1530
1503
|
const specialsFolder = config.specialsFolder ?? "Specials";
|
|
1504
|
+
const dryTag = dryRun ? import_termkit9.Color.white.faint.encoder(" [dry]") : "";
|
|
1531
1505
|
const lookupMovie = async (parsed) => {
|
|
1532
1506
|
let tmdbId;
|
|
1533
1507
|
let resolvedTitle = parsed.title;
|
|
1534
1508
|
let resolvedYear = parsed.year;
|
|
1535
1509
|
if (config.tmdbApiKey) {
|
|
1536
|
-
|
|
1510
|
+
spinner7.update(`TMDb: ${parsed.title}`);
|
|
1537
1511
|
const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
|
|
1538
1512
|
if (results.length === 1) {
|
|
1539
1513
|
tmdbId = results[0].id;
|
|
1540
1514
|
resolvedTitle = results[0].title;
|
|
1541
1515
|
resolvedYear = results[0].year ?? parsed.year;
|
|
1542
1516
|
} else if (results.length > 1) {
|
|
1543
|
-
|
|
1544
|
-
const select = new
|
|
1517
|
+
spinner7.stop();
|
|
1518
|
+
const select = new import_termkit9.Select();
|
|
1545
1519
|
const items = results.map((r) => ({
|
|
1546
1520
|
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
1547
1521
|
description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
|
|
1548
1522
|
...r
|
|
1549
1523
|
}));
|
|
1550
1524
|
const picked = await select.ask(`Multiple movies found for "${parsed.title}":`, items);
|
|
1551
|
-
|
|
1525
|
+
spinner7.start();
|
|
1552
1526
|
if (picked) {
|
|
1553
1527
|
tmdbId = picked.id;
|
|
1554
1528
|
resolvedTitle = picked.title;
|
|
@@ -1563,12 +1537,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1563
1537
|
const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
|
|
1564
1538
|
const destFolder = (0, import_path12.resolve)(destRoot, folderName);
|
|
1565
1539
|
if ((0, import_fs11.existsSync)(destFolder)) {
|
|
1566
|
-
|
|
1540
|
+
spinner7.log(`${typeColor.movie(folderName)}${typeTag("movie")}`, greyGlyph);
|
|
1567
1541
|
return false;
|
|
1568
1542
|
}
|
|
1569
1543
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
1570
1544
|
if (!videoFile) {
|
|
1571
|
-
|
|
1545
|
+
spinner7.log(`${entry}${typeTag("movie")}`, warnGlyph);
|
|
1572
1546
|
return false;
|
|
1573
1547
|
}
|
|
1574
1548
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
@@ -1589,7 +1563,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1589
1563
|
(0, import_fs11.linkSync)(videoSourcePath, destVideoPath);
|
|
1590
1564
|
mode = "hardlink";
|
|
1591
1565
|
} catch {
|
|
1592
|
-
|
|
1566
|
+
spinner7.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
1593
1567
|
(0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
|
|
1594
1568
|
mode = "copy";
|
|
1595
1569
|
}
|
|
@@ -1615,10 +1589,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1615
1589
|
recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
|
|
1616
1590
|
}
|
|
1617
1591
|
}
|
|
1618
|
-
|
|
1592
|
+
spinner7.log(`${typeColor.movie(folderName)}${typeTag("movie")}${dryTag}`, checkGlyph("movie"));
|
|
1619
1593
|
return true;
|
|
1620
1594
|
};
|
|
1621
|
-
|
|
1595
|
+
spinner7.start();
|
|
1622
1596
|
if (config.sources.length === 0) throw new Error("no sources configured \u2014 run: reelsort config add source <dir>");
|
|
1623
1597
|
let imported = 0, skipped = 0;
|
|
1624
1598
|
const pendingMovies = [];
|
|
@@ -1629,15 +1603,19 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1629
1603
|
const seenIgnored = /* @__PURE__ */ new Set();
|
|
1630
1604
|
for (const source of config.sources) {
|
|
1631
1605
|
if (!(0, import_fs11.existsSync)(source)) {
|
|
1632
|
-
|
|
1606
|
+
spinner7.warn(`source not found: ${import_termkit9.Color.white.encoder(source)}`);
|
|
1633
1607
|
continue;
|
|
1634
1608
|
}
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1609
|
+
spinner7.update(`scanning ${import_termkit9.Color.white.encoder(source)}`);
|
|
1610
|
+
const entries = gatherEntries(source).sort(
|
|
1611
|
+
(a, b) => a.entry.localeCompare(b.entry, void 0, { sensitivity: "base" })
|
|
1612
|
+
);
|
|
1613
|
+
for (const { entry, entryPath, isDir } of entries) {
|
|
1614
|
+
spinner7.update(`scanning: ${entry}`);
|
|
1638
1615
|
if (ignoreSet.has(entry)) {
|
|
1639
1616
|
seenIgnored.add(entry);
|
|
1640
|
-
if (isVerbose())
|
|
1617
|
+
if (isVerbose()) spinner7.log(entry, greyGlyph);
|
|
1618
|
+
skipped++;
|
|
1641
1619
|
continue;
|
|
1642
1620
|
}
|
|
1643
1621
|
const ext = entry.match(/([^.]+$)/)?.[0];
|
|
@@ -1657,7 +1635,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1657
1635
|
}
|
|
1658
1636
|
const destRoot = config.dest[detectedType];
|
|
1659
1637
|
if (!destRoot) {
|
|
1660
|
-
if (isVerbose())
|
|
1638
|
+
if (isVerbose()) spinner7.log(`${entry}${typeTag(detectedType)}`, greyGlyph);
|
|
1661
1639
|
skipped++;
|
|
1662
1640
|
continue;
|
|
1663
1641
|
}
|
|
@@ -1671,7 +1649,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1671
1649
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
1672
1650
|
const destPath = (0, import_path12.resolve)(destRoot, destName);
|
|
1673
1651
|
if ((0, import_fs11.existsSync)(destPath)) {
|
|
1674
|
-
|
|
1652
|
+
spinner7.log(`${typeColor.ps3(destName)}${typeTag("ps3")}`, greyGlyph);
|
|
1675
1653
|
skipped++;
|
|
1676
1654
|
continue;
|
|
1677
1655
|
}
|
|
@@ -1679,14 +1657,14 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1679
1657
|
moveFolder(entryPath, destPath);
|
|
1680
1658
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
1681
1659
|
}
|
|
1682
|
-
|
|
1660
|
+
spinner7.log(`${typeColor.ps3(destName)}${typeTag("ps3")}${dryTag}`, checkGlyph("ps3"));
|
|
1683
1661
|
imported++;
|
|
1684
1662
|
continue;
|
|
1685
1663
|
}
|
|
1686
1664
|
if (detectedType === "book") {
|
|
1687
1665
|
const destPath = (0, import_path12.resolve)(destRoot, entry);
|
|
1688
1666
|
if ((0, import_fs11.existsSync)(destPath)) {
|
|
1689
|
-
|
|
1667
|
+
spinner7.log(`${typeColor.book(entry)}${typeTag("book")}`, greyGlyph);
|
|
1690
1668
|
skipped++;
|
|
1691
1669
|
continue;
|
|
1692
1670
|
}
|
|
@@ -1704,7 +1682,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1704
1682
|
}
|
|
1705
1683
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
1706
1684
|
}
|
|
1707
|
-
|
|
1685
|
+
spinner7.log(`${typeColor.book(entry)}${typeTag("book")}${dryTag}`, checkGlyph("book"));
|
|
1708
1686
|
imported++;
|
|
1709
1687
|
continue;
|
|
1710
1688
|
}
|
|
@@ -1713,10 +1691,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1713
1691
|
if (videoCount === 0) {
|
|
1714
1692
|
if (containsPdf(entryPath)) {
|
|
1715
1693
|
pendingBooks.push({ entry, entryPath });
|
|
1716
|
-
} else
|
|
1717
|
-
|
|
1694
|
+
} else {
|
|
1695
|
+
if (isVerbose()) spinner7.log(entry, greyGlyph);
|
|
1696
|
+
skipped++;
|
|
1718
1697
|
}
|
|
1719
|
-
skipped++;
|
|
1720
1698
|
continue;
|
|
1721
1699
|
}
|
|
1722
1700
|
if (videoCount >= 2) {
|
|
@@ -1726,14 +1704,14 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1726
1704
|
}
|
|
1727
1705
|
const parsed = parseDownloadName(entry);
|
|
1728
1706
|
if (!parsed) {
|
|
1729
|
-
if (isVerbose())
|
|
1707
|
+
if (isVerbose()) spinner7.log(entry, greyGlyph);
|
|
1730
1708
|
skipped++;
|
|
1731
1709
|
continue;
|
|
1732
1710
|
}
|
|
1733
1711
|
if (detectedType === "movie") {
|
|
1734
1712
|
const confidence = classifyMovieConfidence(entry);
|
|
1735
1713
|
if (confidence === "skip") {
|
|
1736
|
-
if (isVerbose())
|
|
1714
|
+
if (isVerbose()) spinner7.log(entry, greyGlyph);
|
|
1737
1715
|
skipped++;
|
|
1738
1716
|
continue;
|
|
1739
1717
|
}
|
|
@@ -1747,22 +1725,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1747
1725
|
let resolvedYear = parsed.year;
|
|
1748
1726
|
if (config.tmdbApiKey) {
|
|
1749
1727
|
if (detectedType === "tv") {
|
|
1750
|
-
|
|
1728
|
+
spinner7.update(`TMDb: ${parsed.title}`);
|
|
1751
1729
|
const results = await searchTv(parsed.title, config.tmdbApiKey);
|
|
1752
1730
|
if (results.length === 1) {
|
|
1753
1731
|
tmdbId = results[0].id;
|
|
1754
1732
|
resolvedTitle = results[0].title;
|
|
1755
1733
|
resolvedYear = results[0].year ?? parsed.year;
|
|
1756
1734
|
} else if (results.length > 1) {
|
|
1757
|
-
|
|
1758
|
-
const select = new
|
|
1735
|
+
spinner7.stop();
|
|
1736
|
+
const select = new import_termkit9.Select();
|
|
1759
1737
|
const items = results.map((r) => ({
|
|
1760
1738
|
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
1761
1739
|
description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
|
|
1762
1740
|
...r
|
|
1763
1741
|
}));
|
|
1764
1742
|
const picked = await select.ask(`Multiple shows found for "${parsed.title}":`, items);
|
|
1765
|
-
|
|
1743
|
+
spinner7.start();
|
|
1766
1744
|
if (picked) {
|
|
1767
1745
|
tmdbId = picked.id;
|
|
1768
1746
|
resolvedTitle = picked.title;
|
|
@@ -1778,7 +1756,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1778
1756
|
}
|
|
1779
1757
|
if (detectedType === "tv") {
|
|
1780
1758
|
if (parsed.season === void 0) {
|
|
1781
|
-
if (isVerbose())
|
|
1759
|
+
if (isVerbose()) spinner7.log(entry, greyGlyph);
|
|
1782
1760
|
skipped++;
|
|
1783
1761
|
continue;
|
|
1784
1762
|
}
|
|
@@ -1789,7 +1767,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1789
1767
|
showPath = registeredShow.path;
|
|
1790
1768
|
showFolderName = showPath.split("/").pop() ?? registeredShow.path;
|
|
1791
1769
|
} else {
|
|
1792
|
-
const existingFolder =
|
|
1770
|
+
const existingFolder = getShowDirIndex(destRoot).get(nTitle(resolvedTitle)) ?? null;
|
|
1793
1771
|
if (existingFolder) {
|
|
1794
1772
|
showFolderName = existingFolder;
|
|
1795
1773
|
showPath = (0, import_path12.resolve)(destRoot, existingFolder);
|
|
@@ -1797,6 +1775,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1797
1775
|
showFolderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear);
|
|
1798
1776
|
showPath = (0, import_path12.resolve)(destRoot, showFolderName);
|
|
1799
1777
|
if (!dryRun) upsertShow(showPath, tmdbId ?? null, resolvedTitle);
|
|
1778
|
+
getShowDirIndex(destRoot).set(nTitle(resolvedTitle), showFolderName);
|
|
1800
1779
|
} else {
|
|
1801
1780
|
pendingTv.push({ entry, entryPath, isDir, parsed, resolvedTitle, destRoot });
|
|
1802
1781
|
continue;
|
|
@@ -1806,12 +1785,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1806
1785
|
const seasonPath = (0, import_path12.resolve)(showPath, seasonFolderName);
|
|
1807
1786
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
1808
1787
|
if (!videoFile) {
|
|
1809
|
-
|
|
1788
|
+
spinner7.log(`${entry}${typeTag("tv")}`, warnGlyph);
|
|
1810
1789
|
skipped++;
|
|
1811
1790
|
continue;
|
|
1812
1791
|
}
|
|
1813
1792
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
1814
|
-
if (tmdbId && config.tmdbApiKey)
|
|
1793
|
+
if (tmdbId && config.tmdbApiKey) spinner7.update(`TMDb: episode name for ${resolvedTitle}`);
|
|
1815
1794
|
const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
1816
1795
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
|
|
1817
1796
|
const destVideoName = `${episodeName}.${videoExt}`;
|
|
@@ -1821,17 +1800,17 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1821
1800
|
const isRepack = /\brepack\d*\b|\bproper\b/i.test(entry);
|
|
1822
1801
|
let shouldReplace = force || isRepack;
|
|
1823
1802
|
if (!shouldReplace && interactive) {
|
|
1824
|
-
|
|
1825
|
-
const select = new
|
|
1803
|
+
spinner7.stop();
|
|
1804
|
+
const select = new import_termkit9.Select();
|
|
1826
1805
|
const picked = await select.ask(`Already exists \u2014 replace?`, [
|
|
1827
1806
|
{ label: `${showFolderName} / ${seasonFolderName} / ${episodeName}`, value: "replace" },
|
|
1828
1807
|
{ label: "Skip", value: "skip" }
|
|
1829
1808
|
]);
|
|
1830
|
-
|
|
1809
|
+
spinner7.start();
|
|
1831
1810
|
shouldReplace = picked?.value === "replace";
|
|
1832
1811
|
}
|
|
1833
1812
|
if (!shouldReplace) {
|
|
1834
|
-
|
|
1813
|
+
spinner7.log(`${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}`, greyGlyph);
|
|
1835
1814
|
skipped++;
|
|
1836
1815
|
continue;
|
|
1837
1816
|
}
|
|
@@ -1855,7 +1834,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1855
1834
|
(0, import_fs11.linkSync)(videoSourcePath, destVideoPath);
|
|
1856
1835
|
mode = "hardlink";
|
|
1857
1836
|
} catch {
|
|
1858
|
-
|
|
1837
|
+
spinner7.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
1859
1838
|
(0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
|
|
1860
1839
|
mode = "copy";
|
|
1861
1840
|
}
|
|
@@ -1872,7 +1851,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1872
1851
|
}
|
|
1873
1852
|
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
|
|
1874
1853
|
}
|
|
1875
|
-
|
|
1854
|
+
spinner7.log(`${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}${dryTag}`, checkGlyph("tv"));
|
|
1876
1855
|
imported++;
|
|
1877
1856
|
continue;
|
|
1878
1857
|
}
|
|
@@ -1883,25 +1862,27 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1883
1862
|
}
|
|
1884
1863
|
}
|
|
1885
1864
|
}
|
|
1865
|
+
let uncertainMovies = 0;
|
|
1886
1866
|
if (pendingMovies.length > 0) {
|
|
1887
|
-
spinner_default.warn(`${pendingMovies.length} uncertain movie match${pendingMovies.length > 1 ? "es" : ""} skipped \u2014 use -i to review or -f to import all`);
|
|
1888
|
-
for (const p of pendingMovies) spinner_default.info(` ${typeGlyph("movie")} ${p.entry.replace(/\/$/, "")}${typeTag("movie")}`);
|
|
1889
1867
|
let toProcess = [];
|
|
1890
1868
|
if (interactive) {
|
|
1891
|
-
|
|
1892
|
-
const ms = new
|
|
1869
|
+
spinner7.stop();
|
|
1870
|
+
const ms = new import_termkit9.MultiSelect({ allowSkip: true, search: true, maxHeight: 20 });
|
|
1893
1871
|
const items = pendingMovies.map((p) => ({
|
|
1894
1872
|
label: p.entry.replace(/\/$/, ""),
|
|
1895
1873
|
description: p.parsed.year ? `parsed: ${p.parsed.title} \xB7 ${p.parsed.year}` : `parsed: ${p.parsed.title}`,
|
|
1896
1874
|
...p
|
|
1897
1875
|
}));
|
|
1898
1876
|
toProcess = await ms.ask("Select entries to import as movies:", items) ?? [];
|
|
1899
|
-
|
|
1900
|
-
skipped += pendingMovies.length - toProcess.length;
|
|
1877
|
+
spinner7.start();
|
|
1901
1878
|
} else if (force) {
|
|
1902
1879
|
toProcess = pendingMovies;
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1880
|
+
}
|
|
1881
|
+
const toSkip = pendingMovies.filter((p) => !toProcess.includes(p));
|
|
1882
|
+
uncertainMovies = toSkip.length;
|
|
1883
|
+
sortByEntry(toSkip);
|
|
1884
|
+
for (const p of toSkip) {
|
|
1885
|
+
spinner7.log(`${typeColor.movie(p.entry.replace(/\/$/, ""))}${typeTag("movie")}`, typeGlyph("movie"));
|
|
1905
1886
|
}
|
|
1906
1887
|
for (const p of toProcess) {
|
|
1907
1888
|
const { tmdbId, resolvedTitle, resolvedYear } = await lookupMovie(p.parsed);
|
|
@@ -1913,18 +1894,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1913
1894
|
}
|
|
1914
1895
|
}
|
|
1915
1896
|
if (pendingTv.length > 0) {
|
|
1916
|
-
|
|
1917
|
-
for (const p of pendingTv)
|
|
1918
|
-
|
|
1897
|
+
pendingTv.sort((a, b) => a.resolvedTitle.localeCompare(b.resolvedTitle, void 0, { sensitivity: "base" }));
|
|
1898
|
+
for (const p of pendingTv) {
|
|
1899
|
+
spinner7.log(`${typeColor.tv(p.resolvedTitle)} \u2014 ${p.entry.replace(/\/$/, "")}${typeTag("tv")}`, typeGlyph("tv"));
|
|
1900
|
+
}
|
|
1919
1901
|
}
|
|
1920
1902
|
if (pendingBooks.length > 0) {
|
|
1921
|
-
|
|
1922
|
-
for (const p of pendingBooks)
|
|
1903
|
+
sortByEntry(pendingBooks);
|
|
1904
|
+
for (const p of pendingBooks) {
|
|
1905
|
+
spinner7.log(`${typeColor.book(p.entry)}${typeTag("book")}`, typeGlyph("book"));
|
|
1906
|
+
}
|
|
1923
1907
|
}
|
|
1924
1908
|
if (pendingAnime.length > 0) {
|
|
1925
|
-
|
|
1926
|
-
for (const p of pendingAnime)
|
|
1927
|
-
|
|
1909
|
+
sortByEntry(pendingAnime);
|
|
1910
|
+
for (const p of pendingAnime) {
|
|
1911
|
+
spinner7.log(`${typeColor.tv(p.entry)} (${p.videoCount} video${p.videoCount > 1 ? "s" : ""})${typeTag("tv")}`, typeGlyph("tv"));
|
|
1912
|
+
}
|
|
1928
1913
|
}
|
|
1929
1914
|
if (ignoreSet.size > 0) {
|
|
1930
1915
|
const stale = [...ignoreSet].filter((name) => !seenIgnored.has(name));
|
|
@@ -1932,25 +1917,31 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1932
1917
|
const updated = config.ignore.filter((name) => !stale.includes(name));
|
|
1933
1918
|
config.ignore = updated;
|
|
1934
1919
|
saveConfig(config);
|
|
1935
|
-
for (const name of stale)
|
|
1920
|
+
for (const name of stale) spinner7.info(`removed from ignore list (not found): ${import_termkit9.Color.white.encoder(name)}`);
|
|
1936
1921
|
}
|
|
1937
1922
|
}
|
|
1938
|
-
|
|
1939
|
-
if (skipped)
|
|
1940
|
-
|
|
1923
|
+
const summaryParts = [`${dryRun ? "would import" : "imported"} ${imported}`];
|
|
1924
|
+
if (skipped) summaryParts.push(`skipped ${skipped}`);
|
|
1925
|
+
if (uncertainMovies) summaryParts.push(`uncertain movie ${uncertainMovies}`);
|
|
1926
|
+
if (pendingTv.length) summaryParts.push(`uncertain tv ${pendingTv.length}`);
|
|
1927
|
+
if (pendingBooks.length) summaryParts.push(`uncertain book ${pendingBooks.length}`);
|
|
1928
|
+
if (pendingAnime.length) summaryParts.push(`uncertain anime ${pendingAnime.length}`);
|
|
1929
|
+
spinner7.succeed(summaryParts.join(", "));
|
|
1930
|
+
spinner7.stop();
|
|
1941
1931
|
};
|
|
1942
1932
|
var scan_default = scan;
|
|
1943
1933
|
|
|
1944
1934
|
// src/actions/undo.ts
|
|
1945
1935
|
var import_fs12 = require("fs");
|
|
1946
|
-
var
|
|
1936
|
+
var import_termkit10 = require("termkit");
|
|
1937
|
+
var spinner8 = new import_termkit10.Spinner();
|
|
1947
1938
|
var undo = async () => {
|
|
1948
|
-
|
|
1939
|
+
spinner8.start();
|
|
1949
1940
|
const renameRecords = getLastSession();
|
|
1950
1941
|
const importRecords = getLastImportSession();
|
|
1951
1942
|
if (renameRecords.length === 0 && importRecords.length === 0) {
|
|
1952
|
-
|
|
1953
|
-
|
|
1943
|
+
spinner8.info("nothing to undo");
|
|
1944
|
+
spinner8.stop();
|
|
1954
1945
|
return;
|
|
1955
1946
|
}
|
|
1956
1947
|
const useImports = importRecords.length > 0 && (renameRecords.length === 0 || importRecords[0].sessionId > renameRecords[0].sessionId);
|
|
@@ -1958,40 +1949,40 @@ var undo = async () => {
|
|
|
1958
1949
|
let undone2 = 0;
|
|
1959
1950
|
for (const record of renameRecords) {
|
|
1960
1951
|
(0, import_fs12.renameSync)(record.newPath, record.oldPath);
|
|
1961
|
-
|
|
1952
|
+
spinner8.succeed(`${import_termkit10.Color.green.encoder(record.newPath)} \u2192 ${import_termkit10.Color.white.encoder(record.oldPath)}`);
|
|
1962
1953
|
undone2++;
|
|
1963
1954
|
}
|
|
1964
1955
|
deleteSession(renameRecords[0].sessionId);
|
|
1965
|
-
|
|
1966
|
-
|
|
1956
|
+
spinner8.succeed(`undid ${undone2} renames`);
|
|
1957
|
+
spinner8.stop();
|
|
1967
1958
|
return;
|
|
1968
1959
|
}
|
|
1969
1960
|
let undone = 0;
|
|
1970
1961
|
let skipped = 0;
|
|
1971
1962
|
for (const record of importRecords) {
|
|
1972
1963
|
if (record.mode !== "move") {
|
|
1973
|
-
|
|
1964
|
+
spinner8.info(`skipped ${record.destinationPath} (${record.mode} \u2014 source file unchanged)`);
|
|
1974
1965
|
skipped++;
|
|
1975
1966
|
continue;
|
|
1976
1967
|
}
|
|
1977
1968
|
if (record.type === "tv") {
|
|
1978
|
-
|
|
1969
|
+
spinner8.info(`skipped TV import \u2014 season folder cannot be cleanly reversed: ${record.destinationPath}`);
|
|
1979
1970
|
skipped++;
|
|
1980
1971
|
continue;
|
|
1981
1972
|
}
|
|
1982
1973
|
if (!(0, import_fs12.existsSync)(record.destinationPath)) {
|
|
1983
|
-
|
|
1974
|
+
spinner8.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
|
|
1984
1975
|
skipped++;
|
|
1985
1976
|
continue;
|
|
1986
1977
|
}
|
|
1987
1978
|
(0, import_fs12.renameSync)(record.destinationPath, record.sourcePath);
|
|
1988
|
-
|
|
1979
|
+
spinner8.succeed(`${import_termkit10.Color.green.encoder(record.destinationPath)} \u2192 ${import_termkit10.Color.white.encoder(record.sourcePath)}`);
|
|
1989
1980
|
undone++;
|
|
1990
1981
|
}
|
|
1991
1982
|
deleteImportSession(importRecords[0].sessionId);
|
|
1992
|
-
if (undone > 0)
|
|
1993
|
-
if (skipped > 0)
|
|
1994
|
-
|
|
1983
|
+
if (undone > 0) spinner8.succeed(`undid ${undone} import${undone !== 1 ? "s" : ""}`);
|
|
1984
|
+
if (skipped > 0) spinner8.info(`skipped ${skipped} item${skipped !== 1 ? "s" : ""} (TV or non-move mode)`);
|
|
1985
|
+
spinner8.stop();
|
|
1995
1986
|
};
|
|
1996
1987
|
var undo_default = undo;
|
|
1997
1988
|
|
|
@@ -1999,7 +1990,8 @@ var undo_default = undo;
|
|
|
1999
1990
|
var import_chokidar = __toESM(require("chokidar"));
|
|
2000
1991
|
var import_fs13 = require("fs");
|
|
2001
1992
|
var import_path13 = require("path");
|
|
2002
|
-
var
|
|
1993
|
+
var import_termkit11 = require("termkit");
|
|
1994
|
+
var spinner9 = new import_termkit11.Spinner();
|
|
2003
1995
|
var sameDev2 = (a, b) => {
|
|
2004
1996
|
try {
|
|
2005
1997
|
let bExisting = b;
|
|
@@ -2139,7 +2131,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2139
2131
|
}
|
|
2140
2132
|
const destRoot = config.dest[detectedType];
|
|
2141
2133
|
if (!destRoot) {
|
|
2142
|
-
if (isVerbose())
|
|
2134
|
+
if (isVerbose()) spinner9.info(`no ${detectedType} destination configured, skipped: ${entry}`);
|
|
2143
2135
|
return;
|
|
2144
2136
|
}
|
|
2145
2137
|
if (detectedType === "ps3") {
|
|
@@ -2149,18 +2141,18 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2149
2141
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
2150
2142
|
const destPath = (0, import_path13.resolve)(destRoot, destName);
|
|
2151
2143
|
if ((0, import_fs13.existsSync)(destPath)) {
|
|
2152
|
-
|
|
2144
|
+
spinner9.warn(`already exists: ${destName}`);
|
|
2153
2145
|
return;
|
|
2154
2146
|
}
|
|
2155
2147
|
moveItem(entryPath, destPath);
|
|
2156
2148
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
2157
|
-
|
|
2149
|
+
spinner9.succeed(`imported ${import_termkit11.Color.green.encoder(destName)}`);
|
|
2158
2150
|
return;
|
|
2159
2151
|
}
|
|
2160
2152
|
if (detectedType === "book") {
|
|
2161
2153
|
const destPath = (0, import_path13.resolve)(destRoot, entry);
|
|
2162
2154
|
if ((0, import_fs13.existsSync)(destPath)) {
|
|
2163
|
-
|
|
2155
|
+
spinner9.warn(`already exists: ${entry}`);
|
|
2164
2156
|
return;
|
|
2165
2157
|
}
|
|
2166
2158
|
if (isDir || isBookDir) {
|
|
@@ -2175,17 +2167,17 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2175
2167
|
}
|
|
2176
2168
|
}
|
|
2177
2169
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
2178
|
-
|
|
2170
|
+
spinner9.succeed(`imported ${import_termkit11.Color.green.encoder(entry)}`);
|
|
2179
2171
|
return;
|
|
2180
2172
|
}
|
|
2181
2173
|
const parsed = parseDownloadName(entry);
|
|
2182
2174
|
if (!parsed) {
|
|
2183
|
-
if (isVerbose())
|
|
2175
|
+
if (isVerbose()) spinner9.info(`could not parse: ${entry}`);
|
|
2184
2176
|
return;
|
|
2185
2177
|
}
|
|
2186
2178
|
if (detectedType === "tv") {
|
|
2187
2179
|
if (parsed.season === void 0) {
|
|
2188
|
-
if (isVerbose())
|
|
2180
|
+
if (isVerbose()) spinner9.info(`could not detect season from: ${entry}`);
|
|
2189
2181
|
return;
|
|
2190
2182
|
}
|
|
2191
2183
|
const registeredShow = getShowByTitle(parsed.title);
|
|
@@ -2199,14 +2191,14 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2199
2191
|
showPath = (0, import_path13.resolve)(destRoot, showFolderName);
|
|
2200
2192
|
upsertShow(showPath, null, parsed.title);
|
|
2201
2193
|
} else {
|
|
2202
|
-
if (isVerbose())
|
|
2194
|
+
if (isVerbose()) spinner9.info(`not registered, skipped: ${parsed.title} \u2014 run: reelsort add "${parsed.title}"`);
|
|
2203
2195
|
return;
|
|
2204
2196
|
}
|
|
2205
2197
|
const seasonFolderName = findSeasonFolder2(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
2206
2198
|
const seasonPath = (0, import_path13.resolve)(showPath, seasonFolderName);
|
|
2207
2199
|
const videoFile2 = isDir ? findVideo2(entryPath) : entry;
|
|
2208
2200
|
if (!videoFile2) {
|
|
2209
|
-
if (isVerbose())
|
|
2201
|
+
if (isVerbose()) spinner9.info(`no video found in: ${entry}`);
|
|
2210
2202
|
return;
|
|
2211
2203
|
}
|
|
2212
2204
|
const videoExt2 = videoFile2.match(/([^.]+$)/)?.[0];
|
|
@@ -2216,7 +2208,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2216
2208
|
const destVideoPath = (0, import_path13.resolve)(seasonPath, destVideoName2);
|
|
2217
2209
|
const videoSourcePath2 = isDir ? (0, import_path13.resolve)(entryPath, videoFile2) : entryPath;
|
|
2218
2210
|
if ((0, import_fs13.existsSync)(destVideoPath)) {
|
|
2219
|
-
|
|
2211
|
+
spinner9.warn(`already exists: ${episodeName}`);
|
|
2220
2212
|
return;
|
|
2221
2213
|
}
|
|
2222
2214
|
const dirFiles2 = isDir ? (0, import_fs13.readdirSync)(entryPath) : [];
|
|
@@ -2232,7 +2224,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2232
2224
|
(0, import_fs13.linkSync)(videoSourcePath2, destVideoPath);
|
|
2233
2225
|
mode = "hardlink";
|
|
2234
2226
|
} catch {
|
|
2235
|
-
|
|
2227
|
+
spinner9.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
2236
2228
|
(0, import_fs13.cpSync)(videoSourcePath2, destVideoPath);
|
|
2237
2229
|
mode = "copy";
|
|
2238
2230
|
}
|
|
@@ -2248,19 +2240,19 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2248
2240
|
if (isDir) (0, import_fs13.rmSync)(entryPath, { recursive: true, force: true });
|
|
2249
2241
|
}
|
|
2250
2242
|
recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
|
|
2251
|
-
|
|
2243
|
+
spinner9.succeed(`imported ${import_termkit11.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
2252
2244
|
return;
|
|
2253
2245
|
}
|
|
2254
2246
|
const edition = detectEdition(entry);
|
|
2255
2247
|
const folderName = formatMovieName(movieFormat, parsed.title, parsed.year, edition);
|
|
2256
2248
|
const destFolder = (0, import_path13.resolve)(destRoot, folderName);
|
|
2257
2249
|
if ((0, import_fs13.existsSync)(destFolder)) {
|
|
2258
|
-
|
|
2250
|
+
spinner9.warn(`already exists: ${folderName}`);
|
|
2259
2251
|
return;
|
|
2260
2252
|
}
|
|
2261
2253
|
const videoFile = isDir ? findVideo2(entryPath) : entry;
|
|
2262
2254
|
if (!videoFile) {
|
|
2263
|
-
if (isVerbose())
|
|
2255
|
+
if (isVerbose()) spinner9.info(`no video found in: ${entry}`);
|
|
2264
2256
|
return;
|
|
2265
2257
|
}
|
|
2266
2258
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
@@ -2280,7 +2272,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2280
2272
|
(0, import_fs13.linkSync)(videoSourcePath, destVideoPath);
|
|
2281
2273
|
mode = "hardlink";
|
|
2282
2274
|
} catch {
|
|
2283
|
-
|
|
2275
|
+
spinner9.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
2284
2276
|
(0, import_fs13.cpSync)(videoSourcePath, destVideoPath);
|
|
2285
2277
|
mode = "copy";
|
|
2286
2278
|
}
|
|
@@ -2305,7 +2297,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2305
2297
|
}
|
|
2306
2298
|
recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
|
|
2307
2299
|
}
|
|
2308
|
-
|
|
2300
|
+
spinner9.succeed(`imported ${import_termkit11.Color.green.encoder(folderName)}`);
|
|
2309
2301
|
};
|
|
2310
2302
|
var watch = async ({ hardlink = false, auto = false }) => {
|
|
2311
2303
|
const config = getConfig();
|
|
@@ -2324,7 +2316,7 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2324
2316
|
await processItem(entry, hardlink, language, auto);
|
|
2325
2317
|
}
|
|
2326
2318
|
} catch (err) {
|
|
2327
|
-
|
|
2319
|
+
spinner9.fail(`error processing ${path}: ${err.message}`);
|
|
2328
2320
|
}
|
|
2329
2321
|
}, 5e3)
|
|
2330
2322
|
);
|
|
@@ -2336,10 +2328,10 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2336
2328
|
});
|
|
2337
2329
|
watcher.on("addDir", handle);
|
|
2338
2330
|
watcher.on("add", handle);
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
for (const s of config.sources)
|
|
2342
|
-
|
|
2331
|
+
spinner9.start();
|
|
2332
|
+
spinner9.succeed(`watching ${config.sources.length} source${config.sources.length !== 1 ? "s" : ""}`);
|
|
2333
|
+
for (const s of config.sources) spinner9.info(` ${import_termkit11.Color.white.encoder(s)}`);
|
|
2334
|
+
spinner9.stop();
|
|
2343
2335
|
process.stdin.resume();
|
|
2344
2336
|
};
|
|
2345
2337
|
var watch_default = watch;
|