reelsort 0.2.0 → 0.2.1
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 +321 -152
- package/dist/index.d.mts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +303 -138
- package/dist/index.mjs +304 -139
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -280,17 +280,17 @@ var clean = async ({ dryRun, olderThan }) => {
|
|
|
280
280
|
continue;
|
|
281
281
|
}
|
|
282
282
|
if (dryRun) {
|
|
283
|
-
spinner_default.succeed(`[dry] would remove ${import_termkit2.Color.
|
|
283
|
+
spinner_default.succeed(`[dry] would remove ${import_termkit2.Color.white.encoder(imp.sourcePath)}`);
|
|
284
284
|
cleaned++;
|
|
285
285
|
continue;
|
|
286
286
|
}
|
|
287
287
|
try {
|
|
288
288
|
(0, import_fs2.rmSync)(imp.sourcePath, { recursive: true, force: true });
|
|
289
289
|
deleteImport(imp.id);
|
|
290
|
-
spinner_default.succeed(`removed ${import_termkit2.Color.
|
|
290
|
+
spinner_default.succeed(`removed ${import_termkit2.Color.white.encoder(imp.sourcePath)}`);
|
|
291
291
|
cleaned++;
|
|
292
292
|
} catch {
|
|
293
|
-
spinner_default.warn(`locked or inaccessible, skipped: ${import_termkit2.Color.
|
|
293
|
+
spinner_default.warn(`locked or inaccessible, skipped: ${import_termkit2.Color.white.encoder(imp.sourcePath)}`);
|
|
294
294
|
skipped++;
|
|
295
295
|
}
|
|
296
296
|
}
|
|
@@ -349,20 +349,20 @@ var formatMovieName = (template, title, year, edition) => {
|
|
|
349
349
|
};
|
|
350
350
|
|
|
351
351
|
// src/actions/config.ts
|
|
352
|
-
var DEST_TYPES = ["movie", "tv", "ps3"];
|
|
352
|
+
var DEST_TYPES = ["movie", "tv", "ps3", "book"];
|
|
353
353
|
var sourceAdd = async ({ dir }) => {
|
|
354
354
|
const resolved = (0, import_path3.resolve)(dir);
|
|
355
355
|
const config = getConfig();
|
|
356
356
|
if (config.sources.includes(resolved)) {
|
|
357
357
|
spinner_default.start();
|
|
358
|
-
spinner_default.info(`source already configured: ${import_termkit3.Color.
|
|
358
|
+
spinner_default.info(`source already configured: ${import_termkit3.Color.white.encoder(resolved)}`);
|
|
359
359
|
spinner_default.stop();
|
|
360
360
|
return;
|
|
361
361
|
}
|
|
362
362
|
config.sources.push(resolved);
|
|
363
363
|
saveConfig(config);
|
|
364
364
|
spinner_default.start();
|
|
365
|
-
spinner_default.succeed(`added source: ${import_termkit3.Color.
|
|
365
|
+
spinner_default.succeed(`added source: ${import_termkit3.Color.white.encoder(resolved)}`);
|
|
366
366
|
spinner_default.stop();
|
|
367
367
|
};
|
|
368
368
|
var sourceRemove = async ({ dir }) => {
|
|
@@ -371,14 +371,14 @@ var sourceRemove = async ({ dir }) => {
|
|
|
371
371
|
const index = config.sources.indexOf(resolved);
|
|
372
372
|
if (index === -1) {
|
|
373
373
|
spinner_default.start();
|
|
374
|
-
spinner_default.warn(`source not found: ${import_termkit3.Color.
|
|
374
|
+
spinner_default.warn(`source not found: ${import_termkit3.Color.white.encoder(resolved)}`);
|
|
375
375
|
spinner_default.stop();
|
|
376
376
|
return;
|
|
377
377
|
}
|
|
378
378
|
config.sources.splice(index, 1);
|
|
379
379
|
saveConfig(config);
|
|
380
380
|
spinner_default.start();
|
|
381
|
-
spinner_default.succeed(`removed source: ${import_termkit3.Color.
|
|
381
|
+
spinner_default.succeed(`removed source: ${import_termkit3.Color.white.encoder(resolved)}`);
|
|
382
382
|
spinner_default.stop();
|
|
383
383
|
};
|
|
384
384
|
var destAdd = async ({ type, dir }) => {
|
|
@@ -390,7 +390,7 @@ var destAdd = async ({ type, dir }) => {
|
|
|
390
390
|
config.dest[type] = resolved;
|
|
391
391
|
saveConfig(config);
|
|
392
392
|
spinner_default.start();
|
|
393
|
-
spinner_default.succeed(`set ${type} destination: ${import_termkit3.Color.
|
|
393
|
+
spinner_default.succeed(`set ${type} destination: ${import_termkit3.Color.green.encoder(resolved)}`);
|
|
394
394
|
spinner_default.stop();
|
|
395
395
|
};
|
|
396
396
|
var destRemove = async ({ type }) => {
|
|
@@ -416,7 +416,7 @@ var configSet = async ({ key, subkey, value }) => {
|
|
|
416
416
|
config.language = subkey;
|
|
417
417
|
saveConfig(config);
|
|
418
418
|
spinner_default.start();
|
|
419
|
-
spinner_default.succeed(`set subtitle language: ${import_termkit3.Color.
|
|
419
|
+
spinner_default.succeed(`set subtitle language: ${import_termkit3.Color.green.encoder(subkey)}`);
|
|
420
420
|
spinner_default.stop();
|
|
421
421
|
return;
|
|
422
422
|
}
|
|
@@ -446,7 +446,7 @@ var configSet = async ({ key, subkey, value }) => {
|
|
|
446
446
|
}
|
|
447
447
|
saveConfig(config);
|
|
448
448
|
spinner_default.start();
|
|
449
|
-
spinner_default.succeed(`set ${subkey} format: ${import_termkit3.Color.
|
|
449
|
+
spinner_default.succeed(`set ${subkey} format: ${import_termkit3.Color.green.encoder(value ?? subkey)}`);
|
|
450
450
|
spinner_default.stop();
|
|
451
451
|
return;
|
|
452
452
|
}
|
|
@@ -458,7 +458,7 @@ var configShow = async () => {
|
|
|
458
458
|
if (config.sources.length === 0) {
|
|
459
459
|
console.log(" (none)");
|
|
460
460
|
} else {
|
|
461
|
-
for (const s of config.sources) console.log(` ${import_termkit3.Color.
|
|
461
|
+
for (const s of config.sources) console.log(` ${import_termkit3.Color.white.encoder(s)}`);
|
|
462
462
|
}
|
|
463
463
|
console.log("\nDestinations:");
|
|
464
464
|
const entries = DEST_TYPES.map((t) => ({ type: t, path: config.dest[t] })).filter((e) => e.path);
|
|
@@ -466,15 +466,15 @@ var configShow = async () => {
|
|
|
466
466
|
console.log(" (none)");
|
|
467
467
|
} else {
|
|
468
468
|
for (const { type, path } of entries) {
|
|
469
|
-
console.log(` ${type.padEnd(6)} ${import_termkit3.Color.
|
|
469
|
+
console.log(` ${type.padEnd(6)} ${import_termkit3.Color.green.encoder(path)}`);
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
472
|
console.log(`
|
|
473
|
-
Subtitle language: ${import_termkit3.Color.
|
|
473
|
+
Subtitle language: ${import_termkit3.Color.green.encoder(config.language ?? "eng (default)")}`);
|
|
474
474
|
console.log(`TMDb API key: ${config.tmdbApiKey ? import_termkit3.Color.green.encoder("configured") : import_termkit3.Color.red.encoder("not set")}`);
|
|
475
|
-
console.log(`Movie format: ${import_termkit3.Color.
|
|
476
|
-
console.log(`Episode format: ${import_termkit3.Color.
|
|
477
|
-
console.log(`Season folder: ${import_termkit3.Color.
|
|
475
|
+
console.log(`Movie format: ${import_termkit3.Color.green.encoder(config.format?.movie ?? DEFAULT_MOVIE_FORMAT + " (default)")}`);
|
|
476
|
+
console.log(`Episode format: ${import_termkit3.Color.green.encoder(config.format?.episode ?? DEFAULT_EPISODE_FORMAT + " (default)")}`);
|
|
477
|
+
console.log(`Season folder: ${import_termkit3.Color.green.encoder(config.format?.season ?? DEFAULT_SEASON_FORMAT + " (default)")}`);
|
|
478
478
|
console.log();
|
|
479
479
|
};
|
|
480
480
|
|
|
@@ -485,7 +485,7 @@ var import_termkit4 = require("termkit");
|
|
|
485
485
|
var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
|
|
486
486
|
let dir1 = rawDir1;
|
|
487
487
|
let dir2 = rawDir2;
|
|
488
|
-
spinner_default.text = `checking differences between ${import_termkit4.Color.
|
|
488
|
+
spinner_default.text = `checking differences between ${import_termkit4.Color.white.encoder(dir1)} and ${import_termkit4.Color.white.encoder(dir2)}`;
|
|
489
489
|
spinner_default.start();
|
|
490
490
|
dir1 = (0, import_path4.resolve)(dir1);
|
|
491
491
|
dir2 = (0, import_path4.resolve)(dir2);
|
|
@@ -521,7 +521,7 @@ var differences = async ({ dir1: rawDir1, dir2: rawDir2, only, ignore }) => {
|
|
|
521
521
|
removed.push(l);
|
|
522
522
|
}
|
|
523
523
|
}
|
|
524
|
-
spinner_default.succeed(`checked differences between ${import_termkit4.Color.
|
|
524
|
+
spinner_default.succeed(`checked differences between ${import_termkit4.Color.white.encoder(dir1)} and ${import_termkit4.Color.white.encoder(dir2)}`);
|
|
525
525
|
spinner_default.succeed(`found ${added.length} added files`);
|
|
526
526
|
spinner_default.succeed(`found ${removed.length} removed files`);
|
|
527
527
|
spinner_default.stop();
|
|
@@ -547,8 +547,8 @@ var history = async ({ limit, imports }) => {
|
|
|
547
547
|
${import_termkit5.Color.yellow.encoder(label)} (${session.records.length} item${session.records.length !== 1 ? "s" : ""})`);
|
|
548
548
|
for (const r of session.records) {
|
|
549
549
|
const src = (0, import_path5.basename)(r.sourcePath);
|
|
550
|
-
const dest = import_termkit5.Color.
|
|
551
|
-
const mode = r.mode !== "move" ? ` ${import_termkit5.Color.
|
|
550
|
+
const dest = import_termkit5.Color.green.encoder(r.destinationPath);
|
|
551
|
+
const mode = r.mode !== "move" ? ` ${import_termkit5.Color.white.encoder(`[${r.mode}]`)}` : "";
|
|
552
552
|
console.log(` ${src} \u2192 ${dest}${mode}`);
|
|
553
553
|
}
|
|
554
554
|
}
|
|
@@ -567,7 +567,7 @@ ${import_termkit5.Color.yellow.encoder(label)} (${folders.length} item${folders
|
|
|
567
567
|
for (const r of folders) {
|
|
568
568
|
const oldName = (0, import_path5.basename)(r.oldPath);
|
|
569
569
|
const newName = (0, import_path5.basename)(r.newPath);
|
|
570
|
-
console.log(` ${import_termkit5.Color.
|
|
570
|
+
console.log(` ${import_termkit5.Color.white.encoder(oldName)} \u2192 ${import_termkit5.Color.green.encoder(newName)}`);
|
|
571
571
|
}
|
|
572
572
|
}
|
|
573
573
|
}
|
|
@@ -718,7 +718,7 @@ var list = async ({ type, missingSubs, codec: codecFilter, resolution: resFilter
|
|
|
718
718
|
const destRoot = config.dest[t];
|
|
719
719
|
if (!(0, import_fs6.existsSync)(destRoot)) {
|
|
720
720
|
console.log(`
|
|
721
|
-
${t.toUpperCase()} ${import_termkit6.Color.
|
|
721
|
+
${t.toUpperCase()} ${import_termkit6.Color.white.encoder(destRoot)} (not found)`);
|
|
722
722
|
continue;
|
|
723
723
|
}
|
|
724
724
|
const folders = (0, import_fs6.readdirSync)(destRoot).filter((f) => {
|
|
@@ -749,7 +749,7 @@ ${t.toUpperCase()} ${import_termkit6.Color.blue.encoder(destRoot)} (not found)
|
|
|
749
749
|
return yearDiff !== 0 ? yearDiff : a.title.localeCompare(b.title);
|
|
750
750
|
});
|
|
751
751
|
console.log(`
|
|
752
|
-
${import_termkit6.Color.yellow.encoder(t.toUpperCase())} ${import_termkit6.Color.
|
|
752
|
+
${import_termkit6.Color.yellow.encoder(t.toUpperCase())} ${import_termkit6.Color.white.encoder(destRoot)}`);
|
|
753
753
|
new import_termkit6.Table(
|
|
754
754
|
filtered.map((e) => ({
|
|
755
755
|
title: e.title,
|
|
@@ -857,7 +857,7 @@ var probe = async ({ type, force, verbose }) => {
|
|
|
857
857
|
for (const t of types) {
|
|
858
858
|
const destRoot = config.dest[t];
|
|
859
859
|
if (!(0, import_fs7.existsSync)(destRoot)) continue;
|
|
860
|
-
spinner_default.text = `scanning ${import_termkit7.Color.
|
|
860
|
+
spinner_default.text = `scanning ${import_termkit7.Color.white.encoder(destRoot)}`;
|
|
861
861
|
const files = walkVideoFiles(destRoot);
|
|
862
862
|
for (const filePath of files) {
|
|
863
863
|
if (!force && getMediaInfo(filePath)) {
|
|
@@ -865,7 +865,7 @@ var probe = async ({ type, force, verbose }) => {
|
|
|
865
865
|
skipped++;
|
|
866
866
|
continue;
|
|
867
867
|
}
|
|
868
|
-
spinner_default.text = `probing ${import_termkit7.Color.
|
|
868
|
+
spinner_default.text = `probing ${import_termkit7.Color.white.encoder(filePath)}`;
|
|
869
869
|
const result = runFfprobe(filePath);
|
|
870
870
|
if (!result) {
|
|
871
871
|
if (verbose) spinner_default.warn(`ffprobe failed: ${filePath}`);
|
|
@@ -949,13 +949,13 @@ var rename = async ({ dir: inputDir, type, verbose }) => {
|
|
|
949
949
|
const config = getConfig();
|
|
950
950
|
const language = config.language ?? "eng";
|
|
951
951
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
952
|
-
spinner_default.text = `renaming in ${import_termkit8.Color.
|
|
952
|
+
spinner_default.text = `renaming in ${import_termkit8.Color.white.encoder(dir)}`;
|
|
953
953
|
spinner_default.start();
|
|
954
954
|
if (!(0, import_fs8.existsSync)(dir)) throw new Error(`dir ${dir} does not exist`);
|
|
955
955
|
const list2 = (0, import_fs8.readdirSync)(dir);
|
|
956
956
|
let renamed = 0, removed = 0, skipped = 0;
|
|
957
957
|
for (const [index, entry] of list2.entries()) {
|
|
958
|
-
spinner_default.text = `renaming in ${import_termkit8.Color.
|
|
958
|
+
spinner_default.text = `renaming in ${import_termkit8.Color.white.encoder(dir)} ${index + 1}/${list2.length}`;
|
|
959
959
|
if (!(0, import_fs8.lstatSync)((0, import_path9.resolve)(dir, entry)).isDirectory()) {
|
|
960
960
|
if (verbose) spinner_default.info(`skipped ${entry}`);
|
|
961
961
|
skipped++;
|
|
@@ -1040,7 +1040,7 @@ var rename = async ({ dir: inputDir, type, verbose }) => {
|
|
|
1040
1040
|
spinner_default.succeed(`renamed ${renamed} files`);
|
|
1041
1041
|
if (removed) spinner_default.info(`removed ${removed} files`);
|
|
1042
1042
|
spinner_default.info(`skipped ${skipped} files`);
|
|
1043
|
-
spinner_default.succeed(`done in ${import_termkit8.Color.
|
|
1043
|
+
spinner_default.succeed(`done in ${import_termkit8.Color.green.encoder(dir)}`);
|
|
1044
1044
|
spinner_default.stop();
|
|
1045
1045
|
};
|
|
1046
1046
|
var rename_default = rename;
|
|
@@ -1051,7 +1051,7 @@ var import_path10 = require("path");
|
|
|
1051
1051
|
var import_termkit9 = require("termkit");
|
|
1052
1052
|
var reset = async ({ dir: inputDir, double }) => {
|
|
1053
1053
|
let dir = inputDir;
|
|
1054
|
-
spinner_default.text = `resetting episodes in ${import_termkit9.Color.
|
|
1054
|
+
spinner_default.text = `resetting episodes in ${import_termkit9.Color.white.encoder(dir)}`;
|
|
1055
1055
|
spinner_default.start();
|
|
1056
1056
|
dir = (0, import_path10.resolve)(dir);
|
|
1057
1057
|
if (!(0, import_fs9.existsSync)(dir)) throw new Error(`dir ${dir} does not exist`);
|
|
@@ -1080,7 +1080,7 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1080
1080
|
const episodeFormat = getConfig().format?.episode;
|
|
1081
1081
|
let renamed = 0, skipped = other.length;
|
|
1082
1082
|
for (const [index, i] of sublist.entries()) {
|
|
1083
|
-
spinner_default.text = `resetting episodes in ${import_termkit9.Color.
|
|
1083
|
+
spinner_default.text = `resetting episodes in ${import_termkit9.Color.white.encoder(dir)} ${index}/${list2.length}`;
|
|
1084
1084
|
const ext = i.match(/([^.]+$)/)?.[0];
|
|
1085
1085
|
const episode = double ? index * 2 + 1 : index + 1;
|
|
1086
1086
|
const name = `${formatEpisode(seasonNum, episode, episodeFormat, double, showTitle)}.${ext}`;
|
|
@@ -1093,7 +1093,7 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1093
1093
|
}
|
|
1094
1094
|
spinner_default.succeed(`renamed ${renamed} files`);
|
|
1095
1095
|
spinner_default.info(`skipped ${skipped} files`);
|
|
1096
|
-
spinner_default.succeed(`done in ${import_termkit9.Color.
|
|
1096
|
+
spinner_default.succeed(`done in ${import_termkit9.Color.green.encoder(dir)}`);
|
|
1097
1097
|
spinner_default.stop();
|
|
1098
1098
|
};
|
|
1099
1099
|
var reset_default = reset;
|
|
@@ -1174,18 +1174,17 @@ var searchMovie = async (title, year, apiKey) => {
|
|
|
1174
1174
|
if (year) url.searchParams.set("year", String(year));
|
|
1175
1175
|
try {
|
|
1176
1176
|
const res = await fetch(url.toString());
|
|
1177
|
-
if (!res.ok) return
|
|
1177
|
+
if (!res.ok) return [];
|
|
1178
1178
|
const data = await res.json();
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
};
|
|
1179
|
+
return data.results.slice(0, 5).map((r) => ({
|
|
1180
|
+
id: r.id,
|
|
1181
|
+
title: r.title,
|
|
1182
|
+
year: r.release_date ? parseInt(r.release_date.slice(0, 4)) : void 0,
|
|
1183
|
+
overview: r.overview || void 0,
|
|
1184
|
+
url: `${TMDB_WEB}/movie/${r.id}`
|
|
1185
|
+
}));
|
|
1187
1186
|
} catch {
|
|
1188
|
-
return
|
|
1187
|
+
return [];
|
|
1189
1188
|
}
|
|
1190
1189
|
};
|
|
1191
1190
|
var getEpisodeName = async (seriesId, season, episode, apiKey) => {
|
|
@@ -1220,6 +1219,9 @@ var searchTv = async (title, apiKey) => {
|
|
|
1220
1219
|
}
|
|
1221
1220
|
};
|
|
1222
1221
|
|
|
1222
|
+
// src/refs/bookExtensions.json
|
|
1223
|
+
var bookExtensions_default = ["epub", "mobi", "azw3", "azw"];
|
|
1224
|
+
|
|
1223
1225
|
// src/actions/scan.ts
|
|
1224
1226
|
var sameDev = (a, b) => {
|
|
1225
1227
|
try {
|
|
@@ -1242,6 +1244,10 @@ var findVideo = (dir) => (0, import_fs10.readdirSync)(dir).find((f) => {
|
|
|
1242
1244
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1243
1245
|
return ext && videoExtensions_default.includes(ext);
|
|
1244
1246
|
}) ?? null;
|
|
1247
|
+
var containsBook = (dir) => (0, import_fs10.readdirSync)(dir).some((f) => {
|
|
1248
|
+
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1249
|
+
return ext && bookExtensions_default.includes(ext);
|
|
1250
|
+
});
|
|
1245
1251
|
var findSeasonFolder = (showPath, season) => {
|
|
1246
1252
|
if (!(0, import_fs10.existsSync)(showPath)) return null;
|
|
1247
1253
|
const folders = (0, import_fs10.readdirSync)(showPath).filter((f) => {
|
|
@@ -1256,27 +1262,128 @@ var findSeasonFolder = (showPath, season) => {
|
|
|
1256
1262
|
return match && parseInt(match[1]) === season;
|
|
1257
1263
|
}) ?? null;
|
|
1258
1264
|
};
|
|
1259
|
-
var
|
|
1265
|
+
var classifyMovieConfidence = (entry) => {
|
|
1266
|
+
if (/^\[(?:Team|Group)\s/i.test(entry)) return "skip";
|
|
1267
|
+
if (/\bepisodes?\s+\d+[-–]\d+/i.test(entry)) return "skip";
|
|
1268
|
+
if (/\b(?:patch|keygen|crack)\b|\bkeys?\s*\{/i.test(entry)) return "skip";
|
|
1269
|
+
if (/\[YTS[.\-]/i.test(entry)) return "auto";
|
|
1270
|
+
if (/\(\d{4}\)/.test(entry) && /\[(?:2160p|1080p|720p|480p|576p|BluRay|BDRip|BDRemux|WEBRip|WEB-DL|HDRip|DVDRip|HDTV)/i.test(entry)) return "auto";
|
|
1271
|
+
if (/\.\d{4}\..*?(?:2160p|1080p|720p|BluRay|WEBRip|WEB-DL|HDTV)/i.test(entry)) return "auto";
|
|
1272
|
+
return "ambiguous";
|
|
1273
|
+
};
|
|
1274
|
+
var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto, force, interactive }) => {
|
|
1260
1275
|
const config = getConfig();
|
|
1261
1276
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
1262
1277
|
const language = config.language ?? "eng";
|
|
1263
1278
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1264
1279
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
1280
|
+
const lookupMovie = async (parsed) => {
|
|
1281
|
+
let tmdbId;
|
|
1282
|
+
let resolvedTitle = parsed.title;
|
|
1283
|
+
let resolvedYear = parsed.year;
|
|
1284
|
+
if (config.tmdbApiKey) {
|
|
1285
|
+
const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
|
|
1286
|
+
if (results.length === 1) {
|
|
1287
|
+
tmdbId = results[0].id;
|
|
1288
|
+
resolvedTitle = results[0].title;
|
|
1289
|
+
resolvedYear = results[0].year ?? parsed.year;
|
|
1290
|
+
} else if (results.length > 1) {
|
|
1291
|
+
spinner_default.stop();
|
|
1292
|
+
const select = new import_termkit10.Select();
|
|
1293
|
+
const items = results.map((r) => ({
|
|
1294
|
+
label: r.year ? `${r.title} (${r.year})` : r.title,
|
|
1295
|
+
description: [r.overview?.slice(0, 60), hyperlink(r.url, "themoviedb.org")].filter(Boolean).join(" \xB7 "),
|
|
1296
|
+
...r
|
|
1297
|
+
}));
|
|
1298
|
+
const picked = await select.ask(`Multiple movies found for "${parsed.title}":`, items);
|
|
1299
|
+
spinner_default.start();
|
|
1300
|
+
if (picked) {
|
|
1301
|
+
tmdbId = picked.id;
|
|
1302
|
+
resolvedTitle = picked.title;
|
|
1303
|
+
resolvedYear = picked.year ?? parsed.year;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
return { tmdbId, resolvedTitle, resolvedYear };
|
|
1308
|
+
};
|
|
1309
|
+
const importMovie = async (entry, entryPath, isDir, resolvedTitle, resolvedYear, tmdbId, destRoot) => {
|
|
1310
|
+
const edition = detectEdition(entry);
|
|
1311
|
+
const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
|
|
1312
|
+
const destFolder = (0, import_path11.resolve)(destRoot, folderName);
|
|
1313
|
+
if ((0, import_fs10.existsSync)(destFolder)) {
|
|
1314
|
+
spinner_default.warn(`already exists: ${folderName}`);
|
|
1315
|
+
return false;
|
|
1316
|
+
}
|
|
1317
|
+
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
1318
|
+
if (!videoFile) {
|
|
1319
|
+
if (verbose) spinner_default.info(`no video found in: ${entry}`);
|
|
1320
|
+
return false;
|
|
1321
|
+
}
|
|
1322
|
+
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
1323
|
+
const destVideoName = `${folderName}.${videoExt}`;
|
|
1324
|
+
const videoSourcePath = isDir ? (0, import_path11.resolve)(entryPath, videoFile) : entryPath;
|
|
1325
|
+
const dirFiles = isDir ? (0, import_fs10.readdirSync)(entryPath) : [];
|
|
1326
|
+
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
1327
|
+
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
1328
|
+
const subtitleSourcePath = subtitle ? (0, import_path11.resolve)(entryPath, subtitle) : null;
|
|
1329
|
+
const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
|
|
1330
|
+
if (!dryRun) {
|
|
1331
|
+
if (useHardlink) {
|
|
1332
|
+
(0, import_fs10.mkdirSync)(destFolder, { recursive: true });
|
|
1333
|
+
const destVideoPath = (0, import_path11.resolve)(destFolder, destVideoName);
|
|
1334
|
+
let mode;
|
|
1335
|
+
try {
|
|
1336
|
+
if (!sameDev(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
|
|
1337
|
+
(0, import_fs10.linkSync)(videoSourcePath, destVideoPath);
|
|
1338
|
+
mode = "hardlink";
|
|
1339
|
+
} catch {
|
|
1340
|
+
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
1341
|
+
(0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
|
|
1342
|
+
mode = "copy";
|
|
1343
|
+
}
|
|
1344
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs10.cpSync)(subtitleSourcePath, (0, import_path11.resolve)(destFolder, destSubtitleName));
|
|
1345
|
+
recordImport(sessionId, entryPath, destFolder, mode, tmdbId);
|
|
1346
|
+
} else {
|
|
1347
|
+
if (isDir) {
|
|
1348
|
+
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
1349
|
+
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0, import_fs10.rmSync)((0, import_path11.resolve)(entryPath, f), { recursive: true, force: true });
|
|
1350
|
+
(0, import_fs10.renameSync)(videoSourcePath, (0, import_path11.resolve)(entryPath, destVideoName));
|
|
1351
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs10.renameSync)(subtitleSourcePath, (0, import_path11.resolve)(entryPath, destSubtitleName));
|
|
1352
|
+
moveFolder(entryPath, destFolder);
|
|
1353
|
+
} else {
|
|
1354
|
+
(0, import_fs10.mkdirSync)(destFolder, { recursive: true });
|
|
1355
|
+
const destVideoPath = (0, import_path11.resolve)(destFolder, destVideoName);
|
|
1356
|
+
if (sameDev(videoSourcePath, destRoot)) {
|
|
1357
|
+
(0, import_fs10.renameSync)(videoSourcePath, destVideoPath);
|
|
1358
|
+
} else {
|
|
1359
|
+
(0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
|
|
1360
|
+
(0, import_fs10.rmSync)(videoSourcePath);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
recordImport(sessionId, entryPath, destFolder, "move", tmdbId);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${folderName}`);
|
|
1367
|
+
return true;
|
|
1368
|
+
};
|
|
1265
1369
|
spinner_default.start();
|
|
1266
1370
|
if (config.sources.length === 0) throw new Error("no sources configured \u2014 run: reelsort config add source <dir>");
|
|
1267
1371
|
let imported = 0, skipped = 0;
|
|
1372
|
+
const pendingMovies = [];
|
|
1268
1373
|
for (const source of config.sources) {
|
|
1269
1374
|
if (!(0, import_fs10.existsSync)(source)) {
|
|
1270
|
-
spinner_default.warn(`source not found: ${import_termkit10.Color.
|
|
1375
|
+
spinner_default.warn(`source not found: ${import_termkit10.Color.white.encoder(source)}`);
|
|
1271
1376
|
continue;
|
|
1272
1377
|
}
|
|
1273
|
-
spinner_default.text = `scanning ${import_termkit10.Color.
|
|
1378
|
+
spinner_default.text = `scanning ${import_termkit10.Color.white.encoder(source)}`;
|
|
1274
1379
|
for (const entry of (0, import_fs10.readdirSync)(source)) {
|
|
1275
1380
|
const entryPath = (0, import_path11.resolve)(source, entry);
|
|
1276
1381
|
const isDir = (0, import_fs10.lstatSync)(entryPath).isDirectory();
|
|
1277
1382
|
const ext = entry.match(/([^.]+$)/)?.[0];
|
|
1278
1383
|
const isVideo = !isDir && ext && videoExtensions_default.includes(ext);
|
|
1279
|
-
|
|
1384
|
+
const isBook = !isDir && ext && bookExtensions_default.includes(ext);
|
|
1385
|
+
const isBookDir = isDir && containsBook(entryPath);
|
|
1386
|
+
if (!isDir && !isVideo && !isBook) {
|
|
1280
1387
|
if (verbose) spinner_default.info(`skipped ${entry}`);
|
|
1281
1388
|
skipped++;
|
|
1282
1389
|
continue;
|
|
@@ -1284,6 +1391,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
|
|
|
1284
1391
|
let detectedType;
|
|
1285
1392
|
if (type) {
|
|
1286
1393
|
detectedType = type;
|
|
1394
|
+
} else if (isBook || isBookDir) {
|
|
1395
|
+
detectedType = "book";
|
|
1287
1396
|
} else if (isDir && /(?<=\[).+?(?=\])/.test(entry)) {
|
|
1288
1397
|
detectedType = "ps3";
|
|
1289
1398
|
} else if (/S\d{2,3}E\d{2,3}/i.test(entry) || /\d+x\d{2,3}/i.test(entry)) {
|
|
@@ -1319,12 +1428,49 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
|
|
|
1319
1428
|
imported++;
|
|
1320
1429
|
continue;
|
|
1321
1430
|
}
|
|
1431
|
+
if (detectedType === "book") {
|
|
1432
|
+
const destPath = (0, import_path11.resolve)(destRoot, entry);
|
|
1433
|
+
if ((0, import_fs10.existsSync)(destPath)) {
|
|
1434
|
+
spinner_default.warn(`already exists: ${entry}`);
|
|
1435
|
+
skipped++;
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1438
|
+
if (!dryRun) {
|
|
1439
|
+
if (isDir || isBookDir) {
|
|
1440
|
+
moveFolder(entryPath, destPath);
|
|
1441
|
+
} else {
|
|
1442
|
+
(0, import_fs10.mkdirSync)(destRoot, { recursive: true });
|
|
1443
|
+
if (sameDev(entryPath, destRoot)) {
|
|
1444
|
+
(0, import_fs10.renameSync)(entryPath, destPath);
|
|
1445
|
+
} else {
|
|
1446
|
+
(0, import_fs10.cpSync)(entryPath, destPath);
|
|
1447
|
+
(0, import_fs10.rmSync)(entryPath);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
recordImport(sessionId, entryPath, destPath, "move");
|
|
1451
|
+
}
|
|
1452
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${entry}`);
|
|
1453
|
+
imported++;
|
|
1454
|
+
continue;
|
|
1455
|
+
}
|
|
1322
1456
|
const parsed = parseDownloadName(entry);
|
|
1323
1457
|
if (!parsed) {
|
|
1324
1458
|
if (verbose) spinner_default.info(`could not parse: ${entry}`);
|
|
1325
1459
|
skipped++;
|
|
1326
1460
|
continue;
|
|
1327
1461
|
}
|
|
1462
|
+
if (detectedType === "movie") {
|
|
1463
|
+
const confidence = classifyMovieConfidence(entry);
|
|
1464
|
+
if (confidence === "skip") {
|
|
1465
|
+
if (verbose) spinner_default.info(`skipped (uncertain): ${entry}`);
|
|
1466
|
+
skipped++;
|
|
1467
|
+
continue;
|
|
1468
|
+
}
|
|
1469
|
+
if (confidence === "ambiguous") {
|
|
1470
|
+
pendingMovies.push({ entry, entryPath, isDir, parsed, destRoot });
|
|
1471
|
+
continue;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1328
1474
|
let tmdbId;
|
|
1329
1475
|
let resolvedTitle = parsed.title;
|
|
1330
1476
|
let resolvedYear = parsed.year;
|
|
@@ -1352,12 +1498,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
|
|
|
1352
1498
|
}
|
|
1353
1499
|
}
|
|
1354
1500
|
} else {
|
|
1355
|
-
const
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
resolvedYear = tmdb.year ?? parsed.year;
|
|
1360
|
-
}
|
|
1501
|
+
const result = await lookupMovie(parsed);
|
|
1502
|
+
tmdbId = result.tmdbId;
|
|
1503
|
+
resolvedTitle = result.resolvedTitle;
|
|
1504
|
+
resolvedYear = result.resolvedYear;
|
|
1361
1505
|
}
|
|
1362
1506
|
}
|
|
1363
1507
|
if (detectedType === "tv") {
|
|
@@ -1383,50 +1527,68 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
|
|
|
1383
1527
|
}
|
|
1384
1528
|
const seasonFolderName = findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
1385
1529
|
const seasonPath = (0, import_path11.resolve)(showPath, seasonFolderName);
|
|
1386
|
-
const
|
|
1387
|
-
if (!
|
|
1530
|
+
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
1531
|
+
if (!videoFile) {
|
|
1388
1532
|
if (verbose) spinner_default.info(`no video found in: ${entry}`);
|
|
1389
1533
|
skipped++;
|
|
1390
1534
|
continue;
|
|
1391
1535
|
}
|
|
1392
|
-
const
|
|
1536
|
+
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
1393
1537
|
const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
1394
1538
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
|
|
1395
|
-
const
|
|
1396
|
-
const destVideoPath = (0, import_path11.resolve)(seasonPath,
|
|
1397
|
-
const
|
|
1539
|
+
const destVideoName = `${episodeName}.${videoExt}`;
|
|
1540
|
+
const destVideoPath = (0, import_path11.resolve)(seasonPath, destVideoName);
|
|
1541
|
+
const videoSourcePath = isDir ? (0, import_path11.resolve)(entryPath, videoFile) : entryPath;
|
|
1398
1542
|
if ((0, import_fs10.existsSync)(destVideoPath)) {
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1543
|
+
let shouldReplace = force;
|
|
1544
|
+
if (!shouldReplace && interactive) {
|
|
1545
|
+
spinner_default.stop();
|
|
1546
|
+
const select = new import_termkit10.Select();
|
|
1547
|
+
const picked = await select.ask(`Already exists \u2014 replace?`, [
|
|
1548
|
+
{ label: `${showFolderName} / ${seasonFolderName} / ${episodeName}`, value: "replace" },
|
|
1549
|
+
{ label: "Skip", value: "skip" }
|
|
1550
|
+
]);
|
|
1551
|
+
spinner_default.start();
|
|
1552
|
+
shouldReplace = picked?.value === "replace";
|
|
1553
|
+
}
|
|
1554
|
+
if (!shouldReplace) {
|
|
1555
|
+
spinner_default.warn(`already exists: ${episodeName}`);
|
|
1556
|
+
skipped++;
|
|
1557
|
+
continue;
|
|
1558
|
+
}
|
|
1559
|
+
if (!dryRun) {
|
|
1560
|
+
for (const f of (0, import_fs10.readdirSync)(seasonPath)) {
|
|
1561
|
+
if (f.startsWith(`${episodeName}.`)) (0, import_fs10.rmSync)((0, import_path11.resolve)(seasonPath, f));
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1402
1564
|
}
|
|
1403
|
-
const
|
|
1404
|
-
const
|
|
1405
|
-
const
|
|
1406
|
-
const
|
|
1407
|
-
const
|
|
1565
|
+
const dirFiles = isDir ? (0, import_fs10.readdirSync)(entryPath) : [];
|
|
1566
|
+
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
1567
|
+
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
1568
|
+
const subtitleSourcePath = subtitle ? (0, import_path11.resolve)(entryPath, subtitle) : null;
|
|
1569
|
+
const destSubtitleName = subtitle && subtitleExt ? `${episodeName}.${subtitleExt}` : null;
|
|
1408
1570
|
if (!dryRun) {
|
|
1409
1571
|
(0, import_fs10.mkdirSync)(seasonPath, { recursive: true });
|
|
1410
1572
|
let mode = "move";
|
|
1411
1573
|
if (useHardlink) {
|
|
1412
1574
|
try {
|
|
1413
|
-
if (!sameDev(
|
|
1414
|
-
(0, import_fs10.linkSync)(
|
|
1575
|
+
if (!sameDev(videoSourcePath, seasonPath)) throw new Error("cross-filesystem");
|
|
1576
|
+
(0, import_fs10.linkSync)(videoSourcePath, destVideoPath);
|
|
1415
1577
|
mode = "hardlink";
|
|
1416
1578
|
} catch {
|
|
1417
1579
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
1418
|
-
(0, import_fs10.cpSync)(
|
|
1580
|
+
(0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
|
|
1419
1581
|
mode = "copy";
|
|
1420
1582
|
}
|
|
1421
|
-
if (
|
|
1583
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs10.cpSync)(subtitleSourcePath, (0, import_path11.resolve)(seasonPath, destSubtitleName));
|
|
1422
1584
|
} else {
|
|
1423
|
-
if (sameDev(
|
|
1424
|
-
(0, import_fs10.renameSync)(
|
|
1585
|
+
if (sameDev(videoSourcePath, seasonPath)) {
|
|
1586
|
+
(0, import_fs10.renameSync)(videoSourcePath, destVideoPath);
|
|
1425
1587
|
} else {
|
|
1426
|
-
(0, import_fs10.cpSync)(
|
|
1427
|
-
(0, import_fs10.rmSync)(
|
|
1588
|
+
(0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
|
|
1589
|
+
(0, import_fs10.rmSync)(videoSourcePath);
|
|
1428
1590
|
}
|
|
1429
|
-
if (
|
|
1591
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs10.renameSync)(subtitleSourcePath, (0, import_path11.resolve)(seasonPath, destSubtitleName));
|
|
1430
1592
|
if (isDir) (0, import_fs10.rmSync)(entryPath, { recursive: true, force: true });
|
|
1431
1593
|
}
|
|
1432
1594
|
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId);
|
|
@@ -1435,66 +1597,40 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, verbose, auto }) => {
|
|
|
1435
1597
|
imported++;
|
|
1436
1598
|
continue;
|
|
1437
1599
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
if ((0, import_fs10.existsSync)(destFolder)) {
|
|
1442
|
-
spinner_default.warn(`already exists: ${folderName}`);
|
|
1600
|
+
if (await importMovie(entry, entryPath, isDir, resolvedTitle, resolvedYear, tmdbId, destRoot)) {
|
|
1601
|
+
imported++;
|
|
1602
|
+
} else {
|
|
1443
1603
|
skipped++;
|
|
1444
|
-
continue;
|
|
1445
1604
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
if (pendingMovies.length > 0) {
|
|
1608
|
+
spinner_default.warn(`${pendingMovies.length} uncertain movie match${pendingMovies.length > 1 ? "es" : ""} skipped \u2014 use -i to review or -f to import all`);
|
|
1609
|
+
for (const p of pendingMovies) spinner_default.info(` ? ${p.entry.replace(/\/$/, "")}`);
|
|
1610
|
+
let toProcess = [];
|
|
1611
|
+
if (interactive) {
|
|
1612
|
+
spinner_default.stop();
|
|
1613
|
+
const ms = new import_termkit10.MultiSelect({ allowSkip: true, search: true, maxHeight: 20 });
|
|
1614
|
+
const items = pendingMovies.map((p) => ({
|
|
1615
|
+
label: p.entry.replace(/\/$/, ""),
|
|
1616
|
+
description: p.parsed.year ? `parsed: ${p.parsed.title} \xB7 ${p.parsed.year}` : `parsed: ${p.parsed.title}`,
|
|
1617
|
+
...p
|
|
1618
|
+
}));
|
|
1619
|
+
toProcess = await ms.ask("Select entries to import as movies:", items) ?? [];
|
|
1620
|
+
spinner_default.start();
|
|
1621
|
+
skipped += pendingMovies.length - toProcess.length;
|
|
1622
|
+
} else if (force) {
|
|
1623
|
+
toProcess = pendingMovies;
|
|
1624
|
+
} else {
|
|
1625
|
+
skipped += pendingMovies.length;
|
|
1626
|
+
}
|
|
1627
|
+
for (const p of toProcess) {
|
|
1628
|
+
const { tmdbId, resolvedTitle, resolvedYear } = await lookupMovie(p.parsed);
|
|
1629
|
+
if (await importMovie(p.entry, p.entryPath, p.isDir, resolvedTitle, resolvedYear, tmdbId, p.destRoot)) {
|
|
1630
|
+
imported++;
|
|
1631
|
+
} else {
|
|
1449
1632
|
skipped++;
|
|
1450
|
-
continue;
|
|
1451
1633
|
}
|
|
1452
|
-
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
1453
|
-
const destVideoName = `${folderName}.${videoExt}`;
|
|
1454
|
-
const videoSourcePath = isDir ? (0, import_path11.resolve)(entryPath, videoFile) : entryPath;
|
|
1455
|
-
const dirFiles = isDir ? (0, import_fs10.readdirSync)(entryPath) : [];
|
|
1456
|
-
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
1457
|
-
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
1458
|
-
const subtitleSourcePath = subtitle ? (0, import_path11.resolve)(entryPath, subtitle) : null;
|
|
1459
|
-
const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
|
|
1460
|
-
if (!dryRun) {
|
|
1461
|
-
if (useHardlink) {
|
|
1462
|
-
(0, import_fs10.mkdirSync)(destFolder, { recursive: true });
|
|
1463
|
-
const destVideoPath = (0, import_path11.resolve)(destFolder, destVideoName);
|
|
1464
|
-
let mode;
|
|
1465
|
-
try {
|
|
1466
|
-
if (!sameDev(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
|
|
1467
|
-
(0, import_fs10.linkSync)(videoSourcePath, destVideoPath);
|
|
1468
|
-
mode = "hardlink";
|
|
1469
|
-
} catch {
|
|
1470
|
-
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
1471
|
-
(0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
|
|
1472
|
-
mode = "copy";
|
|
1473
|
-
}
|
|
1474
|
-
if (subtitleSourcePath && destSubtitleName) (0, import_fs10.cpSync)(subtitleSourcePath, (0, import_path11.resolve)(destFolder, destSubtitleName));
|
|
1475
|
-
recordImport(sessionId, entryPath, destFolder, mode, tmdbId);
|
|
1476
|
-
} else {
|
|
1477
|
-
if (isDir) {
|
|
1478
|
-
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
1479
|
-
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0, import_fs10.rmSync)((0, import_path11.resolve)(entryPath, f), { recursive: true, force: true });
|
|
1480
|
-
(0, import_fs10.renameSync)(videoSourcePath, (0, import_path11.resolve)(entryPath, destVideoName));
|
|
1481
|
-
if (subtitleSourcePath && destSubtitleName) (0, import_fs10.renameSync)(subtitleSourcePath, (0, import_path11.resolve)(entryPath, destSubtitleName));
|
|
1482
|
-
moveFolder(entryPath, destFolder);
|
|
1483
|
-
} else {
|
|
1484
|
-
(0, import_fs10.mkdirSync)(destFolder, { recursive: true });
|
|
1485
|
-
const destVideoPath = (0, import_path11.resolve)(destFolder, destVideoName);
|
|
1486
|
-
if (sameDev(videoSourcePath, destRoot)) {
|
|
1487
|
-
(0, import_fs10.renameSync)(videoSourcePath, destVideoPath);
|
|
1488
|
-
} else {
|
|
1489
|
-
(0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
|
|
1490
|
-
(0, import_fs10.rmSync)(videoSourcePath);
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
recordImport(sessionId, entryPath, destFolder, "move", tmdbId);
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${folderName}`);
|
|
1497
|
-
imported++;
|
|
1498
1634
|
}
|
|
1499
1635
|
}
|
|
1500
1636
|
spinner_default.succeed(`imported ${imported} items`);
|
|
@@ -1517,7 +1653,7 @@ var undo = async () => {
|
|
|
1517
1653
|
let undone = 0;
|
|
1518
1654
|
for (const record of records) {
|
|
1519
1655
|
(0, import_fs11.renameSync)(record.newPath, record.oldPath);
|
|
1520
|
-
spinner_default.succeed(`${import_termkit11.Color.
|
|
1656
|
+
spinner_default.succeed(`${import_termkit11.Color.green.encoder(record.newPath)} \u2192 ${import_termkit11.Color.white.encoder(record.oldPath)}`);
|
|
1521
1657
|
undone++;
|
|
1522
1658
|
}
|
|
1523
1659
|
deleteSession(records[0].sessionId);
|
|
@@ -1552,6 +1688,10 @@ var findVideo2 = (dir) => (0, import_fs12.readdirSync)(dir).find((f) => {
|
|
|
1552
1688
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1553
1689
|
return ext && videoExtensions_default.includes(ext);
|
|
1554
1690
|
}) ?? null;
|
|
1691
|
+
var containsBook2 = (dir) => (0, import_fs12.readdirSync)(dir).some((f) => {
|
|
1692
|
+
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1693
|
+
return ext && bookExtensions_default.includes(ext);
|
|
1694
|
+
});
|
|
1555
1695
|
var findSeasonFolder2 = (showPath, season) => {
|
|
1556
1696
|
if (!(0, import_fs12.existsSync)(showPath)) return null;
|
|
1557
1697
|
const folders = (0, import_fs12.readdirSync)(showPath).filter((f) => {
|
|
@@ -1575,9 +1715,13 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
|
|
|
1575
1715
|
const isDir = (0, import_fs12.lstatSync)(entryPath).isDirectory();
|
|
1576
1716
|
const ext = entry.match(/([^.]+$)/)?.[0];
|
|
1577
1717
|
const isVideo = !isDir && ext && videoExtensions_default.includes(ext);
|
|
1578
|
-
|
|
1718
|
+
const isBook = !isDir && ext && bookExtensions_default.includes(ext);
|
|
1719
|
+
const isBookDir = isDir && containsBook2(entryPath);
|
|
1720
|
+
if (!isDir && !isVideo && !isBook) return;
|
|
1579
1721
|
let detectedType;
|
|
1580
|
-
if (
|
|
1722
|
+
if (isBook || isBookDir) {
|
|
1723
|
+
detectedType = "book";
|
|
1724
|
+
} else if (isDir && /(?<=\[).+?(?=\])/.test(entry)) {
|
|
1581
1725
|
detectedType = "ps3";
|
|
1582
1726
|
} else if (/S\d{2,3}E\d{2,3}/i.test(entry) || /\d+x\d{2,3}/i.test(entry)) {
|
|
1583
1727
|
detectedType = "tv";
|
|
@@ -1601,7 +1745,28 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
|
|
|
1601
1745
|
}
|
|
1602
1746
|
moveItem(entryPath, destPath);
|
|
1603
1747
|
recordImport(sessionId, entryPath, destPath, "move");
|
|
1604
|
-
spinner_default.succeed(`imported ${import_termkit12.Color.
|
|
1748
|
+
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(destName)}`);
|
|
1749
|
+
return;
|
|
1750
|
+
}
|
|
1751
|
+
if (detectedType === "book") {
|
|
1752
|
+
const destPath = (0, import_path12.resolve)(destRoot, entry);
|
|
1753
|
+
if ((0, import_fs12.existsSync)(destPath)) {
|
|
1754
|
+
spinner_default.warn(`already exists: ${entry}`);
|
|
1755
|
+
return;
|
|
1756
|
+
}
|
|
1757
|
+
if (isDir || isBookDir) {
|
|
1758
|
+
moveItem(entryPath, destPath);
|
|
1759
|
+
} else {
|
|
1760
|
+
(0, import_fs12.mkdirSync)(destRoot, { recursive: true });
|
|
1761
|
+
if (sameDev2(entryPath, destRoot)) {
|
|
1762
|
+
(0, import_fs12.renameSync)(entryPath, destPath);
|
|
1763
|
+
} else {
|
|
1764
|
+
(0, import_fs12.cpSync)(entryPath, destPath);
|
|
1765
|
+
(0, import_fs12.rmSync)(entryPath);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
recordImport(sessionId, entryPath, destPath, "move");
|
|
1769
|
+
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(entry)}`);
|
|
1605
1770
|
return;
|
|
1606
1771
|
}
|
|
1607
1772
|
const parsed = parseDownloadName(entry);
|
|
@@ -1674,7 +1839,7 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
|
|
|
1674
1839
|
if (isDir) (0, import_fs12.rmSync)(entryPath, { recursive: true, force: true });
|
|
1675
1840
|
}
|
|
1676
1841
|
recordImport(sessionId, entryPath, seasonPath, mode);
|
|
1677
|
-
spinner_default.succeed(`imported ${import_termkit12.Color.
|
|
1842
|
+
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
1678
1843
|
return;
|
|
1679
1844
|
}
|
|
1680
1845
|
const edition = detectEdition(entry);
|
|
@@ -1731,7 +1896,7 @@ var processItem = async (entryPath, useHardlink, verbose, language, auto) => {
|
|
|
1731
1896
|
}
|
|
1732
1897
|
recordImport(sessionId, entryPath, destFolder, "move");
|
|
1733
1898
|
}
|
|
1734
|
-
spinner_default.succeed(`imported ${import_termkit12.Color.
|
|
1899
|
+
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(folderName)}`);
|
|
1735
1900
|
};
|
|
1736
1901
|
var watch = async ({ hardlink = false, verbose = false, auto = false }) => {
|
|
1737
1902
|
const config = getConfig();
|
|
@@ -1762,7 +1927,7 @@ var watch = async ({ hardlink = false, verbose = false, auto = false }) => {
|
|
|
1762
1927
|
watcher.on("add", handle);
|
|
1763
1928
|
spinner_default.start();
|
|
1764
1929
|
spinner_default.succeed(`watching ${config.sources.length} source${config.sources.length !== 1 ? "s" : ""}`);
|
|
1765
|
-
for (const s of config.sources) spinner_default.info(` ${import_termkit12.Color.
|
|
1930
|
+
for (const s of config.sources) spinner_default.info(` ${import_termkit12.Color.white.encoder(s)}`);
|
|
1766
1931
|
spinner_default.stop();
|
|
1767
1932
|
process.stdin.resume();
|
|
1768
1933
|
};
|