Compare commits

..

No commits in common. "f44a321e747fffaff95ef1bd062fc95e07100e0c" and "9f59b6e7ca548e5b6050ed7107d3a3a37491c7dc" have entirely different histories.

3 changed files with 10 additions and 112 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "real-debrid-downloader", "name": "real-debrid-downloader",
"version": "1.7.132", "version": "1.7.131",
"description": "Desktop downloader", "description": "Desktop downloader",
"main": "build/main/main/main.js", "main": "build/main/main/main.js",
"author": "Sucukdeluxe", "author": "Sucukdeluxe",

View File

@ -135,24 +135,15 @@ const LARGE_BINARY_FILE_RE = /\.(?:part\d+\.rar|rar|r\d{2,3}|zip(?:\.\d+)?|7z(?:
const KNOWN_SMALL_FILE_RE = /\.(?:sfv|nfo|nzb|md5|sha1|sha256|crc|txt|url|lnk|srr)$/i; const KNOWN_SMALL_FILE_RE = /\.(?:sfv|nfo|nzb|md5|sha1|sha256|crc|txt|url|lnk|srr)$/i;
/** Folder name patterns indicating bonus/extras content that should NOT be moved /** Folder name patterns indicating bonus/extras content that should NOT be moved
* to the flat MKV library or auto-renamed. Matches after normalizing separators * to the flat MKV library or auto-renamed. Matches as a substring of the folder name. */
* (so "Making.Of", "making-of", "making of", "makingof" all match "makingof"). */ const BONUS_DIR_PATTERNS = [
const BONUS_DIR_NORMALIZED_PATTERNS = [ "extras", "extra", "bonus", "featurettes", "featurette", "specials", "special features",
"extras", "extra", "bonus", "featurettes", "featurette", "behind the scenes", "behindthescenes", "deleted scenes", "deletedscenes",
"specials", "specialfeatures", "making of", "makingof", "outtakes", "trailers", "interviews", "documentaries"
"behindthescenes", "deletedscenes", "deletedscene",
"makingof", "outtakes", "trailers", "interviews", "documentaries",
"alternateending", "gagreel"
]; ];
/** Filename token patterns for bonus content (e.g. "making-of-e02.mkv"). */ /** Filename token patterns for bonus content (e.g. "making-of-e02.mkv"). */
const BONUS_FILENAME_RE = /(?:^|[._\-\s])(?:making[._\-\s]?of|behind[._\-\s]?the[._\-\s]?scenes|deleted[._\-\s]?scene|alternate[._\-\s]?ending|gag[._\-\s]?reel|featurette|outtakes?|bloopers?|interview|extended[._\-\s]?scene|exclusive[._\-\s]?scene|inside[._\-\s]?e\d+|making[._\-\s]?of[._\-\s]?e\d+)(?:[._\-\s]|$)/i; const BONUS_FILENAME_RE = /(?:^|[._\-\s])(?:making[._\-\s]?of|behind[._\-\s]?the[._\-\s]?scenes|deleted[._\-\s]?scene|alternate[._\-\s]?ending|gag[._\-\s]?reel|featurette|outtakes?|bloopers?|interview|extended[._\-\s]?scene|exclusive[._\-\s]?scene)(?:[._\-\s]|$)/i;
/** Normalize a folder/file segment for bonus-pattern matching: lowercase and
* strip common separators so "Making.Of" "makingof". */
function normalizeBonusSegment(segment: string): string {
return String(segment || "").toLowerCase().replace(/[._\-\s]+/g, "");
}
/** Detect if a file path lies inside a bonus/extras subdirectory of the package. /** Detect if a file path lies inside a bonus/extras subdirectory of the package.
* Walks up the path from filePath until packageDir and checks each segment. */ * Walks up the path from filePath until packageDir and checks each segment. */
@ -165,9 +156,9 @@ function isInsideBonusDir(filePath: string, packageDir: string): boolean {
const resolvedCurrent = path.resolve(current); const resolvedCurrent = path.resolve(current);
if (resolvedCurrent === root) return false; if (resolvedCurrent === root) return false;
if (!isPathInsideDir(current, packageDir)) return false; if (!isPathInsideDir(current, packageDir)) return false;
const normalized = normalizeBonusSegment(path.basename(current)); const segment = path.basename(current).toLowerCase();
for (const pattern of BONUS_DIR_NORMALIZED_PATTERNS) { for (const pattern of BONUS_DIR_PATTERNS) {
if (normalized.includes(pattern)) return true; if (segment.includes(pattern)) return true;
} }
const parent = path.dirname(current); const parent = path.dirname(current);
if (!parent || parent === current) break; if (!parent || parent === current) break;

View File

@ -9451,99 +9451,6 @@ describe("download manager", () => {
expect(fs.existsSync(flattenedEpisode)).toBe(true); expect(fs.existsSync(flattenedEpisode)).toBe(true);
}, 20000); }, 20000);
it("detects dot-separated bonus subdirectories (Making.Of, Behind.The.Scenes)", async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
tempDirs.push(root);
const packageName = "Breaking.Bad.S05.GERMAN.DL.720p.BluRay.x264-TSCC";
const outputDir = path.join(root, "downloads", packageName);
const extractDir = path.join(root, "extract", packageName);
fs.mkdirSync(outputDir, { recursive: true });
// Mix of dot-separated bonus subdirs - must all be detected as bonus
const zip = new AdmZip();
zip.addFile("Breaking.Bad.S05E01.GERMAN.DL.720p.BluRay.x264-TSCC.mkv", Buffer.from("real-episode"));
zip.addFile("Breaking.Bad.S05.Making.Of/SomeBonusClip.mkv", Buffer.from("bonus-1"));
zip.addFile("Breaking.Bad.S05.Behind.The.Scenes/AnotherClip.mkv", Buffer.from("bonus-2"));
zip.addFile("Breaking.Bad.S05.Deleted.Scenes/DeletedClip.mkv", Buffer.from("bonus-3"));
zip.addFile("Breaking.Bad.S05.Gag.Reel/GagClip.mkv", Buffer.from("bonus-4"));
const archivePath = path.join(outputDir, "episode.zip");
zip.writeZip(archivePath);
const archiveSize = fs.statSync(archivePath).size;
const session = emptySession();
const packageId = `${packageName}-pkg`;
const itemId = `${packageName}-item`;
const createdAt = Date.now() - 20_000;
session.packageOrder = [packageId];
session.packages[packageId] = {
id: packageId,
name: packageName,
outputDir,
extractDir,
status: "downloading",
itemIds: [itemId],
cancelled: false,
enabled: true,
createdAt,
updatedAt: createdAt
};
session.items[itemId] = {
id: itemId,
packageId,
url: `https://dummy/${packageName}`,
provider: "realdebrid",
status: "completed",
retries: 0,
speedBps: 0,
downloadedBytes: archiveSize,
totalBytes: archiveSize,
progressPercent: 100,
fileName: "episode.zip",
targetPath: archivePath,
resumable: true,
attempts: 1,
lastError: "",
fullStatus: "Fertig (100 MB)",
createdAt,
updatedAt: createdAt
};
const mkvLibraryDir = path.join(root, "mkv-library");
new DownloadManager(
{
...defaultSettings(),
token: "rd-token",
outputDir: path.join(root, "downloads"),
extractDir: path.join(root, "extract"),
autoExtract: true,
autoRename4sf4sj: false,
collectMkvToLibrary: true,
mkvLibraryDir,
enableIntegrityCheck: false,
cleanupMode: "none"
},
session,
createStoragePaths(path.join(root, "state"))
);
const flattenedEpisode = path.join(mkvLibraryDir, "Breaking.Bad.S05E01.GERMAN.DL.720p.BluRay.x264-TSCC.mkv");
await waitFor(() => fs.existsSync(flattenedEpisode), 12000);
// None of the bonus files should have landed in the flat library
expect(fs.existsSync(path.join(mkvLibraryDir, "SomeBonusClip.mkv"))).toBe(false);
expect(fs.existsSync(path.join(mkvLibraryDir, "AnotherClip.mkv"))).toBe(false);
expect(fs.existsSync(path.join(mkvLibraryDir, "DeletedClip.mkv"))).toBe(false);
expect(fs.existsSync(path.join(mkvLibraryDir, "GagClip.mkv"))).toBe(false);
// All bonus files must still exist in their respective subfolders
expect(fs.existsSync(path.join(extractDir, "Breaking.Bad.S05.Making.Of", "SomeBonusClip.mkv"))).toBe(true);
expect(fs.existsSync(path.join(extractDir, "Breaking.Bad.S05.Behind.The.Scenes", "AnotherClip.mkv"))).toBe(true);
expect(fs.existsSync(path.join(extractDir, "Breaking.Bad.S05.Deleted.Scenes", "DeletedClip.mkv"))).toBe(true);
expect(fs.existsSync(path.join(extractDir, "Breaking.Bad.S05.Gag.Reel", "GagClip.mkv"))).toBe(true);
}, 20000);
it("keeps existing MKV names and appends a suffix while flattening", async () => { it("keeps existing MKV names and appends a suffix while flattening", async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-")); const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
tempDirs.push(root); tempDirs.push(root);