reelsort 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +49 -4
- package/dist/index.js +45 -0
- package/dist/index.mjs +45 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1634,6 +1634,24 @@ var containsBook = (dir, depth = 2) => (0, import_fs15.readdirSync)(dir).some((f
|
|
|
1634
1634
|
}
|
|
1635
1635
|
return false;
|
|
1636
1636
|
});
|
|
1637
|
+
var containsPdf = (dir) => {
|
|
1638
|
+
try {
|
|
1639
|
+
return (0, import_fs15.readdirSync)(dir).some((f) => /\.pdf$/i.test(f));
|
|
1640
|
+
} catch {
|
|
1641
|
+
return false;
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
var countVideos = (dir) => {
|
|
1645
|
+
try {
|
|
1646
|
+
return (0, import_fs15.readdirSync)(dir).filter((f) => {
|
|
1647
|
+
if (/\bsample\b/i.test(f)) return false;
|
|
1648
|
+
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1649
|
+
return !!(ext && videoExtensions_default.includes(ext));
|
|
1650
|
+
}).length;
|
|
1651
|
+
} catch {
|
|
1652
|
+
return 0;
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1637
1655
|
var isTvEpisodeName = (name) => /S\d{2,3}E\d{2,3}/i.test(name) || /\d+x\d{2,3}/i.test(name);
|
|
1638
1656
|
var isSeasonDirName = (name) => !isTvEpisodeName(name) && /(?:^|[.\s_-])(?:season|s)\s*0*\d+(?:[.\s_-]|$)/i.test(name);
|
|
1639
1657
|
var gatherEntries = (source) => {
|
|
@@ -1893,6 +1911,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1893
1911
|
let imported = 0, skipped = 0;
|
|
1894
1912
|
const pendingMovies = [];
|
|
1895
1913
|
const pendingTv = [];
|
|
1914
|
+
const pendingBooks = [];
|
|
1915
|
+
const pendingAnime = [];
|
|
1896
1916
|
const ignoreSet = new Set(config.ignore ?? []);
|
|
1897
1917
|
const seenIgnored = /* @__PURE__ */ new Set();
|
|
1898
1918
|
for (const source of config.sources) {
|
|
@@ -1976,6 +1996,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1976
1996
|
imported++;
|
|
1977
1997
|
continue;
|
|
1978
1998
|
}
|
|
1999
|
+
if (detectedType === "movie" && isDir) {
|
|
2000
|
+
const videoCount = countVideos(entryPath);
|
|
2001
|
+
if (videoCount === 0) {
|
|
2002
|
+
if (containsPdf(entryPath)) {
|
|
2003
|
+
pendingBooks.push({ entry, entryPath });
|
|
2004
|
+
} else if (isVerbose()) {
|
|
2005
|
+
spinner_default.info(`no media found, skipped: ${entry}`);
|
|
2006
|
+
}
|
|
2007
|
+
skipped++;
|
|
2008
|
+
continue;
|
|
2009
|
+
}
|
|
2010
|
+
if (videoCount >= 2) {
|
|
2011
|
+
pendingAnime.push({ entry, entryPath, videoCount });
|
|
2012
|
+
continue;
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
1979
2015
|
const parsed = parseDownloadName(entry);
|
|
1980
2016
|
if (!parsed) {
|
|
1981
2017
|
if (isVerbose()) spinner_default.info(`could not parse: ${entry}`);
|
|
@@ -2169,6 +2205,15 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2169
2205
|
for (const p of pendingTv) spinner_default.info(` ${typeGlyph("tv")} ${p.resolvedTitle} \u2014 ${p.entry.replace(/\/$/, "")}${typeTag("tv")}`);
|
|
2170
2206
|
skipped += pendingTv.length;
|
|
2171
2207
|
}
|
|
2208
|
+
if (pendingBooks.length > 0) {
|
|
2209
|
+
spinner_default.warn(`${pendingBooks.length} uncertain book${pendingBooks.length > 1 ? "s" : ""} skipped \u2014 contains PDFs, review manually`);
|
|
2210
|
+
for (const p of pendingBooks) spinner_default.info(` ${typeGlyph("book")} ${p.entry}${typeTag("book")}`);
|
|
2211
|
+
}
|
|
2212
|
+
if (pendingAnime.length > 0) {
|
|
2213
|
+
spinner_default.warn(`${pendingAnime.length} uncertain anime/TV director${pendingAnime.length > 1 ? "ies" : "y"} skipped \u2014 multiple videos with no episode naming`);
|
|
2214
|
+
for (const p of pendingAnime) spinner_default.info(` ${typeGlyph("tv")} ${p.entry} (${p.videoCount} video${p.videoCount > 1 ? "s" : ""})${typeTag("tv")}`);
|
|
2215
|
+
skipped += pendingAnime.length;
|
|
2216
|
+
}
|
|
2172
2217
|
if (ignoreSet.size > 0) {
|
|
2173
2218
|
const stale = [...ignoreSet].filter((name) => !seenIgnored.has(name));
|
|
2174
2219
|
if (stale.length > 0 && !dryRun) {
|
|
@@ -2238,12 +2283,12 @@ var shows_default = shows;
|
|
|
2238
2283
|
var import_fs17 = require("fs");
|
|
2239
2284
|
var import_path16 = require("path");
|
|
2240
2285
|
var import_termkit16 = require("termkit");
|
|
2241
|
-
var
|
|
2286
|
+
var countVideos2 = (dir) => {
|
|
2242
2287
|
let count = 0;
|
|
2243
2288
|
try {
|
|
2244
2289
|
for (const entry of (0, import_fs17.readdirSync)(dir, { withFileTypes: true })) {
|
|
2245
2290
|
if (entry.isDirectory()) {
|
|
2246
|
-
count +=
|
|
2291
|
+
count += countVideos2((0, import_path16.resolve)(dir, entry.name));
|
|
2247
2292
|
} else {
|
|
2248
2293
|
const ext = entry.name.match(/([^.]+$)/)?.[0]?.toLowerCase();
|
|
2249
2294
|
if (ext && videoExtensions_default.includes(ext)) count++;
|
|
@@ -2277,7 +2322,7 @@ var stats = async () => {
|
|
|
2277
2322
|
const tvDest = config.dest.tv;
|
|
2278
2323
|
if (tvDest && (0, import_fs17.existsSync)(tvDest)) {
|
|
2279
2324
|
rows.push({ category: "Shows", count: shows2.length, size: formatSize(dirSize(tvDest)) });
|
|
2280
|
-
rows.push({ category: "Episodes", count:
|
|
2325
|
+
rows.push({ category: "Episodes", count: countVideos2(tvDest) });
|
|
2281
2326
|
}
|
|
2282
2327
|
const ps3Dest = config.dest.ps3;
|
|
2283
2328
|
if (ps3Dest && (0, import_fs17.existsSync)(ps3Dest)) {
|
|
@@ -2702,7 +2747,7 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2702
2747
|
var watch_default = watch;
|
|
2703
2748
|
|
|
2704
2749
|
// package.json
|
|
2705
|
-
var version = "0.2.
|
|
2750
|
+
var version = "0.2.7";
|
|
2706
2751
|
|
|
2707
2752
|
// src/program.ts
|
|
2708
2753
|
var toCamel = (s) => s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
package/dist/index.js
CHANGED
|
@@ -1346,6 +1346,24 @@ var containsBook = (dir, depth = 2) => (0, import_fs11.readdirSync)(dir).some((f
|
|
|
1346
1346
|
}
|
|
1347
1347
|
return false;
|
|
1348
1348
|
});
|
|
1349
|
+
var containsPdf = (dir) => {
|
|
1350
|
+
try {
|
|
1351
|
+
return (0, import_fs11.readdirSync)(dir).some((f) => /\.pdf$/i.test(f));
|
|
1352
|
+
} catch {
|
|
1353
|
+
return false;
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1356
|
+
var countVideos = (dir) => {
|
|
1357
|
+
try {
|
|
1358
|
+
return (0, import_fs11.readdirSync)(dir).filter((f) => {
|
|
1359
|
+
if (/\bsample\b/i.test(f)) return false;
|
|
1360
|
+
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1361
|
+
return !!(ext && videoExtensions_default.includes(ext));
|
|
1362
|
+
}).length;
|
|
1363
|
+
} catch {
|
|
1364
|
+
return 0;
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1349
1367
|
var isTvEpisodeName = (name) => /S\d{2,3}E\d{2,3}/i.test(name) || /\d+x\d{2,3}/i.test(name);
|
|
1350
1368
|
var isSeasonDirName = (name) => !isTvEpisodeName(name) && /(?:^|[.\s_-])(?:season|s)\s*0*\d+(?:[.\s_-]|$)/i.test(name);
|
|
1351
1369
|
var gatherEntries = (source) => {
|
|
@@ -1605,6 +1623,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1605
1623
|
let imported = 0, skipped = 0;
|
|
1606
1624
|
const pendingMovies = [];
|
|
1607
1625
|
const pendingTv = [];
|
|
1626
|
+
const pendingBooks = [];
|
|
1627
|
+
const pendingAnime = [];
|
|
1608
1628
|
const ignoreSet = new Set(config.ignore ?? []);
|
|
1609
1629
|
const seenIgnored = /* @__PURE__ */ new Set();
|
|
1610
1630
|
for (const source of config.sources) {
|
|
@@ -1688,6 +1708,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1688
1708
|
imported++;
|
|
1689
1709
|
continue;
|
|
1690
1710
|
}
|
|
1711
|
+
if (detectedType === "movie" && isDir) {
|
|
1712
|
+
const videoCount = countVideos(entryPath);
|
|
1713
|
+
if (videoCount === 0) {
|
|
1714
|
+
if (containsPdf(entryPath)) {
|
|
1715
|
+
pendingBooks.push({ entry, entryPath });
|
|
1716
|
+
} else if (isVerbose()) {
|
|
1717
|
+
spinner_default.info(`no media found, skipped: ${entry}`);
|
|
1718
|
+
}
|
|
1719
|
+
skipped++;
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
if (videoCount >= 2) {
|
|
1723
|
+
pendingAnime.push({ entry, entryPath, videoCount });
|
|
1724
|
+
continue;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1691
1727
|
const parsed = parseDownloadName(entry);
|
|
1692
1728
|
if (!parsed) {
|
|
1693
1729
|
if (isVerbose()) spinner_default.info(`could not parse: ${entry}`);
|
|
@@ -1881,6 +1917,15 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1881
1917
|
for (const p of pendingTv) spinner_default.info(` ${typeGlyph("tv")} ${p.resolvedTitle} \u2014 ${p.entry.replace(/\/$/, "")}${typeTag("tv")}`);
|
|
1882
1918
|
skipped += pendingTv.length;
|
|
1883
1919
|
}
|
|
1920
|
+
if (pendingBooks.length > 0) {
|
|
1921
|
+
spinner_default.warn(`${pendingBooks.length} uncertain book${pendingBooks.length > 1 ? "s" : ""} skipped \u2014 contains PDFs, review manually`);
|
|
1922
|
+
for (const p of pendingBooks) spinner_default.info(` ${typeGlyph("book")} ${p.entry}${typeTag("book")}`);
|
|
1923
|
+
}
|
|
1924
|
+
if (pendingAnime.length > 0) {
|
|
1925
|
+
spinner_default.warn(`${pendingAnime.length} uncertain anime/TV director${pendingAnime.length > 1 ? "ies" : "y"} skipped \u2014 multiple videos with no episode naming`);
|
|
1926
|
+
for (const p of pendingAnime) spinner_default.info(` ${typeGlyph("tv")} ${p.entry} (${p.videoCount} video${p.videoCount > 1 ? "s" : ""})${typeTag("tv")}`);
|
|
1927
|
+
skipped += pendingAnime.length;
|
|
1928
|
+
}
|
|
1884
1929
|
if (ignoreSet.size > 0) {
|
|
1885
1930
|
const stale = [...ignoreSet].filter((name) => !seenIgnored.has(name));
|
|
1886
1931
|
if (stale.length > 0 && !dryRun) {
|
package/dist/index.mjs
CHANGED
|
@@ -1268,6 +1268,24 @@ var containsBook = (dir, depth = 2) => readdirSync7(dir).some((f) => {
|
|
|
1268
1268
|
}
|
|
1269
1269
|
return false;
|
|
1270
1270
|
});
|
|
1271
|
+
var containsPdf = (dir) => {
|
|
1272
|
+
try {
|
|
1273
|
+
return readdirSync7(dir).some((f) => /\.pdf$/i.test(f));
|
|
1274
|
+
} catch {
|
|
1275
|
+
return false;
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
var countVideos = (dir) => {
|
|
1279
|
+
try {
|
|
1280
|
+
return readdirSync7(dir).filter((f) => {
|
|
1281
|
+
if (/\bsample\b/i.test(f)) return false;
|
|
1282
|
+
const ext = f.match(/([^.]+$)/)?.[0];
|
|
1283
|
+
return !!(ext && videoExtensions_default.includes(ext));
|
|
1284
|
+
}).length;
|
|
1285
|
+
} catch {
|
|
1286
|
+
return 0;
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1271
1289
|
var isTvEpisodeName = (name) => /S\d{2,3}E\d{2,3}/i.test(name) || /\d+x\d{2,3}/i.test(name);
|
|
1272
1290
|
var isSeasonDirName = (name) => !isTvEpisodeName(name) && /(?:^|[.\s_-])(?:season|s)\s*0*\d+(?:[.\s_-]|$)/i.test(name);
|
|
1273
1291
|
var gatherEntries = (source) => {
|
|
@@ -1527,6 +1545,8 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1527
1545
|
let imported = 0, skipped = 0;
|
|
1528
1546
|
const pendingMovies = [];
|
|
1529
1547
|
const pendingTv = [];
|
|
1548
|
+
const pendingBooks = [];
|
|
1549
|
+
const pendingAnime = [];
|
|
1530
1550
|
const ignoreSet = new Set(config.ignore ?? []);
|
|
1531
1551
|
const seenIgnored = /* @__PURE__ */ new Set();
|
|
1532
1552
|
for (const source of config.sources) {
|
|
@@ -1610,6 +1630,22 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1610
1630
|
imported++;
|
|
1611
1631
|
continue;
|
|
1612
1632
|
}
|
|
1633
|
+
if (detectedType === "movie" && isDir) {
|
|
1634
|
+
const videoCount = countVideos(entryPath);
|
|
1635
|
+
if (videoCount === 0) {
|
|
1636
|
+
if (containsPdf(entryPath)) {
|
|
1637
|
+
pendingBooks.push({ entry, entryPath });
|
|
1638
|
+
} else if (isVerbose()) {
|
|
1639
|
+
spinner_default.info(`no media found, skipped: ${entry}`);
|
|
1640
|
+
}
|
|
1641
|
+
skipped++;
|
|
1642
|
+
continue;
|
|
1643
|
+
}
|
|
1644
|
+
if (videoCount >= 2) {
|
|
1645
|
+
pendingAnime.push({ entry, entryPath, videoCount });
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1613
1649
|
const parsed = parseDownloadName(entry);
|
|
1614
1650
|
if (!parsed) {
|
|
1615
1651
|
if (isVerbose()) spinner_default.info(`could not parse: ${entry}`);
|
|
@@ -1803,6 +1839,15 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1803
1839
|
for (const p of pendingTv) spinner_default.info(` ${typeGlyph("tv")} ${p.resolvedTitle} \u2014 ${p.entry.replace(/\/$/, "")}${typeTag("tv")}`);
|
|
1804
1840
|
skipped += pendingTv.length;
|
|
1805
1841
|
}
|
|
1842
|
+
if (pendingBooks.length > 0) {
|
|
1843
|
+
spinner_default.warn(`${pendingBooks.length} uncertain book${pendingBooks.length > 1 ? "s" : ""} skipped \u2014 contains PDFs, review manually`);
|
|
1844
|
+
for (const p of pendingBooks) spinner_default.info(` ${typeGlyph("book")} ${p.entry}${typeTag("book")}`);
|
|
1845
|
+
}
|
|
1846
|
+
if (pendingAnime.length > 0) {
|
|
1847
|
+
spinner_default.warn(`${pendingAnime.length} uncertain anime/TV director${pendingAnime.length > 1 ? "ies" : "y"} skipped \u2014 multiple videos with no episode naming`);
|
|
1848
|
+
for (const p of pendingAnime) spinner_default.info(` ${typeGlyph("tv")} ${p.entry} (${p.videoCount} video${p.videoCount > 1 ? "s" : ""})${typeTag("tv")}`);
|
|
1849
|
+
skipped += pendingAnime.length;
|
|
1850
|
+
}
|
|
1806
1851
|
if (ignoreSet.size > 0) {
|
|
1807
1852
|
const stale = [...ignoreSet].filter((name) => !seenIgnored.has(name));
|
|
1808
1853
|
if (stale.length > 0 && !dryRun) {
|
package/package.json
CHANGED