reelsort 0.2.5 → 0.2.7
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 +266 -172
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +252 -158
- package/dist/index.mjs +157 -63
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1150,8 +1150,8 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1150
1150
|
var reset_default = reset;
|
|
1151
1151
|
|
|
1152
1152
|
// src/actions/scan.ts
|
|
1153
|
-
var
|
|
1154
|
-
var
|
|
1153
|
+
var import_fs11 = require("fs");
|
|
1154
|
+
var import_path12 = require("path");
|
|
1155
1155
|
var import_termkit10 = require("termkit");
|
|
1156
1156
|
|
|
1157
1157
|
// src/helpers/detectEdition.ts
|
|
@@ -1175,10 +1175,49 @@ var detectEdition = (filename) => {
|
|
|
1175
1175
|
// src/helpers/hyperlink.ts
|
|
1176
1176
|
var hyperlink = (url, text) => `\x1B]8;;${url}\x1B\\${text}\x1B]8;;\x1B\\`;
|
|
1177
1177
|
|
|
1178
|
+
// src/helpers/trash.ts
|
|
1179
|
+
var import_child_process2 = require("child_process");
|
|
1180
|
+
var import_fs10 = require("fs");
|
|
1181
|
+
var import_os3 = require("os");
|
|
1182
|
+
var import_path11 = require("path");
|
|
1183
|
+
var trashDir = () => {
|
|
1184
|
+
if (process.platform === "linux") return (0, import_path11.join)((0, import_os3.homedir)(), ".local", "share", "Trash", "files");
|
|
1185
|
+
return (0, import_path11.join)((0, import_os3.homedir)(), ".Trash");
|
|
1186
|
+
};
|
|
1187
|
+
var trashPath = (filePath, opts) => {
|
|
1188
|
+
if (process.platform === "darwin" || process.platform === "linux") {
|
|
1189
|
+
try {
|
|
1190
|
+
const dir = trashDir();
|
|
1191
|
+
if (!(0, import_fs10.existsSync)(dir)) (0, import_fs10.mkdirSync)(dir, { recursive: true });
|
|
1192
|
+
const name = (0, import_path11.basename)(filePath);
|
|
1193
|
+
let dest = (0, import_path11.join)(dir, name);
|
|
1194
|
+
let i = 1;
|
|
1195
|
+
while ((0, import_fs10.existsSync)(dest)) dest = (0, import_path11.join)(dir, `${name} ${i++}`);
|
|
1196
|
+
(0, import_fs10.renameSync)(filePath, dest);
|
|
1197
|
+
return;
|
|
1198
|
+
} catch {
|
|
1199
|
+
}
|
|
1200
|
+
} else if (process.platform === "win32") {
|
|
1201
|
+
try {
|
|
1202
|
+
const escaped = filePath.replace(/'/g, "''");
|
|
1203
|
+
const isDir = opts?.recursive ?? false;
|
|
1204
|
+
const method = isDir ? "DeleteDirectory" : "DeleteFile";
|
|
1205
|
+
(0, import_child_process2.execSync)(
|
|
1206
|
+
`powershell -NoProfile -NonInteractive -Command "Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::${method}('${escaped}', 'OnlyErrorDialogs', 'SendToRecycleBin')"`,
|
|
1207
|
+
{ stdio: "ignore" }
|
|
1208
|
+
);
|
|
1209
|
+
return;
|
|
1210
|
+
} catch {
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
(0, import_fs10.rmSync)(filePath, { recursive: opts?.recursive ?? false, force: true });
|
|
1214
|
+
};
|
|
1215
|
+
|
|
1178
1216
|
// src/helpers/parseDownloadName.ts
|
|
1179
1217
|
var QUALITY_TOKENS = /* @__PURE__ */ new Set(["480p", "576p", "720p", "1080p", "2160p", "4k", "8k", "bluray", "bdrip", "bdremux", "brrip", "webrip", "web-dl", "webdl", "web", "hdtv", "dvdrip", "dvdscr", "cam", "ts", "scr", "x264", "x265", "hevc", "avc", "h264", "h265", "xvid", "divx", "dts", "ac3", "aac", "mp3", "truehd", "atmos", "dd5", "hdr", "hdr10", "hlg", "dv", "dolby", "remux", "proper", "repack", "extended", "theatrical", "unrated", "multi", "dubbed", "subbed", "internal"]);
|
|
1180
1218
|
var TV_PATTERN = /^(.*?)[.\s_-]*(?:S(\d{2,3})E(\d{2,3})|(\d{1,2})x(\d{2,3})|Season[\s.](\d+))/i;
|
|
1181
|
-
var parseDownloadName = (
|
|
1219
|
+
var parseDownloadName = (rawName) => {
|
|
1220
|
+
const name = rawName.replace(/^[\w.-]+\.\w{2,6}\s+-+\s+/i, "");
|
|
1182
1221
|
const base = name.replace(/\.[a-z0-9]{2,4}$/i, "");
|
|
1183
1222
|
const tvMatch = TV_PATTERN.exec(base);
|
|
1184
1223
|
if (tvMatch) {
|
|
@@ -1277,45 +1316,63 @@ var bookExtensions_default = ["epub", "mobi", "azw3", "azw"];
|
|
|
1277
1316
|
var sameDev = (a, b) => {
|
|
1278
1317
|
try {
|
|
1279
1318
|
let bExisting = b;
|
|
1280
|
-
while (!(0,
|
|
1281
|
-
return (0,
|
|
1319
|
+
while (!(0, import_fs11.existsSync)(bExisting)) bExisting = (0, import_path12.dirname)(bExisting);
|
|
1320
|
+
return (0, import_fs11.statSync)(a).dev === (0, import_fs11.statSync)(bExisting).dev;
|
|
1282
1321
|
} catch {
|
|
1283
1322
|
return false;
|
|
1284
1323
|
}
|
|
1285
1324
|
};
|
|
1286
1325
|
var moveFolder = (src, dest) => {
|
|
1287
1326
|
if (sameDev(src, dest)) {
|
|
1288
|
-
(0,
|
|
1327
|
+
(0, import_fs11.renameSync)(src, dest);
|
|
1289
1328
|
} else {
|
|
1290
|
-
(0,
|
|
1291
|
-
(
|
|
1329
|
+
(0, import_fs11.cpSync)(src, dest, { recursive: true });
|
|
1330
|
+
trashPath(src, { recursive: true });
|
|
1292
1331
|
}
|
|
1293
1332
|
};
|
|
1294
|
-
var findVideo = (dir) => (0,
|
|
1333
|
+
var findVideo = (dir) => (0, import_fs11.readdirSync)(dir).find((f) => {
|
|
1295
1334
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1296
1335
|
return ext && videoExtensions_default.includes(ext);
|
|
1297
1336
|
}) ?? null;
|
|
1298
|
-
var containsBook = (dir, depth = 2) => (0,
|
|
1337
|
+
var containsBook = (dir, depth = 2) => (0, import_fs11.readdirSync)(dir).some((f) => {
|
|
1299
1338
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1300
1339
|
if (ext && bookExtensions_default.includes(ext)) return true;
|
|
1301
1340
|
if (depth > 1) {
|
|
1302
1341
|
try {
|
|
1303
|
-
const sub = (0,
|
|
1304
|
-
if ((0,
|
|
1342
|
+
const sub = (0, import_path12.resolve)(dir, f);
|
|
1343
|
+
if ((0, import_fs11.lstatSync)(sub).isDirectory()) return containsBook(sub, depth - 1);
|
|
1305
1344
|
} catch {
|
|
1306
1345
|
}
|
|
1307
1346
|
}
|
|
1308
1347
|
return false;
|
|
1309
1348
|
});
|
|
1349
|
+
var containsPdf = (dir) => {
|
|
1350
|
+
try {
|
|
1351
|
+
return (0, import_fs11.readdirSync)(dir).some((f) => /\.pdf$/i.test(f));
|
|
1352
|
+
} catch {
|
|
1353
|
+
return false;
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1356
|
+
var countVideos = (dir) => {
|
|
1357
|
+
try {
|
|
1358
|
+
return (0, import_fs11.readdirSync)(dir).filter((f) => {
|
|
1359
|
+
if (/\bsample\b/i.test(f)) return false;
|
|
1360
|
+
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1361
|
+
return !!(ext && videoExtensions_default.includes(ext));
|
|
1362
|
+
}).length;
|
|
1363
|
+
} catch {
|
|
1364
|
+
return 0;
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1310
1367
|
var isTvEpisodeName = (name) => /S\d{2,3}E\d{2,3}/i.test(name) || /\d+x\d{2,3}/i.test(name);
|
|
1311
1368
|
var isSeasonDirName = (name) => !isTvEpisodeName(name) && /(?:^|[.\s_-])(?:season|s)\s*0*\d+(?:[.\s_-]|$)/i.test(name);
|
|
1312
1369
|
var gatherEntries = (source) => {
|
|
1313
1370
|
const result = [];
|
|
1314
|
-
for (const name of (0,
|
|
1315
|
-
const fullPath = (0,
|
|
1371
|
+
for (const name of (0, import_fs11.readdirSync)(source)) {
|
|
1372
|
+
const fullPath = (0, import_path12.resolve)(source, name);
|
|
1316
1373
|
let isDir;
|
|
1317
1374
|
try {
|
|
1318
|
-
isDir = (0,
|
|
1375
|
+
isDir = (0, import_fs11.lstatSync)(fullPath).isDirectory();
|
|
1319
1376
|
} catch {
|
|
1320
1377
|
continue;
|
|
1321
1378
|
}
|
|
@@ -1333,17 +1390,17 @@ var gatherEntries = (source) => {
|
|
|
1333
1390
|
}
|
|
1334
1391
|
let children;
|
|
1335
1392
|
try {
|
|
1336
|
-
children = (0,
|
|
1393
|
+
children = (0, import_fs11.readdirSync)(fullPath);
|
|
1337
1394
|
} catch {
|
|
1338
1395
|
result.push({ entry: name, entryPath: fullPath, isDir: true });
|
|
1339
1396
|
continue;
|
|
1340
1397
|
}
|
|
1341
1398
|
if (children.some((c) => isTvEpisodeName(c))) {
|
|
1342
1399
|
for (const child of children) {
|
|
1343
|
-
const childPath = (0,
|
|
1400
|
+
const childPath = (0, import_path12.resolve)(fullPath, child);
|
|
1344
1401
|
let childIsDir;
|
|
1345
1402
|
try {
|
|
1346
|
-
childIsDir = (0,
|
|
1403
|
+
childIsDir = (0, import_fs11.lstatSync)(childPath).isDirectory();
|
|
1347
1404
|
} catch {
|
|
1348
1405
|
continue;
|
|
1349
1406
|
}
|
|
@@ -1355,25 +1412,25 @@ var gatherEntries = (source) => {
|
|
|
1355
1412
|
}
|
|
1356
1413
|
const seasonDirs = children.filter((c) => {
|
|
1357
1414
|
try {
|
|
1358
|
-
return isSeasonDirName(c) && (0,
|
|
1415
|
+
return isSeasonDirName(c) && (0, import_fs11.lstatSync)((0, import_path12.resolve)(fullPath, c)).isDirectory();
|
|
1359
1416
|
} catch {
|
|
1360
1417
|
return false;
|
|
1361
1418
|
}
|
|
1362
1419
|
});
|
|
1363
1420
|
if (seasonDirs.length > 0) {
|
|
1364
1421
|
for (const seasonDir of seasonDirs) {
|
|
1365
|
-
const seasonPath = (0,
|
|
1422
|
+
const seasonPath = (0, import_path12.resolve)(fullPath, seasonDir);
|
|
1366
1423
|
let seasonChildren;
|
|
1367
1424
|
try {
|
|
1368
|
-
seasonChildren = (0,
|
|
1425
|
+
seasonChildren = (0, import_fs11.readdirSync)(seasonPath);
|
|
1369
1426
|
} catch {
|
|
1370
1427
|
continue;
|
|
1371
1428
|
}
|
|
1372
1429
|
for (const child of seasonChildren) {
|
|
1373
|
-
const childPath = (0,
|
|
1430
|
+
const childPath = (0, import_path12.resolve)(seasonPath, child);
|
|
1374
1431
|
let childIsDir;
|
|
1375
1432
|
try {
|
|
1376
|
-
childIsDir = (0,
|
|
1433
|
+
childIsDir = (0, import_fs11.lstatSync)(childPath).isDirectory();
|
|
1377
1434
|
} catch {
|
|
1378
1435
|
continue;
|
|
1379
1436
|
}
|
|
@@ -1389,19 +1446,19 @@ var gatherEntries = (source) => {
|
|
|
1389
1446
|
return result;
|
|
1390
1447
|
};
|
|
1391
1448
|
var findShowFolder = (destRoot, title) => {
|
|
1392
|
-
if (!(0,
|
|
1449
|
+
if (!(0, import_fs11.existsSync)(destRoot)) return null;
|
|
1393
1450
|
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1394
1451
|
const target = normalize(title);
|
|
1395
|
-
return (0,
|
|
1452
|
+
return (0, import_fs11.readdirSync)(destRoot).filter((f) => {
|
|
1396
1453
|
try {
|
|
1397
|
-
return (0,
|
|
1454
|
+
return (0, import_fs11.lstatSync)((0, import_path12.resolve)(destRoot, f)).isDirectory();
|
|
1398
1455
|
} catch {
|
|
1399
1456
|
return false;
|
|
1400
1457
|
}
|
|
1401
1458
|
}).find((f) => normalize(f) === target) ?? null;
|
|
1402
1459
|
};
|
|
1403
1460
|
var findShowFolderByContent = (destRoot, title) => {
|
|
1404
|
-
if (!(0,
|
|
1461
|
+
if (!(0, import_fs11.existsSync)(destRoot)) return null;
|
|
1405
1462
|
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1406
1463
|
const target = normalize(title);
|
|
1407
1464
|
const matchesTitle = (name) => {
|
|
@@ -1409,18 +1466,18 @@ var findShowFolderByContent = (destRoot, title) => {
|
|
|
1409
1466
|
const p = parseDownloadName(name);
|
|
1410
1467
|
return !!p && normalize(p.title) === target;
|
|
1411
1468
|
};
|
|
1412
|
-
for (const folder of (0,
|
|
1469
|
+
for (const folder of (0, import_fs11.readdirSync)(destRoot)) {
|
|
1413
1470
|
try {
|
|
1414
|
-
const folderPath = (0,
|
|
1415
|
-
if (!(0,
|
|
1416
|
-
const children = (0,
|
|
1471
|
+
const folderPath = (0, import_path12.resolve)(destRoot, folder);
|
|
1472
|
+
if (!(0, import_fs11.lstatSync)(folderPath).isDirectory()) continue;
|
|
1473
|
+
const children = (0, import_fs11.readdirSync)(folderPath);
|
|
1417
1474
|
if (children.some(matchesTitle)) return folder;
|
|
1418
1475
|
for (const child of children) {
|
|
1419
1476
|
if (!isSeasonDirName(child)) continue;
|
|
1420
1477
|
try {
|
|
1421
|
-
const seasonPath = (0,
|
|
1422
|
-
if (!(0,
|
|
1423
|
-
if ((0,
|
|
1478
|
+
const seasonPath = (0, import_path12.resolve)(folderPath, child);
|
|
1479
|
+
if (!(0, import_fs11.lstatSync)(seasonPath).isDirectory()) continue;
|
|
1480
|
+
if ((0, import_fs11.readdirSync)(seasonPath).some(matchesTitle)) return folder;
|
|
1424
1481
|
} catch {
|
|
1425
1482
|
}
|
|
1426
1483
|
}
|
|
@@ -1429,15 +1486,19 @@ var findShowFolderByContent = (destRoot, title) => {
|
|
|
1429
1486
|
}
|
|
1430
1487
|
return null;
|
|
1431
1488
|
};
|
|
1432
|
-
var findSeasonFolder = (showPath, season) => {
|
|
1433
|
-
if (!(0,
|
|
1434
|
-
const folders = (0,
|
|
1489
|
+
var findSeasonFolder = (showPath, season, specialsFolder) => {
|
|
1490
|
+
if (!(0, import_fs11.existsSync)(showPath)) return null;
|
|
1491
|
+
const folders = (0, import_fs11.readdirSync)(showPath).filter((f) => {
|
|
1435
1492
|
try {
|
|
1436
|
-
return (0,
|
|
1493
|
+
return (0, import_fs11.lstatSync)((0, import_path12.resolve)(showPath, f)).isDirectory();
|
|
1437
1494
|
} catch {
|
|
1438
1495
|
return false;
|
|
1439
1496
|
}
|
|
1440
1497
|
});
|
|
1498
|
+
if (season === 0 && specialsFolder) {
|
|
1499
|
+
const existing = folders.find((f) => f.toLowerCase() === specialsFolder.toLowerCase());
|
|
1500
|
+
if (existing) return existing;
|
|
1501
|
+
}
|
|
1441
1502
|
return folders.find((f) => {
|
|
1442
1503
|
const match = f.match(/(?:season|s)\s*0*(\d+)/i);
|
|
1443
1504
|
return match && parseInt(match[1]) === season;
|
|
@@ -1466,11 +1527,13 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1466
1527
|
const language = config.language ?? "eng";
|
|
1467
1528
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1468
1529
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
1530
|
+
const specialsFolder = config.specialsFolder ?? "Specials";
|
|
1469
1531
|
const lookupMovie = async (parsed) => {
|
|
1470
1532
|
let tmdbId;
|
|
1471
1533
|
let resolvedTitle = parsed.title;
|
|
1472
1534
|
let resolvedYear = parsed.year;
|
|
1473
1535
|
if (config.tmdbApiKey) {
|
|
1536
|
+
spinner_default.text = `TMDb: ${parsed.title}`;
|
|
1474
1537
|
const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
|
|
1475
1538
|
if (results.length === 1) {
|
|
1476
1539
|
tmdbId = results[0].id;
|
|
@@ -1498,8 +1561,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1498
1561
|
const importMovie = async (entry, entryPath, isDir, resolvedTitle, resolvedYear, tmdbId, destRoot) => {
|
|
1499
1562
|
const edition = detectEdition(entry);
|
|
1500
1563
|
const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
|
|
1501
|
-
const destFolder = (0,
|
|
1502
|
-
if ((0,
|
|
1564
|
+
const destFolder = (0, import_path12.resolve)(destRoot, folderName);
|
|
1565
|
+
if ((0, import_fs11.existsSync)(destFolder)) {
|
|
1503
1566
|
spinner_default.warn(`already exists: ${folderName}`);
|
|
1504
1567
|
return false;
|
|
1505
1568
|
}
|
|
@@ -1510,43 +1573,43 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1510
1573
|
}
|
|
1511
1574
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
1512
1575
|
const destVideoName = `${folderName}.${videoExt}`;
|
|
1513
|
-
const videoSourcePath = isDir ? (0,
|
|
1514
|
-
const dirFiles = isDir ? (0,
|
|
1576
|
+
const videoSourcePath = isDir ? (0, import_path12.resolve)(entryPath, videoFile) : entryPath;
|
|
1577
|
+
const dirFiles = isDir ? (0, import_fs11.readdirSync)(entryPath) : [];
|
|
1515
1578
|
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
1516
1579
|
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
1517
|
-
const subtitleSourcePath = subtitle ? (0,
|
|
1580
|
+
const subtitleSourcePath = subtitle ? (0, import_path12.resolve)(entryPath, subtitle) : null;
|
|
1518
1581
|
const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
|
|
1519
1582
|
if (!dryRun) {
|
|
1520
1583
|
if (useHardlink) {
|
|
1521
|
-
(0,
|
|
1522
|
-
const destVideoPath = (0,
|
|
1584
|
+
(0, import_fs11.mkdirSync)(destFolder, { recursive: true });
|
|
1585
|
+
const destVideoPath = (0, import_path12.resolve)(destFolder, destVideoName);
|
|
1523
1586
|
let mode;
|
|
1524
1587
|
try {
|
|
1525
1588
|
if (!sameDev(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
|
|
1526
|
-
(0,
|
|
1589
|
+
(0, import_fs11.linkSync)(videoSourcePath, destVideoPath);
|
|
1527
1590
|
mode = "hardlink";
|
|
1528
1591
|
} catch {
|
|
1529
1592
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
1530
|
-
(0,
|
|
1593
|
+
(0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
|
|
1531
1594
|
mode = "copy";
|
|
1532
1595
|
}
|
|
1533
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
1596
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs11.cpSync)(subtitleSourcePath, (0, import_path12.resolve)(destFolder, destSubtitleName));
|
|
1534
1597
|
recordImport(sessionId, entryPath, destFolder, mode, tmdbId, "movie");
|
|
1535
1598
|
} else {
|
|
1536
1599
|
if (isDir) {
|
|
1537
1600
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
1538
|
-
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (
|
|
1539
|
-
(0,
|
|
1540
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
1601
|
+
for (const f of dirFiles.filter((f2) => !keep.has(f2))) trashPath((0, import_path12.resolve)(entryPath, f), { recursive: true });
|
|
1602
|
+
(0, import_fs11.renameSync)(videoSourcePath, (0, import_path12.resolve)(entryPath, destVideoName));
|
|
1603
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs11.renameSync)(subtitleSourcePath, (0, import_path12.resolve)(entryPath, destSubtitleName));
|
|
1541
1604
|
moveFolder(entryPath, destFolder);
|
|
1542
1605
|
} else {
|
|
1543
|
-
(0,
|
|
1544
|
-
const destVideoPath = (0,
|
|
1606
|
+
(0, import_fs11.mkdirSync)(destFolder, { recursive: true });
|
|
1607
|
+
const destVideoPath = (0, import_path12.resolve)(destFolder, destVideoName);
|
|
1545
1608
|
if (sameDev(videoSourcePath, destRoot)) {
|
|
1546
|
-
(0,
|
|
1609
|
+
(0, import_fs11.renameSync)(videoSourcePath, destVideoPath);
|
|
1547
1610
|
} else {
|
|
1548
|
-
(0,
|
|
1549
|
-
(
|
|
1611
|
+
(0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
|
|
1612
|
+
trashPath(videoSourcePath);
|
|
1550
1613
|
}
|
|
1551
1614
|
}
|
|
1552
1615
|
recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
|
|
@@ -1560,15 +1623,18 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1560
1623
|
let imported = 0, skipped = 0;
|
|
1561
1624
|
const pendingMovies = [];
|
|
1562
1625
|
const pendingTv = [];
|
|
1626
|
+
const pendingBooks = [];
|
|
1627
|
+
const pendingAnime = [];
|
|
1563
1628
|
const ignoreSet = new Set(config.ignore ?? []);
|
|
1564
1629
|
const seenIgnored = /* @__PURE__ */ new Set();
|
|
1565
1630
|
for (const source of config.sources) {
|
|
1566
|
-
if (!(0,
|
|
1631
|
+
if (!(0, import_fs11.existsSync)(source)) {
|
|
1567
1632
|
spinner_default.warn(`source not found: ${import_termkit10.Color.white.encoder(source)}`);
|
|
1568
1633
|
continue;
|
|
1569
1634
|
}
|
|
1570
1635
|
spinner_default.text = `scanning ${import_termkit10.Color.white.encoder(source)}`;
|
|
1571
1636
|
for (const { entry, entryPath, isDir } of gatherEntries(source)) {
|
|
1637
|
+
spinner_default.text = `scanning: ${entry}`;
|
|
1572
1638
|
if (ignoreSet.has(entry)) {
|
|
1573
1639
|
seenIgnored.add(entry);
|
|
1574
1640
|
if (isVerbose()) spinner_default.info(`ignored: ${entry}`);
|
|
@@ -1603,8 +1669,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1603
1669
|
continue;
|
|
1604
1670
|
}
|
|
1605
1671
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
1606
|
-
const destPath = (0,
|
|
1607
|
-
if ((0,
|
|
1672
|
+
const destPath = (0, import_path12.resolve)(destRoot, destName);
|
|
1673
|
+
if ((0, import_fs11.existsSync)(destPath)) {
|
|
1608
1674
|
spinner_default.warn(`already exists: ${destName}`);
|
|
1609
1675
|
skipped++;
|
|
1610
1676
|
continue;
|
|
@@ -1618,8 +1684,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1618
1684
|
continue;
|
|
1619
1685
|
}
|
|
1620
1686
|
if (detectedType === "book") {
|
|
1621
|
-
const destPath = (0,
|
|
1622
|
-
if ((0,
|
|
1687
|
+
const destPath = (0, import_path12.resolve)(destRoot, entry);
|
|
1688
|
+
if ((0, import_fs11.existsSync)(destPath)) {
|
|
1623
1689
|
spinner_default.warn(`already exists: ${entry}`);
|
|
1624
1690
|
skipped++;
|
|
1625
1691
|
continue;
|
|
@@ -1628,12 +1694,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1628
1694
|
if (isDir || isBookDir) {
|
|
1629
1695
|
moveFolder(entryPath, destPath);
|
|
1630
1696
|
} else {
|
|
1631
|
-
(0,
|
|
1697
|
+
(0, import_fs11.mkdirSync)(destRoot, { recursive: true });
|
|
1632
1698
|
if (sameDev(entryPath, destRoot)) {
|
|
1633
|
-
(0,
|
|
1699
|
+
(0, import_fs11.renameSync)(entryPath, destPath);
|
|
1634
1700
|
} else {
|
|
1635
|
-
(0,
|
|
1636
|
-
(0,
|
|
1701
|
+
(0, import_fs11.cpSync)(entryPath, destPath);
|
|
1702
|
+
(0, import_fs11.rmSync)(entryPath);
|
|
1637
1703
|
}
|
|
1638
1704
|
}
|
|
1639
1705
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
@@ -1642,6 +1708,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1642
1708
|
imported++;
|
|
1643
1709
|
continue;
|
|
1644
1710
|
}
|
|
1711
|
+
if (detectedType === "movie" && isDir) {
|
|
1712
|
+
const videoCount = countVideos(entryPath);
|
|
1713
|
+
if (videoCount === 0) {
|
|
1714
|
+
if (containsPdf(entryPath)) {
|
|
1715
|
+
pendingBooks.push({ entry, entryPath });
|
|
1716
|
+
} else if (isVerbose()) {
|
|
1717
|
+
spinner_default.info(`no media found, skipped: ${entry}`);
|
|
1718
|
+
}
|
|
1719
|
+
skipped++;
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
if (videoCount >= 2) {
|
|
1723
|
+
pendingAnime.push({ entry, entryPath, videoCount });
|
|
1724
|
+
continue;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1645
1727
|
const parsed = parseDownloadName(entry);
|
|
1646
1728
|
if (!parsed) {
|
|
1647
1729
|
if (isVerbose()) spinner_default.info(`could not parse: ${entry}`);
|
|
@@ -1665,6 +1747,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1665
1747
|
let resolvedYear = parsed.year;
|
|
1666
1748
|
if (config.tmdbApiKey) {
|
|
1667
1749
|
if (detectedType === "tv") {
|
|
1750
|
+
spinner_default.text = `TMDb: ${parsed.title}`;
|
|
1668
1751
|
const results = await searchTv(parsed.title, config.tmdbApiKey);
|
|
1669
1752
|
if (results.length === 1) {
|
|
1670
1753
|
tmdbId = results[0].id;
|
|
@@ -1709,18 +1792,18 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1709
1792
|
const existingFolder = findShowFolder(destRoot, resolvedTitle) ?? findShowFolderByContent(destRoot, resolvedTitle);
|
|
1710
1793
|
if (existingFolder) {
|
|
1711
1794
|
showFolderName = existingFolder;
|
|
1712
|
-
showPath = (0,
|
|
1795
|
+
showPath = (0, import_path12.resolve)(destRoot, existingFolder);
|
|
1713
1796
|
} else if (auto) {
|
|
1714
1797
|
showFolderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear);
|
|
1715
|
-
showPath = (0,
|
|
1798
|
+
showPath = (0, import_path12.resolve)(destRoot, showFolderName);
|
|
1716
1799
|
if (!dryRun) upsertShow(showPath, tmdbId ?? null, resolvedTitle);
|
|
1717
1800
|
} else {
|
|
1718
1801
|
pendingTv.push({ entry, entryPath, isDir, parsed, resolvedTitle, destRoot });
|
|
1719
1802
|
continue;
|
|
1720
1803
|
}
|
|
1721
1804
|
}
|
|
1722
|
-
const seasonFolderName = findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
1723
|
-
const seasonPath = (0,
|
|
1805
|
+
const seasonFolderName = parsed.season === 0 ? findSeasonFolder(showPath, 0, specialsFolder) ?? specialsFolder : findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
1806
|
+
const seasonPath = (0, import_path12.resolve)(showPath, seasonFolderName);
|
|
1724
1807
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
1725
1808
|
if (!videoFile) {
|
|
1726
1809
|
if (isVerbose()) spinner_default.info(`no video found in: ${entry}`);
|
|
@@ -1728,13 +1811,15 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1728
1811
|
continue;
|
|
1729
1812
|
}
|
|
1730
1813
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
1814
|
+
if (tmdbId && config.tmdbApiKey) spinner_default.text = `TMDb: episode name for ${resolvedTitle}`;
|
|
1731
1815
|
const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
1732
1816
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
|
|
1733
1817
|
const destVideoName = `${episodeName}.${videoExt}`;
|
|
1734
|
-
const destVideoPath = (0,
|
|
1735
|
-
const videoSourcePath = isDir ? (0,
|
|
1736
|
-
if ((0,
|
|
1737
|
-
|
|
1818
|
+
const destVideoPath = (0, import_path12.resolve)(seasonPath, destVideoName);
|
|
1819
|
+
const videoSourcePath = isDir ? (0, import_path12.resolve)(entryPath, videoFile) : entryPath;
|
|
1820
|
+
if ((0, import_fs11.existsSync)(destVideoPath)) {
|
|
1821
|
+
const isRepack = /\brepack\d*\b|\bproper\b/i.test(entry);
|
|
1822
|
+
let shouldReplace = force || isRepack;
|
|
1738
1823
|
if (!shouldReplace && interactive) {
|
|
1739
1824
|
spinner_default.stop();
|
|
1740
1825
|
const select = new import_termkit10.Select();
|
|
@@ -1751,39 +1836,39 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1751
1836
|
continue;
|
|
1752
1837
|
}
|
|
1753
1838
|
if (!dryRun) {
|
|
1754
|
-
for (const f of (0,
|
|
1755
|
-
if (f.startsWith(`${episodeName}.`)) (
|
|
1839
|
+
for (const f of (0, import_fs11.readdirSync)(seasonPath)) {
|
|
1840
|
+
if (f.startsWith(`${episodeName}.`)) trashPath((0, import_path12.resolve)(seasonPath, f));
|
|
1756
1841
|
}
|
|
1757
1842
|
}
|
|
1758
1843
|
}
|
|
1759
|
-
const dirFiles = isDir ? (0,
|
|
1844
|
+
const dirFiles = isDir ? (0, import_fs11.readdirSync)(entryPath) : [];
|
|
1760
1845
|
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
1761
1846
|
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
1762
|
-
const subtitleSourcePath = subtitle ? (0,
|
|
1847
|
+
const subtitleSourcePath = subtitle ? (0, import_path12.resolve)(entryPath, subtitle) : null;
|
|
1763
1848
|
const destSubtitleName = subtitle && subtitleExt ? `${episodeName}.${subtitleExt}` : null;
|
|
1764
1849
|
if (!dryRun) {
|
|
1765
|
-
(0,
|
|
1850
|
+
(0, import_fs11.mkdirSync)(seasonPath, { recursive: true });
|
|
1766
1851
|
let mode = "move";
|
|
1767
1852
|
if (useHardlink) {
|
|
1768
1853
|
try {
|
|
1769
1854
|
if (!sameDev(videoSourcePath, seasonPath)) throw new Error("cross-filesystem");
|
|
1770
|
-
(0,
|
|
1855
|
+
(0, import_fs11.linkSync)(videoSourcePath, destVideoPath);
|
|
1771
1856
|
mode = "hardlink";
|
|
1772
1857
|
} catch {
|
|
1773
1858
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
1774
|
-
(0,
|
|
1859
|
+
(0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
|
|
1775
1860
|
mode = "copy";
|
|
1776
1861
|
}
|
|
1777
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
1862
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs11.cpSync)(subtitleSourcePath, (0, import_path12.resolve)(seasonPath, destSubtitleName));
|
|
1778
1863
|
} else {
|
|
1779
1864
|
if (sameDev(videoSourcePath, seasonPath)) {
|
|
1780
|
-
(0,
|
|
1865
|
+
(0, import_fs11.renameSync)(videoSourcePath, destVideoPath);
|
|
1781
1866
|
} else {
|
|
1782
|
-
(0,
|
|
1783
|
-
(
|
|
1867
|
+
(0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
|
|
1868
|
+
trashPath(videoSourcePath);
|
|
1784
1869
|
}
|
|
1785
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
1786
|
-
if (isDir) (
|
|
1870
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs11.renameSync)(subtitleSourcePath, (0, import_path12.resolve)(seasonPath, destSubtitleName));
|
|
1871
|
+
if (isDir) trashPath(entryPath, { recursive: true });
|
|
1787
1872
|
}
|
|
1788
1873
|
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
|
|
1789
1874
|
}
|
|
@@ -1832,6 +1917,15 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1832
1917
|
for (const p of pendingTv) spinner_default.info(` ${typeGlyph("tv")} ${p.resolvedTitle} \u2014 ${p.entry.replace(/\/$/, "")}${typeTag("tv")}`);
|
|
1833
1918
|
skipped += pendingTv.length;
|
|
1834
1919
|
}
|
|
1920
|
+
if (pendingBooks.length > 0) {
|
|
1921
|
+
spinner_default.warn(`${pendingBooks.length} uncertain book${pendingBooks.length > 1 ? "s" : ""} skipped \u2014 contains PDFs, review manually`);
|
|
1922
|
+
for (const p of pendingBooks) spinner_default.info(` ${typeGlyph("book")} ${p.entry}${typeTag("book")}`);
|
|
1923
|
+
}
|
|
1924
|
+
if (pendingAnime.length > 0) {
|
|
1925
|
+
spinner_default.warn(`${pendingAnime.length} uncertain anime/TV director${pendingAnime.length > 1 ? "ies" : "y"} skipped \u2014 multiple videos with no episode naming`);
|
|
1926
|
+
for (const p of pendingAnime) spinner_default.info(` ${typeGlyph("tv")} ${p.entry} (${p.videoCount} video${p.videoCount > 1 ? "s" : ""})${typeTag("tv")}`);
|
|
1927
|
+
skipped += pendingAnime.length;
|
|
1928
|
+
}
|
|
1835
1929
|
if (ignoreSet.size > 0) {
|
|
1836
1930
|
const stale = [...ignoreSet].filter((name) => !seenIgnored.has(name));
|
|
1837
1931
|
if (stale.length > 0 && !dryRun) {
|
|
@@ -1848,7 +1942,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1848
1942
|
var scan_default = scan;
|
|
1849
1943
|
|
|
1850
1944
|
// src/actions/undo.ts
|
|
1851
|
-
var
|
|
1945
|
+
var import_fs12 = require("fs");
|
|
1852
1946
|
var import_termkit11 = require("termkit");
|
|
1853
1947
|
var undo = async () => {
|
|
1854
1948
|
spinner_default.start();
|
|
@@ -1863,7 +1957,7 @@ var undo = async () => {
|
|
|
1863
1957
|
if (!useImports) {
|
|
1864
1958
|
let undone2 = 0;
|
|
1865
1959
|
for (const record of renameRecords) {
|
|
1866
|
-
(0,
|
|
1960
|
+
(0, import_fs12.renameSync)(record.newPath, record.oldPath);
|
|
1867
1961
|
spinner_default.succeed(`${import_termkit11.Color.green.encoder(record.newPath)} \u2192 ${import_termkit11.Color.white.encoder(record.oldPath)}`);
|
|
1868
1962
|
undone2++;
|
|
1869
1963
|
}
|
|
@@ -1885,12 +1979,12 @@ var undo = async () => {
|
|
|
1885
1979
|
skipped++;
|
|
1886
1980
|
continue;
|
|
1887
1981
|
}
|
|
1888
|
-
if (!(0,
|
|
1982
|
+
if (!(0, import_fs12.existsSync)(record.destinationPath)) {
|
|
1889
1983
|
spinner_default.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
|
|
1890
1984
|
skipped++;
|
|
1891
1985
|
continue;
|
|
1892
1986
|
}
|
|
1893
|
-
(0,
|
|
1987
|
+
(0, import_fs12.renameSync)(record.destinationPath, record.sourcePath);
|
|
1894
1988
|
spinner_default.succeed(`${import_termkit11.Color.green.encoder(record.destinationPath)} \u2192 ${import_termkit11.Color.white.encoder(record.sourcePath)}`);
|
|
1895
1989
|
undone++;
|
|
1896
1990
|
}
|
|
@@ -1903,37 +1997,37 @@ var undo_default = undo;
|
|
|
1903
1997
|
|
|
1904
1998
|
// src/actions/watch.ts
|
|
1905
1999
|
var import_chokidar = __toESM(require("chokidar"));
|
|
1906
|
-
var
|
|
1907
|
-
var
|
|
2000
|
+
var import_fs13 = require("fs");
|
|
2001
|
+
var import_path13 = require("path");
|
|
1908
2002
|
var import_termkit12 = require("termkit");
|
|
1909
2003
|
var sameDev2 = (a, b) => {
|
|
1910
2004
|
try {
|
|
1911
2005
|
let bExisting = b;
|
|
1912
|
-
while (!(0,
|
|
1913
|
-
return (0,
|
|
2006
|
+
while (!(0, import_fs13.existsSync)(bExisting)) bExisting = (0, import_path13.dirname)(bExisting);
|
|
2007
|
+
return (0, import_fs13.statSync)(a).dev === (0, import_fs13.statSync)(bExisting).dev;
|
|
1914
2008
|
} catch {
|
|
1915
2009
|
return false;
|
|
1916
2010
|
}
|
|
1917
2011
|
};
|
|
1918
2012
|
var moveItem = (src, dest) => {
|
|
1919
2013
|
if (sameDev2(src, dest)) {
|
|
1920
|
-
(0,
|
|
2014
|
+
(0, import_fs13.renameSync)(src, dest);
|
|
1921
2015
|
} else {
|
|
1922
|
-
(0,
|
|
1923
|
-
(0,
|
|
2016
|
+
(0, import_fs13.cpSync)(src, dest, { recursive: true });
|
|
2017
|
+
(0, import_fs13.rmSync)(src, { recursive: true, force: true });
|
|
1924
2018
|
}
|
|
1925
2019
|
};
|
|
1926
|
-
var findVideo2 = (dir) => (0,
|
|
2020
|
+
var findVideo2 = (dir) => (0, import_fs13.readdirSync)(dir).find((f) => {
|
|
1927
2021
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1928
2022
|
return ext && videoExtensions_default.includes(ext);
|
|
1929
2023
|
}) ?? null;
|
|
1930
|
-
var containsBook2 = (dir, depth = 2) => (0,
|
|
2024
|
+
var containsBook2 = (dir, depth = 2) => (0, import_fs13.readdirSync)(dir).some((f) => {
|
|
1931
2025
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1932
2026
|
if (ext && bookExtensions_default.includes(ext)) return true;
|
|
1933
2027
|
if (depth > 1) {
|
|
1934
2028
|
try {
|
|
1935
|
-
const sub = (0,
|
|
1936
|
-
if ((0,
|
|
2029
|
+
const sub = (0, import_path13.resolve)(dir, f);
|
|
2030
|
+
if ((0, import_fs13.lstatSync)(sub).isDirectory()) return containsBook2(sub, depth - 1);
|
|
1937
2031
|
} catch {
|
|
1938
2032
|
}
|
|
1939
2033
|
}
|
|
@@ -1944,26 +2038,26 @@ var isSeasonDirName2 = (name) => !isTvEpisodeName2(name) && /(?:^|[.\s_-])(?:sea
|
|
|
1944
2038
|
var expandWatchPath = (p) => {
|
|
1945
2039
|
let isDir;
|
|
1946
2040
|
try {
|
|
1947
|
-
isDir = (0,
|
|
2041
|
+
isDir = (0, import_fs13.lstatSync)(p).isDirectory();
|
|
1948
2042
|
} catch {
|
|
1949
2043
|
return [p];
|
|
1950
2044
|
}
|
|
1951
2045
|
if (!isDir) return [p];
|
|
1952
|
-
const name = (0,
|
|
2046
|
+
const name = (0, import_path13.basename)(p);
|
|
1953
2047
|
if (isTvEpisodeName2(name) || /(?<=\[).+?(?=\])/.test(name) || /^[A-Z]{4}\d{5}/i.test(name)) return [p];
|
|
1954
2048
|
let children;
|
|
1955
2049
|
try {
|
|
1956
|
-
children = (0,
|
|
2050
|
+
children = (0, import_fs13.readdirSync)(p);
|
|
1957
2051
|
} catch {
|
|
1958
2052
|
return [p];
|
|
1959
2053
|
}
|
|
1960
2054
|
if (children.some((c) => isTvEpisodeName2(c))) {
|
|
1961
2055
|
const entries = [];
|
|
1962
2056
|
for (const child of children) {
|
|
1963
|
-
const cp = (0,
|
|
2057
|
+
const cp = (0, import_path13.resolve)(p, child);
|
|
1964
2058
|
let cd;
|
|
1965
2059
|
try {
|
|
1966
|
-
cd = (0,
|
|
2060
|
+
cd = (0, import_fs13.lstatSync)(cp).isDirectory();
|
|
1967
2061
|
} catch {
|
|
1968
2062
|
continue;
|
|
1969
2063
|
}
|
|
@@ -1975,7 +2069,7 @@ var expandWatchPath = (p) => {
|
|
|
1975
2069
|
}
|
|
1976
2070
|
const seasonDirs = children.filter((c) => {
|
|
1977
2071
|
try {
|
|
1978
|
-
return isSeasonDirName2(c) && (0,
|
|
2072
|
+
return isSeasonDirName2(c) && (0, import_fs13.lstatSync)((0, import_path13.resolve)(p, c)).isDirectory();
|
|
1979
2073
|
} catch {
|
|
1980
2074
|
return false;
|
|
1981
2075
|
}
|
|
@@ -1983,18 +2077,18 @@ var expandWatchPath = (p) => {
|
|
|
1983
2077
|
if (seasonDirs.length > 0) {
|
|
1984
2078
|
const entries = [];
|
|
1985
2079
|
for (const sd of seasonDirs) {
|
|
1986
|
-
const sp = (0,
|
|
2080
|
+
const sp = (0, import_path13.resolve)(p, sd);
|
|
1987
2081
|
let sc;
|
|
1988
2082
|
try {
|
|
1989
|
-
sc = (0,
|
|
2083
|
+
sc = (0, import_fs13.readdirSync)(sp);
|
|
1990
2084
|
} catch {
|
|
1991
2085
|
continue;
|
|
1992
2086
|
}
|
|
1993
2087
|
for (const child of sc) {
|
|
1994
|
-
const cp = (0,
|
|
2088
|
+
const cp = (0, import_path13.resolve)(sp, child);
|
|
1995
2089
|
let cd;
|
|
1996
2090
|
try {
|
|
1997
|
-
cd = (0,
|
|
2091
|
+
cd = (0, import_fs13.lstatSync)(cp).isDirectory();
|
|
1998
2092
|
} catch {
|
|
1999
2093
|
continue;
|
|
2000
2094
|
}
|
|
@@ -2008,10 +2102,10 @@ var expandWatchPath = (p) => {
|
|
|
2008
2102
|
return [p];
|
|
2009
2103
|
};
|
|
2010
2104
|
var findSeasonFolder2 = (showPath, season) => {
|
|
2011
|
-
if (!(0,
|
|
2012
|
-
const folders = (0,
|
|
2105
|
+
if (!(0, import_fs13.existsSync)(showPath)) return null;
|
|
2106
|
+
const folders = (0, import_fs13.readdirSync)(showPath).filter((f) => {
|
|
2013
2107
|
try {
|
|
2014
|
-
return (0,
|
|
2108
|
+
return (0, import_fs13.lstatSync)((0, import_path13.resolve)(showPath, f)).isDirectory();
|
|
2015
2109
|
} catch {
|
|
2016
2110
|
return false;
|
|
2017
2111
|
}
|
|
@@ -2024,10 +2118,10 @@ var findSeasonFolder2 = (showPath, season) => {
|
|
|
2024
2118
|
var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
2025
2119
|
const config = getConfig();
|
|
2026
2120
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
2027
|
-
const entry = (0,
|
|
2121
|
+
const entry = (0, import_path13.basename)(entryPath);
|
|
2028
2122
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
2029
2123
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
2030
|
-
const isDir = (0,
|
|
2124
|
+
const isDir = (0, import_fs13.lstatSync)(entryPath).isDirectory();
|
|
2031
2125
|
const ext = entry.match(/([^.]+$)/)?.[0];
|
|
2032
2126
|
const isVideo = !isDir && ext && videoExtensions_default.includes(ext);
|
|
2033
2127
|
const isBook = !isDir && ext && bookExtensions_default.includes(ext);
|
|
@@ -2053,8 +2147,8 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2053
2147
|
const id = entry.split("-")[0];
|
|
2054
2148
|
if (!nameMatch || !id) return;
|
|
2055
2149
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
2056
|
-
const destPath = (0,
|
|
2057
|
-
if ((0,
|
|
2150
|
+
const destPath = (0, import_path13.resolve)(destRoot, destName);
|
|
2151
|
+
if ((0, import_fs13.existsSync)(destPath)) {
|
|
2058
2152
|
spinner_default.warn(`already exists: ${destName}`);
|
|
2059
2153
|
return;
|
|
2060
2154
|
}
|
|
@@ -2064,20 +2158,20 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2064
2158
|
return;
|
|
2065
2159
|
}
|
|
2066
2160
|
if (detectedType === "book") {
|
|
2067
|
-
const destPath = (0,
|
|
2068
|
-
if ((0,
|
|
2161
|
+
const destPath = (0, import_path13.resolve)(destRoot, entry);
|
|
2162
|
+
if ((0, import_fs13.existsSync)(destPath)) {
|
|
2069
2163
|
spinner_default.warn(`already exists: ${entry}`);
|
|
2070
2164
|
return;
|
|
2071
2165
|
}
|
|
2072
2166
|
if (isDir || isBookDir) {
|
|
2073
2167
|
moveItem(entryPath, destPath);
|
|
2074
2168
|
} else {
|
|
2075
|
-
(0,
|
|
2169
|
+
(0, import_fs13.mkdirSync)(destRoot, { recursive: true });
|
|
2076
2170
|
if (sameDev2(entryPath, destRoot)) {
|
|
2077
|
-
(0,
|
|
2171
|
+
(0, import_fs13.renameSync)(entryPath, destPath);
|
|
2078
2172
|
} else {
|
|
2079
|
-
(0,
|
|
2080
|
-
(0,
|
|
2173
|
+
(0, import_fs13.cpSync)(entryPath, destPath);
|
|
2174
|
+
(0, import_fs13.rmSync)(entryPath);
|
|
2081
2175
|
}
|
|
2082
2176
|
}
|
|
2083
2177
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
@@ -2102,14 +2196,14 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2102
2196
|
showFolderName = showPath.split("/").pop() ?? registeredShow.path;
|
|
2103
2197
|
} else if (auto) {
|
|
2104
2198
|
showFolderName = formatMovieName(movieFormat, parsed.title, parsed.year);
|
|
2105
|
-
showPath = (0,
|
|
2199
|
+
showPath = (0, import_path13.resolve)(destRoot, showFolderName);
|
|
2106
2200
|
upsertShow(showPath, null, parsed.title);
|
|
2107
2201
|
} else {
|
|
2108
2202
|
if (isVerbose()) spinner_default.info(`not registered, skipped: ${parsed.title} \u2014 run: reelsort add "${parsed.title}"`);
|
|
2109
2203
|
return;
|
|
2110
2204
|
}
|
|
2111
2205
|
const seasonFolderName = findSeasonFolder2(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
2112
|
-
const seasonPath = (0,
|
|
2206
|
+
const seasonPath = (0, import_path13.resolve)(showPath, seasonFolderName);
|
|
2113
2207
|
const videoFile2 = isDir ? findVideo2(entryPath) : entry;
|
|
2114
2208
|
if (!videoFile2) {
|
|
2115
2209
|
if (isVerbose()) spinner_default.info(`no video found in: ${entry}`);
|
|
@@ -2119,39 +2213,39 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2119
2213
|
const tmdbEpisodeName = registeredShow?.tmdbId && config.tmdbApiKey ? await getEpisodeName(registeredShow.tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
2120
2214
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, parsed.title, tmdbEpisodeName ?? void 0);
|
|
2121
2215
|
const destVideoName2 = `${episodeName}.${videoExt2}`;
|
|
2122
|
-
const destVideoPath = (0,
|
|
2123
|
-
const videoSourcePath2 = isDir ? (0,
|
|
2124
|
-
if ((0,
|
|
2216
|
+
const destVideoPath = (0, import_path13.resolve)(seasonPath, destVideoName2);
|
|
2217
|
+
const videoSourcePath2 = isDir ? (0, import_path13.resolve)(entryPath, videoFile2) : entryPath;
|
|
2218
|
+
if ((0, import_fs13.existsSync)(destVideoPath)) {
|
|
2125
2219
|
spinner_default.warn(`already exists: ${episodeName}`);
|
|
2126
2220
|
return;
|
|
2127
2221
|
}
|
|
2128
|
-
const dirFiles2 = isDir ? (0,
|
|
2222
|
+
const dirFiles2 = isDir ? (0, import_fs13.readdirSync)(entryPath) : [];
|
|
2129
2223
|
const subtitle2 = isDir ? findSubtitle(dirFiles2, language) : null;
|
|
2130
2224
|
const subtitleExt2 = subtitle2?.match(/([^.]+$)/)?.[0];
|
|
2131
|
-
const subtitleSourcePath2 = subtitle2 ? (0,
|
|
2225
|
+
const subtitleSourcePath2 = subtitle2 ? (0, import_path13.resolve)(entryPath, subtitle2) : null;
|
|
2132
2226
|
const destSubtitleName2 = subtitle2 && subtitleExt2 ? `${episodeName}.${subtitleExt2}` : null;
|
|
2133
|
-
(0,
|
|
2227
|
+
(0, import_fs13.mkdirSync)(seasonPath, { recursive: true });
|
|
2134
2228
|
let mode = "move";
|
|
2135
2229
|
if (useHardlink) {
|
|
2136
2230
|
try {
|
|
2137
2231
|
if (!sameDev2(videoSourcePath2, seasonPath)) throw new Error("cross-filesystem");
|
|
2138
|
-
(0,
|
|
2232
|
+
(0, import_fs13.linkSync)(videoSourcePath2, destVideoPath);
|
|
2139
2233
|
mode = "hardlink";
|
|
2140
2234
|
} catch {
|
|
2141
2235
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
2142
|
-
(0,
|
|
2236
|
+
(0, import_fs13.cpSync)(videoSourcePath2, destVideoPath);
|
|
2143
2237
|
mode = "copy";
|
|
2144
2238
|
}
|
|
2145
|
-
if (subtitleSourcePath2 && destSubtitleName2) (0,
|
|
2239
|
+
if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs13.cpSync)(subtitleSourcePath2, (0, import_path13.resolve)(seasonPath, destSubtitleName2));
|
|
2146
2240
|
} else {
|
|
2147
2241
|
if (sameDev2(videoSourcePath2, seasonPath)) {
|
|
2148
|
-
(0,
|
|
2242
|
+
(0, import_fs13.renameSync)(videoSourcePath2, destVideoPath);
|
|
2149
2243
|
} else {
|
|
2150
|
-
(0,
|
|
2151
|
-
(0,
|
|
2244
|
+
(0, import_fs13.cpSync)(videoSourcePath2, destVideoPath);
|
|
2245
|
+
(0, import_fs13.rmSync)(videoSourcePath2);
|
|
2152
2246
|
}
|
|
2153
|
-
if (subtitleSourcePath2 && destSubtitleName2) (0,
|
|
2154
|
-
if (isDir) (0,
|
|
2247
|
+
if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs13.renameSync)(subtitleSourcePath2, (0, import_path13.resolve)(seasonPath, destSubtitleName2));
|
|
2248
|
+
if (isDir) (0, import_fs13.rmSync)(entryPath, { recursive: true, force: true });
|
|
2155
2249
|
}
|
|
2156
2250
|
recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
|
|
2157
2251
|
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
@@ -2159,8 +2253,8 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2159
2253
|
}
|
|
2160
2254
|
const edition = detectEdition(entry);
|
|
2161
2255
|
const folderName = formatMovieName(movieFormat, parsed.title, parsed.year, edition);
|
|
2162
|
-
const destFolder = (0,
|
|
2163
|
-
if ((0,
|
|
2256
|
+
const destFolder = (0, import_path13.resolve)(destRoot, folderName);
|
|
2257
|
+
if ((0, import_fs13.existsSync)(destFolder)) {
|
|
2164
2258
|
spinner_default.warn(`already exists: ${folderName}`);
|
|
2165
2259
|
return;
|
|
2166
2260
|
}
|
|
@@ -2171,42 +2265,42 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2171
2265
|
}
|
|
2172
2266
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
2173
2267
|
const destVideoName = `${folderName}.${videoExt}`;
|
|
2174
|
-
const videoSourcePath = isDir ? (0,
|
|
2175
|
-
const dirFiles = isDir ? (0,
|
|
2268
|
+
const videoSourcePath = isDir ? (0, import_path13.resolve)(entryPath, videoFile) : entryPath;
|
|
2269
|
+
const dirFiles = isDir ? (0, import_fs13.readdirSync)(entryPath) : [];
|
|
2176
2270
|
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
2177
2271
|
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
2178
|
-
const subtitleSourcePath = subtitle ? (0,
|
|
2272
|
+
const subtitleSourcePath = subtitle ? (0, import_path13.resolve)(entryPath, subtitle) : null;
|
|
2179
2273
|
const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
|
|
2180
2274
|
if (useHardlink) {
|
|
2181
|
-
(0,
|
|
2182
|
-
const destVideoPath = (0,
|
|
2275
|
+
(0, import_fs13.mkdirSync)(destFolder, { recursive: true });
|
|
2276
|
+
const destVideoPath = (0, import_path13.resolve)(destFolder, destVideoName);
|
|
2183
2277
|
let mode;
|
|
2184
2278
|
try {
|
|
2185
2279
|
if (!sameDev2(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
|
|
2186
|
-
(0,
|
|
2280
|
+
(0, import_fs13.linkSync)(videoSourcePath, destVideoPath);
|
|
2187
2281
|
mode = "hardlink";
|
|
2188
2282
|
} catch {
|
|
2189
2283
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
2190
|
-
(0,
|
|
2284
|
+
(0, import_fs13.cpSync)(videoSourcePath, destVideoPath);
|
|
2191
2285
|
mode = "copy";
|
|
2192
2286
|
}
|
|
2193
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
2287
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs13.cpSync)(subtitleSourcePath, (0, import_path13.resolve)(destFolder, destSubtitleName));
|
|
2194
2288
|
recordImport(sessionId, entryPath, destFolder, mode, void 0, "movie");
|
|
2195
2289
|
} else {
|
|
2196
2290
|
if (isDir) {
|
|
2197
2291
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
2198
|
-
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0,
|
|
2199
|
-
(0,
|
|
2200
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
2292
|
+
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0, import_fs13.rmSync)((0, import_path13.resolve)(entryPath, f), { recursive: true, force: true });
|
|
2293
|
+
(0, import_fs13.renameSync)(videoSourcePath, (0, import_path13.resolve)(entryPath, destVideoName));
|
|
2294
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs13.renameSync)(subtitleSourcePath, (0, import_path13.resolve)(entryPath, destSubtitleName));
|
|
2201
2295
|
moveItem(entryPath, destFolder);
|
|
2202
2296
|
} else {
|
|
2203
|
-
(0,
|
|
2204
|
-
const destVideoPath = (0,
|
|
2297
|
+
(0, import_fs13.mkdirSync)(destFolder, { recursive: true });
|
|
2298
|
+
const destVideoPath = (0, import_path13.resolve)(destFolder, destVideoName);
|
|
2205
2299
|
if (sameDev2(videoSourcePath, destRoot)) {
|
|
2206
|
-
(0,
|
|
2300
|
+
(0, import_fs13.renameSync)(videoSourcePath, destVideoPath);
|
|
2207
2301
|
} else {
|
|
2208
|
-
(0,
|
|
2209
|
-
(0,
|
|
2302
|
+
(0, import_fs13.cpSync)(videoSourcePath, destVideoPath);
|
|
2303
|
+
(0, import_fs13.rmSync)(videoSourcePath);
|
|
2210
2304
|
}
|
|
2211
2305
|
}
|
|
2212
2306
|
recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
|