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.mjs CHANGED
@@ -1072,7 +1072,7 @@ var reset = async ({ dir: inputDir, double }) => {
1072
1072
  var reset_default = reset;
1073
1073
 
1074
1074
  // src/actions/scan.ts
1075
- import { cpSync, existsSync as existsSync9, linkSync, lstatSync as lstatSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync7, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync2 } from "fs";
1075
+ import { cpSync, existsSync as existsSync10, linkSync, lstatSync as lstatSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync7, renameSync as renameSync4, rmSync as rmSync3, statSync as statSync2 } from "fs";
1076
1076
  import { dirname as dirname2, resolve as resolve8 } from "path";
1077
1077
  import { Color as Color9, MultiSelect as MultiSelect2, Select as Select2 } from "termkit";
1078
1078
 
@@ -1097,10 +1097,49 @@ var detectEdition = (filename) => {
1097
1097
  // src/helpers/hyperlink.ts
1098
1098
  var hyperlink = (url, text) => `\x1B]8;;${url}\x1B\\${text}\x1B]8;;\x1B\\`;
1099
1099
 
1100
+ // src/helpers/trash.ts
1101
+ import { execSync } from "child_process";
1102
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, renameSync as renameSync3, rmSync as rmSync2 } from "fs";
1103
+ import { homedir as homedir3 } from "os";
1104
+ import { basename as basename3, join as join3 } from "path";
1105
+ var trashDir = () => {
1106
+ if (process.platform === "linux") return join3(homedir3(), ".local", "share", "Trash", "files");
1107
+ return join3(homedir3(), ".Trash");
1108
+ };
1109
+ var trashPath = (filePath, opts) => {
1110
+ if (process.platform === "darwin" || process.platform === "linux") {
1111
+ try {
1112
+ const dir = trashDir();
1113
+ if (!existsSync9(dir)) mkdirSync3(dir, { recursive: true });
1114
+ const name = basename3(filePath);
1115
+ let dest = join3(dir, name);
1116
+ let i = 1;
1117
+ while (existsSync9(dest)) dest = join3(dir, `${name} ${i++}`);
1118
+ renameSync3(filePath, dest);
1119
+ return;
1120
+ } catch {
1121
+ }
1122
+ } else if (process.platform === "win32") {
1123
+ try {
1124
+ const escaped = filePath.replace(/'/g, "''");
1125
+ const isDir = opts?.recursive ?? false;
1126
+ const method = isDir ? "DeleteDirectory" : "DeleteFile";
1127
+ execSync(
1128
+ `powershell -NoProfile -NonInteractive -Command "Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::${method}('${escaped}', 'OnlyErrorDialogs', 'SendToRecycleBin')"`,
1129
+ { stdio: "ignore" }
1130
+ );
1131
+ return;
1132
+ } catch {
1133
+ }
1134
+ }
1135
+ rmSync2(filePath, { recursive: opts?.recursive ?? false, force: true });
1136
+ };
1137
+
1100
1138
  // src/helpers/parseDownloadName.ts
1101
1139
  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"]);
1102
1140
  var TV_PATTERN = /^(.*?)[.\s_-]*(?:S(\d{2,3})E(\d{2,3})|(\d{1,2})x(\d{2,3})|Season[\s.](\d+))/i;
1103
- var parseDownloadName = (name) => {
1141
+ var parseDownloadName = (rawName) => {
1142
+ const name = rawName.replace(/^[\w.-]+\.\w{2,6}\s+-+\s+/i, "");
1104
1143
  const base = name.replace(/\.[a-z0-9]{2,4}$/i, "");
1105
1144
  const tvMatch = TV_PATTERN.exec(base);
1106
1145
  if (tvMatch) {
@@ -1199,7 +1238,7 @@ var bookExtensions_default = ["epub", "mobi", "azw3", "azw"];
1199
1238
  var sameDev = (a, b) => {
1200
1239
  try {
1201
1240
  let bExisting = b;
1202
- while (!existsSync9(bExisting)) bExisting = dirname2(bExisting);
1241
+ while (!existsSync10(bExisting)) bExisting = dirname2(bExisting);
1203
1242
  return statSync2(a).dev === statSync2(bExisting).dev;
1204
1243
  } catch {
1205
1244
  return false;
@@ -1207,10 +1246,10 @@ var sameDev = (a, b) => {
1207
1246
  };
1208
1247
  var moveFolder = (src, dest) => {
1209
1248
  if (sameDev(src, dest)) {
1210
- renameSync3(src, dest);
1249
+ renameSync4(src, dest);
1211
1250
  } else {
1212
1251
  cpSync(src, dest, { recursive: true });
1213
- rmSync2(src, { recursive: true, force: true });
1252
+ trashPath(src, { recursive: true });
1214
1253
  }
1215
1254
  };
1216
1255
  var findVideo = (dir) => readdirSync7(dir).find((f) => {
@@ -1311,7 +1350,7 @@ var gatherEntries = (source) => {
1311
1350
  return result;
1312
1351
  };
1313
1352
  var findShowFolder = (destRoot, title) => {
1314
- if (!existsSync9(destRoot)) return null;
1353
+ if (!existsSync10(destRoot)) return null;
1315
1354
  const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
1316
1355
  const target = normalize(title);
1317
1356
  return readdirSync7(destRoot).filter((f) => {
@@ -1323,7 +1362,7 @@ var findShowFolder = (destRoot, title) => {
1323
1362
  }).find((f) => normalize(f) === target) ?? null;
1324
1363
  };
1325
1364
  var findShowFolderByContent = (destRoot, title) => {
1326
- if (!existsSync9(destRoot)) return null;
1365
+ if (!existsSync10(destRoot)) return null;
1327
1366
  const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
1328
1367
  const target = normalize(title);
1329
1368
  const matchesTitle = (name) => {
@@ -1351,8 +1390,8 @@ var findShowFolderByContent = (destRoot, title) => {
1351
1390
  }
1352
1391
  return null;
1353
1392
  };
1354
- var findSeasonFolder = (showPath, season) => {
1355
- if (!existsSync9(showPath)) return null;
1393
+ var findSeasonFolder = (showPath, season, specialsFolder) => {
1394
+ if (!existsSync10(showPath)) return null;
1356
1395
  const folders = readdirSync7(showPath).filter((f) => {
1357
1396
  try {
1358
1397
  return lstatSync4(resolve8(showPath, f)).isDirectory();
@@ -1360,6 +1399,10 @@ var findSeasonFolder = (showPath, season) => {
1360
1399
  return false;
1361
1400
  }
1362
1401
  });
1402
+ if (season === 0 && specialsFolder) {
1403
+ const existing = folders.find((f) => f.toLowerCase() === specialsFolder.toLowerCase());
1404
+ if (existing) return existing;
1405
+ }
1363
1406
  return folders.find((f) => {
1364
1407
  const match = f.match(/(?:season|s)\s*0*(\d+)/i);
1365
1408
  return match && parseInt(match[1]) === season;
@@ -1388,11 +1431,13 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1388
1431
  const language = config.language ?? "eng";
1389
1432
  const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
1390
1433
  const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
1434
+ const specialsFolder = config.specialsFolder ?? "Specials";
1391
1435
  const lookupMovie = async (parsed) => {
1392
1436
  let tmdbId;
1393
1437
  let resolvedTitle = parsed.title;
1394
1438
  let resolvedYear = parsed.year;
1395
1439
  if (config.tmdbApiKey) {
1440
+ spinner_default.text = `TMDb: ${parsed.title}`;
1396
1441
  const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
1397
1442
  if (results.length === 1) {
1398
1443
  tmdbId = results[0].id;
@@ -1421,7 +1466,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1421
1466
  const edition = detectEdition(entry);
1422
1467
  const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
1423
1468
  const destFolder = resolve8(destRoot, folderName);
1424
- if (existsSync9(destFolder)) {
1469
+ if (existsSync10(destFolder)) {
1425
1470
  spinner_default.warn(`already exists: ${folderName}`);
1426
1471
  return false;
1427
1472
  }
@@ -1440,7 +1485,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1440
1485
  const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
1441
1486
  if (!dryRun) {
1442
1487
  if (useHardlink) {
1443
- mkdirSync3(destFolder, { recursive: true });
1488
+ mkdirSync4(destFolder, { recursive: true });
1444
1489
  const destVideoPath = resolve8(destFolder, destVideoName);
1445
1490
  let mode;
1446
1491
  try {
@@ -1457,18 +1502,18 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1457
1502
  } else {
1458
1503
  if (isDir) {
1459
1504
  const keep = new Set([videoFile, subtitle].filter(Boolean));
1460
- for (const f of dirFiles.filter((f2) => !keep.has(f2))) rmSync2(resolve8(entryPath, f), { recursive: true, force: true });
1461
- renameSync3(videoSourcePath, resolve8(entryPath, destVideoName));
1462
- if (subtitleSourcePath && destSubtitleName) renameSync3(subtitleSourcePath, resolve8(entryPath, destSubtitleName));
1505
+ for (const f of dirFiles.filter((f2) => !keep.has(f2))) trashPath(resolve8(entryPath, f), { recursive: true });
1506
+ renameSync4(videoSourcePath, resolve8(entryPath, destVideoName));
1507
+ if (subtitleSourcePath && destSubtitleName) renameSync4(subtitleSourcePath, resolve8(entryPath, destSubtitleName));
1463
1508
  moveFolder(entryPath, destFolder);
1464
1509
  } else {
1465
- mkdirSync3(destFolder, { recursive: true });
1510
+ mkdirSync4(destFolder, { recursive: true });
1466
1511
  const destVideoPath = resolve8(destFolder, destVideoName);
1467
1512
  if (sameDev(videoSourcePath, destRoot)) {
1468
- renameSync3(videoSourcePath, destVideoPath);
1513
+ renameSync4(videoSourcePath, destVideoPath);
1469
1514
  } else {
1470
1515
  cpSync(videoSourcePath, destVideoPath);
1471
- rmSync2(videoSourcePath);
1516
+ trashPath(videoSourcePath);
1472
1517
  }
1473
1518
  }
1474
1519
  recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
@@ -1485,12 +1530,13 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1485
1530
  const ignoreSet = new Set(config.ignore ?? []);
1486
1531
  const seenIgnored = /* @__PURE__ */ new Set();
1487
1532
  for (const source of config.sources) {
1488
- if (!existsSync9(source)) {
1533
+ if (!existsSync10(source)) {
1489
1534
  spinner_default.warn(`source not found: ${Color9.white.encoder(source)}`);
1490
1535
  continue;
1491
1536
  }
1492
1537
  spinner_default.text = `scanning ${Color9.white.encoder(source)}`;
1493
1538
  for (const { entry, entryPath, isDir } of gatherEntries(source)) {
1539
+ spinner_default.text = `scanning: ${entry}`;
1494
1540
  if (ignoreSet.has(entry)) {
1495
1541
  seenIgnored.add(entry);
1496
1542
  if (isVerbose()) spinner_default.info(`ignored: ${entry}`);
@@ -1526,7 +1572,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1526
1572
  }
1527
1573
  const destName = `${nameMatch[0]} [${id}]`;
1528
1574
  const destPath = resolve8(destRoot, destName);
1529
- if (existsSync9(destPath)) {
1575
+ if (existsSync10(destPath)) {
1530
1576
  spinner_default.warn(`already exists: ${destName}`);
1531
1577
  skipped++;
1532
1578
  continue;
@@ -1541,7 +1587,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1541
1587
  }
1542
1588
  if (detectedType === "book") {
1543
1589
  const destPath = resolve8(destRoot, entry);
1544
- if (existsSync9(destPath)) {
1590
+ if (existsSync10(destPath)) {
1545
1591
  spinner_default.warn(`already exists: ${entry}`);
1546
1592
  skipped++;
1547
1593
  continue;
@@ -1550,12 +1596,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1550
1596
  if (isDir || isBookDir) {
1551
1597
  moveFolder(entryPath, destPath);
1552
1598
  } else {
1553
- mkdirSync3(destRoot, { recursive: true });
1599
+ mkdirSync4(destRoot, { recursive: true });
1554
1600
  if (sameDev(entryPath, destRoot)) {
1555
- renameSync3(entryPath, destPath);
1601
+ renameSync4(entryPath, destPath);
1556
1602
  } else {
1557
1603
  cpSync(entryPath, destPath);
1558
- rmSync2(entryPath);
1604
+ rmSync3(entryPath);
1559
1605
  }
1560
1606
  }
1561
1607
  recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
@@ -1587,6 +1633,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1587
1633
  let resolvedYear = parsed.year;
1588
1634
  if (config.tmdbApiKey) {
1589
1635
  if (detectedType === "tv") {
1636
+ spinner_default.text = `TMDb: ${parsed.title}`;
1590
1637
  const results = await searchTv(parsed.title, config.tmdbApiKey);
1591
1638
  if (results.length === 1) {
1592
1639
  tmdbId = results[0].id;
@@ -1641,7 +1688,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1641
1688
  continue;
1642
1689
  }
1643
1690
  }
1644
- const seasonFolderName = findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
1691
+ const seasonFolderName = parsed.season === 0 ? findSeasonFolder(showPath, 0, specialsFolder) ?? specialsFolder : findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
1645
1692
  const seasonPath = resolve8(showPath, seasonFolderName);
1646
1693
  const videoFile = isDir ? findVideo(entryPath) : entry;
1647
1694
  if (!videoFile) {
@@ -1650,13 +1697,15 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1650
1697
  continue;
1651
1698
  }
1652
1699
  const videoExt = videoFile.match(/([^.]+$)/)?.[0];
1700
+ if (tmdbId && config.tmdbApiKey) spinner_default.text = `TMDb: episode name for ${resolvedTitle}`;
1653
1701
  const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
1654
1702
  const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
1655
1703
  const destVideoName = `${episodeName}.${videoExt}`;
1656
1704
  const destVideoPath = resolve8(seasonPath, destVideoName);
1657
1705
  const videoSourcePath = isDir ? resolve8(entryPath, videoFile) : entryPath;
1658
- if (existsSync9(destVideoPath)) {
1659
- let shouldReplace = force;
1706
+ if (existsSync10(destVideoPath)) {
1707
+ const isRepack = /\brepack\d*\b|\bproper\b/i.test(entry);
1708
+ let shouldReplace = force || isRepack;
1660
1709
  if (!shouldReplace && interactive) {
1661
1710
  spinner_default.stop();
1662
1711
  const select = new Select2();
@@ -1674,7 +1723,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1674
1723
  }
1675
1724
  if (!dryRun) {
1676
1725
  for (const f of readdirSync7(seasonPath)) {
1677
- if (f.startsWith(`${episodeName}.`)) rmSync2(resolve8(seasonPath, f));
1726
+ if (f.startsWith(`${episodeName}.`)) trashPath(resolve8(seasonPath, f));
1678
1727
  }
1679
1728
  }
1680
1729
  }
@@ -1684,7 +1733,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1684
1733
  const subtitleSourcePath = subtitle ? resolve8(entryPath, subtitle) : null;
1685
1734
  const destSubtitleName = subtitle && subtitleExt ? `${episodeName}.${subtitleExt}` : null;
1686
1735
  if (!dryRun) {
1687
- mkdirSync3(seasonPath, { recursive: true });
1736
+ mkdirSync4(seasonPath, { recursive: true });
1688
1737
  let mode = "move";
1689
1738
  if (useHardlink) {
1690
1739
  try {
@@ -1699,13 +1748,13 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1699
1748
  if (subtitleSourcePath && destSubtitleName) cpSync(subtitleSourcePath, resolve8(seasonPath, destSubtitleName));
1700
1749
  } else {
1701
1750
  if (sameDev(videoSourcePath, seasonPath)) {
1702
- renameSync3(videoSourcePath, destVideoPath);
1751
+ renameSync4(videoSourcePath, destVideoPath);
1703
1752
  } else {
1704
1753
  cpSync(videoSourcePath, destVideoPath);
1705
- rmSync2(videoSourcePath);
1754
+ trashPath(videoSourcePath);
1706
1755
  }
1707
- if (subtitleSourcePath && destSubtitleName) renameSync3(subtitleSourcePath, resolve8(seasonPath, destSubtitleName));
1708
- if (isDir) rmSync2(entryPath, { recursive: true, force: true });
1756
+ if (subtitleSourcePath && destSubtitleName) renameSync4(subtitleSourcePath, resolve8(seasonPath, destSubtitleName));
1757
+ if (isDir) trashPath(entryPath, { recursive: true });
1709
1758
  }
1710
1759
  recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
1711
1760
  }
@@ -1770,7 +1819,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
1770
1819
  var scan_default = scan;
1771
1820
 
1772
1821
  // src/actions/undo.ts
1773
- import { existsSync as existsSync10, renameSync as renameSync4 } from "fs";
1822
+ import { existsSync as existsSync11, renameSync as renameSync5 } from "fs";
1774
1823
  import { Color as Color10 } from "termkit";
1775
1824
  var undo = async () => {
1776
1825
  spinner_default.start();
@@ -1785,7 +1834,7 @@ var undo = async () => {
1785
1834
  if (!useImports) {
1786
1835
  let undone2 = 0;
1787
1836
  for (const record of renameRecords) {
1788
- renameSync4(record.newPath, record.oldPath);
1837
+ renameSync5(record.newPath, record.oldPath);
1789
1838
  spinner_default.succeed(`${Color10.green.encoder(record.newPath)} \u2192 ${Color10.white.encoder(record.oldPath)}`);
1790
1839
  undone2++;
1791
1840
  }
@@ -1807,12 +1856,12 @@ var undo = async () => {
1807
1856
  skipped++;
1808
1857
  continue;
1809
1858
  }
1810
- if (!existsSync10(record.destinationPath)) {
1859
+ if (!existsSync11(record.destinationPath)) {
1811
1860
  spinner_default.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
1812
1861
  skipped++;
1813
1862
  continue;
1814
1863
  }
1815
- renameSync4(record.destinationPath, record.sourcePath);
1864
+ renameSync5(record.destinationPath, record.sourcePath);
1816
1865
  spinner_default.succeed(`${Color10.green.encoder(record.destinationPath)} \u2192 ${Color10.white.encoder(record.sourcePath)}`);
1817
1866
  undone++;
1818
1867
  }
@@ -1825,13 +1874,13 @@ var undo_default = undo;
1825
1874
 
1826
1875
  // src/actions/watch.ts
1827
1876
  import chokidar from "chokidar";
1828
- import { cpSync as cpSync2, existsSync as existsSync11, linkSync as linkSync2, lstatSync as lstatSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync8, renameSync as renameSync5, rmSync as rmSync3, statSync as statSync3 } from "fs";
1829
- import { basename as basename3, dirname as dirname3, resolve as resolve9 } from "path";
1877
+ import { cpSync as cpSync2, existsSync as existsSync12, linkSync as linkSync2, lstatSync as lstatSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync8, renameSync as renameSync6, rmSync as rmSync4, statSync as statSync3 } from "fs";
1878
+ import { basename as basename4, dirname as dirname3, resolve as resolve9 } from "path";
1830
1879
  import { Color as Color11 } from "termkit";
1831
1880
  var sameDev2 = (a, b) => {
1832
1881
  try {
1833
1882
  let bExisting = b;
1834
- while (!existsSync11(bExisting)) bExisting = dirname3(bExisting);
1883
+ while (!existsSync12(bExisting)) bExisting = dirname3(bExisting);
1835
1884
  return statSync3(a).dev === statSync3(bExisting).dev;
1836
1885
  } catch {
1837
1886
  return false;
@@ -1839,10 +1888,10 @@ var sameDev2 = (a, b) => {
1839
1888
  };
1840
1889
  var moveItem = (src, dest) => {
1841
1890
  if (sameDev2(src, dest)) {
1842
- renameSync5(src, dest);
1891
+ renameSync6(src, dest);
1843
1892
  } else {
1844
1893
  cpSync2(src, dest, { recursive: true });
1845
- rmSync3(src, { recursive: true, force: true });
1894
+ rmSync4(src, { recursive: true, force: true });
1846
1895
  }
1847
1896
  };
1848
1897
  var findVideo2 = (dir) => readdirSync8(dir).find((f) => {
@@ -1871,7 +1920,7 @@ var expandWatchPath = (p) => {
1871
1920
  return [p];
1872
1921
  }
1873
1922
  if (!isDir) return [p];
1874
- const name = basename3(p);
1923
+ const name = basename4(p);
1875
1924
  if (isTvEpisodeName2(name) || /(?<=\[).+?(?=\])/.test(name) || /^[A-Z]{4}\d{5}/i.test(name)) return [p];
1876
1925
  let children;
1877
1926
  try {
@@ -1930,7 +1979,7 @@ var expandWatchPath = (p) => {
1930
1979
  return [p];
1931
1980
  };
1932
1981
  var findSeasonFolder2 = (showPath, season) => {
1933
- if (!existsSync11(showPath)) return null;
1982
+ if (!existsSync12(showPath)) return null;
1934
1983
  const folders = readdirSync8(showPath).filter((f) => {
1935
1984
  try {
1936
1985
  return lstatSync5(resolve9(showPath, f)).isDirectory();
@@ -1946,7 +1995,7 @@ var findSeasonFolder2 = (showPath, season) => {
1946
1995
  var processItem = async (entryPath, useHardlink, language, auto) => {
1947
1996
  const config = getConfig();
1948
1997
  const sessionId = (/* @__PURE__ */ new Date()).toISOString();
1949
- const entry = basename3(entryPath);
1998
+ const entry = basename4(entryPath);
1950
1999
  const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
1951
2000
  const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
1952
2001
  const isDir = lstatSync5(entryPath).isDirectory();
@@ -1976,7 +2025,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
1976
2025
  if (!nameMatch || !id) return;
1977
2026
  const destName = `${nameMatch[0]} [${id}]`;
1978
2027
  const destPath = resolve9(destRoot, destName);
1979
- if (existsSync11(destPath)) {
2028
+ if (existsSync12(destPath)) {
1980
2029
  spinner_default.warn(`already exists: ${destName}`);
1981
2030
  return;
1982
2031
  }
@@ -1987,19 +2036,19 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
1987
2036
  }
1988
2037
  if (detectedType === "book") {
1989
2038
  const destPath = resolve9(destRoot, entry);
1990
- if (existsSync11(destPath)) {
2039
+ if (existsSync12(destPath)) {
1991
2040
  spinner_default.warn(`already exists: ${entry}`);
1992
2041
  return;
1993
2042
  }
1994
2043
  if (isDir || isBookDir) {
1995
2044
  moveItem(entryPath, destPath);
1996
2045
  } else {
1997
- mkdirSync4(destRoot, { recursive: true });
2046
+ mkdirSync5(destRoot, { recursive: true });
1998
2047
  if (sameDev2(entryPath, destRoot)) {
1999
- renameSync5(entryPath, destPath);
2048
+ renameSync6(entryPath, destPath);
2000
2049
  } else {
2001
2050
  cpSync2(entryPath, destPath);
2002
- rmSync3(entryPath);
2051
+ rmSync4(entryPath);
2003
2052
  }
2004
2053
  }
2005
2054
  recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
@@ -2043,7 +2092,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2043
2092
  const destVideoName2 = `${episodeName}.${videoExt2}`;
2044
2093
  const destVideoPath = resolve9(seasonPath, destVideoName2);
2045
2094
  const videoSourcePath2 = isDir ? resolve9(entryPath, videoFile2) : entryPath;
2046
- if (existsSync11(destVideoPath)) {
2095
+ if (existsSync12(destVideoPath)) {
2047
2096
  spinner_default.warn(`already exists: ${episodeName}`);
2048
2097
  return;
2049
2098
  }
@@ -2052,7 +2101,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2052
2101
  const subtitleExt2 = subtitle2?.match(/([^.]+$)/)?.[0];
2053
2102
  const subtitleSourcePath2 = subtitle2 ? resolve9(entryPath, subtitle2) : null;
2054
2103
  const destSubtitleName2 = subtitle2 && subtitleExt2 ? `${episodeName}.${subtitleExt2}` : null;
2055
- mkdirSync4(seasonPath, { recursive: true });
2104
+ mkdirSync5(seasonPath, { recursive: true });
2056
2105
  let mode = "move";
2057
2106
  if (useHardlink) {
2058
2107
  try {
@@ -2067,13 +2116,13 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2067
2116
  if (subtitleSourcePath2 && destSubtitleName2) cpSync2(subtitleSourcePath2, resolve9(seasonPath, destSubtitleName2));
2068
2117
  } else {
2069
2118
  if (sameDev2(videoSourcePath2, seasonPath)) {
2070
- renameSync5(videoSourcePath2, destVideoPath);
2119
+ renameSync6(videoSourcePath2, destVideoPath);
2071
2120
  } else {
2072
2121
  cpSync2(videoSourcePath2, destVideoPath);
2073
- rmSync3(videoSourcePath2);
2122
+ rmSync4(videoSourcePath2);
2074
2123
  }
2075
- if (subtitleSourcePath2 && destSubtitleName2) renameSync5(subtitleSourcePath2, resolve9(seasonPath, destSubtitleName2));
2076
- if (isDir) rmSync3(entryPath, { recursive: true, force: true });
2124
+ if (subtitleSourcePath2 && destSubtitleName2) renameSync6(subtitleSourcePath2, resolve9(seasonPath, destSubtitleName2));
2125
+ if (isDir) rmSync4(entryPath, { recursive: true, force: true });
2077
2126
  }
2078
2127
  recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
2079
2128
  spinner_default.succeed(`imported ${Color11.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
@@ -2082,7 +2131,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2082
2131
  const edition = detectEdition(entry);
2083
2132
  const folderName = formatMovieName(movieFormat, parsed.title, parsed.year, edition);
2084
2133
  const destFolder = resolve9(destRoot, folderName);
2085
- if (existsSync11(destFolder)) {
2134
+ if (existsSync12(destFolder)) {
2086
2135
  spinner_default.warn(`already exists: ${folderName}`);
2087
2136
  return;
2088
2137
  }
@@ -2100,7 +2149,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2100
2149
  const subtitleSourcePath = subtitle ? resolve9(entryPath, subtitle) : null;
2101
2150
  const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
2102
2151
  if (useHardlink) {
2103
- mkdirSync4(destFolder, { recursive: true });
2152
+ mkdirSync5(destFolder, { recursive: true });
2104
2153
  const destVideoPath = resolve9(destFolder, destVideoName);
2105
2154
  let mode;
2106
2155
  try {
@@ -2117,18 +2166,18 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
2117
2166
  } else {
2118
2167
  if (isDir) {
2119
2168
  const keep = new Set([videoFile, subtitle].filter(Boolean));
2120
- for (const f of dirFiles.filter((f2) => !keep.has(f2))) rmSync3(resolve9(entryPath, f), { recursive: true, force: true });
2121
- renameSync5(videoSourcePath, resolve9(entryPath, destVideoName));
2122
- if (subtitleSourcePath && destSubtitleName) renameSync5(subtitleSourcePath, resolve9(entryPath, destSubtitleName));
2169
+ for (const f of dirFiles.filter((f2) => !keep.has(f2))) rmSync4(resolve9(entryPath, f), { recursive: true, force: true });
2170
+ renameSync6(videoSourcePath, resolve9(entryPath, destVideoName));
2171
+ if (subtitleSourcePath && destSubtitleName) renameSync6(subtitleSourcePath, resolve9(entryPath, destSubtitleName));
2123
2172
  moveItem(entryPath, destFolder);
2124
2173
  } else {
2125
- mkdirSync4(destFolder, { recursive: true });
2174
+ mkdirSync5(destFolder, { recursive: true });
2126
2175
  const destVideoPath = resolve9(destFolder, destVideoName);
2127
2176
  if (sameDev2(videoSourcePath, destRoot)) {
2128
- renameSync5(videoSourcePath, destVideoPath);
2177
+ renameSync6(videoSourcePath, destVideoPath);
2129
2178
  } else {
2130
2179
  cpSync2(videoSourcePath, destVideoPath);
2131
- rmSync3(videoSourcePath);
2180
+ rmSync4(videoSourcePath);
2132
2181
  }
2133
2182
  }
2134
2183
  recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reelsort",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "CLI to rename, organize, and manage your movie and TV library — Plex-compatible naming, hardlink support, and automated watch mode.",
5
5
  "keywords": [
6
6
  "media",