reelsort 0.2.5 → 0.2.6

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/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 import_fs10 = require("fs");
1154
- var import_path11 = require("path");
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 = (name) => {
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,31 +1316,31 @@ var bookExtensions_default = ["epub", "mobi", "azw3", "azw"];
1277
1316
  var sameDev = (a, b) => {
1278
1317
  try {
1279
1318
  let bExisting = b;
1280
- while (!(0, import_fs10.existsSync)(bExisting)) bExisting = (0, import_path11.dirname)(bExisting);
1281
- return (0, import_fs10.statSync)(a).dev === (0, import_fs10.statSync)(bExisting).dev;
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, import_fs10.renameSync)(src, dest);
1327
+ (0, import_fs11.renameSync)(src, dest);
1289
1328
  } else {
1290
- (0, import_fs10.cpSync)(src, dest, { recursive: true });
1291
- (0, import_fs10.rmSync)(src, { recursive: true, force: true });
1329
+ (0, import_fs11.cpSync)(src, dest, { recursive: true });
1330
+ trashPath(src, { recursive: true });
1292
1331
  }
1293
1332
  };
1294
- var findVideo = (dir) => (0, import_fs10.readdirSync)(dir).find((f) => {
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, import_fs10.readdirSync)(dir).some((f) => {
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, import_path11.resolve)(dir, f);
1304
- if ((0, import_fs10.lstatSync)(sub).isDirectory()) return containsBook(sub, depth - 1);
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
  }
@@ -1311,11 +1350,11 @@ var isTvEpisodeName = (name) => /S\d{2,3}E\d{2,3}/i.test(name) || /\d+x\d{2,3}/i
1311
1350
  var isSeasonDirName = (name) => !isTvEpisodeName(name) && /(?:^|[.\s_-])(?:season|s)\s*0*\d+(?:[.\s_-]|$)/i.test(name);
1312
1351
  var gatherEntries = (source) => {
1313
1352
  const result = [];
1314
- for (const name of (0, import_fs10.readdirSync)(source)) {
1315
- const fullPath = (0, import_path11.resolve)(source, name);
1353
+ for (const name of (0, import_fs11.readdirSync)(source)) {
1354
+ const fullPath = (0, import_path12.resolve)(source, name);
1316
1355
  let isDir;
1317
1356
  try {
1318
- isDir = (0, import_fs10.lstatSync)(fullPath).isDirectory();
1357
+ isDir = (0, import_fs11.lstatSync)(fullPath).isDirectory();
1319
1358
  } catch {
1320
1359
  continue;
1321
1360
  }
@@ -1333,17 +1372,17 @@ var gatherEntries = (source) => {
1333
1372
  }
1334
1373
  let children;
1335
1374
  try {
1336
- children = (0, import_fs10.readdirSync)(fullPath);
1375
+ children = (0, import_fs11.readdirSync)(fullPath);
1337
1376
  } catch {
1338
1377
  result.push({ entry: name, entryPath: fullPath, isDir: true });
1339
1378
  continue;
1340
1379
  }
1341
1380
  if (children.some((c) => isTvEpisodeName(c))) {
1342
1381
  for (const child of children) {
1343
- const childPath = (0, import_path11.resolve)(fullPath, child);
1382
+ const childPath = (0, import_path12.resolve)(fullPath, child);
1344
1383
  let childIsDir;
1345
1384
  try {
1346
- childIsDir = (0, import_fs10.lstatSync)(childPath).isDirectory();
1385
+ childIsDir = (0, import_fs11.lstatSync)(childPath).isDirectory();
1347
1386
  } catch {
1348
1387
  continue;
1349
1388
  }
@@ -1355,25 +1394,25 @@ var gatherEntries = (source) => {
1355
1394
  }
1356
1395
  const seasonDirs = children.filter((c) => {
1357
1396
  try {
1358
- return isSeasonDirName(c) && (0, import_fs10.lstatSync)((0, import_path11.resolve)(fullPath, c)).isDirectory();
1397
+ return isSeasonDirName(c) && (0, import_fs11.lstatSync)((0, import_path12.resolve)(fullPath, c)).isDirectory();
1359
1398
  } catch {
1360
1399
  return false;
1361
1400
  }
1362
1401
  });
1363
1402
  if (seasonDirs.length > 0) {
1364
1403
  for (const seasonDir of seasonDirs) {
1365
- const seasonPath = (0, import_path11.resolve)(fullPath, seasonDir);
1404
+ const seasonPath = (0, import_path12.resolve)(fullPath, seasonDir);
1366
1405
  let seasonChildren;
1367
1406
  try {
1368
- seasonChildren = (0, import_fs10.readdirSync)(seasonPath);
1407
+ seasonChildren = (0, import_fs11.readdirSync)(seasonPath);
1369
1408
  } catch {
1370
1409
  continue;
1371
1410
  }
1372
1411
  for (const child of seasonChildren) {
1373
- const childPath = (0, import_path11.resolve)(seasonPath, child);
1412
+ const childPath = (0, import_path12.resolve)(seasonPath, child);
1374
1413
  let childIsDir;
1375
1414
  try {
1376
- childIsDir = (0, import_fs10.lstatSync)(childPath).isDirectory();
1415
+ childIsDir = (0, import_fs11.lstatSync)(childPath).isDirectory();
1377
1416
  } catch {
1378
1417
  continue;
1379
1418
  }
@@ -1389,19 +1428,19 @@ var gatherEntries = (source) => {
1389
1428
  return result;
1390
1429
  };
1391
1430
  var findShowFolder = (destRoot, title) => {
1392
- if (!(0, import_fs10.existsSync)(destRoot)) return null;
1431
+ if (!(0, import_fs11.existsSync)(destRoot)) return null;
1393
1432
  const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
1394
1433
  const target = normalize(title);
1395
- return (0, import_fs10.readdirSync)(destRoot).filter((f) => {
1434
+ return (0, import_fs11.readdirSync)(destRoot).filter((f) => {
1396
1435
  try {
1397
- return (0, import_fs10.lstatSync)((0, import_path11.resolve)(destRoot, f)).isDirectory();
1436
+ return (0, import_fs11.lstatSync)((0, import_path12.resolve)(destRoot, f)).isDirectory();
1398
1437
  } catch {
1399
1438
  return false;
1400
1439
  }
1401
1440
  }).find((f) => normalize(f) === target) ?? null;
1402
1441
  };
1403
1442
  var findShowFolderByContent = (destRoot, title) => {
1404
- if (!(0, import_fs10.existsSync)(destRoot)) return null;
1443
+ if (!(0, import_fs11.existsSync)(destRoot)) return null;
1405
1444
  const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
1406
1445
  const target = normalize(title);
1407
1446
  const matchesTitle = (name) => {
@@ -1409,18 +1448,18 @@ var findShowFolderByContent = (destRoot, title) => {
1409
1448
  const p = parseDownloadName(name);
1410
1449
  return !!p && normalize(p.title) === target;
1411
1450
  };
1412
- for (const folder of (0, import_fs10.readdirSync)(destRoot)) {
1451
+ for (const folder of (0, import_fs11.readdirSync)(destRoot)) {
1413
1452
  try {
1414
- const folderPath = (0, import_path11.resolve)(destRoot, folder);
1415
- if (!(0, import_fs10.lstatSync)(folderPath).isDirectory()) continue;
1416
- const children = (0, import_fs10.readdirSync)(folderPath);
1453
+ const folderPath = (0, import_path12.resolve)(destRoot, folder);
1454
+ if (!(0, import_fs11.lstatSync)(folderPath).isDirectory()) continue;
1455
+ const children = (0, import_fs11.readdirSync)(folderPath);
1417
1456
  if (children.some(matchesTitle)) return folder;
1418
1457
  for (const child of children) {
1419
1458
  if (!isSeasonDirName(child)) continue;
1420
1459
  try {
1421
- const seasonPath = (0, import_path11.resolve)(folderPath, child);
1422
- if (!(0, import_fs10.lstatSync)(seasonPath).isDirectory()) continue;
1423
- if ((0, import_fs10.readdirSync)(seasonPath).some(matchesTitle)) return folder;
1460
+ const seasonPath = (0, import_path12.resolve)(folderPath, child);
1461
+ if (!(0, import_fs11.lstatSync)(seasonPath).isDirectory()) continue;
1462
+ if ((0, import_fs11.readdirSync)(seasonPath).some(matchesTitle)) return folder;
1424
1463
  } catch {
1425
1464
  }
1426
1465
  }
@@ -1429,15 +1468,19 @@ var findShowFolderByContent = (destRoot, title) => {
1429
1468
  }
1430
1469
  return null;
1431
1470
  };
1432
- var findSeasonFolder = (showPath, season) => {
1433
- if (!(0, import_fs10.existsSync)(showPath)) return null;
1434
- const folders = (0, import_fs10.readdirSync)(showPath).filter((f) => {
1471
+ var findSeasonFolder = (showPath, season, specialsFolder) => {
1472
+ if (!(0, import_fs11.existsSync)(showPath)) return null;
1473
+ const folders = (0, import_fs11.readdirSync)(showPath).filter((f) => {
1435
1474
  try {
1436
- return (0, import_fs10.lstatSync)((0, import_path11.resolve)(showPath, f)).isDirectory();
1475
+ return (0, import_fs11.lstatSync)((0, import_path12.resolve)(showPath, f)).isDirectory();
1437
1476
  } catch {
1438
1477
  return false;
1439
1478
  }
1440
1479
  });
1480
+ if (season === 0 && specialsFolder) {
1481
+ const existing = folders.find((f) => f.toLowerCase() === specialsFolder.toLowerCase());
1482
+ if (existing) return existing;
1483
+ }
1441
1484
  return folders.find((f) => {
1442
1485
  const match = f.match(/(?:season|s)\s*0*(\d+)/i);
1443
1486
  return match && parseInt(match[1]) === season;
@@ -1466,11 +1509,13 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1466
1509
  const language = config.language ?? "eng";
1467
1510
  const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
1468
1511
  const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
1512
+ const specialsFolder = config.specialsFolder ?? "Specials";
1469
1513
  const lookupMovie = async (parsed) => {
1470
1514
  let tmdbId;
1471
1515
  let resolvedTitle = parsed.title;
1472
1516
  let resolvedYear = parsed.year;
1473
1517
  if (config.tmdbApiKey) {
1518
+ spinner_default.text = `TMDb: ${parsed.title}`;
1474
1519
  const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
1475
1520
  if (results.length === 1) {
1476
1521
  tmdbId = results[0].id;
@@ -1498,8 +1543,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1498
1543
  const importMovie = async (entry, entryPath, isDir, resolvedTitle, resolvedYear, tmdbId, destRoot) => {
1499
1544
  const edition = detectEdition(entry);
1500
1545
  const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
1501
- const destFolder = (0, import_path11.resolve)(destRoot, folderName);
1502
- if ((0, import_fs10.existsSync)(destFolder)) {
1546
+ const destFolder = (0, import_path12.resolve)(destRoot, folderName);
1547
+ if ((0, import_fs11.existsSync)(destFolder)) {
1503
1548
  spinner_default.warn(`already exists: ${folderName}`);
1504
1549
  return false;
1505
1550
  }
@@ -1510,43 +1555,43 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1510
1555
  }
1511
1556
  const videoExt = videoFile.match(/([^.]+$)/)?.[0];
1512
1557
  const destVideoName = `${folderName}.${videoExt}`;
1513
- const videoSourcePath = isDir ? (0, import_path11.resolve)(entryPath, videoFile) : entryPath;
1514
- const dirFiles = isDir ? (0, import_fs10.readdirSync)(entryPath) : [];
1558
+ const videoSourcePath = isDir ? (0, import_path12.resolve)(entryPath, videoFile) : entryPath;
1559
+ const dirFiles = isDir ? (0, import_fs11.readdirSync)(entryPath) : [];
1515
1560
  const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
1516
1561
  const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
1517
- const subtitleSourcePath = subtitle ? (0, import_path11.resolve)(entryPath, subtitle) : null;
1562
+ const subtitleSourcePath = subtitle ? (0, import_path12.resolve)(entryPath, subtitle) : null;
1518
1563
  const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
1519
1564
  if (!dryRun) {
1520
1565
  if (useHardlink) {
1521
- (0, import_fs10.mkdirSync)(destFolder, { recursive: true });
1522
- const destVideoPath = (0, import_path11.resolve)(destFolder, destVideoName);
1566
+ (0, import_fs11.mkdirSync)(destFolder, { recursive: true });
1567
+ const destVideoPath = (0, import_path12.resolve)(destFolder, destVideoName);
1523
1568
  let mode;
1524
1569
  try {
1525
1570
  if (!sameDev(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
1526
- (0, import_fs10.linkSync)(videoSourcePath, destVideoPath);
1571
+ (0, import_fs11.linkSync)(videoSourcePath, destVideoPath);
1527
1572
  mode = "hardlink";
1528
1573
  } catch {
1529
1574
  spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
1530
- (0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
1575
+ (0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
1531
1576
  mode = "copy";
1532
1577
  }
1533
- if (subtitleSourcePath && destSubtitleName) (0, import_fs10.cpSync)(subtitleSourcePath, (0, import_path11.resolve)(destFolder, destSubtitleName));
1578
+ if (subtitleSourcePath && destSubtitleName) (0, import_fs11.cpSync)(subtitleSourcePath, (0, import_path12.resolve)(destFolder, destSubtitleName));
1534
1579
  recordImport(sessionId, entryPath, destFolder, mode, tmdbId, "movie");
1535
1580
  } else {
1536
1581
  if (isDir) {
1537
1582
  const keep = new Set([videoFile, subtitle].filter(Boolean));
1538
- for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0, import_fs10.rmSync)((0, import_path11.resolve)(entryPath, f), { recursive: true, force: true });
1539
- (0, import_fs10.renameSync)(videoSourcePath, (0, import_path11.resolve)(entryPath, destVideoName));
1540
- if (subtitleSourcePath && destSubtitleName) (0, import_fs10.renameSync)(subtitleSourcePath, (0, import_path11.resolve)(entryPath, destSubtitleName));
1583
+ for (const f of dirFiles.filter((f2) => !keep.has(f2))) trashPath((0, import_path12.resolve)(entryPath, f), { recursive: true });
1584
+ (0, import_fs11.renameSync)(videoSourcePath, (0, import_path12.resolve)(entryPath, destVideoName));
1585
+ if (subtitleSourcePath && destSubtitleName) (0, import_fs11.renameSync)(subtitleSourcePath, (0, import_path12.resolve)(entryPath, destSubtitleName));
1541
1586
  moveFolder(entryPath, destFolder);
1542
1587
  } else {
1543
- (0, import_fs10.mkdirSync)(destFolder, { recursive: true });
1544
- const destVideoPath = (0, import_path11.resolve)(destFolder, destVideoName);
1588
+ (0, import_fs11.mkdirSync)(destFolder, { recursive: true });
1589
+ const destVideoPath = (0, import_path12.resolve)(destFolder, destVideoName);
1545
1590
  if (sameDev(videoSourcePath, destRoot)) {
1546
- (0, import_fs10.renameSync)(videoSourcePath, destVideoPath);
1591
+ (0, import_fs11.renameSync)(videoSourcePath, destVideoPath);
1547
1592
  } else {
1548
- (0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
1549
- (0, import_fs10.rmSync)(videoSourcePath);
1593
+ (0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
1594
+ trashPath(videoSourcePath);
1550
1595
  }
1551
1596
  }
1552
1597
  recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
@@ -1563,12 +1608,13 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1563
1608
  const ignoreSet = new Set(config.ignore ?? []);
1564
1609
  const seenIgnored = /* @__PURE__ */ new Set();
1565
1610
  for (const source of config.sources) {
1566
- if (!(0, import_fs10.existsSync)(source)) {
1611
+ if (!(0, import_fs11.existsSync)(source)) {
1567
1612
  spinner_default.warn(`source not found: ${import_termkit10.Color.white.encoder(source)}`);
1568
1613
  continue;
1569
1614
  }
1570
1615
  spinner_default.text = `scanning ${import_termkit10.Color.white.encoder(source)}`;
1571
1616
  for (const { entry, entryPath, isDir } of gatherEntries(source)) {
1617
+ spinner_default.text = `scanning: ${entry}`;
1572
1618
  if (ignoreSet.has(entry)) {
1573
1619
  seenIgnored.add(entry);
1574
1620
  if (isVerbose()) spinner_default.info(`ignored: ${entry}`);
@@ -1603,8 +1649,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1603
1649
  continue;
1604
1650
  }
1605
1651
  const destName = `${nameMatch[0]} [${id}]`;
1606
- const destPath = (0, import_path11.resolve)(destRoot, destName);
1607
- if ((0, import_fs10.existsSync)(destPath)) {
1652
+ const destPath = (0, import_path12.resolve)(destRoot, destName);
1653
+ if ((0, import_fs11.existsSync)(destPath)) {
1608
1654
  spinner_default.warn(`already exists: ${destName}`);
1609
1655
  skipped++;
1610
1656
  continue;
@@ -1618,8 +1664,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1618
1664
  continue;
1619
1665
  }
1620
1666
  if (detectedType === "book") {
1621
- const destPath = (0, import_path11.resolve)(destRoot, entry);
1622
- if ((0, import_fs10.existsSync)(destPath)) {
1667
+ const destPath = (0, import_path12.resolve)(destRoot, entry);
1668
+ if ((0, import_fs11.existsSync)(destPath)) {
1623
1669
  spinner_default.warn(`already exists: ${entry}`);
1624
1670
  skipped++;
1625
1671
  continue;
@@ -1628,12 +1674,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1628
1674
  if (isDir || isBookDir) {
1629
1675
  moveFolder(entryPath, destPath);
1630
1676
  } else {
1631
- (0, import_fs10.mkdirSync)(destRoot, { recursive: true });
1677
+ (0, import_fs11.mkdirSync)(destRoot, { recursive: true });
1632
1678
  if (sameDev(entryPath, destRoot)) {
1633
- (0, import_fs10.renameSync)(entryPath, destPath);
1679
+ (0, import_fs11.renameSync)(entryPath, destPath);
1634
1680
  } else {
1635
- (0, import_fs10.cpSync)(entryPath, destPath);
1636
- (0, import_fs10.rmSync)(entryPath);
1681
+ (0, import_fs11.cpSync)(entryPath, destPath);
1682
+ (0, import_fs11.rmSync)(entryPath);
1637
1683
  }
1638
1684
  }
1639
1685
  recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
@@ -1665,6 +1711,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1665
1711
  let resolvedYear = parsed.year;
1666
1712
  if (config.tmdbApiKey) {
1667
1713
  if (detectedType === "tv") {
1714
+ spinner_default.text = `TMDb: ${parsed.title}`;
1668
1715
  const results = await searchTv(parsed.title, config.tmdbApiKey);
1669
1716
  if (results.length === 1) {
1670
1717
  tmdbId = results[0].id;
@@ -1709,18 +1756,18 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1709
1756
  const existingFolder = findShowFolder(destRoot, resolvedTitle) ?? findShowFolderByContent(destRoot, resolvedTitle);
1710
1757
  if (existingFolder) {
1711
1758
  showFolderName = existingFolder;
1712
- showPath = (0, import_path11.resolve)(destRoot, existingFolder);
1759
+ showPath = (0, import_path12.resolve)(destRoot, existingFolder);
1713
1760
  } else if (auto) {
1714
1761
  showFolderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear);
1715
- showPath = (0, import_path11.resolve)(destRoot, showFolderName);
1762
+ showPath = (0, import_path12.resolve)(destRoot, showFolderName);
1716
1763
  if (!dryRun) upsertShow(showPath, tmdbId ?? null, resolvedTitle);
1717
1764
  } else {
1718
1765
  pendingTv.push({ entry, entryPath, isDir, parsed, resolvedTitle, destRoot });
1719
1766
  continue;
1720
1767
  }
1721
1768
  }
1722
- const seasonFolderName = findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
1723
- const seasonPath = (0, import_path11.resolve)(showPath, seasonFolderName);
1769
+ const seasonFolderName = parsed.season === 0 ? findSeasonFolder(showPath, 0, specialsFolder) ?? specialsFolder : findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
1770
+ const seasonPath = (0, import_path12.resolve)(showPath, seasonFolderName);
1724
1771
  const videoFile = isDir ? findVideo(entryPath) : entry;
1725
1772
  if (!videoFile) {
1726
1773
  if (isVerbose()) spinner_default.info(`no video found in: ${entry}`);
@@ -1728,13 +1775,15 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1728
1775
  continue;
1729
1776
  }
1730
1777
  const videoExt = videoFile.match(/([^.]+$)/)?.[0];
1778
+ if (tmdbId && config.tmdbApiKey) spinner_default.text = `TMDb: episode name for ${resolvedTitle}`;
1731
1779
  const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
1732
1780
  const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
1733
1781
  const destVideoName = `${episodeName}.${videoExt}`;
1734
- const destVideoPath = (0, import_path11.resolve)(seasonPath, destVideoName);
1735
- const videoSourcePath = isDir ? (0, import_path11.resolve)(entryPath, videoFile) : entryPath;
1736
- if ((0, import_fs10.existsSync)(destVideoPath)) {
1737
- let shouldReplace = force;
1782
+ const destVideoPath = (0, import_path12.resolve)(seasonPath, destVideoName);
1783
+ const videoSourcePath = isDir ? (0, import_path12.resolve)(entryPath, videoFile) : entryPath;
1784
+ if ((0, import_fs11.existsSync)(destVideoPath)) {
1785
+ const isRepack = /\brepack\d*\b|\bproper\b/i.test(entry);
1786
+ let shouldReplace = force || isRepack;
1738
1787
  if (!shouldReplace && interactive) {
1739
1788
  spinner_default.stop();
1740
1789
  const select = new import_termkit10.Select();
@@ -1751,39 +1800,39 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1751
1800
  continue;
1752
1801
  }
1753
1802
  if (!dryRun) {
1754
- for (const f of (0, import_fs10.readdirSync)(seasonPath)) {
1755
- if (f.startsWith(`${episodeName}.`)) (0, import_fs10.rmSync)((0, import_path11.resolve)(seasonPath, f));
1803
+ for (const f of (0, import_fs11.readdirSync)(seasonPath)) {
1804
+ if (f.startsWith(`${episodeName}.`)) trashPath((0, import_path12.resolve)(seasonPath, f));
1756
1805
  }
1757
1806
  }
1758
1807
  }
1759
- const dirFiles = isDir ? (0, import_fs10.readdirSync)(entryPath) : [];
1808
+ const dirFiles = isDir ? (0, import_fs11.readdirSync)(entryPath) : [];
1760
1809
  const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
1761
1810
  const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
1762
- const subtitleSourcePath = subtitle ? (0, import_path11.resolve)(entryPath, subtitle) : null;
1811
+ const subtitleSourcePath = subtitle ? (0, import_path12.resolve)(entryPath, subtitle) : null;
1763
1812
  const destSubtitleName = subtitle && subtitleExt ? `${episodeName}.${subtitleExt}` : null;
1764
1813
  if (!dryRun) {
1765
- (0, import_fs10.mkdirSync)(seasonPath, { recursive: true });
1814
+ (0, import_fs11.mkdirSync)(seasonPath, { recursive: true });
1766
1815
  let mode = "move";
1767
1816
  if (useHardlink) {
1768
1817
  try {
1769
1818
  if (!sameDev(videoSourcePath, seasonPath)) throw new Error("cross-filesystem");
1770
- (0, import_fs10.linkSync)(videoSourcePath, destVideoPath);
1819
+ (0, import_fs11.linkSync)(videoSourcePath, destVideoPath);
1771
1820
  mode = "hardlink";
1772
1821
  } catch {
1773
1822
  spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
1774
- (0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
1823
+ (0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
1775
1824
  mode = "copy";
1776
1825
  }
1777
- if (subtitleSourcePath && destSubtitleName) (0, import_fs10.cpSync)(subtitleSourcePath, (0, import_path11.resolve)(seasonPath, destSubtitleName));
1826
+ if (subtitleSourcePath && destSubtitleName) (0, import_fs11.cpSync)(subtitleSourcePath, (0, import_path12.resolve)(seasonPath, destSubtitleName));
1778
1827
  } else {
1779
1828
  if (sameDev(videoSourcePath, seasonPath)) {
1780
- (0, import_fs10.renameSync)(videoSourcePath, destVideoPath);
1829
+ (0, import_fs11.renameSync)(videoSourcePath, destVideoPath);
1781
1830
  } else {
1782
- (0, import_fs10.cpSync)(videoSourcePath, destVideoPath);
1783
- (0, import_fs10.rmSync)(videoSourcePath);
1831
+ (0, import_fs11.cpSync)(videoSourcePath, destVideoPath);
1832
+ trashPath(videoSourcePath);
1784
1833
  }
1785
- if (subtitleSourcePath && destSubtitleName) (0, import_fs10.renameSync)(subtitleSourcePath, (0, import_path11.resolve)(seasonPath, destSubtitleName));
1786
- if (isDir) (0, import_fs10.rmSync)(entryPath, { recursive: true, force: true });
1834
+ if (subtitleSourcePath && destSubtitleName) (0, import_fs11.renameSync)(subtitleSourcePath, (0, import_path12.resolve)(seasonPath, destSubtitleName));
1835
+ if (isDir) trashPath(entryPath, { recursive: true });
1787
1836
  }
1788
1837
  recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
1789
1838
  }
@@ -1848,7 +1897,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1848
1897
  var scan_default = scan;
1849
1898
 
1850
1899
  // src/actions/undo.ts
1851
- var import_fs11 = require("fs");
1900
+ var import_fs12 = require("fs");
1852
1901
  var import_termkit11 = require("termkit");
1853
1902
  var undo = async () => {
1854
1903
  spinner_default.start();
@@ -1863,7 +1912,7 @@ var undo = async () => {
1863
1912
  if (!useImports) {
1864
1913
  let undone2 = 0;
1865
1914
  for (const record of renameRecords) {
1866
- (0, import_fs11.renameSync)(record.newPath, record.oldPath);
1915
+ (0, import_fs12.renameSync)(record.newPath, record.oldPath);
1867
1916
  spinner_default.succeed(`${import_termkit11.Color.green.encoder(record.newPath)} \u2192 ${import_termkit11.Color.white.encoder(record.oldPath)}`);
1868
1917
  undone2++;
1869
1918
  }
@@ -1885,12 +1934,12 @@ var undo = async () => {
1885
1934
  skipped++;
1886
1935
  continue;
1887
1936
  }
1888
- if (!(0, import_fs11.existsSync)(record.destinationPath)) {
1937
+ if (!(0, import_fs12.existsSync)(record.destinationPath)) {
1889
1938
  spinner_default.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
1890
1939
  skipped++;
1891
1940
  continue;
1892
1941
  }
1893
- (0, import_fs11.renameSync)(record.destinationPath, record.sourcePath);
1942
+ (0, import_fs12.renameSync)(record.destinationPath, record.sourcePath);
1894
1943
  spinner_default.succeed(`${import_termkit11.Color.green.encoder(record.destinationPath)} \u2192 ${import_termkit11.Color.white.encoder(record.sourcePath)}`);
1895
1944
  undone++;
1896
1945
  }
@@ -1903,37 +1952,37 @@ var undo_default = undo;
1903
1952
 
1904
1953
  // src/actions/watch.ts
1905
1954
  var import_chokidar = __toESM(require("chokidar"));
1906
- var import_fs12 = require("fs");
1907
- var import_path12 = require("path");
1955
+ var import_fs13 = require("fs");
1956
+ var import_path13 = require("path");
1908
1957
  var import_termkit12 = require("termkit");
1909
1958
  var sameDev2 = (a, b) => {
1910
1959
  try {
1911
1960
  let bExisting = b;
1912
- while (!(0, import_fs12.existsSync)(bExisting)) bExisting = (0, import_path12.dirname)(bExisting);
1913
- return (0, import_fs12.statSync)(a).dev === (0, import_fs12.statSync)(bExisting).dev;
1961
+ while (!(0, import_fs13.existsSync)(bExisting)) bExisting = (0, import_path13.dirname)(bExisting);
1962
+ return (0, import_fs13.statSync)(a).dev === (0, import_fs13.statSync)(bExisting).dev;
1914
1963
  } catch {
1915
1964
  return false;
1916
1965
  }
1917
1966
  };
1918
1967
  var moveItem = (src, dest) => {
1919
1968
  if (sameDev2(src, dest)) {
1920
- (0, import_fs12.renameSync)(src, dest);
1969
+ (0, import_fs13.renameSync)(src, dest);
1921
1970
  } else {
1922
- (0, import_fs12.cpSync)(src, dest, { recursive: true });
1923
- (0, import_fs12.rmSync)(src, { recursive: true, force: true });
1971
+ (0, import_fs13.cpSync)(src, dest, { recursive: true });
1972
+ (0, import_fs13.rmSync)(src, { recursive: true, force: true });
1924
1973
  }
1925
1974
  };
1926
- var findVideo2 = (dir) => (0, import_fs12.readdirSync)(dir).find((f) => {
1975
+ var findVideo2 = (dir) => (0, import_fs13.readdirSync)(dir).find((f) => {
1927
1976
  const ext = f.match(/([^.]+$)/)?.[0];
1928
1977
  return ext && videoExtensions_default.includes(ext);
1929
1978
  }) ?? null;
1930
- var containsBook2 = (dir, depth = 2) => (0, import_fs12.readdirSync)(dir).some((f) => {
1979
+ var containsBook2 = (dir, depth = 2) => (0, import_fs13.readdirSync)(dir).some((f) => {
1931
1980
  const ext = f.match(/([^.]+$)/)?.[0];
1932
1981
  if (ext && bookExtensions_default.includes(ext)) return true;
1933
1982
  if (depth > 1) {
1934
1983
  try {
1935
- const sub = (0, import_path12.resolve)(dir, f);
1936
- if ((0, import_fs12.lstatSync)(sub).isDirectory()) return containsBook2(sub, depth - 1);
1984
+ const sub = (0, import_path13.resolve)(dir, f);
1985
+ if ((0, import_fs13.lstatSync)(sub).isDirectory()) return containsBook2(sub, depth - 1);
1937
1986
  } catch {
1938
1987
  }
1939
1988
  }
@@ -1944,26 +1993,26 @@ var isSeasonDirName2 = (name) => !isTvEpisodeName2(name) && /(?:^|[.\s_-])(?:sea
1944
1993
  var expandWatchPath = (p) => {
1945
1994
  let isDir;
1946
1995
  try {
1947
- isDir = (0, import_fs12.lstatSync)(p).isDirectory();
1996
+ isDir = (0, import_fs13.lstatSync)(p).isDirectory();
1948
1997
  } catch {
1949
1998
  return [p];
1950
1999
  }
1951
2000
  if (!isDir) return [p];
1952
- const name = (0, import_path12.basename)(p);
2001
+ const name = (0, import_path13.basename)(p);
1953
2002
  if (isTvEpisodeName2(name) || /(?<=\[).+?(?=\])/.test(name) || /^[A-Z]{4}\d{5}/i.test(name)) return [p];
1954
2003
  let children;
1955
2004
  try {
1956
- children = (0, import_fs12.readdirSync)(p);
2005
+ children = (0, import_fs13.readdirSync)(p);
1957
2006
  } catch {
1958
2007
  return [p];
1959
2008
  }
1960
2009
  if (children.some((c) => isTvEpisodeName2(c))) {
1961
2010
  const entries = [];
1962
2011
  for (const child of children) {
1963
- const cp = (0, import_path12.resolve)(p, child);
2012
+ const cp = (0, import_path13.resolve)(p, child);
1964
2013
  let cd;
1965
2014
  try {
1966
- cd = (0, import_fs12.lstatSync)(cp).isDirectory();
2015
+ cd = (0, import_fs13.lstatSync)(cp).isDirectory();
1967
2016
  } catch {
1968
2017
  continue;
1969
2018
  }
@@ -1975,7 +2024,7 @@ var expandWatchPath = (p) => {
1975
2024
  }
1976
2025
  const seasonDirs = children.filter((c) => {
1977
2026
  try {
1978
- return isSeasonDirName2(c) && (0, import_fs12.lstatSync)((0, import_path12.resolve)(p, c)).isDirectory();
2027
+ return isSeasonDirName2(c) && (0, import_fs13.lstatSync)((0, import_path13.resolve)(p, c)).isDirectory();
1979
2028
  } catch {
1980
2029
  return false;
1981
2030
  }
@@ -1983,18 +2032,18 @@ var expandWatchPath = (p) => {
1983
2032
  if (seasonDirs.length > 0) {
1984
2033
  const entries = [];
1985
2034
  for (const sd of seasonDirs) {
1986
- const sp = (0, import_path12.resolve)(p, sd);
2035
+ const sp = (0, import_path13.resolve)(p, sd);
1987
2036
  let sc;
1988
2037
  try {
1989
- sc = (0, import_fs12.readdirSync)(sp);
2038
+ sc = (0, import_fs13.readdirSync)(sp);
1990
2039
  } catch {
1991
2040
  continue;
1992
2041
  }
1993
2042
  for (const child of sc) {
1994
- const cp = (0, import_path12.resolve)(sp, child);
2043
+ const cp = (0, import_path13.resolve)(sp, child);
1995
2044
  let cd;
1996
2045
  try {
1997
- cd = (0, import_fs12.lstatSync)(cp).isDirectory();
2046
+ cd = (0, import_fs13.lstatSync)(cp).isDirectory();
1998
2047
  } catch {
1999
2048
  continue;
2000
2049
  }
@@ -2008,10 +2057,10 @@ var expandWatchPath = (p) => {
2008
2057
  return [p];
2009
2058
  };
2010
2059
  var findSeasonFolder2 = (showPath, season) => {
2011
- if (!(0, import_fs12.existsSync)(showPath)) return null;
2012
- const folders = (0, import_fs12.readdirSync)(showPath).filter((f) => {
2060
+ if (!(0, import_fs13.existsSync)(showPath)) return null;
2061
+ const folders = (0, import_fs13.readdirSync)(showPath).filter((f) => {
2013
2062
  try {
2014
- return (0, import_fs12.lstatSync)((0, import_path12.resolve)(showPath, f)).isDirectory();
2063
+ return (0, import_fs13.lstatSync)((0, import_path13.resolve)(showPath, f)).isDirectory();
2015
2064
  } catch {
2016
2065
  return false;
2017
2066
  }
@@ -2024,10 +2073,10 @@ var findSeasonFolder2 = (showPath, season) => {
2024
2073
  var processItem = async (entryPath, useHardlink, language, auto) => {
2025
2074
  const config = getConfig();
2026
2075
  const sessionId = (/* @__PURE__ */ new Date()).toISOString();
2027
- const entry = (0, import_path12.basename)(entryPath);
2076
+ const entry = (0, import_path13.basename)(entryPath);
2028
2077
  const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
2029
2078
  const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
2030
- const isDir = (0, import_fs12.lstatSync)(entryPath).isDirectory();
2079
+ const isDir = (0, import_fs13.lstatSync)(entryPath).isDirectory();
2031
2080
  const ext = entry.match(/([^.]+$)/)?.[0];
2032
2081
  const isVideo = !isDir && ext && videoExtensions_default.includes(ext);
2033
2082
  const isBook = !isDir && ext && bookExtensions_default.includes(ext);
@@ -2053,8 +2102,8 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2053
2102
  const id = entry.split("-")[0];
2054
2103
  if (!nameMatch || !id) return;
2055
2104
  const destName = `${nameMatch[0]} [${id}]`;
2056
- const destPath = (0, import_path12.resolve)(destRoot, destName);
2057
- if ((0, import_fs12.existsSync)(destPath)) {
2105
+ const destPath = (0, import_path13.resolve)(destRoot, destName);
2106
+ if ((0, import_fs13.existsSync)(destPath)) {
2058
2107
  spinner_default.warn(`already exists: ${destName}`);
2059
2108
  return;
2060
2109
  }
@@ -2064,20 +2113,20 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2064
2113
  return;
2065
2114
  }
2066
2115
  if (detectedType === "book") {
2067
- const destPath = (0, import_path12.resolve)(destRoot, entry);
2068
- if ((0, import_fs12.existsSync)(destPath)) {
2116
+ const destPath = (0, import_path13.resolve)(destRoot, entry);
2117
+ if ((0, import_fs13.existsSync)(destPath)) {
2069
2118
  spinner_default.warn(`already exists: ${entry}`);
2070
2119
  return;
2071
2120
  }
2072
2121
  if (isDir || isBookDir) {
2073
2122
  moveItem(entryPath, destPath);
2074
2123
  } else {
2075
- (0, import_fs12.mkdirSync)(destRoot, { recursive: true });
2124
+ (0, import_fs13.mkdirSync)(destRoot, { recursive: true });
2076
2125
  if (sameDev2(entryPath, destRoot)) {
2077
- (0, import_fs12.renameSync)(entryPath, destPath);
2126
+ (0, import_fs13.renameSync)(entryPath, destPath);
2078
2127
  } else {
2079
- (0, import_fs12.cpSync)(entryPath, destPath);
2080
- (0, import_fs12.rmSync)(entryPath);
2128
+ (0, import_fs13.cpSync)(entryPath, destPath);
2129
+ (0, import_fs13.rmSync)(entryPath);
2081
2130
  }
2082
2131
  }
2083
2132
  recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
@@ -2102,14 +2151,14 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2102
2151
  showFolderName = showPath.split("/").pop() ?? registeredShow.path;
2103
2152
  } else if (auto) {
2104
2153
  showFolderName = formatMovieName(movieFormat, parsed.title, parsed.year);
2105
- showPath = (0, import_path12.resolve)(destRoot, showFolderName);
2154
+ showPath = (0, import_path13.resolve)(destRoot, showFolderName);
2106
2155
  upsertShow(showPath, null, parsed.title);
2107
2156
  } else {
2108
2157
  if (isVerbose()) spinner_default.info(`not registered, skipped: ${parsed.title} \u2014 run: reelsort add "${parsed.title}"`);
2109
2158
  return;
2110
2159
  }
2111
2160
  const seasonFolderName = findSeasonFolder2(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
2112
- const seasonPath = (0, import_path12.resolve)(showPath, seasonFolderName);
2161
+ const seasonPath = (0, import_path13.resolve)(showPath, seasonFolderName);
2113
2162
  const videoFile2 = isDir ? findVideo2(entryPath) : entry;
2114
2163
  if (!videoFile2) {
2115
2164
  if (isVerbose()) spinner_default.info(`no video found in: ${entry}`);
@@ -2119,39 +2168,39 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2119
2168
  const tmdbEpisodeName = registeredShow?.tmdbId && config.tmdbApiKey ? await getEpisodeName(registeredShow.tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
2120
2169
  const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, parsed.title, tmdbEpisodeName ?? void 0);
2121
2170
  const destVideoName2 = `${episodeName}.${videoExt2}`;
2122
- const destVideoPath = (0, import_path12.resolve)(seasonPath, destVideoName2);
2123
- const videoSourcePath2 = isDir ? (0, import_path12.resolve)(entryPath, videoFile2) : entryPath;
2124
- if ((0, import_fs12.existsSync)(destVideoPath)) {
2171
+ const destVideoPath = (0, import_path13.resolve)(seasonPath, destVideoName2);
2172
+ const videoSourcePath2 = isDir ? (0, import_path13.resolve)(entryPath, videoFile2) : entryPath;
2173
+ if ((0, import_fs13.existsSync)(destVideoPath)) {
2125
2174
  spinner_default.warn(`already exists: ${episodeName}`);
2126
2175
  return;
2127
2176
  }
2128
- const dirFiles2 = isDir ? (0, import_fs12.readdirSync)(entryPath) : [];
2177
+ const dirFiles2 = isDir ? (0, import_fs13.readdirSync)(entryPath) : [];
2129
2178
  const subtitle2 = isDir ? findSubtitle(dirFiles2, language) : null;
2130
2179
  const subtitleExt2 = subtitle2?.match(/([^.]+$)/)?.[0];
2131
- const subtitleSourcePath2 = subtitle2 ? (0, import_path12.resolve)(entryPath, subtitle2) : null;
2180
+ const subtitleSourcePath2 = subtitle2 ? (0, import_path13.resolve)(entryPath, subtitle2) : null;
2132
2181
  const destSubtitleName2 = subtitle2 && subtitleExt2 ? `${episodeName}.${subtitleExt2}` : null;
2133
- (0, import_fs12.mkdirSync)(seasonPath, { recursive: true });
2182
+ (0, import_fs13.mkdirSync)(seasonPath, { recursive: true });
2134
2183
  let mode = "move";
2135
2184
  if (useHardlink) {
2136
2185
  try {
2137
2186
  if (!sameDev2(videoSourcePath2, seasonPath)) throw new Error("cross-filesystem");
2138
- (0, import_fs12.linkSync)(videoSourcePath2, destVideoPath);
2187
+ (0, import_fs13.linkSync)(videoSourcePath2, destVideoPath);
2139
2188
  mode = "hardlink";
2140
2189
  } catch {
2141
2190
  spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
2142
- (0, import_fs12.cpSync)(videoSourcePath2, destVideoPath);
2191
+ (0, import_fs13.cpSync)(videoSourcePath2, destVideoPath);
2143
2192
  mode = "copy";
2144
2193
  }
2145
- if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs12.cpSync)(subtitleSourcePath2, (0, import_path12.resolve)(seasonPath, destSubtitleName2));
2194
+ if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs13.cpSync)(subtitleSourcePath2, (0, import_path13.resolve)(seasonPath, destSubtitleName2));
2146
2195
  } else {
2147
2196
  if (sameDev2(videoSourcePath2, seasonPath)) {
2148
- (0, import_fs12.renameSync)(videoSourcePath2, destVideoPath);
2197
+ (0, import_fs13.renameSync)(videoSourcePath2, destVideoPath);
2149
2198
  } else {
2150
- (0, import_fs12.cpSync)(videoSourcePath2, destVideoPath);
2151
- (0, import_fs12.rmSync)(videoSourcePath2);
2199
+ (0, import_fs13.cpSync)(videoSourcePath2, destVideoPath);
2200
+ (0, import_fs13.rmSync)(videoSourcePath2);
2152
2201
  }
2153
- if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs12.renameSync)(subtitleSourcePath2, (0, import_path12.resolve)(seasonPath, destSubtitleName2));
2154
- if (isDir) (0, import_fs12.rmSync)(entryPath, { recursive: true, force: true });
2202
+ if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs13.renameSync)(subtitleSourcePath2, (0, import_path13.resolve)(seasonPath, destSubtitleName2));
2203
+ if (isDir) (0, import_fs13.rmSync)(entryPath, { recursive: true, force: true });
2155
2204
  }
2156
2205
  recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
2157
2206
  spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
@@ -2159,8 +2208,8 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2159
2208
  }
2160
2209
  const edition = detectEdition(entry);
2161
2210
  const folderName = formatMovieName(movieFormat, parsed.title, parsed.year, edition);
2162
- const destFolder = (0, import_path12.resolve)(destRoot, folderName);
2163
- if ((0, import_fs12.existsSync)(destFolder)) {
2211
+ const destFolder = (0, import_path13.resolve)(destRoot, folderName);
2212
+ if ((0, import_fs13.existsSync)(destFolder)) {
2164
2213
  spinner_default.warn(`already exists: ${folderName}`);
2165
2214
  return;
2166
2215
  }
@@ -2171,42 +2220,42 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2171
2220
  }
2172
2221
  const videoExt = videoFile.match(/([^.]+$)/)?.[0];
2173
2222
  const destVideoName = `${folderName}.${videoExt}`;
2174
- const videoSourcePath = isDir ? (0, import_path12.resolve)(entryPath, videoFile) : entryPath;
2175
- const dirFiles = isDir ? (0, import_fs12.readdirSync)(entryPath) : [];
2223
+ const videoSourcePath = isDir ? (0, import_path13.resolve)(entryPath, videoFile) : entryPath;
2224
+ const dirFiles = isDir ? (0, import_fs13.readdirSync)(entryPath) : [];
2176
2225
  const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
2177
2226
  const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
2178
- const subtitleSourcePath = subtitle ? (0, import_path12.resolve)(entryPath, subtitle) : null;
2227
+ const subtitleSourcePath = subtitle ? (0, import_path13.resolve)(entryPath, subtitle) : null;
2179
2228
  const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
2180
2229
  if (useHardlink) {
2181
- (0, import_fs12.mkdirSync)(destFolder, { recursive: true });
2182
- const destVideoPath = (0, import_path12.resolve)(destFolder, destVideoName);
2230
+ (0, import_fs13.mkdirSync)(destFolder, { recursive: true });
2231
+ const destVideoPath = (0, import_path13.resolve)(destFolder, destVideoName);
2183
2232
  let mode;
2184
2233
  try {
2185
2234
  if (!sameDev2(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
2186
- (0, import_fs12.linkSync)(videoSourcePath, destVideoPath);
2235
+ (0, import_fs13.linkSync)(videoSourcePath, destVideoPath);
2187
2236
  mode = "hardlink";
2188
2237
  } catch {
2189
2238
  spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
2190
- (0, import_fs12.cpSync)(videoSourcePath, destVideoPath);
2239
+ (0, import_fs13.cpSync)(videoSourcePath, destVideoPath);
2191
2240
  mode = "copy";
2192
2241
  }
2193
- if (subtitleSourcePath && destSubtitleName) (0, import_fs12.cpSync)(subtitleSourcePath, (0, import_path12.resolve)(destFolder, destSubtitleName));
2242
+ if (subtitleSourcePath && destSubtitleName) (0, import_fs13.cpSync)(subtitleSourcePath, (0, import_path13.resolve)(destFolder, destSubtitleName));
2194
2243
  recordImport(sessionId, entryPath, destFolder, mode, void 0, "movie");
2195
2244
  } else {
2196
2245
  if (isDir) {
2197
2246
  const keep = new Set([videoFile, subtitle].filter(Boolean));
2198
- for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0, import_fs12.rmSync)((0, import_path12.resolve)(entryPath, f), { recursive: true, force: true });
2199
- (0, import_fs12.renameSync)(videoSourcePath, (0, import_path12.resolve)(entryPath, destVideoName));
2200
- if (subtitleSourcePath && destSubtitleName) (0, import_fs12.renameSync)(subtitleSourcePath, (0, import_path12.resolve)(entryPath, destSubtitleName));
2247
+ for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0, import_fs13.rmSync)((0, import_path13.resolve)(entryPath, f), { recursive: true, force: true });
2248
+ (0, import_fs13.renameSync)(videoSourcePath, (0, import_path13.resolve)(entryPath, destVideoName));
2249
+ if (subtitleSourcePath && destSubtitleName) (0, import_fs13.renameSync)(subtitleSourcePath, (0, import_path13.resolve)(entryPath, destSubtitleName));
2201
2250
  moveItem(entryPath, destFolder);
2202
2251
  } else {
2203
- (0, import_fs12.mkdirSync)(destFolder, { recursive: true });
2204
- const destVideoPath = (0, import_path12.resolve)(destFolder, destVideoName);
2252
+ (0, import_fs13.mkdirSync)(destFolder, { recursive: true });
2253
+ const destVideoPath = (0, import_path13.resolve)(destFolder, destVideoName);
2205
2254
  if (sameDev2(videoSourcePath, destRoot)) {
2206
- (0, import_fs12.renameSync)(videoSourcePath, destVideoPath);
2255
+ (0, import_fs13.renameSync)(videoSourcePath, destVideoPath);
2207
2256
  } else {
2208
- (0, import_fs12.cpSync)(videoSourcePath, destVideoPath);
2209
- (0, import_fs12.rmSync)(videoSourcePath);
2257
+ (0, import_fs13.cpSync)(videoSourcePath, destVideoPath);
2258
+ (0, import_fs13.rmSync)(videoSourcePath);
2210
2259
  }
2211
2260
  }
2212
2261
  recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");