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 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 countVideos = (dir) => {
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 += countVideos((0, import_path16.resolve)(dir, entry.name));
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: countVideos(tvDest) });
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.6";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reelsort",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
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",