Switch updater and docs from GitHub to Codeberg
This commit is contained in:
parent
fe59e064f0
commit
43bc95b7fc
@ -43,17 +43,17 @@ Desktop downloader for **Real-Debrid, Mega-Debrid, BestDebrid, and AllDebrid** w
|
|||||||
- Minimize-to-tray with tray menu controls.
|
- Minimize-to-tray with tray menu controls.
|
||||||
- Speed limits globally or per download.
|
- Speed limits globally or per download.
|
||||||
- Bandwidth schedules for time-based speed profiles.
|
- Bandwidth schedules for time-based speed profiles.
|
||||||
- Built-in update checks via GitHub Releases.
|
- Built-in update checks via Codeberg Releases.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Option A: prebuilt releases (recommended)
|
### 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.
|
2. Run the installer or portable build.
|
||||||
3. Add your debrid tokens in Settings.
|
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
|
### Option B: build from source
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ The app stores runtime files in Electron's `userData` directory, including:
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
Release history is available in `CHANGELOG.md` and on GitHub Releases.
|
Release history is available in `CHANGELOG.md` and on Codeberg Releases.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@ -78,7 +78,7 @@ function createWindow(): BrowserWindow {
|
|||||||
responseHeaders: {
|
responseHeaders: {
|
||||||
...details.responseHeaders,
|
...details.responseHeaders,
|
||||||
"Content-Security-Policy": [
|
"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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -17,6 +17,8 @@ const DOWNLOAD_BODY_IDLE_TIMEOUT_MS = 45000;
|
|||||||
const RETRIES_PER_CANDIDATE = 3;
|
const RETRIES_PER_CANDIDATE = 3;
|
||||||
const RETRY_DELAY_MS = 1500;
|
const RETRY_DELAY_MS = 1500;
|
||||||
const UPDATE_USER_AGENT = `RD-Node-Downloader/${APP_VERSION}`;
|
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;
|
let activeUpdateAbortController: AbortController | null = null;
|
||||||
|
|
||||||
@ -45,9 +47,9 @@ export function normalizeUpdateRepo(repo: string): string {
|
|||||||
|
|
||||||
const normalizeParts = (input: string): string => {
|
const normalizeParts = (input: string): string => {
|
||||||
const cleaned = input
|
const cleaned = input
|
||||||
.replace(/^https?:\/\/(?:www\.)?github\.com\//i, "")
|
.replace(/^https?:\/\/(?:www\.)?(?:codeberg\.org|github\.com)\//i, "")
|
||||||
.replace(/^(?:www\.)?github\.com\//i, "")
|
.replace(/^(?:www\.)?(?:codeberg\.org|github\.com)\//i, "")
|
||||||
.replace(/^git@github\.com:/i, "")
|
.replace(/^git@(?:codeberg\.org|github\.com):/i, "")
|
||||||
.replace(/\.git$/i, "")
|
.replace(/\.git$/i, "")
|
||||||
.replace(/^\/+|\/+$/g, "");
|
.replace(/^\/+|\/+$/g, "");
|
||||||
const parts = cleaned.split("/").filter(Boolean);
|
const parts = cleaned.split("/").filter(Boolean);
|
||||||
@ -64,7 +66,7 @@ export function normalizeUpdateRepo(repo: string): string {
|
|||||||
try {
|
try {
|
||||||
const url = new URL(raw);
|
const url = new URL(raw);
|
||||||
const host = url.hostname.toLowerCase();
|
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);
|
const normalized = normalizeParts(url.pathname);
|
||||||
if (normalized) {
|
if (normalized) {
|
||||||
return normalized;
|
return normalized;
|
||||||
@ -158,7 +160,7 @@ function createFallbackResult(repo: string): UpdateCheckResult {
|
|||||||
currentVersion: APP_VERSION,
|
currentVersion: APP_VERSION,
|
||||||
latestVersion: APP_VERSION,
|
latestVersion: APP_VERSION,
|
||||||
latestTag: `v${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);
|
const timeout = timeoutController(RELEASE_FETCH_TIMEOUT_MS);
|
||||||
let response: Response;
|
let response: Response;
|
||||||
try {
|
try {
|
||||||
response = await fetch(`https://api.github.com/repos/${safeRepo}/${endpoint}`, {
|
response = await fetch(`${UPDATE_API_BASE}/repos/${safeRepo}/${endpoint}`, {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/vnd.github+json",
|
Accept: "application/vnd.github+json",
|
||||||
"User-Agent": UPDATE_USER_AGENT
|
"User-Agent": UPDATE_USER_AGENT
|
||||||
@ -250,9 +252,9 @@ function buildDownloadCandidates(safeRepo: string, check: UpdateCheckResult): st
|
|||||||
const candidates = [setupAssetUrl];
|
const candidates = [setupAssetUrl];
|
||||||
if (setupAssetName) {
|
if (setupAssetName) {
|
||||||
const encodedName = encodeURIComponent(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) {
|
if (latestTag) {
|
||||||
candidates.push(`https://github.com/${safeRepo}/releases/download/${encodeURIComponent(latestTag)}/${encodedName}`);
|
candidates.push(`${UPDATE_WEB_BASE}/${safeRepo}/releases/download/${encodeURIComponent(latestTag)}/${encodedName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1527,7 +1527,7 @@ export function App(): ReactElement {
|
|||||||
<option value="ask">nachfragen</option>
|
<option value="ask">nachfragen</option>
|
||||||
</select></div>
|
</select></div>
|
||||||
</div>
|
</div>
|
||||||
<label>GitHub Repo</label>
|
<label>Codeberg Repo</label>
|
||||||
<input value={settingsDraft.updateRepo} onChange={(e) => setText("updateRepo", e.target.value)} />
|
<input value={settingsDraft.updateRepo} onChange={(e) => setText("updateRepo", e.target.value)} />
|
||||||
<label className="toggle-line"><input type="checkbox" checked={settingsDraft.autoUpdateCheck} onChange={(e) => setBool("autoUpdateCheck", e.target.checked)} /> Beim Start auf Updates prüfen</label>
|
<label className="toggle-line"><input type="checkbox" checked={settingsDraft.autoUpdateCheck} onChange={(e) => setBool("autoUpdateCheck", e.target.checked)} /> Beim Start auf Updates prüfen</label>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@ -20,21 +20,21 @@ describe("update", () => {
|
|||||||
it("normalizes update repo input", () => {
|
it("normalizes update repo input", () => {
|
||||||
expect(normalizeUpdateRepo("")).toBe("Sucukdeluxe/real-debrid-downloader");
|
expect(normalizeUpdateRepo("")).toBe("Sucukdeluxe/real-debrid-downloader");
|
||||||
expect(normalizeUpdateRepo("owner/repo")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("owner/repo")).toBe("owner/repo");
|
||||||
expect(normalizeUpdateRepo("https://github.com/owner/repo")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("https://codeberg.org/owner/repo")).toBe("owner/repo");
|
||||||
expect(normalizeUpdateRepo("https://www.github.com/owner/repo")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("https://www.codeberg.org/owner/repo")).toBe("owner/repo");
|
||||||
expect(normalizeUpdateRepo("https://github.com/owner/repo/releases/tag/v1.2.3")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("https://codeberg.org/owner/repo/releases/tag/v1.2.3")).toBe("owner/repo");
|
||||||
expect(normalizeUpdateRepo("github.com/owner/repo.git")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("codeberg.org/owner/repo.git")).toBe("owner/repo");
|
||||||
expect(normalizeUpdateRepo("git@github.com: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 = "";
|
let requestedUrl = "";
|
||||||
globalThis.fetch = (async (input: RequestInfo | URL): Promise<Response> => {
|
globalThis.fetch = (async (input: RequestInfo | URL): Promise<Response> => {
|
||||||
requestedUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
requestedUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
tag_name: `v${APP_VERSION}`,
|
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: []
|
assets: []
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@ -44,8 +44,8 @@ describe("update", () => {
|
|||||||
);
|
);
|
||||||
}) as typeof fetch;
|
}) as typeof fetch;
|
||||||
|
|
||||||
const result = await checkGitHubUpdate("https://github.com/owner/repo/releases");
|
const result = await checkGitHubUpdate("https://codeberg.org/owner/repo/releases");
|
||||||
expect(requestedUrl).toBe("https://api.github.com/repos/owner/repo/releases/latest");
|
expect(requestedUrl).toBe("https://codeberg.org/api/v1/repos/owner/repo/releases/latest");
|
||||||
expect(result.currentVersion).toBe(APP_VERSION);
|
expect(result.currentVersion).toBe(APP_VERSION);
|
||||||
expect(result.latestVersion).toBe(APP_VERSION);
|
expect(result.latestVersion).toBe(APP_VERSION);
|
||||||
expect(result.updateAvailable).toBe(false);
|
expect(result.updateAvailable).toBe(false);
|
||||||
@ -55,7 +55,7 @@ describe("update", () => {
|
|||||||
globalThis.fetch = (async (): Promise<Response> => new Response(
|
globalThis.fetch = (async (): Promise<Response> => new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
tag_name: "v9.9.9",
|
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: [
|
assets: [
|
||||||
{
|
{
|
||||||
name: "Real-Debrid-Downloader 9.9.9.exe",
|
name: "Real-Debrid-Downloader 9.9.9.exe",
|
||||||
@ -105,7 +105,7 @@ describe("update", () => {
|
|||||||
currentVersion: APP_VERSION,
|
currentVersion: APP_VERSION,
|
||||||
latestVersion: "9.9.9",
|
latestVersion: "9.9.9",
|
||||||
latestTag: "v9.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",
|
setupAssetUrl: "https://example.invalid/stale-setup.exe",
|
||||||
setupAssetName: "Real-Debrid-Downloader Setup 9.9.9.exe",
|
setupAssetName: "Real-Debrid-Downloader Setup 9.9.9.exe",
|
||||||
setupAssetDigest: `sha256:${executableDigest}`
|
setupAssetDigest: `sha256:${executableDigest}`
|
||||||
@ -176,7 +176,7 @@ describe("update", () => {
|
|||||||
currentVersion: APP_VERSION,
|
currentVersion: APP_VERSION,
|
||||||
latestVersion: "9.9.9",
|
latestVersion: "9.9.9",
|
||||||
latestTag: "v9.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: "",
|
setupAssetUrl: "",
|
||||||
setupAssetName: ""
|
setupAssetName: ""
|
||||||
};
|
};
|
||||||
@ -240,7 +240,7 @@ describe("update", () => {
|
|||||||
currentVersion: APP_VERSION,
|
currentVersion: APP_VERSION,
|
||||||
latestVersion: "9.9.9",
|
latestVersion: "9.9.9",
|
||||||
latestTag: "v9.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",
|
setupAssetUrl: "https://example.invalid/hang-setup.exe",
|
||||||
setupAssetName: "",
|
setupAssetName: "",
|
||||||
setupAssetDigest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
setupAssetDigest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||||
@ -276,7 +276,7 @@ describe("update", () => {
|
|||||||
currentVersion: APP_VERSION,
|
currentVersion: APP_VERSION,
|
||||||
latestVersion: "9.9.9",
|
latestVersion: "9.9.9",
|
||||||
latestTag: "v9.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",
|
setupAssetUrl: "https://example.invalid/mismatch-setup.exe",
|
||||||
setupAssetName: "setup.exe",
|
setupAssetName: "setup.exe",
|
||||||
setupAssetDigest: "sha256:1111111111111111111111111111111111111111111111111111111111111111"
|
setupAssetDigest: "sha256:1111111111111111111111111111111111111111111111111111111111111111"
|
||||||
@ -292,11 +292,11 @@ describe("normalizeUpdateRepo extended", () => {
|
|||||||
it("handles trailing slashes and extra path segments", () => {
|
it("handles trailing slashes and extra path segments", () => {
|
||||||
expect(normalizeUpdateRepo("owner/repo/")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("owner/repo/")).toBe("owner/repo");
|
||||||
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", () => {
|
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", () => {
|
it("returns default for malformed inputs", () => {
|
||||||
@ -307,12 +307,12 @@ describe("normalizeUpdateRepo extended", () => {
|
|||||||
it("rejects traversal-like owner or repo segments", () => {
|
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("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", () => {
|
it("handles www prefix", () => {
|
||||||
expect(normalizeUpdateRepo("https://www.github.com/owner/repo")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("https://www.codeberg.org/owner/repo")).toBe("owner/repo");
|
||||||
expect(normalizeUpdateRepo("www.github.com/owner/repo")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("www.codeberg.org/owner/repo")).toBe("owner/repo");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user