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/cli.js +219 -170
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +207 -158
- package/dist/index.mjs +112 -63
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1496,8 +1496,8 @@ var reset = async ({ dir: inputDir, double }) => {
|
|
|
1496
1496
|
var reset_default = reset;
|
|
1497
1497
|
|
|
1498
1498
|
// src/actions/scan.ts
|
|
1499
|
-
var
|
|
1500
|
-
var
|
|
1499
|
+
var import_fs15 = require("fs");
|
|
1500
|
+
var import_path15 = require("path");
|
|
1501
1501
|
var import_termkit14 = require("termkit");
|
|
1502
1502
|
|
|
1503
1503
|
// src/helpers/detectEdition.ts
|
|
@@ -1518,10 +1518,49 @@ var detectEdition = (filename) => {
|
|
|
1518
1518
|
return null;
|
|
1519
1519
|
};
|
|
1520
1520
|
|
|
1521
|
+
// src/helpers/trash.ts
|
|
1522
|
+
var import_child_process2 = require("child_process");
|
|
1523
|
+
var import_fs14 = require("fs");
|
|
1524
|
+
var import_os3 = require("os");
|
|
1525
|
+
var import_path14 = require("path");
|
|
1526
|
+
var trashDir = () => {
|
|
1527
|
+
if (process.platform === "linux") return (0, import_path14.join)((0, import_os3.homedir)(), ".local", "share", "Trash", "files");
|
|
1528
|
+
return (0, import_path14.join)((0, import_os3.homedir)(), ".Trash");
|
|
1529
|
+
};
|
|
1530
|
+
var trashPath = (filePath, opts) => {
|
|
1531
|
+
if (process.platform === "darwin" || process.platform === "linux") {
|
|
1532
|
+
try {
|
|
1533
|
+
const dir = trashDir();
|
|
1534
|
+
if (!(0, import_fs14.existsSync)(dir)) (0, import_fs14.mkdirSync)(dir, { recursive: true });
|
|
1535
|
+
const name = (0, import_path14.basename)(filePath);
|
|
1536
|
+
let dest = (0, import_path14.join)(dir, name);
|
|
1537
|
+
let i = 1;
|
|
1538
|
+
while ((0, import_fs14.existsSync)(dest)) dest = (0, import_path14.join)(dir, `${name} ${i++}`);
|
|
1539
|
+
(0, import_fs14.renameSync)(filePath, dest);
|
|
1540
|
+
return;
|
|
1541
|
+
} catch {
|
|
1542
|
+
}
|
|
1543
|
+
} else if (process.platform === "win32") {
|
|
1544
|
+
try {
|
|
1545
|
+
const escaped = filePath.replace(/'/g, "''");
|
|
1546
|
+
const isDir = opts?.recursive ?? false;
|
|
1547
|
+
const method = isDir ? "DeleteDirectory" : "DeleteFile";
|
|
1548
|
+
(0, import_child_process2.execSync)(
|
|
1549
|
+
`powershell -NoProfile -NonInteractive -Command "Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::${method}('${escaped}', 'OnlyErrorDialogs', 'SendToRecycleBin')"`,
|
|
1550
|
+
{ stdio: "ignore" }
|
|
1551
|
+
);
|
|
1552
|
+
return;
|
|
1553
|
+
} catch {
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
(0, import_fs14.rmSync)(filePath, { recursive: opts?.recursive ?? false, force: true });
|
|
1557
|
+
};
|
|
1558
|
+
|
|
1521
1559
|
// src/helpers/parseDownloadName.ts
|
|
1522
1560
|
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"]);
|
|
1523
1561
|
var TV_PATTERN = /^(.*?)[.\s_-]*(?:S(\d{2,3})E(\d{2,3})|(\d{1,2})x(\d{2,3})|Season[\s.](\d+))/i;
|
|
1524
|
-
var parseDownloadName = (
|
|
1562
|
+
var parseDownloadName = (rawName) => {
|
|
1563
|
+
const name = rawName.replace(/^[\w.-]+\.\w{2,6}\s+-+\s+/i, "");
|
|
1525
1564
|
const base = name.replace(/\.[a-z0-9]{2,4}$/i, "");
|
|
1526
1565
|
const tvMatch = TV_PATTERN.exec(base);
|
|
1527
1566
|
if (tvMatch) {
|
|
@@ -1565,31 +1604,31 @@ var bookExtensions_default = ["epub", "mobi", "azw3", "azw"];
|
|
|
1565
1604
|
var sameDev = (a, b) => {
|
|
1566
1605
|
try {
|
|
1567
1606
|
let bExisting = b;
|
|
1568
|
-
while (!(0,
|
|
1569
|
-
return (0,
|
|
1607
|
+
while (!(0, import_fs15.existsSync)(bExisting)) bExisting = (0, import_path15.dirname)(bExisting);
|
|
1608
|
+
return (0, import_fs15.statSync)(a).dev === (0, import_fs15.statSync)(bExisting).dev;
|
|
1570
1609
|
} catch {
|
|
1571
1610
|
return false;
|
|
1572
1611
|
}
|
|
1573
1612
|
};
|
|
1574
1613
|
var moveFolder = (src, dest) => {
|
|
1575
1614
|
if (sameDev(src, dest)) {
|
|
1576
|
-
(0,
|
|
1615
|
+
(0, import_fs15.renameSync)(src, dest);
|
|
1577
1616
|
} else {
|
|
1578
|
-
(0,
|
|
1579
|
-
(
|
|
1617
|
+
(0, import_fs15.cpSync)(src, dest, { recursive: true });
|
|
1618
|
+
trashPath(src, { recursive: true });
|
|
1580
1619
|
}
|
|
1581
1620
|
};
|
|
1582
|
-
var findVideo = (dir) => (0,
|
|
1621
|
+
var findVideo = (dir) => (0, import_fs15.readdirSync)(dir).find((f) => {
|
|
1583
1622
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1584
1623
|
return ext && videoExtensions_default.includes(ext);
|
|
1585
1624
|
}) ?? null;
|
|
1586
|
-
var containsBook = (dir, depth = 2) => (0,
|
|
1625
|
+
var containsBook = (dir, depth = 2) => (0, import_fs15.readdirSync)(dir).some((f) => {
|
|
1587
1626
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1588
1627
|
if (ext && bookExtensions_default.includes(ext)) return true;
|
|
1589
1628
|
if (depth > 1) {
|
|
1590
1629
|
try {
|
|
1591
|
-
const sub = (0,
|
|
1592
|
-
if ((0,
|
|
1630
|
+
const sub = (0, import_path15.resolve)(dir, f);
|
|
1631
|
+
if ((0, import_fs15.lstatSync)(sub).isDirectory()) return containsBook(sub, depth - 1);
|
|
1593
1632
|
} catch {
|
|
1594
1633
|
}
|
|
1595
1634
|
}
|
|
@@ -1599,11 +1638,11 @@ var isTvEpisodeName = (name) => /S\d{2,3}E\d{2,3}/i.test(name) || /\d+x\d{2,3}/i
|
|
|
1599
1638
|
var isSeasonDirName = (name) => !isTvEpisodeName(name) && /(?:^|[.\s_-])(?:season|s)\s*0*\d+(?:[.\s_-]|$)/i.test(name);
|
|
1600
1639
|
var gatherEntries = (source) => {
|
|
1601
1640
|
const result = [];
|
|
1602
|
-
for (const name of (0,
|
|
1603
|
-
const fullPath = (0,
|
|
1641
|
+
for (const name of (0, import_fs15.readdirSync)(source)) {
|
|
1642
|
+
const fullPath = (0, import_path15.resolve)(source, name);
|
|
1604
1643
|
let isDir;
|
|
1605
1644
|
try {
|
|
1606
|
-
isDir = (0,
|
|
1645
|
+
isDir = (0, import_fs15.lstatSync)(fullPath).isDirectory();
|
|
1607
1646
|
} catch {
|
|
1608
1647
|
continue;
|
|
1609
1648
|
}
|
|
@@ -1621,17 +1660,17 @@ var gatherEntries = (source) => {
|
|
|
1621
1660
|
}
|
|
1622
1661
|
let children;
|
|
1623
1662
|
try {
|
|
1624
|
-
children = (0,
|
|
1663
|
+
children = (0, import_fs15.readdirSync)(fullPath);
|
|
1625
1664
|
} catch {
|
|
1626
1665
|
result.push({ entry: name, entryPath: fullPath, isDir: true });
|
|
1627
1666
|
continue;
|
|
1628
1667
|
}
|
|
1629
1668
|
if (children.some((c) => isTvEpisodeName(c))) {
|
|
1630
1669
|
for (const child of children) {
|
|
1631
|
-
const childPath = (0,
|
|
1670
|
+
const childPath = (0, import_path15.resolve)(fullPath, child);
|
|
1632
1671
|
let childIsDir;
|
|
1633
1672
|
try {
|
|
1634
|
-
childIsDir = (0,
|
|
1673
|
+
childIsDir = (0, import_fs15.lstatSync)(childPath).isDirectory();
|
|
1635
1674
|
} catch {
|
|
1636
1675
|
continue;
|
|
1637
1676
|
}
|
|
@@ -1643,25 +1682,25 @@ var gatherEntries = (source) => {
|
|
|
1643
1682
|
}
|
|
1644
1683
|
const seasonDirs = children.filter((c) => {
|
|
1645
1684
|
try {
|
|
1646
|
-
return isSeasonDirName(c) && (0,
|
|
1685
|
+
return isSeasonDirName(c) && (0, import_fs15.lstatSync)((0, import_path15.resolve)(fullPath, c)).isDirectory();
|
|
1647
1686
|
} catch {
|
|
1648
1687
|
return false;
|
|
1649
1688
|
}
|
|
1650
1689
|
});
|
|
1651
1690
|
if (seasonDirs.length > 0) {
|
|
1652
1691
|
for (const seasonDir of seasonDirs) {
|
|
1653
|
-
const seasonPath = (0,
|
|
1692
|
+
const seasonPath = (0, import_path15.resolve)(fullPath, seasonDir);
|
|
1654
1693
|
let seasonChildren;
|
|
1655
1694
|
try {
|
|
1656
|
-
seasonChildren = (0,
|
|
1695
|
+
seasonChildren = (0, import_fs15.readdirSync)(seasonPath);
|
|
1657
1696
|
} catch {
|
|
1658
1697
|
continue;
|
|
1659
1698
|
}
|
|
1660
1699
|
for (const child of seasonChildren) {
|
|
1661
|
-
const childPath = (0,
|
|
1700
|
+
const childPath = (0, import_path15.resolve)(seasonPath, child);
|
|
1662
1701
|
let childIsDir;
|
|
1663
1702
|
try {
|
|
1664
|
-
childIsDir = (0,
|
|
1703
|
+
childIsDir = (0, import_fs15.lstatSync)(childPath).isDirectory();
|
|
1665
1704
|
} catch {
|
|
1666
1705
|
continue;
|
|
1667
1706
|
}
|
|
@@ -1677,19 +1716,19 @@ var gatherEntries = (source) => {
|
|
|
1677
1716
|
return result;
|
|
1678
1717
|
};
|
|
1679
1718
|
var findShowFolder = (destRoot, title) => {
|
|
1680
|
-
if (!(0,
|
|
1719
|
+
if (!(0, import_fs15.existsSync)(destRoot)) return null;
|
|
1681
1720
|
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1682
1721
|
const target = normalize(title);
|
|
1683
|
-
return (0,
|
|
1722
|
+
return (0, import_fs15.readdirSync)(destRoot).filter((f) => {
|
|
1684
1723
|
try {
|
|
1685
|
-
return (0,
|
|
1724
|
+
return (0, import_fs15.lstatSync)((0, import_path15.resolve)(destRoot, f)).isDirectory();
|
|
1686
1725
|
} catch {
|
|
1687
1726
|
return false;
|
|
1688
1727
|
}
|
|
1689
1728
|
}).find((f) => normalize(f) === target) ?? null;
|
|
1690
1729
|
};
|
|
1691
1730
|
var findShowFolderByContent = (destRoot, title) => {
|
|
1692
|
-
if (!(0,
|
|
1731
|
+
if (!(0, import_fs15.existsSync)(destRoot)) return null;
|
|
1693
1732
|
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1694
1733
|
const target = normalize(title);
|
|
1695
1734
|
const matchesTitle = (name) => {
|
|
@@ -1697,18 +1736,18 @@ var findShowFolderByContent = (destRoot, title) => {
|
|
|
1697
1736
|
const p = parseDownloadName(name);
|
|
1698
1737
|
return !!p && normalize(p.title) === target;
|
|
1699
1738
|
};
|
|
1700
|
-
for (const folder of (0,
|
|
1739
|
+
for (const folder of (0, import_fs15.readdirSync)(destRoot)) {
|
|
1701
1740
|
try {
|
|
1702
|
-
const folderPath = (0,
|
|
1703
|
-
if (!(0,
|
|
1704
|
-
const children = (0,
|
|
1741
|
+
const folderPath = (0, import_path15.resolve)(destRoot, folder);
|
|
1742
|
+
if (!(0, import_fs15.lstatSync)(folderPath).isDirectory()) continue;
|
|
1743
|
+
const children = (0, import_fs15.readdirSync)(folderPath);
|
|
1705
1744
|
if (children.some(matchesTitle)) return folder;
|
|
1706
1745
|
for (const child of children) {
|
|
1707
1746
|
if (!isSeasonDirName(child)) continue;
|
|
1708
1747
|
try {
|
|
1709
|
-
const seasonPath = (0,
|
|
1710
|
-
if (!(0,
|
|
1711
|
-
if ((0,
|
|
1748
|
+
const seasonPath = (0, import_path15.resolve)(folderPath, child);
|
|
1749
|
+
if (!(0, import_fs15.lstatSync)(seasonPath).isDirectory()) continue;
|
|
1750
|
+
if ((0, import_fs15.readdirSync)(seasonPath).some(matchesTitle)) return folder;
|
|
1712
1751
|
} catch {
|
|
1713
1752
|
}
|
|
1714
1753
|
}
|
|
@@ -1717,15 +1756,19 @@ var findShowFolderByContent = (destRoot, title) => {
|
|
|
1717
1756
|
}
|
|
1718
1757
|
return null;
|
|
1719
1758
|
};
|
|
1720
|
-
var findSeasonFolder = (showPath, season) => {
|
|
1721
|
-
if (!(0,
|
|
1722
|
-
const folders = (0,
|
|
1759
|
+
var findSeasonFolder = (showPath, season, specialsFolder) => {
|
|
1760
|
+
if (!(0, import_fs15.existsSync)(showPath)) return null;
|
|
1761
|
+
const folders = (0, import_fs15.readdirSync)(showPath).filter((f) => {
|
|
1723
1762
|
try {
|
|
1724
|
-
return (0,
|
|
1763
|
+
return (0, import_fs15.lstatSync)((0, import_path15.resolve)(showPath, f)).isDirectory();
|
|
1725
1764
|
} catch {
|
|
1726
1765
|
return false;
|
|
1727
1766
|
}
|
|
1728
1767
|
});
|
|
1768
|
+
if (season === 0 && specialsFolder) {
|
|
1769
|
+
const existing = folders.find((f) => f.toLowerCase() === specialsFolder.toLowerCase());
|
|
1770
|
+
if (existing) return existing;
|
|
1771
|
+
}
|
|
1729
1772
|
return folders.find((f) => {
|
|
1730
1773
|
const match = f.match(/(?:season|s)\s*0*(\d+)/i);
|
|
1731
1774
|
return match && parseInt(match[1]) === season;
|
|
@@ -1754,11 +1797,13 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1754
1797
|
const language = config.language ?? "eng";
|
|
1755
1798
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
1756
1799
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
1800
|
+
const specialsFolder = config.specialsFolder ?? "Specials";
|
|
1757
1801
|
const lookupMovie = async (parsed) => {
|
|
1758
1802
|
let tmdbId;
|
|
1759
1803
|
let resolvedTitle = parsed.title;
|
|
1760
1804
|
let resolvedYear = parsed.year;
|
|
1761
1805
|
if (config.tmdbApiKey) {
|
|
1806
|
+
spinner_default.text = `TMDb: ${parsed.title}`;
|
|
1762
1807
|
const results = await searchMovie(parsed.title, parsed.year, config.tmdbApiKey);
|
|
1763
1808
|
if (results.length === 1) {
|
|
1764
1809
|
tmdbId = results[0].id;
|
|
@@ -1786,8 +1831,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1786
1831
|
const importMovie = async (entry, entryPath, isDir, resolvedTitle, resolvedYear, tmdbId, destRoot) => {
|
|
1787
1832
|
const edition = detectEdition(entry);
|
|
1788
1833
|
const folderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear, edition);
|
|
1789
|
-
const destFolder = (0,
|
|
1790
|
-
if ((0,
|
|
1834
|
+
const destFolder = (0, import_path15.resolve)(destRoot, folderName);
|
|
1835
|
+
if ((0, import_fs15.existsSync)(destFolder)) {
|
|
1791
1836
|
spinner_default.warn(`already exists: ${folderName}`);
|
|
1792
1837
|
return false;
|
|
1793
1838
|
}
|
|
@@ -1798,43 +1843,43 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1798
1843
|
}
|
|
1799
1844
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
1800
1845
|
const destVideoName = `${folderName}.${videoExt}`;
|
|
1801
|
-
const videoSourcePath = isDir ? (0,
|
|
1802
|
-
const dirFiles = isDir ? (0,
|
|
1846
|
+
const videoSourcePath = isDir ? (0, import_path15.resolve)(entryPath, videoFile) : entryPath;
|
|
1847
|
+
const dirFiles = isDir ? (0, import_fs15.readdirSync)(entryPath) : [];
|
|
1803
1848
|
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
1804
1849
|
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
1805
|
-
const subtitleSourcePath = subtitle ? (0,
|
|
1850
|
+
const subtitleSourcePath = subtitle ? (0, import_path15.resolve)(entryPath, subtitle) : null;
|
|
1806
1851
|
const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
|
|
1807
1852
|
if (!dryRun) {
|
|
1808
1853
|
if (useHardlink) {
|
|
1809
|
-
(0,
|
|
1810
|
-
const destVideoPath = (0,
|
|
1854
|
+
(0, import_fs15.mkdirSync)(destFolder, { recursive: true });
|
|
1855
|
+
const destVideoPath = (0, import_path15.resolve)(destFolder, destVideoName);
|
|
1811
1856
|
let mode;
|
|
1812
1857
|
try {
|
|
1813
1858
|
if (!sameDev(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
|
|
1814
|
-
(0,
|
|
1859
|
+
(0, import_fs15.linkSync)(videoSourcePath, destVideoPath);
|
|
1815
1860
|
mode = "hardlink";
|
|
1816
1861
|
} catch {
|
|
1817
1862
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
1818
|
-
(0,
|
|
1863
|
+
(0, import_fs15.cpSync)(videoSourcePath, destVideoPath);
|
|
1819
1864
|
mode = "copy";
|
|
1820
1865
|
}
|
|
1821
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
1866
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs15.cpSync)(subtitleSourcePath, (0, import_path15.resolve)(destFolder, destSubtitleName));
|
|
1822
1867
|
recordImport(sessionId, entryPath, destFolder, mode, tmdbId, "movie");
|
|
1823
1868
|
} else {
|
|
1824
1869
|
if (isDir) {
|
|
1825
1870
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
1826
|
-
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (
|
|
1827
|
-
(0,
|
|
1828
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
1871
|
+
for (const f of dirFiles.filter((f2) => !keep.has(f2))) trashPath((0, import_path15.resolve)(entryPath, f), { recursive: true });
|
|
1872
|
+
(0, import_fs15.renameSync)(videoSourcePath, (0, import_path15.resolve)(entryPath, destVideoName));
|
|
1873
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs15.renameSync)(subtitleSourcePath, (0, import_path15.resolve)(entryPath, destSubtitleName));
|
|
1829
1874
|
moveFolder(entryPath, destFolder);
|
|
1830
1875
|
} else {
|
|
1831
|
-
(0,
|
|
1832
|
-
const destVideoPath = (0,
|
|
1876
|
+
(0, import_fs15.mkdirSync)(destFolder, { recursive: true });
|
|
1877
|
+
const destVideoPath = (0, import_path15.resolve)(destFolder, destVideoName);
|
|
1833
1878
|
if (sameDev(videoSourcePath, destRoot)) {
|
|
1834
|
-
(0,
|
|
1879
|
+
(0, import_fs15.renameSync)(videoSourcePath, destVideoPath);
|
|
1835
1880
|
} else {
|
|
1836
|
-
(0,
|
|
1837
|
-
(
|
|
1881
|
+
(0, import_fs15.cpSync)(videoSourcePath, destVideoPath);
|
|
1882
|
+
trashPath(videoSourcePath);
|
|
1838
1883
|
}
|
|
1839
1884
|
}
|
|
1840
1885
|
recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
|
|
@@ -1851,12 +1896,13 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1851
1896
|
const ignoreSet = new Set(config.ignore ?? []);
|
|
1852
1897
|
const seenIgnored = /* @__PURE__ */ new Set();
|
|
1853
1898
|
for (const source of config.sources) {
|
|
1854
|
-
if (!(0,
|
|
1899
|
+
if (!(0, import_fs15.existsSync)(source)) {
|
|
1855
1900
|
spinner_default.warn(`source not found: ${import_termkit14.Color.white.encoder(source)}`);
|
|
1856
1901
|
continue;
|
|
1857
1902
|
}
|
|
1858
1903
|
spinner_default.text = `scanning ${import_termkit14.Color.white.encoder(source)}`;
|
|
1859
1904
|
for (const { entry, entryPath, isDir } of gatherEntries(source)) {
|
|
1905
|
+
spinner_default.text = `scanning: ${entry}`;
|
|
1860
1906
|
if (ignoreSet.has(entry)) {
|
|
1861
1907
|
seenIgnored.add(entry);
|
|
1862
1908
|
if (isVerbose()) spinner_default.info(`ignored: ${entry}`);
|
|
@@ -1891,8 +1937,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1891
1937
|
continue;
|
|
1892
1938
|
}
|
|
1893
1939
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
1894
|
-
const destPath = (0,
|
|
1895
|
-
if ((0,
|
|
1940
|
+
const destPath = (0, import_path15.resolve)(destRoot, destName);
|
|
1941
|
+
if ((0, import_fs15.existsSync)(destPath)) {
|
|
1896
1942
|
spinner_default.warn(`already exists: ${destName}`);
|
|
1897
1943
|
skipped++;
|
|
1898
1944
|
continue;
|
|
@@ -1906,8 +1952,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1906
1952
|
continue;
|
|
1907
1953
|
}
|
|
1908
1954
|
if (detectedType === "book") {
|
|
1909
|
-
const destPath = (0,
|
|
1910
|
-
if ((0,
|
|
1955
|
+
const destPath = (0, import_path15.resolve)(destRoot, entry);
|
|
1956
|
+
if ((0, import_fs15.existsSync)(destPath)) {
|
|
1911
1957
|
spinner_default.warn(`already exists: ${entry}`);
|
|
1912
1958
|
skipped++;
|
|
1913
1959
|
continue;
|
|
@@ -1916,12 +1962,12 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1916
1962
|
if (isDir || isBookDir) {
|
|
1917
1963
|
moveFolder(entryPath, destPath);
|
|
1918
1964
|
} else {
|
|
1919
|
-
(0,
|
|
1965
|
+
(0, import_fs15.mkdirSync)(destRoot, { recursive: true });
|
|
1920
1966
|
if (sameDev(entryPath, destRoot)) {
|
|
1921
|
-
(0,
|
|
1967
|
+
(0, import_fs15.renameSync)(entryPath, destPath);
|
|
1922
1968
|
} else {
|
|
1923
|
-
(0,
|
|
1924
|
-
(0,
|
|
1969
|
+
(0, import_fs15.cpSync)(entryPath, destPath);
|
|
1970
|
+
(0, import_fs15.rmSync)(entryPath);
|
|
1925
1971
|
}
|
|
1926
1972
|
}
|
|
1927
1973
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
@@ -1953,6 +1999,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1953
1999
|
let resolvedYear = parsed.year;
|
|
1954
2000
|
if (config.tmdbApiKey) {
|
|
1955
2001
|
if (detectedType === "tv") {
|
|
2002
|
+
spinner_default.text = `TMDb: ${parsed.title}`;
|
|
1956
2003
|
const results = await searchTv(parsed.title, config.tmdbApiKey);
|
|
1957
2004
|
if (results.length === 1) {
|
|
1958
2005
|
tmdbId = results[0].id;
|
|
@@ -1997,18 +2044,18 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1997
2044
|
const existingFolder = findShowFolder(destRoot, resolvedTitle) ?? findShowFolderByContent(destRoot, resolvedTitle);
|
|
1998
2045
|
if (existingFolder) {
|
|
1999
2046
|
showFolderName = existingFolder;
|
|
2000
|
-
showPath = (0,
|
|
2047
|
+
showPath = (0, import_path15.resolve)(destRoot, existingFolder);
|
|
2001
2048
|
} else if (auto) {
|
|
2002
2049
|
showFolderName = formatMovieName(movieFormat, resolvedTitle, resolvedYear);
|
|
2003
|
-
showPath = (0,
|
|
2050
|
+
showPath = (0, import_path15.resolve)(destRoot, showFolderName);
|
|
2004
2051
|
if (!dryRun) upsertShow(showPath, tmdbId ?? null, resolvedTitle);
|
|
2005
2052
|
} else {
|
|
2006
2053
|
pendingTv.push({ entry, entryPath, isDir, parsed, resolvedTitle, destRoot });
|
|
2007
2054
|
continue;
|
|
2008
2055
|
}
|
|
2009
2056
|
}
|
|
2010
|
-
const seasonFolderName = findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
2011
|
-
const seasonPath = (0,
|
|
2057
|
+
const seasonFolderName = parsed.season === 0 ? findSeasonFolder(showPath, 0, specialsFolder) ?? specialsFolder : findSeasonFolder(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
2058
|
+
const seasonPath = (0, import_path15.resolve)(showPath, seasonFolderName);
|
|
2012
2059
|
const videoFile = isDir ? findVideo(entryPath) : entry;
|
|
2013
2060
|
if (!videoFile) {
|
|
2014
2061
|
if (isVerbose()) spinner_default.info(`no video found in: ${entry}`);
|
|
@@ -2016,13 +2063,15 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2016
2063
|
continue;
|
|
2017
2064
|
}
|
|
2018
2065
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
2066
|
+
if (tmdbId && config.tmdbApiKey) spinner_default.text = `TMDb: episode name for ${resolvedTitle}`;
|
|
2019
2067
|
const tmdbEpisodeName = tmdbId && config.tmdbApiKey ? await getEpisodeName(tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
2020
2068
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, resolvedTitle, tmdbEpisodeName ?? void 0);
|
|
2021
2069
|
const destVideoName = `${episodeName}.${videoExt}`;
|
|
2022
|
-
const destVideoPath = (0,
|
|
2023
|
-
const videoSourcePath = isDir ? (0,
|
|
2024
|
-
if ((0,
|
|
2025
|
-
|
|
2070
|
+
const destVideoPath = (0, import_path15.resolve)(seasonPath, destVideoName);
|
|
2071
|
+
const videoSourcePath = isDir ? (0, import_path15.resolve)(entryPath, videoFile) : entryPath;
|
|
2072
|
+
if ((0, import_fs15.existsSync)(destVideoPath)) {
|
|
2073
|
+
const isRepack = /\brepack\d*\b|\bproper\b/i.test(entry);
|
|
2074
|
+
let shouldReplace = force || isRepack;
|
|
2026
2075
|
if (!shouldReplace && interactive) {
|
|
2027
2076
|
spinner_default.stop();
|
|
2028
2077
|
const select = new import_termkit14.Select();
|
|
@@ -2039,39 +2088,39 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2039
2088
|
continue;
|
|
2040
2089
|
}
|
|
2041
2090
|
if (!dryRun) {
|
|
2042
|
-
for (const f of (0,
|
|
2043
|
-
if (f.startsWith(`${episodeName}.`)) (
|
|
2091
|
+
for (const f of (0, import_fs15.readdirSync)(seasonPath)) {
|
|
2092
|
+
if (f.startsWith(`${episodeName}.`)) trashPath((0, import_path15.resolve)(seasonPath, f));
|
|
2044
2093
|
}
|
|
2045
2094
|
}
|
|
2046
2095
|
}
|
|
2047
|
-
const dirFiles = isDir ? (0,
|
|
2096
|
+
const dirFiles = isDir ? (0, import_fs15.readdirSync)(entryPath) : [];
|
|
2048
2097
|
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
2049
2098
|
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
2050
|
-
const subtitleSourcePath = subtitle ? (0,
|
|
2099
|
+
const subtitleSourcePath = subtitle ? (0, import_path15.resolve)(entryPath, subtitle) : null;
|
|
2051
2100
|
const destSubtitleName = subtitle && subtitleExt ? `${episodeName}.${subtitleExt}` : null;
|
|
2052
2101
|
if (!dryRun) {
|
|
2053
|
-
(0,
|
|
2102
|
+
(0, import_fs15.mkdirSync)(seasonPath, { recursive: true });
|
|
2054
2103
|
let mode = "move";
|
|
2055
2104
|
if (useHardlink) {
|
|
2056
2105
|
try {
|
|
2057
2106
|
if (!sameDev(videoSourcePath, seasonPath)) throw new Error("cross-filesystem");
|
|
2058
|
-
(0,
|
|
2107
|
+
(0, import_fs15.linkSync)(videoSourcePath, destVideoPath);
|
|
2059
2108
|
mode = "hardlink";
|
|
2060
2109
|
} catch {
|
|
2061
2110
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
2062
|
-
(0,
|
|
2111
|
+
(0, import_fs15.cpSync)(videoSourcePath, destVideoPath);
|
|
2063
2112
|
mode = "copy";
|
|
2064
2113
|
}
|
|
2065
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
2114
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs15.cpSync)(subtitleSourcePath, (0, import_path15.resolve)(seasonPath, destSubtitleName));
|
|
2066
2115
|
} else {
|
|
2067
2116
|
if (sameDev(videoSourcePath, seasonPath)) {
|
|
2068
|
-
(0,
|
|
2117
|
+
(0, import_fs15.renameSync)(videoSourcePath, destVideoPath);
|
|
2069
2118
|
} else {
|
|
2070
|
-
(0,
|
|
2071
|
-
(
|
|
2119
|
+
(0, import_fs15.cpSync)(videoSourcePath, destVideoPath);
|
|
2120
|
+
trashPath(videoSourcePath);
|
|
2072
2121
|
}
|
|
2073
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
2074
|
-
if (isDir) (
|
|
2122
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs15.renameSync)(subtitleSourcePath, (0, import_path15.resolve)(seasonPath, destSubtitleName));
|
|
2123
|
+
if (isDir) trashPath(entryPath, { recursive: true });
|
|
2075
2124
|
}
|
|
2076
2125
|
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
|
|
2077
2126
|
}
|
|
@@ -2136,7 +2185,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2136
2185
|
var scan_default = scan;
|
|
2137
2186
|
|
|
2138
2187
|
// src/actions/shows.ts
|
|
2139
|
-
var
|
|
2188
|
+
var import_fs16 = require("fs");
|
|
2140
2189
|
var import_termkit15 = require("termkit");
|
|
2141
2190
|
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
2142
2191
|
var shows = async () => {
|
|
@@ -2153,7 +2202,7 @@ ${import_termkit15.Color.yellow.encoder("SHOWS")}${destRoot ? ` ${import_termki
|
|
|
2153
2202
|
new import_termkit15.Table(
|
|
2154
2203
|
allShows.map((show) => ({
|
|
2155
2204
|
name: show.path.split("/").pop() ?? show.path,
|
|
2156
|
-
size: (0,
|
|
2205
|
+
size: (0, import_fs16.existsSync)(show.path) ? formatSize(dirSize(show.path)) : "\u2014",
|
|
2157
2206
|
tmdbId: show.tmdbId,
|
|
2158
2207
|
ended: show.ended
|
|
2159
2208
|
})),
|
|
@@ -2186,15 +2235,15 @@ ${import_termkit15.Color.yellow.encoder("SHOWS")}${destRoot ? ` ${import_termki
|
|
|
2186
2235
|
var shows_default = shows;
|
|
2187
2236
|
|
|
2188
2237
|
// src/actions/stats.ts
|
|
2189
|
-
var
|
|
2190
|
-
var
|
|
2238
|
+
var import_fs17 = require("fs");
|
|
2239
|
+
var import_path16 = require("path");
|
|
2191
2240
|
var import_termkit16 = require("termkit");
|
|
2192
2241
|
var countVideos = (dir) => {
|
|
2193
2242
|
let count = 0;
|
|
2194
2243
|
try {
|
|
2195
|
-
for (const entry of (0,
|
|
2244
|
+
for (const entry of (0, import_fs17.readdirSync)(dir, { withFileTypes: true })) {
|
|
2196
2245
|
if (entry.isDirectory()) {
|
|
2197
|
-
count += countVideos((0,
|
|
2246
|
+
count += countVideos((0, import_path16.resolve)(dir, entry.name));
|
|
2198
2247
|
} else {
|
|
2199
2248
|
const ext = entry.name.match(/([^.]+$)/)?.[0]?.toLowerCase();
|
|
2200
2249
|
if (ext && videoExtensions_default.includes(ext)) count++;
|
|
@@ -2206,9 +2255,9 @@ var countVideos = (dir) => {
|
|
|
2206
2255
|
};
|
|
2207
2256
|
var countDirs = (dir) => {
|
|
2208
2257
|
try {
|
|
2209
|
-
return (0,
|
|
2258
|
+
return (0, import_fs17.readdirSync)(dir).filter((f) => {
|
|
2210
2259
|
try {
|
|
2211
|
-
return (0,
|
|
2260
|
+
return (0, import_fs17.lstatSync)((0, import_path16.resolve)(dir, f)).isDirectory();
|
|
2212
2261
|
} catch {
|
|
2213
2262
|
return false;
|
|
2214
2263
|
}
|
|
@@ -2222,16 +2271,16 @@ var stats = async () => {
|
|
|
2222
2271
|
const shows2 = getShows();
|
|
2223
2272
|
const rows = [];
|
|
2224
2273
|
const movieDest = config.dest.movie;
|
|
2225
|
-
if (movieDest && (0,
|
|
2274
|
+
if (movieDest && (0, import_fs17.existsSync)(movieDest)) {
|
|
2226
2275
|
rows.push({ category: "Movies", count: countDirs(movieDest), size: formatSize(dirSize(movieDest)) });
|
|
2227
2276
|
}
|
|
2228
2277
|
const tvDest = config.dest.tv;
|
|
2229
|
-
if (tvDest && (0,
|
|
2278
|
+
if (tvDest && (0, import_fs17.existsSync)(tvDest)) {
|
|
2230
2279
|
rows.push({ category: "Shows", count: shows2.length, size: formatSize(dirSize(tvDest)) });
|
|
2231
2280
|
rows.push({ category: "Episodes", count: countVideos(tvDest) });
|
|
2232
2281
|
}
|
|
2233
2282
|
const ps3Dest = config.dest.ps3;
|
|
2234
|
-
if (ps3Dest && (0,
|
|
2283
|
+
if (ps3Dest && (0, import_fs17.existsSync)(ps3Dest)) {
|
|
2235
2284
|
rows.push({ category: "PS3", count: countDirs(ps3Dest), size: formatSize(dirSize(ps3Dest)) });
|
|
2236
2285
|
}
|
|
2237
2286
|
if (rows.length === 0) return;
|
|
@@ -2250,7 +2299,7 @@ var stats = async () => {
|
|
|
2250
2299
|
var stats_default = stats;
|
|
2251
2300
|
|
|
2252
2301
|
// src/actions/undo.ts
|
|
2253
|
-
var
|
|
2302
|
+
var import_fs18 = require("fs");
|
|
2254
2303
|
var import_termkit17 = require("termkit");
|
|
2255
2304
|
var undo = async () => {
|
|
2256
2305
|
spinner_default.start();
|
|
@@ -2265,7 +2314,7 @@ var undo = async () => {
|
|
|
2265
2314
|
if (!useImports) {
|
|
2266
2315
|
let undone2 = 0;
|
|
2267
2316
|
for (const record of renameRecords) {
|
|
2268
|
-
(0,
|
|
2317
|
+
(0, import_fs18.renameSync)(record.newPath, record.oldPath);
|
|
2269
2318
|
spinner_default.succeed(`${import_termkit17.Color.green.encoder(record.newPath)} \u2192 ${import_termkit17.Color.white.encoder(record.oldPath)}`);
|
|
2270
2319
|
undone2++;
|
|
2271
2320
|
}
|
|
@@ -2287,12 +2336,12 @@ var undo = async () => {
|
|
|
2287
2336
|
skipped++;
|
|
2288
2337
|
continue;
|
|
2289
2338
|
}
|
|
2290
|
-
if (!(0,
|
|
2339
|
+
if (!(0, import_fs18.existsSync)(record.destinationPath)) {
|
|
2291
2340
|
spinner_default.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
|
|
2292
2341
|
skipped++;
|
|
2293
2342
|
continue;
|
|
2294
2343
|
}
|
|
2295
|
-
(0,
|
|
2344
|
+
(0, import_fs18.renameSync)(record.destinationPath, record.sourcePath);
|
|
2296
2345
|
spinner_default.succeed(`${import_termkit17.Color.green.encoder(record.destinationPath)} \u2192 ${import_termkit17.Color.white.encoder(record.sourcePath)}`);
|
|
2297
2346
|
undone++;
|
|
2298
2347
|
}
|
|
@@ -2305,37 +2354,37 @@ var undo_default = undo;
|
|
|
2305
2354
|
|
|
2306
2355
|
// src/actions/watch.ts
|
|
2307
2356
|
var import_chokidar = __toESM(require("chokidar"));
|
|
2308
|
-
var
|
|
2309
|
-
var
|
|
2357
|
+
var import_fs19 = require("fs");
|
|
2358
|
+
var import_path17 = require("path");
|
|
2310
2359
|
var import_termkit18 = require("termkit");
|
|
2311
2360
|
var sameDev2 = (a, b) => {
|
|
2312
2361
|
try {
|
|
2313
2362
|
let bExisting = b;
|
|
2314
|
-
while (!(0,
|
|
2315
|
-
return (0,
|
|
2363
|
+
while (!(0, import_fs19.existsSync)(bExisting)) bExisting = (0, import_path17.dirname)(bExisting);
|
|
2364
|
+
return (0, import_fs19.statSync)(a).dev === (0, import_fs19.statSync)(bExisting).dev;
|
|
2316
2365
|
} catch {
|
|
2317
2366
|
return false;
|
|
2318
2367
|
}
|
|
2319
2368
|
};
|
|
2320
2369
|
var moveItem = (src, dest) => {
|
|
2321
2370
|
if (sameDev2(src, dest)) {
|
|
2322
|
-
(0,
|
|
2371
|
+
(0, import_fs19.renameSync)(src, dest);
|
|
2323
2372
|
} else {
|
|
2324
|
-
(0,
|
|
2325
|
-
(0,
|
|
2373
|
+
(0, import_fs19.cpSync)(src, dest, { recursive: true });
|
|
2374
|
+
(0, import_fs19.rmSync)(src, { recursive: true, force: true });
|
|
2326
2375
|
}
|
|
2327
2376
|
};
|
|
2328
|
-
var findVideo2 = (dir) => (0,
|
|
2377
|
+
var findVideo2 = (dir) => (0, import_fs19.readdirSync)(dir).find((f) => {
|
|
2329
2378
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
2330
2379
|
return ext && videoExtensions_default.includes(ext);
|
|
2331
2380
|
}) ?? null;
|
|
2332
|
-
var containsBook2 = (dir, depth = 2) => (0,
|
|
2381
|
+
var containsBook2 = (dir, depth = 2) => (0, import_fs19.readdirSync)(dir).some((f) => {
|
|
2333
2382
|
const ext = f.match(/([^.]+$)/)?.[0];
|
|
2334
2383
|
if (ext && bookExtensions_default.includes(ext)) return true;
|
|
2335
2384
|
if (depth > 1) {
|
|
2336
2385
|
try {
|
|
2337
|
-
const sub = (0,
|
|
2338
|
-
if ((0,
|
|
2386
|
+
const sub = (0, import_path17.resolve)(dir, f);
|
|
2387
|
+
if ((0, import_fs19.lstatSync)(sub).isDirectory()) return containsBook2(sub, depth - 1);
|
|
2339
2388
|
} catch {
|
|
2340
2389
|
}
|
|
2341
2390
|
}
|
|
@@ -2346,26 +2395,26 @@ var isSeasonDirName2 = (name) => !isTvEpisodeName2(name) && /(?:^|[.\s_-])(?:sea
|
|
|
2346
2395
|
var expandWatchPath = (p) => {
|
|
2347
2396
|
let isDir;
|
|
2348
2397
|
try {
|
|
2349
|
-
isDir = (0,
|
|
2398
|
+
isDir = (0, import_fs19.lstatSync)(p).isDirectory();
|
|
2350
2399
|
} catch {
|
|
2351
2400
|
return [p];
|
|
2352
2401
|
}
|
|
2353
2402
|
if (!isDir) return [p];
|
|
2354
|
-
const name = (0,
|
|
2403
|
+
const name = (0, import_path17.basename)(p);
|
|
2355
2404
|
if (isTvEpisodeName2(name) || /(?<=\[).+?(?=\])/.test(name) || /^[A-Z]{4}\d{5}/i.test(name)) return [p];
|
|
2356
2405
|
let children;
|
|
2357
2406
|
try {
|
|
2358
|
-
children = (0,
|
|
2407
|
+
children = (0, import_fs19.readdirSync)(p);
|
|
2359
2408
|
} catch {
|
|
2360
2409
|
return [p];
|
|
2361
2410
|
}
|
|
2362
2411
|
if (children.some((c) => isTvEpisodeName2(c))) {
|
|
2363
2412
|
const entries = [];
|
|
2364
2413
|
for (const child of children) {
|
|
2365
|
-
const cp = (0,
|
|
2414
|
+
const cp = (0, import_path17.resolve)(p, child);
|
|
2366
2415
|
let cd;
|
|
2367
2416
|
try {
|
|
2368
|
-
cd = (0,
|
|
2417
|
+
cd = (0, import_fs19.lstatSync)(cp).isDirectory();
|
|
2369
2418
|
} catch {
|
|
2370
2419
|
continue;
|
|
2371
2420
|
}
|
|
@@ -2377,7 +2426,7 @@ var expandWatchPath = (p) => {
|
|
|
2377
2426
|
}
|
|
2378
2427
|
const seasonDirs = children.filter((c) => {
|
|
2379
2428
|
try {
|
|
2380
|
-
return isSeasonDirName2(c) && (0,
|
|
2429
|
+
return isSeasonDirName2(c) && (0, import_fs19.lstatSync)((0, import_path17.resolve)(p, c)).isDirectory();
|
|
2381
2430
|
} catch {
|
|
2382
2431
|
return false;
|
|
2383
2432
|
}
|
|
@@ -2385,18 +2434,18 @@ var expandWatchPath = (p) => {
|
|
|
2385
2434
|
if (seasonDirs.length > 0) {
|
|
2386
2435
|
const entries = [];
|
|
2387
2436
|
for (const sd of seasonDirs) {
|
|
2388
|
-
const sp = (0,
|
|
2437
|
+
const sp = (0, import_path17.resolve)(p, sd);
|
|
2389
2438
|
let sc;
|
|
2390
2439
|
try {
|
|
2391
|
-
sc = (0,
|
|
2440
|
+
sc = (0, import_fs19.readdirSync)(sp);
|
|
2392
2441
|
} catch {
|
|
2393
2442
|
continue;
|
|
2394
2443
|
}
|
|
2395
2444
|
for (const child of sc) {
|
|
2396
|
-
const cp = (0,
|
|
2445
|
+
const cp = (0, import_path17.resolve)(sp, child);
|
|
2397
2446
|
let cd;
|
|
2398
2447
|
try {
|
|
2399
|
-
cd = (0,
|
|
2448
|
+
cd = (0, import_fs19.lstatSync)(cp).isDirectory();
|
|
2400
2449
|
} catch {
|
|
2401
2450
|
continue;
|
|
2402
2451
|
}
|
|
@@ -2410,10 +2459,10 @@ var expandWatchPath = (p) => {
|
|
|
2410
2459
|
return [p];
|
|
2411
2460
|
};
|
|
2412
2461
|
var findSeasonFolder2 = (showPath, season) => {
|
|
2413
|
-
if (!(0,
|
|
2414
|
-
const folders = (0,
|
|
2462
|
+
if (!(0, import_fs19.existsSync)(showPath)) return null;
|
|
2463
|
+
const folders = (0, import_fs19.readdirSync)(showPath).filter((f) => {
|
|
2415
2464
|
try {
|
|
2416
|
-
return (0,
|
|
2465
|
+
return (0, import_fs19.lstatSync)((0, import_path17.resolve)(showPath, f)).isDirectory();
|
|
2417
2466
|
} catch {
|
|
2418
2467
|
return false;
|
|
2419
2468
|
}
|
|
@@ -2426,10 +2475,10 @@ var findSeasonFolder2 = (showPath, season) => {
|
|
|
2426
2475
|
var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
2427
2476
|
const config = getConfig();
|
|
2428
2477
|
const sessionId = (/* @__PURE__ */ new Date()).toISOString();
|
|
2429
|
-
const entry = (0,
|
|
2478
|
+
const entry = (0, import_path17.basename)(entryPath);
|
|
2430
2479
|
const movieFormat = config.format?.movie ?? DEFAULT_MOVIE_FORMAT;
|
|
2431
2480
|
const seasonFormat = config.format?.season ?? DEFAULT_SEASON_FORMAT;
|
|
2432
|
-
const isDir = (0,
|
|
2481
|
+
const isDir = (0, import_fs19.lstatSync)(entryPath).isDirectory();
|
|
2433
2482
|
const ext = entry.match(/([^.]+$)/)?.[0];
|
|
2434
2483
|
const isVideo = !isDir && ext && videoExtensions_default.includes(ext);
|
|
2435
2484
|
const isBook = !isDir && ext && bookExtensions_default.includes(ext);
|
|
@@ -2455,8 +2504,8 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2455
2504
|
const id = entry.split("-")[0];
|
|
2456
2505
|
if (!nameMatch || !id) return;
|
|
2457
2506
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
2458
|
-
const destPath = (0,
|
|
2459
|
-
if ((0,
|
|
2507
|
+
const destPath = (0, import_path17.resolve)(destRoot, destName);
|
|
2508
|
+
if ((0, import_fs19.existsSync)(destPath)) {
|
|
2460
2509
|
spinner_default.warn(`already exists: ${destName}`);
|
|
2461
2510
|
return;
|
|
2462
2511
|
}
|
|
@@ -2466,20 +2515,20 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2466
2515
|
return;
|
|
2467
2516
|
}
|
|
2468
2517
|
if (detectedType === "book") {
|
|
2469
|
-
const destPath = (0,
|
|
2470
|
-
if ((0,
|
|
2518
|
+
const destPath = (0, import_path17.resolve)(destRoot, entry);
|
|
2519
|
+
if ((0, import_fs19.existsSync)(destPath)) {
|
|
2471
2520
|
spinner_default.warn(`already exists: ${entry}`);
|
|
2472
2521
|
return;
|
|
2473
2522
|
}
|
|
2474
2523
|
if (isDir || isBookDir) {
|
|
2475
2524
|
moveItem(entryPath, destPath);
|
|
2476
2525
|
} else {
|
|
2477
|
-
(0,
|
|
2526
|
+
(0, import_fs19.mkdirSync)(destRoot, { recursive: true });
|
|
2478
2527
|
if (sameDev2(entryPath, destRoot)) {
|
|
2479
|
-
(0,
|
|
2528
|
+
(0, import_fs19.renameSync)(entryPath, destPath);
|
|
2480
2529
|
} else {
|
|
2481
|
-
(0,
|
|
2482
|
-
(0,
|
|
2530
|
+
(0, import_fs19.cpSync)(entryPath, destPath);
|
|
2531
|
+
(0, import_fs19.rmSync)(entryPath);
|
|
2483
2532
|
}
|
|
2484
2533
|
}
|
|
2485
2534
|
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
@@ -2504,14 +2553,14 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2504
2553
|
showFolderName = showPath.split("/").pop() ?? registeredShow.path;
|
|
2505
2554
|
} else if (auto) {
|
|
2506
2555
|
showFolderName = formatMovieName(movieFormat, parsed.title, parsed.year);
|
|
2507
|
-
showPath = (0,
|
|
2556
|
+
showPath = (0, import_path17.resolve)(destRoot, showFolderName);
|
|
2508
2557
|
upsertShow(showPath, null, parsed.title);
|
|
2509
2558
|
} else {
|
|
2510
2559
|
if (isVerbose()) spinner_default.info(`not registered, skipped: ${parsed.title} \u2014 run: reelsort add "${parsed.title}"`);
|
|
2511
2560
|
return;
|
|
2512
2561
|
}
|
|
2513
2562
|
const seasonFolderName = findSeasonFolder2(showPath, parsed.season) ?? formatSeasonFolder(seasonFormat, parsed.season);
|
|
2514
|
-
const seasonPath = (0,
|
|
2563
|
+
const seasonPath = (0, import_path17.resolve)(showPath, seasonFolderName);
|
|
2515
2564
|
const videoFile2 = isDir ? findVideo2(entryPath) : entry;
|
|
2516
2565
|
if (!videoFile2) {
|
|
2517
2566
|
if (isVerbose()) spinner_default.info(`no video found in: ${entry}`);
|
|
@@ -2521,39 +2570,39 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2521
2570
|
const tmdbEpisodeName = registeredShow?.tmdbId && config.tmdbApiKey ? await getEpisodeName(registeredShow.tmdbId, parsed.season, parsed.episode ?? 1, config.tmdbApiKey) : null;
|
|
2522
2571
|
const episodeName = formatEpisode(parsed.season, parsed.episode ?? 1, config.format?.episode, false, parsed.title, tmdbEpisodeName ?? void 0);
|
|
2523
2572
|
const destVideoName2 = `${episodeName}.${videoExt2}`;
|
|
2524
|
-
const destVideoPath = (0,
|
|
2525
|
-
const videoSourcePath2 = isDir ? (0,
|
|
2526
|
-
if ((0,
|
|
2573
|
+
const destVideoPath = (0, import_path17.resolve)(seasonPath, destVideoName2);
|
|
2574
|
+
const videoSourcePath2 = isDir ? (0, import_path17.resolve)(entryPath, videoFile2) : entryPath;
|
|
2575
|
+
if ((0, import_fs19.existsSync)(destVideoPath)) {
|
|
2527
2576
|
spinner_default.warn(`already exists: ${episodeName}`);
|
|
2528
2577
|
return;
|
|
2529
2578
|
}
|
|
2530
|
-
const dirFiles2 = isDir ? (0,
|
|
2579
|
+
const dirFiles2 = isDir ? (0, import_fs19.readdirSync)(entryPath) : [];
|
|
2531
2580
|
const subtitle2 = isDir ? findSubtitle(dirFiles2, language) : null;
|
|
2532
2581
|
const subtitleExt2 = subtitle2?.match(/([^.]+$)/)?.[0];
|
|
2533
|
-
const subtitleSourcePath2 = subtitle2 ? (0,
|
|
2582
|
+
const subtitleSourcePath2 = subtitle2 ? (0, import_path17.resolve)(entryPath, subtitle2) : null;
|
|
2534
2583
|
const destSubtitleName2 = subtitle2 && subtitleExt2 ? `${episodeName}.${subtitleExt2}` : null;
|
|
2535
|
-
(0,
|
|
2584
|
+
(0, import_fs19.mkdirSync)(seasonPath, { recursive: true });
|
|
2536
2585
|
let mode = "move";
|
|
2537
2586
|
if (useHardlink) {
|
|
2538
2587
|
try {
|
|
2539
2588
|
if (!sameDev2(videoSourcePath2, seasonPath)) throw new Error("cross-filesystem");
|
|
2540
|
-
(0,
|
|
2589
|
+
(0, import_fs19.linkSync)(videoSourcePath2, destVideoPath);
|
|
2541
2590
|
mode = "hardlink";
|
|
2542
2591
|
} catch {
|
|
2543
2592
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${episodeName}`);
|
|
2544
|
-
(0,
|
|
2593
|
+
(0, import_fs19.cpSync)(videoSourcePath2, destVideoPath);
|
|
2545
2594
|
mode = "copy";
|
|
2546
2595
|
}
|
|
2547
|
-
if (subtitleSourcePath2 && destSubtitleName2) (0,
|
|
2596
|
+
if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs19.cpSync)(subtitleSourcePath2, (0, import_path17.resolve)(seasonPath, destSubtitleName2));
|
|
2548
2597
|
} else {
|
|
2549
2598
|
if (sameDev2(videoSourcePath2, seasonPath)) {
|
|
2550
|
-
(0,
|
|
2599
|
+
(0, import_fs19.renameSync)(videoSourcePath2, destVideoPath);
|
|
2551
2600
|
} else {
|
|
2552
|
-
(0,
|
|
2553
|
-
(0,
|
|
2601
|
+
(0, import_fs19.cpSync)(videoSourcePath2, destVideoPath);
|
|
2602
|
+
(0, import_fs19.rmSync)(videoSourcePath2);
|
|
2554
2603
|
}
|
|
2555
|
-
if (subtitleSourcePath2 && destSubtitleName2) (0,
|
|
2556
|
-
if (isDir) (0,
|
|
2604
|
+
if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs19.renameSync)(subtitleSourcePath2, (0, import_path17.resolve)(seasonPath, destSubtitleName2));
|
|
2605
|
+
if (isDir) (0, import_fs19.rmSync)(entryPath, { recursive: true, force: true });
|
|
2557
2606
|
}
|
|
2558
2607
|
recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
|
|
2559
2608
|
spinner_default.succeed(`imported ${import_termkit18.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
@@ -2561,8 +2610,8 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2561
2610
|
}
|
|
2562
2611
|
const edition = detectEdition(entry);
|
|
2563
2612
|
const folderName = formatMovieName(movieFormat, parsed.title, parsed.year, edition);
|
|
2564
|
-
const destFolder = (0,
|
|
2565
|
-
if ((0,
|
|
2613
|
+
const destFolder = (0, import_path17.resolve)(destRoot, folderName);
|
|
2614
|
+
if ((0, import_fs19.existsSync)(destFolder)) {
|
|
2566
2615
|
spinner_default.warn(`already exists: ${folderName}`);
|
|
2567
2616
|
return;
|
|
2568
2617
|
}
|
|
@@ -2573,42 +2622,42 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2573
2622
|
}
|
|
2574
2623
|
const videoExt = videoFile.match(/([^.]+$)/)?.[0];
|
|
2575
2624
|
const destVideoName = `${folderName}.${videoExt}`;
|
|
2576
|
-
const videoSourcePath = isDir ? (0,
|
|
2577
|
-
const dirFiles = isDir ? (0,
|
|
2625
|
+
const videoSourcePath = isDir ? (0, import_path17.resolve)(entryPath, videoFile) : entryPath;
|
|
2626
|
+
const dirFiles = isDir ? (0, import_fs19.readdirSync)(entryPath) : [];
|
|
2578
2627
|
const subtitle = isDir ? findSubtitle(dirFiles, language) : null;
|
|
2579
2628
|
const subtitleExt = subtitle?.match(/([^.]+$)/)?.[0];
|
|
2580
|
-
const subtitleSourcePath = subtitle ? (0,
|
|
2629
|
+
const subtitleSourcePath = subtitle ? (0, import_path17.resolve)(entryPath, subtitle) : null;
|
|
2581
2630
|
const destSubtitleName = subtitle && subtitleExt ? `${folderName}.${subtitleExt}` : null;
|
|
2582
2631
|
if (useHardlink) {
|
|
2583
|
-
(0,
|
|
2584
|
-
const destVideoPath = (0,
|
|
2632
|
+
(0, import_fs19.mkdirSync)(destFolder, { recursive: true });
|
|
2633
|
+
const destVideoPath = (0, import_path17.resolve)(destFolder, destVideoName);
|
|
2585
2634
|
let mode;
|
|
2586
2635
|
try {
|
|
2587
2636
|
if (!sameDev2(videoSourcePath, destRoot)) throw new Error("cross-filesystem");
|
|
2588
|
-
(0,
|
|
2637
|
+
(0, import_fs19.linkSync)(videoSourcePath, destVideoPath);
|
|
2589
2638
|
mode = "hardlink";
|
|
2590
2639
|
} catch {
|
|
2591
2640
|
spinner_default.warn(`hardlink unavailable \u2014 copying instead: ${folderName}`);
|
|
2592
|
-
(0,
|
|
2641
|
+
(0, import_fs19.cpSync)(videoSourcePath, destVideoPath);
|
|
2593
2642
|
mode = "copy";
|
|
2594
2643
|
}
|
|
2595
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
2644
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs19.cpSync)(subtitleSourcePath, (0, import_path17.resolve)(destFolder, destSubtitleName));
|
|
2596
2645
|
recordImport(sessionId, entryPath, destFolder, mode, void 0, "movie");
|
|
2597
2646
|
} else {
|
|
2598
2647
|
if (isDir) {
|
|
2599
2648
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
2600
|
-
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0,
|
|
2601
|
-
(0,
|
|
2602
|
-
if (subtitleSourcePath && destSubtitleName) (0,
|
|
2649
|
+
for (const f of dirFiles.filter((f2) => !keep.has(f2))) (0, import_fs19.rmSync)((0, import_path17.resolve)(entryPath, f), { recursive: true, force: true });
|
|
2650
|
+
(0, import_fs19.renameSync)(videoSourcePath, (0, import_path17.resolve)(entryPath, destVideoName));
|
|
2651
|
+
if (subtitleSourcePath && destSubtitleName) (0, import_fs19.renameSync)(subtitleSourcePath, (0, import_path17.resolve)(entryPath, destSubtitleName));
|
|
2603
2652
|
moveItem(entryPath, destFolder);
|
|
2604
2653
|
} else {
|
|
2605
|
-
(0,
|
|
2606
|
-
const destVideoPath = (0,
|
|
2654
|
+
(0, import_fs19.mkdirSync)(destFolder, { recursive: true });
|
|
2655
|
+
const destVideoPath = (0, import_path17.resolve)(destFolder, destVideoName);
|
|
2607
2656
|
if (sameDev2(videoSourcePath, destRoot)) {
|
|
2608
|
-
(0,
|
|
2657
|
+
(0, import_fs19.renameSync)(videoSourcePath, destVideoPath);
|
|
2609
2658
|
} else {
|
|
2610
|
-
(0,
|
|
2611
|
-
(0,
|
|
2659
|
+
(0, import_fs19.cpSync)(videoSourcePath, destVideoPath);
|
|
2660
|
+
(0, import_fs19.rmSync)(videoSourcePath);
|
|
2612
2661
|
}
|
|
2613
2662
|
}
|
|
2614
2663
|
recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
|
|
@@ -2653,7 +2702,7 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2653
2702
|
var watch_default = watch;
|
|
2654
2703
|
|
|
2655
2704
|
// package.json
|
|
2656
|
-
var version = "0.2.
|
|
2705
|
+
var version = "0.2.6";
|
|
2657
2706
|
|
|
2658
2707
|
// src/program.ts
|
|
2659
2708
|
var toCamel = (s) => s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|