Round 8 bug fixes (20 fixes)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
75775f2798
commit
74920e2e2f
@ -640,7 +640,7 @@ class MegaDebridClient {
|
|||||||
throw new Error("Mega-Web Antwort ohne Download-Link");
|
throw new Error("Mega-Web Antwort ohne Download-Link");
|
||||||
}
|
}
|
||||||
if (!lastError) {
|
if (!lastError) {
|
||||||
lastError = web ? "Mega-Web Antwort ohne Download-Link" : "Mega-Web Antwort leer";
|
lastError = "Mega-Web Antwort leer";
|
||||||
}
|
}
|
||||||
// Don't retry permanent hoster errors (dead link, file removed, etc.)
|
// Don't retry permanent hoster errors (dead link, file removed, etc.)
|
||||||
if (/permanent ungültig|hosternotavailable|file.?not.?found|file.?unavailable|link.?is.?dead/i.test(lastError)) {
|
if (/permanent ungültig|hosternotavailable|file.?not.?found|file.?unavailable|link.?is.?dead/i.test(lastError)) {
|
||||||
|
|||||||
@ -1284,8 +1284,6 @@ export class DownloadManager extends EventEmitter {
|
|||||||
this.packagePostProcessWaiters = [];
|
this.packagePostProcessWaiters = [];
|
||||||
this.summary = null;
|
this.summary = null;
|
||||||
this.nonResumableActive = 0;
|
this.nonResumableActive = 0;
|
||||||
this.retryAfterByItem.clear();
|
|
||||||
this.retryStateByItem.clear();
|
|
||||||
this.resetSessionTotalsIfQueueEmpty();
|
this.resetSessionTotalsIfQueueEmpty();
|
||||||
this.persistNow();
|
this.persistNow();
|
||||||
this.emitState(true);
|
this.emitState(true);
|
||||||
@ -1540,10 +1538,11 @@ export class DownloadManager extends EventEmitter {
|
|||||||
item.attempts = 0;
|
item.attempts = 0;
|
||||||
item.lastError = "";
|
item.lastError = "";
|
||||||
item.fullStatus = "Wartet";
|
item.fullStatus = "Wartet";
|
||||||
|
item.provider = null;
|
||||||
item.updatedAt = nowMs();
|
item.updatedAt = nowMs();
|
||||||
this.assignItemTargetPath(item, path.join(pkg.outputDir, sanitizeFilename(item.fileName || filenameFromUrl(item.url))));
|
this.assignItemTargetPath(item, path.join(pkg.outputDir, sanitizeFilename(item.fileName || filenameFromUrl(item.url))));
|
||||||
this.runOutcomes.delete(itemId);
|
this.runOutcomes.delete(itemId);
|
||||||
this.itemContributedBytes.delete(itemId);
|
this.dropItemContribution(itemId);
|
||||||
this.retryAfterByItem.delete(itemId);
|
this.retryAfterByItem.delete(itemId);
|
||||||
this.retryStateByItem.delete(itemId);
|
this.retryStateByItem.delete(itemId);
|
||||||
if (this.session.running) {
|
if (this.session.running) {
|
||||||
@ -2823,6 +2822,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Not running: start with only items from specified packages
|
// Not running: start with only items from specified packages
|
||||||
|
this.triggerPendingExtractions();
|
||||||
const runItems = Object.values(this.session.items)
|
const runItems = Object.values(this.session.items)
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
if (!targetSet.has(item.packageId)) return false;
|
if (!targetSet.has(item.packageId)) return false;
|
||||||
@ -2928,6 +2928,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Not running: start with only specified items
|
// Not running: start with only specified items
|
||||||
|
this.triggerPendingExtractions();
|
||||||
const runItems = [...targetSet]
|
const runItems = [...targetSet]
|
||||||
.map((id) => this.session.items[id])
|
.map((id) => this.session.items[id])
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
@ -3049,6 +3050,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
this.runOutcomes.clear();
|
this.runOutcomes.clear();
|
||||||
this.runCompletedPackages.clear();
|
this.runCompletedPackages.clear();
|
||||||
this.retryAfterByItem.clear();
|
this.retryAfterByItem.clear();
|
||||||
|
this.retryStateByItem.clear();
|
||||||
this.reservedTargetPaths.clear();
|
this.reservedTargetPaths.clear();
|
||||||
this.claimedTargetPathByItem.clear();
|
this.claimedTargetPathByItem.clear();
|
||||||
this.session.running = false;
|
this.session.running = false;
|
||||||
@ -3323,7 +3325,8 @@ export class DownloadManager extends EventEmitter {
|
|||||||
|| item.status === "paused"
|
|| item.status === "paused"
|
||||||
|| item.status === "reconnect_wait") {
|
|| item.status === "reconnect_wait") {
|
||||||
item.status = "queued";
|
item.status = "queued";
|
||||||
item.fullStatus = "Wartet";
|
const itemPkg = this.session.packages[item.packageId];
|
||||||
|
item.fullStatus = (itemPkg && itemPkg.enabled === false) ? "Paket gestoppt" : "Wartet";
|
||||||
item.speedBps = 0;
|
item.speedBps = 0;
|
||||||
item.updatedAt = nowMs();
|
item.updatedAt = nowMs();
|
||||||
}
|
}
|
||||||
@ -3837,7 +3840,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
let changed = false;
|
let changed = false;
|
||||||
for (const packageId of packageIds) {
|
for (const packageId of packageIds) {
|
||||||
const pkg = this.session.packages[packageId];
|
const pkg = this.session.packages[packageId];
|
||||||
if (!pkg || pkg.cancelled) {
|
if (!pkg || pkg.cancelled || !pkg.enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5714,6 +5717,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
try {
|
try {
|
||||||
await fs.promises.rm(effectiveTargetPath, { force: true });
|
await fs.promises.rm(effectiveTargetPath, { force: true });
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
|
this.releaseTargetPath(active.itemId);
|
||||||
this.dropItemContribution(active.itemId);
|
this.dropItemContribution(active.itemId);
|
||||||
item.downloadedBytes = 0;
|
item.downloadedBytes = 0;
|
||||||
item.progressPercent = 0;
|
item.progressPercent = 0;
|
||||||
@ -6396,7 +6400,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
const updatedAt = nowMs();
|
const updatedAt = nowMs();
|
||||||
for (const entry of archItems) {
|
for (const entry of archItems) {
|
||||||
if (!isExtractedLabel(entry.fullStatus)) {
|
if (!isExtractedLabel(entry.fullStatus) && entry.fullStatus !== label) {
|
||||||
entry.fullStatus = label;
|
entry.fullStatus = label;
|
||||||
entry.updatedAt = updatedAt;
|
entry.updatedAt = updatedAt;
|
||||||
}
|
}
|
||||||
@ -6536,7 +6540,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
if (!allDone && this.settings.hybridExtract && this.settings.autoExtract && failed === 0 && success > 0) {
|
if (!allDone && this.settings.hybridExtract && this.settings.autoExtract && failed === 0 && success > 0) {
|
||||||
await this.runHybridExtraction(packageId, pkg, items, signal);
|
await this.runHybridExtraction(packageId, pkg, items, signal);
|
||||||
if (signal?.aborted) {
|
if (signal?.aborted) {
|
||||||
pkg.status = (pkg.enabled && !this.session.paused) ? "queued" : "paused";
|
pkg.status = (pkg.enabled && this.session.running && !this.session.paused) ? "queued" : "paused";
|
||||||
pkg.updatedAt = nowMs();
|
pkg.updatedAt = nowMs();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -6764,8 +6768,8 @@ export class DownloadManager extends EventEmitter {
|
|||||||
for (const entry of completedItems) {
|
for (const entry of completedItems) {
|
||||||
if (!isExtractedLabel(entry.fullStatus)) {
|
if (!isExtractedLabel(entry.fullStatus)) {
|
||||||
entry.fullStatus = `Entpack-Fehler: ${timeoutReason}`;
|
entry.fullStatus = `Entpack-Fehler: ${timeoutReason}`;
|
||||||
|
entry.updatedAt = nowMs();
|
||||||
}
|
}
|
||||||
entry.updatedAt = nowMs();
|
|
||||||
}
|
}
|
||||||
pkg.status = "failed";
|
pkg.status = "failed";
|
||||||
pkg.updatedAt = nowMs();
|
pkg.updatedAt = nowMs();
|
||||||
|
|||||||
@ -2122,7 +2122,13 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<{
|
|||||||
if (passwordCandidates.length > 1 && pendingCandidates.length > 1) {
|
if (passwordCandidates.length > 1 && pendingCandidates.length > 1) {
|
||||||
logger.info(`Passwort-Discovery: Extrahiere erstes Archiv seriell (${passwordCandidates.length} Passwort-Kandidaten)...`);
|
logger.info(`Passwort-Discovery: Extrahiere erstes Archiv seriell (${passwordCandidates.length} Passwort-Kandidaten)...`);
|
||||||
const first = pendingCandidates[0];
|
const first = pendingCandidates[0];
|
||||||
await extractSingleArchive(first);
|
try {
|
||||||
|
await extractSingleArchive(first);
|
||||||
|
} catch (err) {
|
||||||
|
const errText = String(err);
|
||||||
|
if (/aborted:extract/i.test(errText)) throw err;
|
||||||
|
// noextractor:skipped — handled by noExtractorEncountered flag below
|
||||||
|
}
|
||||||
parallelQueue = pendingCandidates.slice(1);
|
parallelQueue = pendingCandidates.slice(1);
|
||||||
if (parallelQueue.length > 0) {
|
if (parallelQueue.length > 0) {
|
||||||
logger.info(`Passwort-Discovery abgeschlossen, starte parallele Extraktion für ${parallelQueue.length} verbleibende Archive`);
|
logger.info(`Passwort-Discovery abgeschlossen, starte parallele Extraktion für ${parallelQueue.length} verbleibende Archive`);
|
||||||
|
|||||||
@ -485,6 +485,7 @@ async function writeSettingsPayload(paths: StoragePaths, payload: string): Promi
|
|||||||
await fsp.copyFile(tempPath, paths.configFile);
|
await fsp.copyFile(tempPath, paths.configFile);
|
||||||
await fsp.rm(tempPath, { force: true }).catch(() => {});
|
await fsp.rm(tempPath, { force: true }).catch(() => {});
|
||||||
} else {
|
} else {
|
||||||
|
await fsp.rm(tempPath, { force: true }).catch(() => {});
|
||||||
throw renameError;
|
throw renameError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -605,6 +606,7 @@ async function writeSessionPayload(paths: StoragePaths, payload: string, generat
|
|||||||
await fsp.copyFile(tempPath, paths.sessionFile);
|
await fsp.copyFile(tempPath, paths.sessionFile);
|
||||||
await fsp.rm(tempPath, { force: true }).catch(() => {});
|
await fsp.rm(tempPath, { force: true }).catch(() => {});
|
||||||
} else {
|
} else {
|
||||||
|
await fsp.rm(tempPath, { force: true }).catch(() => {});
|
||||||
throw renameError;
|
throw renameError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -316,6 +316,7 @@ const BandwidthChart = memo(function BandwidthChart({ items, running, paused, sp
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
|
if (animationFrameRef.current) cancelAnimationFrame(animationFrameRef.current);
|
||||||
animationFrameRef.current = requestAnimationFrame(drawChart);
|
animationFrameRef.current = requestAnimationFrame(drawChart);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -907,7 +908,7 @@ export function App(): ReactElement {
|
|||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [packages, snapshot.session.items]);
|
}, [packages, snapshot.session.items, collapsedPackages]);
|
||||||
|
|
||||||
const allPackagesCollapsed = useMemo(() => (
|
const allPackagesCollapsed = useMemo(() => (
|
||||||
packages.length > 0 && packages.every((pkg) => collapsedPackages[pkg.id])
|
packages.length > 0 && packages.every((pkg) => collapsedPackages[pkg.id])
|
||||||
@ -1855,7 +1856,7 @@ export function App(): ReactElement {
|
|||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
if (target.tagName !== "INPUT" && target.tagName !== "TEXTAREA") {
|
if (target.tagName !== "INPUT" && target.tagName !== "TEXTAREA") {
|
||||||
// Don't clear selection if an overlay is open — let the overlay close first
|
// Don't clear selection if an overlay is open — let the overlay close first
|
||||||
if (document.querySelector(".ctx-menu") || document.querySelector(".modal-backdrop") || document.querySelector(".link-popup-overlay")) return;
|
if (document.querySelector(".ctx-menu") || document.querySelector(".modal-backdrop")) return;
|
||||||
if (tabRef.current === "downloads") setSelectedIds(new Set());
|
if (tabRef.current === "downloads") setSelectedIds(new Set());
|
||||||
else if (tabRef.current === "history") setSelectedHistoryIds(new Set());
|
else if (tabRef.current === "history") setSelectedHistoryIds(new Set());
|
||||||
}
|
}
|
||||||
@ -2869,7 +2870,7 @@ export function App(): ReactElement {
|
|||||||
const pkg = snapshot.session.packages[id];
|
const pkg = snapshot.session.packages[id];
|
||||||
if (pkg) { for (const iid of pkg.itemIds) removedItemIds.add(iid); }
|
if (pkg) { for (const iid of pkg.itemIds) removedItemIds.add(iid); }
|
||||||
}
|
}
|
||||||
const totalRemaining = Object.keys(snapshot.session.items).length - removedItemIds.size;
|
const totalRemaining = Math.max(0, Object.keys(snapshot.session.items).length - removedItemIds.size);
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
if (pkgCount > 0) parts.push(`${pkgCount} Paket(e)`);
|
if (pkgCount > 0) parts.push(`${pkgCount} Paket(e)`);
|
||||||
if (itemCount > 0) parts.push(`${itemCount} Link(s)`);
|
if (itemCount > 0) parts.push(`${itemCount} Link(s)`);
|
||||||
@ -3257,7 +3258,7 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, isFirs
|
|||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
}, 0);
|
}, 0);
|
||||||
const dlProgress = Math.floor(((done + activeProgress) / total) * (useExtractSplit ? 50 : 100));
|
const dlProgress = Math.min(useExtractSplit ? 50 : 100, Math.floor(((done + activeProgress) / total) * (useExtractSplit ? 50 : 100)));
|
||||||
// Include fractional progress from items currently being extracted
|
// Include fractional progress from items currently being extracted
|
||||||
const extractingProgress = items.reduce((sum, item) => {
|
const extractingProgress = items.reduce((sum, item) => {
|
||||||
const fs = item.fullStatus || "";
|
const fs = item.fullStatus || "";
|
||||||
@ -3266,8 +3267,8 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, isFirs
|
|||||||
if (m) return sum + Number(m[1]) / 100;
|
if (m) return sum + Number(m[1]) / 100;
|
||||||
return sum;
|
return sum;
|
||||||
}, 0);
|
}, 0);
|
||||||
const exProgress = Math.floor(((extracted + extractingProgress) / total) * 50);
|
const exProgress = Math.min(50, Math.floor(((extracted + extractingProgress) / total) * 50));
|
||||||
const combinedProgress = useExtractSplit ? dlProgress + exProgress : dlProgress;
|
const combinedProgress = Math.min(100, useExtractSplit ? dlProgress + exProgress : dlProgress);
|
||||||
|
|
||||||
const onKeyDown = (e: KeyboardEvent<HTMLInputElement>): void => {
|
const onKeyDown = (e: KeyboardEvent<HTMLInputElement>): void => {
|
||||||
if (e.key === "Enter") { onFinishEdit(pkg.id, pkg.name, editingName); }
|
if (e.key === "Enter") { onFinishEdit(pkg.id, pkg.name, editingName); }
|
||||||
@ -3396,7 +3397,7 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, isFirs
|
|||||||
) : ""}
|
) : ""}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
case "hoster": return <span key={col} className="pkg-col pkg-col-hoster" title={extractHoster(item.url)}>{extractHoster(item.url) || ""}</span>;
|
case "hoster": { const h = extractHoster(item.url) || ""; return <span key={col} className="pkg-col pkg-col-hoster" title={h}>{h}</span>; }
|
||||||
case "account": return <span key={col} className="pkg-col pkg-col-account">{item.provider ? providerLabels[item.provider] : ""}</span>;
|
case "account": return <span key={col} className="pkg-col pkg-col-account">{item.provider ? providerLabels[item.provider] : ""}</span>;
|
||||||
case "prio": return <span key={col} className="pkg-col pkg-col-prio"></span>;
|
case "prio": return <span key={col} className="pkg-col pkg-col-prio"></span>;
|
||||||
case "status": return (
|
case "status": return (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user