reelsort 0.2.4 → 0.2.5
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 +74 -31
- package/dist/index.d.mts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +77 -30
- package/dist/index.mjs +83 -38
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -98,6 +98,10 @@ var db = () => {
|
|
|
98
98
|
_db.exec("ALTER TABLE shows ADD COLUMN title TEXT");
|
|
99
99
|
} catch {
|
|
100
100
|
}
|
|
101
|
+
try {
|
|
102
|
+
_db.exec("ALTER TABLE imports ADD COLUMN type TEXT");
|
|
103
|
+
} catch {
|
|
104
|
+
}
|
|
101
105
|
return _db;
|
|
102
106
|
};
|
|
103
107
|
var recordRename = (sessionId, oldPath, newPath) => {
|
|
@@ -111,8 +115,16 @@ var getLastSession = () => {
|
|
|
111
115
|
var deleteSession = (sessionId) => {
|
|
112
116
|
db().prepare("DELETE FROM renameHistory WHERE sessionId = ?").run(sessionId);
|
|
113
117
|
};
|
|
114
|
-
var
|
|
115
|
-
db().prepare("
|
|
118
|
+
var getLastImportSession = () => {
|
|
119
|
+
const last = db().prepare("SELECT sessionId FROM imports ORDER BY id DESC LIMIT 1").get();
|
|
120
|
+
if (!last) return [];
|
|
121
|
+
return db().prepare("SELECT * FROM imports WHERE sessionId = ? ORDER BY id DESC").all(last.sessionId);
|
|
122
|
+
};
|
|
123
|
+
var deleteImportSession = (sessionId) => {
|
|
124
|
+
db().prepare("DELETE FROM imports WHERE sessionId = ?").run(sessionId);
|
|
125
|
+
};
|
|
126
|
+
var recordImport = (sessionId, sourcePath, destPath, mode, tmdbId, type) => {
|
|
127
|
+
db().prepare("INSERT INTO imports (sessionId, sourcePath, destinationPath, mode, tmdbId, type) VALUES (?, ?, ?, ?, ?, ?)").run(sessionId, sourcePath, destPath, mode, tmdbId ?? null, type ?? null);
|
|
116
128
|
};
|
|
117
129
|
var getMediaInfo = (filePath) => {
|
|
118
130
|
return db().prepare("SELECT * FROM mediaInfo WHERE filePath = ?").get(filePath);
|
|
@@ -408,10 +420,10 @@ var import_path4 = require("path");
|
|
|
408
420
|
var import_termkit4 = require("termkit");
|
|
409
421
|
|
|
410
422
|
// src/helpers/formatEpisode.ts
|
|
411
|
-
var DEFAULT_EPISODE_FORMAT = "{s}x{ee}";
|
|
423
|
+
var DEFAULT_EPISODE_FORMAT = "{s}x{ee} - {title}";
|
|
412
424
|
var DEFAULT_SEASON_FORMAT = "Season {s}";
|
|
413
425
|
var renderEpisode = (format, season, episode, title, name) => {
|
|
414
|
-
return format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).replace(/\{eee\}/g, episode.toString().padStart(3, "0")).replace(/\{ee\}/g, episode.toString().padStart(2, "0")).replace(/\{e\}/g, episode.toString()).replace(/\{
|
|
426
|
+
return format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).replace(/\{eee\}/g, episode.toString().padStart(3, "0")).replace(/\{ee\}/g, episode.toString().padStart(2, "0")).replace(/\{e\}/g, episode.toString()).replace(/\{show\}/g, title ?? "").replace(/\{title\}/g, name ?? "").replace(/(\s*-\s*)+$/, "").replace(/^(\s*-\s*)+/, "").replace(/\s+/g, " ").trim();
|
|
415
427
|
};
|
|
416
428
|
var formatSeasonFolder = (format, season) => format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).trim();
|
|
417
429
|
var formatEpisode = (season, episode, format = DEFAULT_EPISODE_FORMAT, double = false, title, name) => {
|
|
@@ -1729,12 +1741,12 @@ var classifyMovieConfidence = (entry) => {
|
|
|
1729
1741
|
return "ambiguous";
|
|
1730
1742
|
};
|
|
1731
1743
|
var typeColor = {
|
|
1732
|
-
movie: import_termkit14.Color.
|
|
1733
|
-
tv: import_termkit14.Color.
|
|
1734
|
-
book: import_termkit14.Color.
|
|
1735
|
-
ps3: import_termkit14.Color.
|
|
1744
|
+
movie: (s) => import_termkit14.Color.cyan.encoder(s),
|
|
1745
|
+
tv: (s) => import_termkit14.Color.green.encoder(s),
|
|
1746
|
+
book: (s) => import_termkit14.Color.yellow.encoder(s),
|
|
1747
|
+
ps3: (s) => import_termkit14.Color.magenta.encoder(s)
|
|
1736
1748
|
};
|
|
1737
|
-
var typeGlyph = (t) => typeColor[t]
|
|
1749
|
+
var typeGlyph = (t) => typeColor[t]("\u25CF");
|
|
1738
1750
|
var typeTag = (t) => isVerbose() ? import_termkit14.Color.white.faint.encoder(` (${t})`) : "";
|
|
1739
1751
|
var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactive }) => {
|
|
1740
1752
|
const config = getConfig();
|
|
@@ -1807,7 +1819,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1807
1819
|
mode = "copy";
|
|
1808
1820
|
}
|
|
1809
1821
|
if (subtitleSourcePath && destSubtitleName) (0, import_fs14.cpSync)(subtitleSourcePath, (0, import_path14.resolve)(destFolder, destSubtitleName));
|
|
1810
|
-
recordImport(sessionId, entryPath, destFolder, mode, tmdbId);
|
|
1822
|
+
recordImport(sessionId, entryPath, destFolder, mode, tmdbId, "movie");
|
|
1811
1823
|
} else {
|
|
1812
1824
|
if (isDir) {
|
|
1813
1825
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
@@ -1825,10 +1837,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1825
1837
|
(0, import_fs14.rmSync)(videoSourcePath);
|
|
1826
1838
|
}
|
|
1827
1839
|
}
|
|
1828
|
-
recordImport(sessionId, entryPath, destFolder, "move", tmdbId);
|
|
1840
|
+
recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
|
|
1829
1841
|
}
|
|
1830
1842
|
}
|
|
1831
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("movie")} ${typeColor.movie
|
|
1843
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("movie")} ${typeColor.movie(folderName)}${typeTag("movie")}`);
|
|
1832
1844
|
return true;
|
|
1833
1845
|
};
|
|
1834
1846
|
spinner_default.start();
|
|
@@ -1887,9 +1899,9 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1887
1899
|
}
|
|
1888
1900
|
if (!dryRun) {
|
|
1889
1901
|
moveFolder(entryPath, destPath);
|
|
1890
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
1902
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
1891
1903
|
}
|
|
1892
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("ps3")} ${typeColor.ps3
|
|
1904
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("ps3")} ${typeColor.ps3(destName)}${typeTag("ps3")}`);
|
|
1893
1905
|
imported++;
|
|
1894
1906
|
continue;
|
|
1895
1907
|
}
|
|
@@ -1912,9 +1924,9 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1912
1924
|
(0, import_fs14.rmSync)(entryPath);
|
|
1913
1925
|
}
|
|
1914
1926
|
}
|
|
1915
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
1927
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
1916
1928
|
}
|
|
1917
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("book")} ${typeColor.book
|
|
1929
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("book")} ${typeColor.book(entry)}${typeTag("book")}`);
|
|
1918
1930
|
imported++;
|
|
1919
1931
|
continue;
|
|
1920
1932
|
}
|
|
@@ -2061,9 +2073,9 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
2061
2073
|
if (subtitleSourcePath && destSubtitleName) (0, import_fs14.renameSync)(subtitleSourcePath, (0, import_path14.resolve)(seasonPath, destSubtitleName));
|
|
2062
2074
|
if (isDir) (0, import_fs14.rmSync)(entryPath, { recursive: true, force: true });
|
|
2063
2075
|
}
|
|
2064
|
-
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId);
|
|
2076
|
+
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
|
|
2065
2077
|
}
|
|
2066
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("tv")} ${typeColor.tv
|
|
2078
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("tv")} ${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}`);
|
|
2067
2079
|
imported++;
|
|
2068
2080
|
continue;
|
|
2069
2081
|
}
|
|
@@ -2242,20 +2254,51 @@ var import_fs17 = require("fs");
|
|
|
2242
2254
|
var import_termkit17 = require("termkit");
|
|
2243
2255
|
var undo = async () => {
|
|
2244
2256
|
spinner_default.start();
|
|
2245
|
-
const
|
|
2246
|
-
|
|
2257
|
+
const renameRecords = getLastSession();
|
|
2258
|
+
const importRecords = getLastImportSession();
|
|
2259
|
+
if (renameRecords.length === 0 && importRecords.length === 0) {
|
|
2247
2260
|
spinner_default.info("nothing to undo");
|
|
2248
2261
|
spinner_default.stop();
|
|
2249
2262
|
return;
|
|
2250
2263
|
}
|
|
2264
|
+
const useImports = importRecords.length > 0 && (renameRecords.length === 0 || importRecords[0].sessionId > renameRecords[0].sessionId);
|
|
2265
|
+
if (!useImports) {
|
|
2266
|
+
let undone2 = 0;
|
|
2267
|
+
for (const record of renameRecords) {
|
|
2268
|
+
(0, import_fs17.renameSync)(record.newPath, record.oldPath);
|
|
2269
|
+
spinner_default.succeed(`${import_termkit17.Color.green.encoder(record.newPath)} \u2192 ${import_termkit17.Color.white.encoder(record.oldPath)}`);
|
|
2270
|
+
undone2++;
|
|
2271
|
+
}
|
|
2272
|
+
deleteSession(renameRecords[0].sessionId);
|
|
2273
|
+
spinner_default.succeed(`undid ${undone2} renames`);
|
|
2274
|
+
spinner_default.stop();
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2251
2277
|
let undone = 0;
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2278
|
+
let skipped = 0;
|
|
2279
|
+
for (const record of importRecords) {
|
|
2280
|
+
if (record.mode !== "move") {
|
|
2281
|
+
spinner_default.info(`skipped ${record.destinationPath} (${record.mode} \u2014 source file unchanged)`);
|
|
2282
|
+
skipped++;
|
|
2283
|
+
continue;
|
|
2284
|
+
}
|
|
2285
|
+
if (record.type === "tv") {
|
|
2286
|
+
spinner_default.info(`skipped TV import \u2014 season folder cannot be cleanly reversed: ${record.destinationPath}`);
|
|
2287
|
+
skipped++;
|
|
2288
|
+
continue;
|
|
2289
|
+
}
|
|
2290
|
+
if (!(0, import_fs17.existsSync)(record.destinationPath)) {
|
|
2291
|
+
spinner_default.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
|
|
2292
|
+
skipped++;
|
|
2293
|
+
continue;
|
|
2294
|
+
}
|
|
2295
|
+
(0, import_fs17.renameSync)(record.destinationPath, record.sourcePath);
|
|
2296
|
+
spinner_default.succeed(`${import_termkit17.Color.green.encoder(record.destinationPath)} \u2192 ${import_termkit17.Color.white.encoder(record.sourcePath)}`);
|
|
2255
2297
|
undone++;
|
|
2256
2298
|
}
|
|
2257
|
-
|
|
2258
|
-
spinner_default.succeed(`undid ${undone}
|
|
2299
|
+
deleteImportSession(importRecords[0].sessionId);
|
|
2300
|
+
if (undone > 0) spinner_default.succeed(`undid ${undone} import${undone !== 1 ? "s" : ""}`);
|
|
2301
|
+
if (skipped > 0) spinner_default.info(`skipped ${skipped} item${skipped !== 1 ? "s" : ""} (TV or non-move mode)`);
|
|
2259
2302
|
spinner_default.stop();
|
|
2260
2303
|
};
|
|
2261
2304
|
var undo_default = undo;
|
|
@@ -2418,7 +2461,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2418
2461
|
return;
|
|
2419
2462
|
}
|
|
2420
2463
|
moveItem(entryPath, destPath);
|
|
2421
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
2464
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
2422
2465
|
spinner_default.succeed(`imported ${import_termkit18.Color.green.encoder(destName)}`);
|
|
2423
2466
|
return;
|
|
2424
2467
|
}
|
|
@@ -2439,7 +2482,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2439
2482
|
(0, import_fs18.rmSync)(entryPath);
|
|
2440
2483
|
}
|
|
2441
2484
|
}
|
|
2442
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
2485
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
2443
2486
|
spinner_default.succeed(`imported ${import_termkit18.Color.green.encoder(entry)}`);
|
|
2444
2487
|
return;
|
|
2445
2488
|
}
|
|
@@ -2512,7 +2555,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2512
2555
|
if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs18.renameSync)(subtitleSourcePath2, (0, import_path16.resolve)(seasonPath, destSubtitleName2));
|
|
2513
2556
|
if (isDir) (0, import_fs18.rmSync)(entryPath, { recursive: true, force: true });
|
|
2514
2557
|
}
|
|
2515
|
-
recordImport(sessionId, entryPath, seasonPath, mode);
|
|
2558
|
+
recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
|
|
2516
2559
|
spinner_default.succeed(`imported ${import_termkit18.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
2517
2560
|
return;
|
|
2518
2561
|
}
|
|
@@ -2550,7 +2593,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2550
2593
|
mode = "copy";
|
|
2551
2594
|
}
|
|
2552
2595
|
if (subtitleSourcePath && destSubtitleName) (0, import_fs18.cpSync)(subtitleSourcePath, (0, import_path16.resolve)(destFolder, destSubtitleName));
|
|
2553
|
-
recordImport(sessionId, entryPath, destFolder, mode);
|
|
2596
|
+
recordImport(sessionId, entryPath, destFolder, mode, void 0, "movie");
|
|
2554
2597
|
} else {
|
|
2555
2598
|
if (isDir) {
|
|
2556
2599
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
@@ -2568,7 +2611,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2568
2611
|
(0, import_fs18.rmSync)(videoSourcePath);
|
|
2569
2612
|
}
|
|
2570
2613
|
}
|
|
2571
|
-
recordImport(sessionId, entryPath, destFolder, "move");
|
|
2614
|
+
recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
|
|
2572
2615
|
}
|
|
2573
2616
|
spinner_default.succeed(`imported ${import_termkit18.Color.green.encoder(folderName)}`);
|
|
2574
2617
|
};
|
|
@@ -2610,7 +2653,7 @@ var watch = async ({ hardlink = false, auto = false }) => {
|
|
|
2610
2653
|
var watch_default = watch;
|
|
2611
2654
|
|
|
2612
2655
|
// package.json
|
|
2613
|
-
var version = "0.2.
|
|
2656
|
+
var version = "0.2.5";
|
|
2614
2657
|
|
|
2615
2658
|
// src/program.ts
|
|
2616
2659
|
var toCamel = (s) => s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
package/dist/index.d.mts
CHANGED
|
@@ -122,11 +122,14 @@ interface RenameRecord {
|
|
|
122
122
|
declare const recordRename: (sessionId: string, oldPath: string, newPath: string) => void;
|
|
123
123
|
declare const getLastSession: () => RenameRecord[];
|
|
124
124
|
declare const deleteSession: (sessionId: string) => void;
|
|
125
|
+
declare const getLastImportSession: () => ImportRecord[];
|
|
126
|
+
declare const deleteImportSession: (sessionId: string) => void;
|
|
125
127
|
interface RenameSession {
|
|
126
128
|
sessionId: string;
|
|
127
129
|
records: RenameRecord[];
|
|
128
130
|
}
|
|
129
131
|
type ImportMode = 'move' | 'hardlink' | 'copy';
|
|
132
|
+
type MediaType = 'movie' | 'tv' | 'ps3' | 'book';
|
|
130
133
|
interface ImportRecord {
|
|
131
134
|
id: number;
|
|
132
135
|
sessionId: string;
|
|
@@ -134,9 +137,10 @@ interface ImportRecord {
|
|
|
134
137
|
destinationPath: string;
|
|
135
138
|
mode: ImportMode;
|
|
136
139
|
tmdbId: number | null;
|
|
140
|
+
type: MediaType | null;
|
|
137
141
|
importedAt: string;
|
|
138
142
|
}
|
|
139
|
-
declare const recordImport: (sessionId: string, sourcePath: string, destPath: string, mode: ImportMode, tmdbId?: number) => void;
|
|
143
|
+
declare const recordImport: (sessionId: string, sourcePath: string, destPath: string, mode: ImportMode, tmdbId?: number, type?: MediaType) => void;
|
|
140
144
|
interface MediaInfoRecord {
|
|
141
145
|
id: number;
|
|
142
146
|
filePath: string;
|
|
@@ -156,7 +160,7 @@ declare const getHistory: (limit?: number) => RenameSession[];
|
|
|
156
160
|
|
|
157
161
|
declare const detectEdition: (filename: string) => string | null;
|
|
158
162
|
|
|
159
|
-
declare const DEFAULT_EPISODE_FORMAT = "{s}x{ee}";
|
|
163
|
+
declare const DEFAULT_EPISODE_FORMAT = "{s}x{ee} - {title}";
|
|
160
164
|
declare const DEFAULT_SEASON_FORMAT = "Season {s}";
|
|
161
165
|
declare const formatSeasonFolder: (format: string, season: number) => string;
|
|
162
166
|
declare const formatEpisode: (season: number, episode: number, format?: string, double?: boolean, title?: string, name?: string) => string;
|
|
@@ -219,4 +223,4 @@ var videoExtensions = [
|
|
|
219
223
|
"yuv"
|
|
220
224
|
];
|
|
221
225
|
|
|
222
|
-
export { type CleanOptions, type ConfigSetOptions, type ConfigShowOptions, DEFAULT_EPISODE_FORMAT, DEFAULT_MOVIE_FORMAT, DEFAULT_SEASON_FORMAT, type DestAddOptions, type DestRemoveOptions, type DifferencesOptions, type HistoryOptions, type ImportMode, type ImportRecord, type ListOptions, type MediaInfoRecord, type ParsedMediaName, type ProbeOptions, type Quality, type ReelSortConfig, type RenameOptions, type RenameRecord, type RenameSession, type ResetOptions, type ScanOptions, type SourceAddOptions, type SourceRemoveOptions, type WatchOptions, clean, configSet, configShow, deleteImport, deleteSession, destAdd, destRemove, detectEdition, differences, formatEpisode, formatMovieName, formatSeasonFolder, getCleanableImports, getConfig, getHistory, getImportByDest, getLastSession, getMediaInfo, history, list, normalizeCodec, normalizeResolution, parseDownloadName, parseQuality, probe, recordImport, recordRename, rename, reset, saveConfig, scan, sourceAdd, sourceRemove, _default as titleCase, undo, upsertMediaInfo, videoExtensions, watch };
|
|
226
|
+
export { type CleanOptions, type ConfigSetOptions, type ConfigShowOptions, DEFAULT_EPISODE_FORMAT, DEFAULT_MOVIE_FORMAT, DEFAULT_SEASON_FORMAT, type DestAddOptions, type DestRemoveOptions, type DifferencesOptions, type HistoryOptions, type ImportMode, type ImportRecord, type ListOptions, type MediaInfoRecord, type MediaType, type ParsedMediaName, type ProbeOptions, type Quality, type ReelSortConfig, type RenameOptions, type RenameRecord, type RenameSession, type ResetOptions, type ScanOptions, type SourceAddOptions, type SourceRemoveOptions, type WatchOptions, clean, configSet, configShow, deleteImport, deleteImportSession, deleteSession, destAdd, destRemove, detectEdition, differences, formatEpisode, formatMovieName, formatSeasonFolder, getCleanableImports, getConfig, getHistory, getImportByDest, getLastImportSession, getLastSession, getMediaInfo, history, list, normalizeCodec, normalizeResolution, parseDownloadName, parseQuality, probe, recordImport, recordRename, rename, reset, saveConfig, scan, sourceAdd, sourceRemove, _default as titleCase, undo, upsertMediaInfo, videoExtensions, watch };
|
package/dist/index.d.ts
CHANGED
|
@@ -122,11 +122,14 @@ interface RenameRecord {
|
|
|
122
122
|
declare const recordRename: (sessionId: string, oldPath: string, newPath: string) => void;
|
|
123
123
|
declare const getLastSession: () => RenameRecord[];
|
|
124
124
|
declare const deleteSession: (sessionId: string) => void;
|
|
125
|
+
declare const getLastImportSession: () => ImportRecord[];
|
|
126
|
+
declare const deleteImportSession: (sessionId: string) => void;
|
|
125
127
|
interface RenameSession {
|
|
126
128
|
sessionId: string;
|
|
127
129
|
records: RenameRecord[];
|
|
128
130
|
}
|
|
129
131
|
type ImportMode = 'move' | 'hardlink' | 'copy';
|
|
132
|
+
type MediaType = 'movie' | 'tv' | 'ps3' | 'book';
|
|
130
133
|
interface ImportRecord {
|
|
131
134
|
id: number;
|
|
132
135
|
sessionId: string;
|
|
@@ -134,9 +137,10 @@ interface ImportRecord {
|
|
|
134
137
|
destinationPath: string;
|
|
135
138
|
mode: ImportMode;
|
|
136
139
|
tmdbId: number | null;
|
|
140
|
+
type: MediaType | null;
|
|
137
141
|
importedAt: string;
|
|
138
142
|
}
|
|
139
|
-
declare const recordImport: (sessionId: string, sourcePath: string, destPath: string, mode: ImportMode, tmdbId?: number) => void;
|
|
143
|
+
declare const recordImport: (sessionId: string, sourcePath: string, destPath: string, mode: ImportMode, tmdbId?: number, type?: MediaType) => void;
|
|
140
144
|
interface MediaInfoRecord {
|
|
141
145
|
id: number;
|
|
142
146
|
filePath: string;
|
|
@@ -156,7 +160,7 @@ declare const getHistory: (limit?: number) => RenameSession[];
|
|
|
156
160
|
|
|
157
161
|
declare const detectEdition: (filename: string) => string | null;
|
|
158
162
|
|
|
159
|
-
declare const DEFAULT_EPISODE_FORMAT = "{s}x{ee}";
|
|
163
|
+
declare const DEFAULT_EPISODE_FORMAT = "{s}x{ee} - {title}";
|
|
160
164
|
declare const DEFAULT_SEASON_FORMAT = "Season {s}";
|
|
161
165
|
declare const formatSeasonFolder: (format: string, season: number) => string;
|
|
162
166
|
declare const formatEpisode: (season: number, episode: number, format?: string, double?: boolean, title?: string, name?: string) => string;
|
|
@@ -219,4 +223,4 @@ var videoExtensions = [
|
|
|
219
223
|
"yuv"
|
|
220
224
|
];
|
|
221
225
|
|
|
222
|
-
export { type CleanOptions, type ConfigSetOptions, type ConfigShowOptions, DEFAULT_EPISODE_FORMAT, DEFAULT_MOVIE_FORMAT, DEFAULT_SEASON_FORMAT, type DestAddOptions, type DestRemoveOptions, type DifferencesOptions, type HistoryOptions, type ImportMode, type ImportRecord, type ListOptions, type MediaInfoRecord, type ParsedMediaName, type ProbeOptions, type Quality, type ReelSortConfig, type RenameOptions, type RenameRecord, type RenameSession, type ResetOptions, type ScanOptions, type SourceAddOptions, type SourceRemoveOptions, type WatchOptions, clean, configSet, configShow, deleteImport, deleteSession, destAdd, destRemove, detectEdition, differences, formatEpisode, formatMovieName, formatSeasonFolder, getCleanableImports, getConfig, getHistory, getImportByDest, getLastSession, getMediaInfo, history, list, normalizeCodec, normalizeResolution, parseDownloadName, parseQuality, probe, recordImport, recordRename, rename, reset, saveConfig, scan, sourceAdd, sourceRemove, _default as titleCase, undo, upsertMediaInfo, videoExtensions, watch };
|
|
226
|
+
export { type CleanOptions, type ConfigSetOptions, type ConfigShowOptions, DEFAULT_EPISODE_FORMAT, DEFAULT_MOVIE_FORMAT, DEFAULT_SEASON_FORMAT, type DestAddOptions, type DestRemoveOptions, type DifferencesOptions, type HistoryOptions, type ImportMode, type ImportRecord, type ListOptions, type MediaInfoRecord, type MediaType, type ParsedMediaName, type ProbeOptions, type Quality, type ReelSortConfig, type RenameOptions, type RenameRecord, type RenameSession, type ResetOptions, type ScanOptions, type SourceAddOptions, type SourceRemoveOptions, type WatchOptions, clean, configSet, configShow, deleteImport, deleteImportSession, deleteSession, destAdd, destRemove, detectEdition, differences, formatEpisode, formatMovieName, formatSeasonFolder, getCleanableImports, getConfig, getHistory, getImportByDest, getLastImportSession, getLastSession, getMediaInfo, history, list, normalizeCodec, normalizeResolution, parseDownloadName, parseQuality, probe, recordImport, recordRename, rename, reset, saveConfig, scan, sourceAdd, sourceRemove, _default as titleCase, undo, upsertMediaInfo, videoExtensions, watch };
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
configSet: () => configSet,
|
|
38
38
|
configShow: () => configShow,
|
|
39
39
|
deleteImport: () => deleteImport,
|
|
40
|
+
deleteImportSession: () => deleteImportSession,
|
|
40
41
|
deleteSession: () => deleteSession,
|
|
41
42
|
destAdd: () => destAdd,
|
|
42
43
|
destRemove: () => destRemove,
|
|
@@ -49,6 +50,7 @@ __export(index_exports, {
|
|
|
49
50
|
getConfig: () => getConfig,
|
|
50
51
|
getHistory: () => getHistory,
|
|
51
52
|
getImportByDest: () => getImportByDest,
|
|
53
|
+
getLastImportSession: () => getLastImportSession,
|
|
52
54
|
getLastSession: () => getLastSession,
|
|
53
55
|
getMediaInfo: () => getMediaInfo,
|
|
54
56
|
history: () => history_default,
|
|
@@ -130,6 +132,10 @@ var db = () => {
|
|
|
130
132
|
_db.exec("ALTER TABLE shows ADD COLUMN title TEXT");
|
|
131
133
|
} catch {
|
|
132
134
|
}
|
|
135
|
+
try {
|
|
136
|
+
_db.exec("ALTER TABLE imports ADD COLUMN type TEXT");
|
|
137
|
+
} catch {
|
|
138
|
+
}
|
|
133
139
|
return _db;
|
|
134
140
|
};
|
|
135
141
|
var recordRename = (sessionId, oldPath, newPath) => {
|
|
@@ -143,8 +149,16 @@ var getLastSession = () => {
|
|
|
143
149
|
var deleteSession = (sessionId) => {
|
|
144
150
|
db().prepare("DELETE FROM renameHistory WHERE sessionId = ?").run(sessionId);
|
|
145
151
|
};
|
|
146
|
-
var
|
|
147
|
-
db().prepare("
|
|
152
|
+
var getLastImportSession = () => {
|
|
153
|
+
const last = db().prepare("SELECT sessionId FROM imports ORDER BY id DESC LIMIT 1").get();
|
|
154
|
+
if (!last) return [];
|
|
155
|
+
return db().prepare("SELECT * FROM imports WHERE sessionId = ? ORDER BY id DESC").all(last.sessionId);
|
|
156
|
+
};
|
|
157
|
+
var deleteImportSession = (sessionId) => {
|
|
158
|
+
db().prepare("DELETE FROM imports WHERE sessionId = ?").run(sessionId);
|
|
159
|
+
};
|
|
160
|
+
var recordImport = (sessionId, sourcePath, destPath, mode, tmdbId, type) => {
|
|
161
|
+
db().prepare("INSERT INTO imports (sessionId, sourcePath, destinationPath, mode, tmdbId, type) VALUES (?, ?, ?, ?, ?, ?)").run(sessionId, sourcePath, destPath, mode, tmdbId ?? null, type ?? null);
|
|
148
162
|
};
|
|
149
163
|
var getMediaInfo = (filePath) => {
|
|
150
164
|
return db().prepare("SELECT * FROM mediaInfo WHERE filePath = ?").get(filePath);
|
|
@@ -320,10 +334,10 @@ var saveConfig = (config) => {
|
|
|
320
334
|
};
|
|
321
335
|
|
|
322
336
|
// src/helpers/formatEpisode.ts
|
|
323
|
-
var DEFAULT_EPISODE_FORMAT = "{s}x{ee}";
|
|
337
|
+
var DEFAULT_EPISODE_FORMAT = "{s}x{ee} - {title}";
|
|
324
338
|
var DEFAULT_SEASON_FORMAT = "Season {s}";
|
|
325
339
|
var renderEpisode = (format, season, episode, title, name) => {
|
|
326
|
-
return format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).replace(/\{eee\}/g, episode.toString().padStart(3, "0")).replace(/\{ee\}/g, episode.toString().padStart(2, "0")).replace(/\{e\}/g, episode.toString()).replace(/\{
|
|
340
|
+
return format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).replace(/\{eee\}/g, episode.toString().padStart(3, "0")).replace(/\{ee\}/g, episode.toString().padStart(2, "0")).replace(/\{e\}/g, episode.toString()).replace(/\{show\}/g, title ?? "").replace(/\{title\}/g, name ?? "").replace(/(\s*-\s*)+$/, "").replace(/^(\s*-\s*)+/, "").replace(/\s+/g, " ").trim();
|
|
327
341
|
};
|
|
328
342
|
var formatSeasonFolder = (format, season) => format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).trim();
|
|
329
343
|
var formatEpisode = (season, episode, format = DEFAULT_EPISODE_FORMAT, double = false, title, name) => {
|
|
@@ -1439,12 +1453,12 @@ var classifyMovieConfidence = (entry) => {
|
|
|
1439
1453
|
return "ambiguous";
|
|
1440
1454
|
};
|
|
1441
1455
|
var typeColor = {
|
|
1442
|
-
movie: import_termkit10.Color.
|
|
1443
|
-
tv: import_termkit10.Color.
|
|
1444
|
-
book: import_termkit10.Color.
|
|
1445
|
-
ps3: import_termkit10.Color.
|
|
1456
|
+
movie: (s) => import_termkit10.Color.cyan.encoder(s),
|
|
1457
|
+
tv: (s) => import_termkit10.Color.green.encoder(s),
|
|
1458
|
+
book: (s) => import_termkit10.Color.yellow.encoder(s),
|
|
1459
|
+
ps3: (s) => import_termkit10.Color.magenta.encoder(s)
|
|
1446
1460
|
};
|
|
1447
|
-
var typeGlyph = (t) => typeColor[t]
|
|
1461
|
+
var typeGlyph = (t) => typeColor[t]("\u25CF");
|
|
1448
1462
|
var typeTag = (t) => isVerbose() ? import_termkit10.Color.white.faint.encoder(` (${t})`) : "";
|
|
1449
1463
|
var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactive }) => {
|
|
1450
1464
|
const config = getConfig();
|
|
@@ -1517,7 +1531,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1517
1531
|
mode = "copy";
|
|
1518
1532
|
}
|
|
1519
1533
|
if (subtitleSourcePath && destSubtitleName) (0, import_fs10.cpSync)(subtitleSourcePath, (0, import_path11.resolve)(destFolder, destSubtitleName));
|
|
1520
|
-
recordImport(sessionId, entryPath, destFolder, mode, tmdbId);
|
|
1534
|
+
recordImport(sessionId, entryPath, destFolder, mode, tmdbId, "movie");
|
|
1521
1535
|
} else {
|
|
1522
1536
|
if (isDir) {
|
|
1523
1537
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
@@ -1535,10 +1549,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1535
1549
|
(0, import_fs10.rmSync)(videoSourcePath);
|
|
1536
1550
|
}
|
|
1537
1551
|
}
|
|
1538
|
-
recordImport(sessionId, entryPath, destFolder, "move", tmdbId);
|
|
1552
|
+
recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
|
|
1539
1553
|
}
|
|
1540
1554
|
}
|
|
1541
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("movie")} ${typeColor.movie
|
|
1555
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("movie")} ${typeColor.movie(folderName)}${typeTag("movie")}`);
|
|
1542
1556
|
return true;
|
|
1543
1557
|
};
|
|
1544
1558
|
spinner_default.start();
|
|
@@ -1597,9 +1611,9 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1597
1611
|
}
|
|
1598
1612
|
if (!dryRun) {
|
|
1599
1613
|
moveFolder(entryPath, destPath);
|
|
1600
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
1614
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
1601
1615
|
}
|
|
1602
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("ps3")} ${typeColor.ps3
|
|
1616
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("ps3")} ${typeColor.ps3(destName)}${typeTag("ps3")}`);
|
|
1603
1617
|
imported++;
|
|
1604
1618
|
continue;
|
|
1605
1619
|
}
|
|
@@ -1622,9 +1636,9 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1622
1636
|
(0, import_fs10.rmSync)(entryPath);
|
|
1623
1637
|
}
|
|
1624
1638
|
}
|
|
1625
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
1639
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
1626
1640
|
}
|
|
1627
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("book")} ${typeColor.book
|
|
1641
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("book")} ${typeColor.book(entry)}${typeTag("book")}`);
|
|
1628
1642
|
imported++;
|
|
1629
1643
|
continue;
|
|
1630
1644
|
}
|
|
@@ -1771,9 +1785,9 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1771
1785
|
if (subtitleSourcePath && destSubtitleName) (0, import_fs10.renameSync)(subtitleSourcePath, (0, import_path11.resolve)(seasonPath, destSubtitleName));
|
|
1772
1786
|
if (isDir) (0, import_fs10.rmSync)(entryPath, { recursive: true, force: true });
|
|
1773
1787
|
}
|
|
1774
|
-
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId);
|
|
1788
|
+
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
|
|
1775
1789
|
}
|
|
1776
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("tv")} ${typeColor.tv
|
|
1790
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("tv")} ${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}`);
|
|
1777
1791
|
imported++;
|
|
1778
1792
|
continue;
|
|
1779
1793
|
}
|
|
@@ -1838,20 +1852,51 @@ var import_fs11 = require("fs");
|
|
|
1838
1852
|
var import_termkit11 = require("termkit");
|
|
1839
1853
|
var undo = async () => {
|
|
1840
1854
|
spinner_default.start();
|
|
1841
|
-
const
|
|
1842
|
-
|
|
1855
|
+
const renameRecords = getLastSession();
|
|
1856
|
+
const importRecords = getLastImportSession();
|
|
1857
|
+
if (renameRecords.length === 0 && importRecords.length === 0) {
|
|
1843
1858
|
spinner_default.info("nothing to undo");
|
|
1844
1859
|
spinner_default.stop();
|
|
1845
1860
|
return;
|
|
1846
1861
|
}
|
|
1862
|
+
const useImports = importRecords.length > 0 && (renameRecords.length === 0 || importRecords[0].sessionId > renameRecords[0].sessionId);
|
|
1863
|
+
if (!useImports) {
|
|
1864
|
+
let undone2 = 0;
|
|
1865
|
+
for (const record of renameRecords) {
|
|
1866
|
+
(0, import_fs11.renameSync)(record.newPath, record.oldPath);
|
|
1867
|
+
spinner_default.succeed(`${import_termkit11.Color.green.encoder(record.newPath)} \u2192 ${import_termkit11.Color.white.encoder(record.oldPath)}`);
|
|
1868
|
+
undone2++;
|
|
1869
|
+
}
|
|
1870
|
+
deleteSession(renameRecords[0].sessionId);
|
|
1871
|
+
spinner_default.succeed(`undid ${undone2} renames`);
|
|
1872
|
+
spinner_default.stop();
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1847
1875
|
let undone = 0;
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1876
|
+
let skipped = 0;
|
|
1877
|
+
for (const record of importRecords) {
|
|
1878
|
+
if (record.mode !== "move") {
|
|
1879
|
+
spinner_default.info(`skipped ${record.destinationPath} (${record.mode} \u2014 source file unchanged)`);
|
|
1880
|
+
skipped++;
|
|
1881
|
+
continue;
|
|
1882
|
+
}
|
|
1883
|
+
if (record.type === "tv") {
|
|
1884
|
+
spinner_default.info(`skipped TV import \u2014 season folder cannot be cleanly reversed: ${record.destinationPath}`);
|
|
1885
|
+
skipped++;
|
|
1886
|
+
continue;
|
|
1887
|
+
}
|
|
1888
|
+
if (!(0, import_fs11.existsSync)(record.destinationPath)) {
|
|
1889
|
+
spinner_default.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
|
|
1890
|
+
skipped++;
|
|
1891
|
+
continue;
|
|
1892
|
+
}
|
|
1893
|
+
(0, import_fs11.renameSync)(record.destinationPath, record.sourcePath);
|
|
1894
|
+
spinner_default.succeed(`${import_termkit11.Color.green.encoder(record.destinationPath)} \u2192 ${import_termkit11.Color.white.encoder(record.sourcePath)}`);
|
|
1851
1895
|
undone++;
|
|
1852
1896
|
}
|
|
1853
|
-
|
|
1854
|
-
spinner_default.succeed(`undid ${undone}
|
|
1897
|
+
deleteImportSession(importRecords[0].sessionId);
|
|
1898
|
+
if (undone > 0) spinner_default.succeed(`undid ${undone} import${undone !== 1 ? "s" : ""}`);
|
|
1899
|
+
if (skipped > 0) spinner_default.info(`skipped ${skipped} item${skipped !== 1 ? "s" : ""} (TV or non-move mode)`);
|
|
1855
1900
|
spinner_default.stop();
|
|
1856
1901
|
};
|
|
1857
1902
|
var undo_default = undo;
|
|
@@ -2014,7 +2059,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2014
2059
|
return;
|
|
2015
2060
|
}
|
|
2016
2061
|
moveItem(entryPath, destPath);
|
|
2017
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
2062
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
2018
2063
|
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(destName)}`);
|
|
2019
2064
|
return;
|
|
2020
2065
|
}
|
|
@@ -2035,7 +2080,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2035
2080
|
(0, import_fs12.rmSync)(entryPath);
|
|
2036
2081
|
}
|
|
2037
2082
|
}
|
|
2038
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
2083
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
2039
2084
|
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(entry)}`);
|
|
2040
2085
|
return;
|
|
2041
2086
|
}
|
|
@@ -2108,7 +2153,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2108
2153
|
if (subtitleSourcePath2 && destSubtitleName2) (0, import_fs12.renameSync)(subtitleSourcePath2, (0, import_path12.resolve)(seasonPath, destSubtitleName2));
|
|
2109
2154
|
if (isDir) (0, import_fs12.rmSync)(entryPath, { recursive: true, force: true });
|
|
2110
2155
|
}
|
|
2111
|
-
recordImport(sessionId, entryPath, seasonPath, mode);
|
|
2156
|
+
recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
|
|
2112
2157
|
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
2113
2158
|
return;
|
|
2114
2159
|
}
|
|
@@ -2146,7 +2191,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2146
2191
|
mode = "copy";
|
|
2147
2192
|
}
|
|
2148
2193
|
if (subtitleSourcePath && destSubtitleName) (0, import_fs12.cpSync)(subtitleSourcePath, (0, import_path12.resolve)(destFolder, destSubtitleName));
|
|
2149
|
-
recordImport(sessionId, entryPath, destFolder, mode);
|
|
2194
|
+
recordImport(sessionId, entryPath, destFolder, mode, void 0, "movie");
|
|
2150
2195
|
} else {
|
|
2151
2196
|
if (isDir) {
|
|
2152
2197
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
@@ -2164,7 +2209,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2164
2209
|
(0, import_fs12.rmSync)(videoSourcePath);
|
|
2165
2210
|
}
|
|
2166
2211
|
}
|
|
2167
|
-
recordImport(sessionId, entryPath, destFolder, "move");
|
|
2212
|
+
recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
|
|
2168
2213
|
}
|
|
2169
2214
|
spinner_default.succeed(`imported ${import_termkit12.Color.green.encoder(folderName)}`);
|
|
2170
2215
|
};
|
|
@@ -2213,6 +2258,7 @@ var watch_default = watch;
|
|
|
2213
2258
|
configSet,
|
|
2214
2259
|
configShow,
|
|
2215
2260
|
deleteImport,
|
|
2261
|
+
deleteImportSession,
|
|
2216
2262
|
deleteSession,
|
|
2217
2263
|
destAdd,
|
|
2218
2264
|
destRemove,
|
|
@@ -2225,6 +2271,7 @@ var watch_default = watch;
|
|
|
2225
2271
|
getConfig,
|
|
2226
2272
|
getHistory,
|
|
2227
2273
|
getImportByDest,
|
|
2274
|
+
getLastImportSession,
|
|
2228
2275
|
getLastSession,
|
|
2229
2276
|
getMediaInfo,
|
|
2230
2277
|
history,
|
package/dist/index.mjs
CHANGED
|
@@ -54,6 +54,10 @@ var db = () => {
|
|
|
54
54
|
_db.exec("ALTER TABLE shows ADD COLUMN title TEXT");
|
|
55
55
|
} catch {
|
|
56
56
|
}
|
|
57
|
+
try {
|
|
58
|
+
_db.exec("ALTER TABLE imports ADD COLUMN type TEXT");
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
57
61
|
return _db;
|
|
58
62
|
};
|
|
59
63
|
var recordRename = (sessionId, oldPath, newPath) => {
|
|
@@ -67,8 +71,16 @@ var getLastSession = () => {
|
|
|
67
71
|
var deleteSession = (sessionId) => {
|
|
68
72
|
db().prepare("DELETE FROM renameHistory WHERE sessionId = ?").run(sessionId);
|
|
69
73
|
};
|
|
70
|
-
var
|
|
71
|
-
db().prepare("
|
|
74
|
+
var getLastImportSession = () => {
|
|
75
|
+
const last = db().prepare("SELECT sessionId FROM imports ORDER BY id DESC LIMIT 1").get();
|
|
76
|
+
if (!last) return [];
|
|
77
|
+
return db().prepare("SELECT * FROM imports WHERE sessionId = ? ORDER BY id DESC").all(last.sessionId);
|
|
78
|
+
};
|
|
79
|
+
var deleteImportSession = (sessionId) => {
|
|
80
|
+
db().prepare("DELETE FROM imports WHERE sessionId = ?").run(sessionId);
|
|
81
|
+
};
|
|
82
|
+
var recordImport = (sessionId, sourcePath, destPath, mode, tmdbId, type) => {
|
|
83
|
+
db().prepare("INSERT INTO imports (sessionId, sourcePath, destinationPath, mode, tmdbId, type) VALUES (?, ?, ?, ?, ?, ?)").run(sessionId, sourcePath, destPath, mode, tmdbId ?? null, type ?? null);
|
|
72
84
|
};
|
|
73
85
|
var getMediaInfo = (filePath) => {
|
|
74
86
|
return db().prepare("SELECT * FROM mediaInfo WHERE filePath = ?").get(filePath);
|
|
@@ -244,10 +256,10 @@ var saveConfig = (config) => {
|
|
|
244
256
|
};
|
|
245
257
|
|
|
246
258
|
// src/helpers/formatEpisode.ts
|
|
247
|
-
var DEFAULT_EPISODE_FORMAT = "{s}x{ee}";
|
|
259
|
+
var DEFAULT_EPISODE_FORMAT = "{s}x{ee} - {title}";
|
|
248
260
|
var DEFAULT_SEASON_FORMAT = "Season {s}";
|
|
249
261
|
var renderEpisode = (format, season, episode, title, name) => {
|
|
250
|
-
return format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).replace(/\{eee\}/g, episode.toString().padStart(3, "0")).replace(/\{ee\}/g, episode.toString().padStart(2, "0")).replace(/\{e\}/g, episode.toString()).replace(/\{
|
|
262
|
+
return format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).replace(/\{eee\}/g, episode.toString().padStart(3, "0")).replace(/\{ee\}/g, episode.toString().padStart(2, "0")).replace(/\{e\}/g, episode.toString()).replace(/\{show\}/g, title ?? "").replace(/\{title\}/g, name ?? "").replace(/(\s*-\s*)+$/, "").replace(/^(\s*-\s*)+/, "").replace(/\s+/g, " ").trim();
|
|
251
263
|
};
|
|
252
264
|
var formatSeasonFolder = (format, season) => format.replace(/\{sss\}/g, season.toString().padStart(3, "0")).replace(/\{ss\}/g, season.toString().padStart(2, "0")).replace(/\{s\}/g, season.toString()).trim();
|
|
253
265
|
var formatEpisode = (season, episode, format = DEFAULT_EPISODE_FORMAT, double = false, title, name) => {
|
|
@@ -1363,12 +1375,12 @@ var classifyMovieConfidence = (entry) => {
|
|
|
1363
1375
|
return "ambiguous";
|
|
1364
1376
|
};
|
|
1365
1377
|
var typeColor = {
|
|
1366
|
-
movie: Color9.
|
|
1367
|
-
tv: Color9.
|
|
1368
|
-
book: Color9.
|
|
1369
|
-
ps3: Color9.
|
|
1378
|
+
movie: (s) => Color9.cyan.encoder(s),
|
|
1379
|
+
tv: (s) => Color9.green.encoder(s),
|
|
1380
|
+
book: (s) => Color9.yellow.encoder(s),
|
|
1381
|
+
ps3: (s) => Color9.magenta.encoder(s)
|
|
1370
1382
|
};
|
|
1371
|
-
var typeGlyph = (t) => typeColor[t]
|
|
1383
|
+
var typeGlyph = (t) => typeColor[t]("\u25CF");
|
|
1372
1384
|
var typeTag = (t) => isVerbose() ? Color9.white.faint.encoder(` (${t})`) : "";
|
|
1373
1385
|
var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactive }) => {
|
|
1374
1386
|
const config = getConfig();
|
|
@@ -1441,7 +1453,7 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1441
1453
|
mode = "copy";
|
|
1442
1454
|
}
|
|
1443
1455
|
if (subtitleSourcePath && destSubtitleName) cpSync(subtitleSourcePath, resolve8(destFolder, destSubtitleName));
|
|
1444
|
-
recordImport(sessionId, entryPath, destFolder, mode, tmdbId);
|
|
1456
|
+
recordImport(sessionId, entryPath, destFolder, mode, tmdbId, "movie");
|
|
1445
1457
|
} else {
|
|
1446
1458
|
if (isDir) {
|
|
1447
1459
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
@@ -1459,10 +1471,10 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1459
1471
|
rmSync2(videoSourcePath);
|
|
1460
1472
|
}
|
|
1461
1473
|
}
|
|
1462
|
-
recordImport(sessionId, entryPath, destFolder, "move", tmdbId);
|
|
1474
|
+
recordImport(sessionId, entryPath, destFolder, "move", tmdbId, "movie");
|
|
1463
1475
|
}
|
|
1464
1476
|
}
|
|
1465
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("movie")} ${typeColor.movie
|
|
1477
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("movie")} ${typeColor.movie(folderName)}${typeTag("movie")}`);
|
|
1466
1478
|
return true;
|
|
1467
1479
|
};
|
|
1468
1480
|
spinner_default.start();
|
|
@@ -1521,9 +1533,9 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1521
1533
|
}
|
|
1522
1534
|
if (!dryRun) {
|
|
1523
1535
|
moveFolder(entryPath, destPath);
|
|
1524
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
1536
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
1525
1537
|
}
|
|
1526
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("ps3")} ${typeColor.ps3
|
|
1538
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("ps3")} ${typeColor.ps3(destName)}${typeTag("ps3")}`);
|
|
1527
1539
|
imported++;
|
|
1528
1540
|
continue;
|
|
1529
1541
|
}
|
|
@@ -1546,9 +1558,9 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1546
1558
|
rmSync2(entryPath);
|
|
1547
1559
|
}
|
|
1548
1560
|
}
|
|
1549
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
1561
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
1550
1562
|
}
|
|
1551
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("book")} ${typeColor.book
|
|
1563
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("book")} ${typeColor.book(entry)}${typeTag("book")}`);
|
|
1552
1564
|
imported++;
|
|
1553
1565
|
continue;
|
|
1554
1566
|
}
|
|
@@ -1695,9 +1707,9 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1695
1707
|
if (subtitleSourcePath && destSubtitleName) renameSync3(subtitleSourcePath, resolve8(seasonPath, destSubtitleName));
|
|
1696
1708
|
if (isDir) rmSync2(entryPath, { recursive: true, force: true });
|
|
1697
1709
|
}
|
|
1698
|
-
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId);
|
|
1710
|
+
recordImport(sessionId, entryPath, seasonPath, mode, tmdbId, "tv");
|
|
1699
1711
|
}
|
|
1700
|
-
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("tv")} ${typeColor.tv
|
|
1712
|
+
spinner_default.succeed(`${dryRun ? "[dry] " : ""}${typeGlyph("tv")} ${typeColor.tv(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}${typeTag("tv")}`);
|
|
1701
1713
|
imported++;
|
|
1702
1714
|
continue;
|
|
1703
1715
|
}
|
|
@@ -1758,37 +1770,68 @@ var scan = async ({ type, hardlink: useHardlink, dryRun, auto, force, interactiv
|
|
|
1758
1770
|
var scan_default = scan;
|
|
1759
1771
|
|
|
1760
1772
|
// src/actions/undo.ts
|
|
1761
|
-
import { renameSync as renameSync4 } from "fs";
|
|
1773
|
+
import { existsSync as existsSync10, renameSync as renameSync4 } from "fs";
|
|
1762
1774
|
import { Color as Color10 } from "termkit";
|
|
1763
1775
|
var undo = async () => {
|
|
1764
1776
|
spinner_default.start();
|
|
1765
|
-
const
|
|
1766
|
-
|
|
1777
|
+
const renameRecords = getLastSession();
|
|
1778
|
+
const importRecords = getLastImportSession();
|
|
1779
|
+
if (renameRecords.length === 0 && importRecords.length === 0) {
|
|
1767
1780
|
spinner_default.info("nothing to undo");
|
|
1768
1781
|
spinner_default.stop();
|
|
1769
1782
|
return;
|
|
1770
1783
|
}
|
|
1784
|
+
const useImports = importRecords.length > 0 && (renameRecords.length === 0 || importRecords[0].sessionId > renameRecords[0].sessionId);
|
|
1785
|
+
if (!useImports) {
|
|
1786
|
+
let undone2 = 0;
|
|
1787
|
+
for (const record of renameRecords) {
|
|
1788
|
+
renameSync4(record.newPath, record.oldPath);
|
|
1789
|
+
spinner_default.succeed(`${Color10.green.encoder(record.newPath)} \u2192 ${Color10.white.encoder(record.oldPath)}`);
|
|
1790
|
+
undone2++;
|
|
1791
|
+
}
|
|
1792
|
+
deleteSession(renameRecords[0].sessionId);
|
|
1793
|
+
spinner_default.succeed(`undid ${undone2} renames`);
|
|
1794
|
+
spinner_default.stop();
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1771
1797
|
let undone = 0;
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1798
|
+
let skipped = 0;
|
|
1799
|
+
for (const record of importRecords) {
|
|
1800
|
+
if (record.mode !== "move") {
|
|
1801
|
+
spinner_default.info(`skipped ${record.destinationPath} (${record.mode} \u2014 source file unchanged)`);
|
|
1802
|
+
skipped++;
|
|
1803
|
+
continue;
|
|
1804
|
+
}
|
|
1805
|
+
if (record.type === "tv") {
|
|
1806
|
+
spinner_default.info(`skipped TV import \u2014 season folder cannot be cleanly reversed: ${record.destinationPath}`);
|
|
1807
|
+
skipped++;
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1810
|
+
if (!existsSync10(record.destinationPath)) {
|
|
1811
|
+
spinner_default.info(`skipped \u2014 destination no longer exists: ${record.destinationPath}`);
|
|
1812
|
+
skipped++;
|
|
1813
|
+
continue;
|
|
1814
|
+
}
|
|
1815
|
+
renameSync4(record.destinationPath, record.sourcePath);
|
|
1816
|
+
spinner_default.succeed(`${Color10.green.encoder(record.destinationPath)} \u2192 ${Color10.white.encoder(record.sourcePath)}`);
|
|
1775
1817
|
undone++;
|
|
1776
1818
|
}
|
|
1777
|
-
|
|
1778
|
-
spinner_default.succeed(`undid ${undone}
|
|
1819
|
+
deleteImportSession(importRecords[0].sessionId);
|
|
1820
|
+
if (undone > 0) spinner_default.succeed(`undid ${undone} import${undone !== 1 ? "s" : ""}`);
|
|
1821
|
+
if (skipped > 0) spinner_default.info(`skipped ${skipped} item${skipped !== 1 ? "s" : ""} (TV or non-move mode)`);
|
|
1779
1822
|
spinner_default.stop();
|
|
1780
1823
|
};
|
|
1781
1824
|
var undo_default = undo;
|
|
1782
1825
|
|
|
1783
1826
|
// src/actions/watch.ts
|
|
1784
1827
|
import chokidar from "chokidar";
|
|
1785
|
-
import { cpSync as cpSync2, existsSync as
|
|
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";
|
|
1786
1829
|
import { basename as basename3, dirname as dirname3, resolve as resolve9 } from "path";
|
|
1787
1830
|
import { Color as Color11 } from "termkit";
|
|
1788
1831
|
var sameDev2 = (a, b) => {
|
|
1789
1832
|
try {
|
|
1790
1833
|
let bExisting = b;
|
|
1791
|
-
while (!
|
|
1834
|
+
while (!existsSync11(bExisting)) bExisting = dirname3(bExisting);
|
|
1792
1835
|
return statSync3(a).dev === statSync3(bExisting).dev;
|
|
1793
1836
|
} catch {
|
|
1794
1837
|
return false;
|
|
@@ -1887,7 +1930,7 @@ var expandWatchPath = (p) => {
|
|
|
1887
1930
|
return [p];
|
|
1888
1931
|
};
|
|
1889
1932
|
var findSeasonFolder2 = (showPath, season) => {
|
|
1890
|
-
if (!
|
|
1933
|
+
if (!existsSync11(showPath)) return null;
|
|
1891
1934
|
const folders = readdirSync8(showPath).filter((f) => {
|
|
1892
1935
|
try {
|
|
1893
1936
|
return lstatSync5(resolve9(showPath, f)).isDirectory();
|
|
@@ -1933,18 +1976,18 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
1933
1976
|
if (!nameMatch || !id) return;
|
|
1934
1977
|
const destName = `${nameMatch[0]} [${id}]`;
|
|
1935
1978
|
const destPath = resolve9(destRoot, destName);
|
|
1936
|
-
if (
|
|
1979
|
+
if (existsSync11(destPath)) {
|
|
1937
1980
|
spinner_default.warn(`already exists: ${destName}`);
|
|
1938
1981
|
return;
|
|
1939
1982
|
}
|
|
1940
1983
|
moveItem(entryPath, destPath);
|
|
1941
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
1984
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "ps3");
|
|
1942
1985
|
spinner_default.succeed(`imported ${Color11.green.encoder(destName)}`);
|
|
1943
1986
|
return;
|
|
1944
1987
|
}
|
|
1945
1988
|
if (detectedType === "book") {
|
|
1946
1989
|
const destPath = resolve9(destRoot, entry);
|
|
1947
|
-
if (
|
|
1990
|
+
if (existsSync11(destPath)) {
|
|
1948
1991
|
spinner_default.warn(`already exists: ${entry}`);
|
|
1949
1992
|
return;
|
|
1950
1993
|
}
|
|
@@ -1959,7 +2002,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
1959
2002
|
rmSync3(entryPath);
|
|
1960
2003
|
}
|
|
1961
2004
|
}
|
|
1962
|
-
recordImport(sessionId, entryPath, destPath, "move");
|
|
2005
|
+
recordImport(sessionId, entryPath, destPath, "move", void 0, "book");
|
|
1963
2006
|
spinner_default.succeed(`imported ${Color11.green.encoder(entry)}`);
|
|
1964
2007
|
return;
|
|
1965
2008
|
}
|
|
@@ -2000,7 +2043,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2000
2043
|
const destVideoName2 = `${episodeName}.${videoExt2}`;
|
|
2001
2044
|
const destVideoPath = resolve9(seasonPath, destVideoName2);
|
|
2002
2045
|
const videoSourcePath2 = isDir ? resolve9(entryPath, videoFile2) : entryPath;
|
|
2003
|
-
if (
|
|
2046
|
+
if (existsSync11(destVideoPath)) {
|
|
2004
2047
|
spinner_default.warn(`already exists: ${episodeName}`);
|
|
2005
2048
|
return;
|
|
2006
2049
|
}
|
|
@@ -2032,14 +2075,14 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2032
2075
|
if (subtitleSourcePath2 && destSubtitleName2) renameSync5(subtitleSourcePath2, resolve9(seasonPath, destSubtitleName2));
|
|
2033
2076
|
if (isDir) rmSync3(entryPath, { recursive: true, force: true });
|
|
2034
2077
|
}
|
|
2035
|
-
recordImport(sessionId, entryPath, seasonPath, mode);
|
|
2078
|
+
recordImport(sessionId, entryPath, seasonPath, mode, void 0, "tv");
|
|
2036
2079
|
spinner_default.succeed(`imported ${Color11.green.encoder(`${showFolderName} / ${seasonFolderName} / ${episodeName}`)}`);
|
|
2037
2080
|
return;
|
|
2038
2081
|
}
|
|
2039
2082
|
const edition = detectEdition(entry);
|
|
2040
2083
|
const folderName = formatMovieName(movieFormat, parsed.title, parsed.year, edition);
|
|
2041
2084
|
const destFolder = resolve9(destRoot, folderName);
|
|
2042
|
-
if (
|
|
2085
|
+
if (existsSync11(destFolder)) {
|
|
2043
2086
|
spinner_default.warn(`already exists: ${folderName}`);
|
|
2044
2087
|
return;
|
|
2045
2088
|
}
|
|
@@ -2070,7 +2113,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2070
2113
|
mode = "copy";
|
|
2071
2114
|
}
|
|
2072
2115
|
if (subtitleSourcePath && destSubtitleName) cpSync2(subtitleSourcePath, resolve9(destFolder, destSubtitleName));
|
|
2073
|
-
recordImport(sessionId, entryPath, destFolder, mode);
|
|
2116
|
+
recordImport(sessionId, entryPath, destFolder, mode, void 0, "movie");
|
|
2074
2117
|
} else {
|
|
2075
2118
|
if (isDir) {
|
|
2076
2119
|
const keep = new Set([videoFile, subtitle].filter(Boolean));
|
|
@@ -2088,7 +2131,7 @@ var processItem = async (entryPath, useHardlink, language, auto) => {
|
|
|
2088
2131
|
rmSync3(videoSourcePath);
|
|
2089
2132
|
}
|
|
2090
2133
|
}
|
|
2091
|
-
recordImport(sessionId, entryPath, destFolder, "move");
|
|
2134
|
+
recordImport(sessionId, entryPath, destFolder, "move", void 0, "movie");
|
|
2092
2135
|
}
|
|
2093
2136
|
spinner_default.succeed(`imported ${Color11.green.encoder(folderName)}`);
|
|
2094
2137
|
};
|
|
@@ -2136,6 +2179,7 @@ export {
|
|
|
2136
2179
|
configSet,
|
|
2137
2180
|
configShow,
|
|
2138
2181
|
deleteImport,
|
|
2182
|
+
deleteImportSession,
|
|
2139
2183
|
deleteSession,
|
|
2140
2184
|
destAdd,
|
|
2141
2185
|
destRemove,
|
|
@@ -2148,6 +2192,7 @@ export {
|
|
|
2148
2192
|
getConfig,
|
|
2149
2193
|
getHistory,
|
|
2150
2194
|
getImportByDest,
|
|
2195
|
+
getLastImportSession,
|
|
2151
2196
|
getLastSession,
|
|
2152
2197
|
getMediaInfo,
|
|
2153
2198
|
history_default as history,
|
package/package.json
CHANGED