diff --git a/README.md b/README.md
index a714014..0bf2968 100644
--- a/README.md
+++ b/README.md
@@ -43,17 +43,17 @@ Desktop downloader for **Real-Debrid, Mega-Debrid, BestDebrid, and AllDebrid** w
- Minimize-to-tray with tray menu controls.
- Speed limits globally or per download.
- Bandwidth schedules for time-based speed profiles.
-- Built-in update checks via GitHub Releases.
+- Built-in update checks via Codeberg Releases.
## Installation
### Option A: prebuilt releases (recommended)
-1. Download a release from the GitHub Releases page.
+1. Download a release from the Codeberg Releases page.
2. Run the installer or portable build.
3. Add your debrid tokens in Settings.
-Releases: `https://github.com/Sucukdeluxe/real-debrid-downloader/releases`
+Releases: `https://codeberg.org/Sucukdeluxe/real-debrid-downloader/releases`
### Option B: build from source
@@ -113,7 +113,7 @@ The app stores runtime files in Electron's `userData` directory, including:
## Changelog
-Release history is available in `CHANGELOG.md` and on GitHub Releases.
+Release history is available in `CHANGELOG.md` and on Codeberg Releases.
## License
diff --git a/src/main/main.ts b/src/main/main.ts
index 915e2ee..0599275 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -78,7 +78,7 @@ function createWindow(): BrowserWindow {
responseHeaders: {
...details.responseHeaders,
"Content-Security-Policy": [
- "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://api.real-debrid.com https://api.github.com https://bestdebrid.com https://api.alldebrid.com https://www.mega-debrid.eu"
+ "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://api.real-debrid.com https://codeberg.org https://bestdebrid.com https://api.alldebrid.com https://www.mega-debrid.eu"
]
}
});
diff --git a/src/main/update.ts b/src/main/update.ts
index fef59c1..89843eb 100644
--- a/src/main/update.ts
+++ b/src/main/update.ts
@@ -17,6 +17,8 @@ const DOWNLOAD_BODY_IDLE_TIMEOUT_MS = 45000;
const RETRIES_PER_CANDIDATE = 3;
const RETRY_DELAY_MS = 1500;
const UPDATE_USER_AGENT = `RD-Node-Downloader/${APP_VERSION}`;
+const UPDATE_WEB_BASE = "https://codeberg.org";
+const UPDATE_API_BASE = "https://codeberg.org/api/v1";
let activeUpdateAbortController: AbortController | null = null;
@@ -45,9 +47,9 @@ export function normalizeUpdateRepo(repo: string): string {
const normalizeParts = (input: string): string => {
const cleaned = input
- .replace(/^https?:\/\/(?:www\.)?github\.com\//i, "")
- .replace(/^(?:www\.)?github\.com\//i, "")
- .replace(/^git@github\.com:/i, "")
+ .replace(/^https?:\/\/(?:www\.)?(?:codeberg\.org|github\.com)\//i, "")
+ .replace(/^(?:www\.)?(?:codeberg\.org|github\.com)\//i, "")
+ .replace(/^git@(?:codeberg\.org|github\.com):/i, "")
.replace(/\.git$/i, "")
.replace(/^\/+|\/+$/g, "");
const parts = cleaned.split("/").filter(Boolean);
@@ -64,7 +66,7 @@ export function normalizeUpdateRepo(repo: string): string {
try {
const url = new URL(raw);
const host = url.hostname.toLowerCase();
- if (host === "github.com" || host === "www.github.com") {
+ if (host === "codeberg.org" || host === "www.codeberg.org" || host === "github.com" || host === "www.github.com") {
const normalized = normalizeParts(url.pathname);
if (normalized) {
return normalized;
@@ -158,7 +160,7 @@ function createFallbackResult(repo: string): UpdateCheckResult {
currentVersion: APP_VERSION,
latestVersion: APP_VERSION,
latestTag: `v${APP_VERSION}`,
- releaseUrl: `https://github.com/${safeRepo}/releases/latest`
+ releaseUrl: `${UPDATE_WEB_BASE}/${safeRepo}/releases/latest`
};
}
@@ -210,7 +212,7 @@ async function fetchReleasePayload(safeRepo: string, endpoint: string): Promise<
const timeout = timeoutController(RELEASE_FETCH_TIMEOUT_MS);
let response: Response;
try {
- response = await fetch(`https://api.github.com/repos/${safeRepo}/${endpoint}`, {
+ response = await fetch(`${UPDATE_API_BASE}/repos/${safeRepo}/${endpoint}`, {
headers: {
Accept: "application/vnd.github+json",
"User-Agent": UPDATE_USER_AGENT
@@ -250,9 +252,9 @@ function buildDownloadCandidates(safeRepo: string, check: UpdateCheckResult): st
const candidates = [setupAssetUrl];
if (setupAssetName) {
const encodedName = encodeURIComponent(setupAssetName);
- candidates.push(`https://github.com/${safeRepo}/releases/latest/download/${encodedName}`);
+ candidates.push(`${UPDATE_WEB_BASE}/${safeRepo}/releases/latest/download/${encodedName}`);
if (latestTag) {
- candidates.push(`https://github.com/${safeRepo}/releases/download/${encodeURIComponent(latestTag)}/${encodedName}`);
+ candidates.push(`${UPDATE_WEB_BASE}/${safeRepo}/releases/download/${encodeURIComponent(latestTag)}/${encodedName}`);
}
}
diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx
index 75cb69c..6cce6d6 100644
--- a/src/renderer/App.tsx
+++ b/src/renderer/App.tsx
@@ -1527,7 +1527,7 @@ export function App(): ReactElement {
-
+
setText("updateRepo", e.target.value)} />
diff --git a/tests/update.test.ts b/tests/update.test.ts
index 2b9ed3c..4cfcd92 100644
--- a/tests/update.test.ts
+++ b/tests/update.test.ts
@@ -20,21 +20,21 @@ describe("update", () => {
it("normalizes update repo input", () => {
expect(normalizeUpdateRepo("")).toBe("Sucukdeluxe/real-debrid-downloader");
expect(normalizeUpdateRepo("owner/repo")).toBe("owner/repo");
- expect(normalizeUpdateRepo("https://github.com/owner/repo")).toBe("owner/repo");
- expect(normalizeUpdateRepo("https://www.github.com/owner/repo")).toBe("owner/repo");
- expect(normalizeUpdateRepo("https://github.com/owner/repo/releases/tag/v1.2.3")).toBe("owner/repo");
- expect(normalizeUpdateRepo("github.com/owner/repo.git")).toBe("owner/repo");
- expect(normalizeUpdateRepo("git@github.com:owner/repo.git")).toBe("owner/repo");
+ expect(normalizeUpdateRepo("https://codeberg.org/owner/repo")).toBe("owner/repo");
+ expect(normalizeUpdateRepo("https://www.codeberg.org/owner/repo")).toBe("owner/repo");
+ expect(normalizeUpdateRepo("https://codeberg.org/owner/repo/releases/tag/v1.2.3")).toBe("owner/repo");
+ expect(normalizeUpdateRepo("codeberg.org/owner/repo.git")).toBe("owner/repo");
+ expect(normalizeUpdateRepo("git@codeberg.org:owner/repo.git")).toBe("owner/repo");
});
- it("uses normalized repo slug for GitHub API requests", async () => {
+ it("uses normalized repo slug for Codeberg API requests", async () => {
let requestedUrl = "";
globalThis.fetch = (async (input: RequestInfo | URL): Promise => {
requestedUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
return new Response(
JSON.stringify({
tag_name: `v${APP_VERSION}`,
- html_url: "https://github.com/owner/repo/releases/tag/v1.0.0",
+ html_url: "https://codeberg.org/owner/repo/releases/tag/v1.0.0",
assets: []
}),
{
@@ -44,8 +44,8 @@ describe("update", () => {
);
}) as typeof fetch;
- const result = await checkGitHubUpdate("https://github.com/owner/repo/releases");
- expect(requestedUrl).toBe("https://api.github.com/repos/owner/repo/releases/latest");
+ const result = await checkGitHubUpdate("https://codeberg.org/owner/repo/releases");
+ expect(requestedUrl).toBe("https://codeberg.org/api/v1/repos/owner/repo/releases/latest");
expect(result.currentVersion).toBe(APP_VERSION);
expect(result.latestVersion).toBe(APP_VERSION);
expect(result.updateAvailable).toBe(false);
@@ -55,7 +55,7 @@ describe("update", () => {
globalThis.fetch = (async (): Promise => new Response(
JSON.stringify({
tag_name: "v9.9.9",
- html_url: "https://github.com/owner/repo/releases/tag/v9.9.9",
+ html_url: "https://codeberg.org/owner/repo/releases/tag/v9.9.9",
assets: [
{
name: "Real-Debrid-Downloader 9.9.9.exe",
@@ -105,7 +105,7 @@ describe("update", () => {
currentVersion: APP_VERSION,
latestVersion: "9.9.9",
latestTag: "v9.9.9",
- releaseUrl: "https://github.com/owner/repo/releases/tag/v9.9.9",
+ releaseUrl: "https://codeberg.org/owner/repo/releases/tag/v9.9.9",
setupAssetUrl: "https://example.invalid/stale-setup.exe",
setupAssetName: "Real-Debrid-Downloader Setup 9.9.9.exe",
setupAssetDigest: `sha256:${executableDigest}`
@@ -176,7 +176,7 @@ describe("update", () => {
currentVersion: APP_VERSION,
latestVersion: "9.9.9",
latestTag: "v9.9.9",
- releaseUrl: "https://github.com/owner/repo/releases/tag/v9.9.9",
+ releaseUrl: "https://codeberg.org/owner/repo/releases/tag/v9.9.9",
setupAssetUrl: "",
setupAssetName: ""
};
@@ -240,7 +240,7 @@ describe("update", () => {
currentVersion: APP_VERSION,
latestVersion: "9.9.9",
latestTag: "v9.9.9",
- releaseUrl: "https://github.com/owner/repo/releases/tag/v9.9.9",
+ releaseUrl: "https://codeberg.org/owner/repo/releases/tag/v9.9.9",
setupAssetUrl: "https://example.invalid/hang-setup.exe",
setupAssetName: "",
setupAssetDigest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
@@ -276,7 +276,7 @@ describe("update", () => {
currentVersion: APP_VERSION,
latestVersion: "9.9.9",
latestTag: "v9.9.9",
- releaseUrl: "https://github.com/owner/repo/releases/tag/v9.9.9",
+ releaseUrl: "https://codeberg.org/owner/repo/releases/tag/v9.9.9",
setupAssetUrl: "https://example.invalid/mismatch-setup.exe",
setupAssetName: "setup.exe",
setupAssetDigest: "sha256:1111111111111111111111111111111111111111111111111111111111111111"
@@ -292,11 +292,11 @@ describe("normalizeUpdateRepo extended", () => {
it("handles trailing slashes and extra path segments", () => {
expect(normalizeUpdateRepo("owner/repo/")).toBe("owner/repo");
expect(normalizeUpdateRepo("/owner/repo/")).toBe("owner/repo");
- expect(normalizeUpdateRepo("https://github.com/owner/repo/tree/main/src")).toBe("owner/repo");
+ expect(normalizeUpdateRepo("https://codeberg.org/owner/repo/tree/main/src")).toBe("owner/repo");
});
it("handles ssh-style git URLs", () => {
- expect(normalizeUpdateRepo("git@github.com:user/project.git")).toBe("user/project");
+ expect(normalizeUpdateRepo("git@codeberg.org:user/project.git")).toBe("user/project");
});
it("returns default for malformed inputs", () => {
@@ -307,12 +307,12 @@ describe("normalizeUpdateRepo extended", () => {
it("rejects traversal-like owner or repo segments", () => {
expect(normalizeUpdateRepo("../owner/repo")).toBe("Sucukdeluxe/real-debrid-downloader");
expect(normalizeUpdateRepo("owner/../repo")).toBe("Sucukdeluxe/real-debrid-downloader");
- expect(normalizeUpdateRepo("https://github.com/owner/../../repo")).toBe("Sucukdeluxe/real-debrid-downloader");
+ expect(normalizeUpdateRepo("https://codeberg.org/owner/../../repo")).toBe("Sucukdeluxe/real-debrid-downloader");
});
it("handles www prefix", () => {
- expect(normalizeUpdateRepo("https://www.github.com/owner/repo")).toBe("owner/repo");
- expect(normalizeUpdateRepo("www.github.com/owner/repo")).toBe("owner/repo");
+ expect(normalizeUpdateRepo("https://www.codeberg.org/owner/repo")).toBe("owner/repo");
+ expect(normalizeUpdateRepo("www.codeberg.org/owner/repo")).toBe("owner/repo");
});
});