Compare commits

..

No commits in common. "9dd5d4eef82083e8a1158a786997ac92e039afc0" and "9bbeffb2df40a27a91727629906b4f65bce34a34" have entirely different histories.

4 changed files with 28 additions and 81 deletions

View File

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

View File

@ -260,20 +260,11 @@ async function createOrGetRelease(baseApi, tag, authHeader, notes) {
prerelease: false prerelease: false
}; };
const created = await apiRequest("POST", `${baseApi}/releases`, authHeader, JSON.stringify(payload)); const created = await apiRequest("POST", `${baseApi}/releases`, authHeader, JSON.stringify(payload));
if (created.ok) { if (!created.ok) {
return created.body;
}
// Gitea can return 409/422/500 UNIQUE when the release was already partially created.
// Retry the GET — it may now exist.
if (created.status === 409 || created.status === 422 || created.status === 500) {
const retry = await apiRequest("GET", `${baseApi}/releases/tags/${encodeURIComponent(tag)}`, authHeader);
if (retry.ok) {
process.stdout.write(`Release already exists, using existing release.\n`);
return retry.body;
}
}
throw new Error(`Failed to create release (${created.status}): ${JSON.stringify(created.body)}`); throw new Error(`Failed to create release (${created.status}): ${JSON.stringify(created.body)}`);
} }
return created.body;
}
async function uploadReleaseAssets(baseApi, releaseId, authHeader, releaseDir, files) { async function uploadReleaseAssets(baseApi, releaseId, authHeader, releaseDir, files) {
for (const fileName of files) { for (const fileName of files) {
@ -331,12 +322,8 @@ async function main() {
const releaseNotes = args.notes || `- Release ${tag}`; const releaseNotes = args.notes || `- Release ${tag}`;
const repo = getGiteaRepo(); const repo = getGiteaRepo();
const tagExists = spawnSync("git", ["rev-parse", "--verify", `refs/tags/${tag}`], { cwd: process.cwd(), stdio: "ignore" }).status === 0;
if (tagExists) {
process.stdout.write(`Tag ${tag} already exists locally — skipping version bump and git operations (recovery mode).\n`);
} else {
ensureNoTrackedChanges(); ensureNoTrackedChanges();
ensureTagMissing(tag);
if (args.dryRun) { if (args.dryRun) {
process.stdout.write(`Dry run: would release ${tag}. No changes made.\n`); process.stdout.write(`Dry run: would release ${tag}. No changes made.\n`);
@ -344,19 +331,16 @@ async function main() {
} }
updatePackageVersion(rootDir, version); updatePackageVersion(rootDir, version);
}
process.stdout.write(`Building release artifacts for ${tag}...\n`); process.stdout.write(`Building release artifacts for ${tag}...\n`);
run(NPM_RELEASE_WIN.command, NPM_RELEASE_WIN.args); run(NPM_RELEASE_WIN.command, NPM_RELEASE_WIN.args);
const assets = ensureAssetsExist(rootDir, version); const assets = ensureAssetsExist(rootDir, version);
if (!tagExists) {
run("git", ["add", "package.json"]); run("git", ["add", "package.json"]);
run("git", ["commit", "-m", `Release ${tag}`]); run("git", ["commit", "-m", `Release ${tag}`]);
run("git", ["push", repo.remote, "main"]); run("git", ["push", repo.remote, "main"]);
run("git", ["tag", tag]); run("git", ["tag", tag]);
run("git", ["push", repo.remote, tag]); run("git", ["push", repo.remote, tag]);
}
const authHeader = getAuthHeader(repo.host); const authHeader = getAuthHeader(repo.host);
const baseApi = `${repo.baseUrl}/api/v1/repos/${repo.owner}/${repo.repo}`; const baseApi = `${repo.baseUrl}/api/v1/repos/${repo.owner}/${repo.repo}`;

View File

@ -4412,28 +4412,6 @@ export class DownloadManager extends EventEmitter {
return false; return false;
} }
private getProviderOrder(): DebridProvider[] {
if (this.settings.providerOrder && this.settings.providerOrder.length > 0) {
return this.settings.providerOrder;
}
return [
this.settings.providerPrimary,
this.settings.providerSecondary !== "none" ? this.settings.providerSecondary : null,
this.settings.providerTertiary !== "none" ? this.settings.providerTertiary : null
].filter(Boolean) as DebridProvider[];
}
/** Returns the first configured provider from the order that is NOT in cooldown. */
private findFallbackProviderNotInCooldown(item: DownloadItem): DebridProvider | null {
const hosterKey = extractHosterKey(item.url);
for (const provider of this.getProviderOrder()) {
if (!this.isProviderConfigured(provider)) continue;
const key = hosterKey && provider === "alldebrid" ? `${provider}:${hosterKey}` : provider;
if (this.getProviderCooldownRemaining(key) === 0) return provider;
}
return null;
}
private getExpectedProviderForItem(item: DownloadItem): DebridProvider | null { private getExpectedProviderForItem(item: DownloadItem): DebridProvider | null {
if (item.provider) { if (item.provider) {
return resolveMegaDebridProvider(this.settings, item.provider); return resolveMegaDebridProvider(this.settings, item.provider);
@ -4446,8 +4424,14 @@ export class DownloadManager extends EventEmitter {
return routedProvider; return routedProvider;
} }
const order = [
this.settings.providerPrimary,
this.settings.providerSecondary !== "none" ? this.settings.providerSecondary : null,
this.settings.providerTertiary !== "none" ? this.settings.providerTertiary : null
].filter(Boolean) as DebridProvider[];
const seen = new Set<DebridProvider>(); const seen = new Set<DebridProvider>();
for (const provider of this.getProviderOrder()) { for (const provider of order) {
if (seen.has(provider)) { if (seen.has(provider)) {
continue; continue;
} }
@ -5138,29 +5122,12 @@ export class DownloadManager extends EventEmitter {
const cooldownProvider = this.getProviderFailureKeyForItem(item); const cooldownProvider = this.getProviderFailureKeyForItem(item);
const cooldownMs = this.getProviderCooldownRemaining(cooldownProvider); const cooldownMs = this.getProviderCooldownRemaining(cooldownProvider);
if (cooldownMs > 0) { if (cooldownMs > 0) {
// If autoProviderFallback is enabled and another provider is ready, switch to it
// instead of waiting out the full cooldown.
if (this.settings.autoProviderFallback) {
const fallback = this.findFallbackProviderNotInCooldown(item);
if (fallback) {
logger.info(`Provider-Cooldown: ${cooldownProvider} noch ${Math.ceil(cooldownMs / 1000)}s, wechsle zu ${fallback} für ${item.fileName || item.url}`);
item.provider = null;
// Continue — debrid.ts will attempt providers in order and reach the fallback
} else {
const delayMs = Math.min(cooldownMs + 1000, 310000); const delayMs = Math.min(cooldownMs + 1000, 310000);
this.queueRetry(item, active, delayMs, `Provider-Cooldown (${Math.ceil(delayMs / 1000)}s)`); this.queueRetry(item, active, delayMs, `Provider-Cooldown (${Math.ceil(delayMs / 1000)}s)`);
this.persistSoon(); this.persistSoon();
this.emitState(); this.emitState();
return; return;
} }
} else {
const delayMs = Math.min(cooldownMs + 1000, 310000);
this.queueRetry(item, active, delayMs, `Provider-Cooldown (${Math.ceil(delayMs / 1000)}s)`);
this.persistSoon();
this.emitState();
return;
}
}
if (await this.maybeApplyAllDebridRapidgatorBackoff(item, active)) { if (await this.maybeApplyAllDebridRapidgatorBackoff(item, active)) {
this.persistSoon(); this.persistSoon();
this.emitState(); this.emitState();

View File

@ -3877,10 +3877,6 @@ export function App(): ReactElement {
const availableHosters = KNOWN_HOSTERS.filter((h) => !usedHosters.has(h.id)); const availableHosters = KNOWN_HOSTERS.filter((h) => !usedHosters.has(h.id));
const setRouting = (newRouting: Record<string, DebridProvider>) => { const setRouting = (newRouting: Record<string, DebridProvider>) => {
settingsDraftRevisionRef.current += 1;
panelDirtyRevisionRef.current += 1;
settingsDirtyRef.current = true;
setSettingsDirty(true);
setSettingsDraft((prev) => ({ ...prev, hosterRouting: newRouting })); setSettingsDraft((prev) => ({ ...prev, hosterRouting: newRouting }));
}; };