Restore update checks and startup notifications
Some checks are pending
Build and Release / build (push) Waiting to run
Some checks are pending
Build and Release / build (push) Waiting to run
This commit is contained in:
parent
c7813c26a8
commit
02370a40b4
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.1.14",
|
"version": "1.1.15",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.1.14",
|
"version": "1.1.15",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.1.14",
|
"version": "1.1.15",
|
||||||
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
|
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
|
||||||
"main": "build/main/main/main.js",
|
"main": "build/main/main/main.js",
|
||||||
"author": "Sucukdeluxe",
|
"author": "Sucukdeluxe",
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import { AddLinksPayload, AppSettings, ParsedPackageInput, UiSnapshot } from "../shared/types";
|
import { AddLinksPayload, AppSettings, ParsedPackageInput, UiSnapshot, UpdateCheckResult } from "../shared/types";
|
||||||
import { importDlcContainers } from "./container";
|
import { importDlcContainers } from "./container";
|
||||||
import { APP_VERSION, defaultSettings } from "./constants";
|
import { APP_VERSION, defaultSettings } from "./constants";
|
||||||
import { DownloadManager } from "./download-manager";
|
import { DownloadManager } from "./download-manager";
|
||||||
import { parseCollectorInput } from "./link-parser";
|
import { parseCollectorInput } from "./link-parser";
|
||||||
import { configureLogger, logger } from "./logger";
|
import { configureLogger, logger } from "./logger";
|
||||||
import { createStoragePaths, emptySession, loadSession, loadSettings, saveSettings } from "./storage";
|
import { createStoragePaths, emptySession, loadSession, loadSettings, saveSettings } from "./storage";
|
||||||
|
import { checkGitHubUpdate } from "./update";
|
||||||
|
|
||||||
export class AppController {
|
export class AppController {
|
||||||
private settings: AppSettings;
|
private settings: AppSettings;
|
||||||
@ -64,6 +65,10 @@ export class AppController {
|
|||||||
return this.settings;
|
return this.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async checkUpdates(): Promise<UpdateCheckResult> {
|
||||||
|
return checkGitHubUpdate(this.settings.updateRepo);
|
||||||
|
}
|
||||||
|
|
||||||
public addLinks(payload: AddLinksPayload): { addedPackages: number; addedLinks: number; invalidCount: number } {
|
public addLinks(payload: AddLinksPayload): { addedPackages: number; addedLinks: number; invalidCount: number } {
|
||||||
const parsed = parseCollectorInput(payload.rawText, payload.packageName || this.settings.packageName);
|
const parsed = parseCollectorInput(payload.rawText, payload.packageName || this.settings.packageName);
|
||||||
if (parsed.length === 0) {
|
if (parsed.length === 0) {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import os from "node:os";
|
|||||||
import { AppSettings } from "../shared/types";
|
import { AppSettings } from "../shared/types";
|
||||||
|
|
||||||
export const APP_NAME = "Debrid Download Manager";
|
export const APP_NAME = "Debrid Download Manager";
|
||||||
export const APP_VERSION = "1.1.14";
|
export const APP_VERSION = "1.1.15";
|
||||||
export const API_BASE_URL = "https://api.real-debrid.com/rest/1.0";
|
export const API_BASE_URL = "https://api.real-debrid.com/rest/1.0";
|
||||||
|
|
||||||
export const DCRYPT_UPLOAD_URL = "https://dcrypt.it/decrypt/upload";
|
export const DCRYPT_UPLOAD_URL = "https://dcrypt.it/decrypt/upload";
|
||||||
|
|||||||
@ -40,6 +40,7 @@ function createWindow(): BrowserWindow {
|
|||||||
function registerIpcHandlers(): void {
|
function registerIpcHandlers(): void {
|
||||||
ipcMain.handle(IPC_CHANNELS.GET_SNAPSHOT, () => controller.getSnapshot());
|
ipcMain.handle(IPC_CHANNELS.GET_SNAPSHOT, () => controller.getSnapshot());
|
||||||
ipcMain.handle(IPC_CHANNELS.GET_VERSION, () => controller.getVersion());
|
ipcMain.handle(IPC_CHANNELS.GET_VERSION, () => controller.getVersion());
|
||||||
|
ipcMain.handle(IPC_CHANNELS.CHECK_UPDATES, async () => controller.checkUpdates());
|
||||||
ipcMain.handle(IPC_CHANNELS.UPDATE_SETTINGS, (_event: IpcMainInvokeEvent, partial: Partial<AppSettings>) => controller.updateSettings(partial ?? {}));
|
ipcMain.handle(IPC_CHANNELS.UPDATE_SETTINGS, (_event: IpcMainInvokeEvent, partial: Partial<AppSettings>) => controller.updateSettings(partial ?? {}));
|
||||||
ipcMain.handle(IPC_CHANNELS.ADD_LINKS, (_event: IpcMainInvokeEvent, payload: AddLinksPayload) => controller.addLinks(payload));
|
ipcMain.handle(IPC_CHANNELS.ADD_LINKS, (_event: IpcMainInvokeEvent, payload: AddLinksPayload) => controller.addLinks(payload));
|
||||||
ipcMain.handle(IPC_CHANNELS.ADD_CONTAINERS, async (_event: IpcMainInvokeEvent, filePaths: string[]) => controller.addContainers(filePaths ?? []));
|
ipcMain.handle(IPC_CHANNELS.ADD_CONTAINERS, async (_event: IpcMainInvokeEvent, filePaths: string[]) => controller.addContainers(filePaths ?? []));
|
||||||
|
|||||||
67
src/main/update.ts
Normal file
67
src/main/update.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { APP_VERSION, DEFAULT_UPDATE_REPO } from "./constants";
|
||||||
|
import { UpdateCheckResult } from "../shared/types";
|
||||||
|
import { compactErrorText } from "./utils";
|
||||||
|
|
||||||
|
function parseVersionParts(version: string): number[] {
|
||||||
|
const cleaned = version.replace(/^v/i, "").trim();
|
||||||
|
return cleaned.split(".").map((part) => Number(part.replace(/[^0-9].*$/, "") || "0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRemoteNewer(currentVersion: string, latestVersion: string): boolean {
|
||||||
|
const current = parseVersionParts(currentVersion);
|
||||||
|
const latest = parseVersionParts(latestVersion);
|
||||||
|
const maxLen = Math.max(current.length, latest.length);
|
||||||
|
for (let i = 0; i < maxLen; i += 1) {
|
||||||
|
const a = current[i] ?? 0;
|
||||||
|
const b = latest[i] ?? 0;
|
||||||
|
if (b > a) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (b < a) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkGitHubUpdate(repo: string): Promise<UpdateCheckResult> {
|
||||||
|
const safeRepo = (repo || DEFAULT_UPDATE_REPO).trim() || DEFAULT_UPDATE_REPO;
|
||||||
|
const fallback: UpdateCheckResult = {
|
||||||
|
updateAvailable: false,
|
||||||
|
currentVersion: APP_VERSION,
|
||||||
|
latestVersion: APP_VERSION,
|
||||||
|
latestTag: `v${APP_VERSION}`,
|
||||||
|
releaseUrl: `https://github.com/${safeRepo}/releases/latest`
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`https://api.github.com/repos/${safeRepo}/releases/latest`, {
|
||||||
|
headers: {
|
||||||
|
Accept: "application/vnd.github+json",
|
||||||
|
"User-Agent": "RD-Node-Downloader/1.1.14"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const payload = await response.json().catch(() => null) as Record<string, unknown> | null;
|
||||||
|
if (!response.ok || !payload) {
|
||||||
|
const reason = String((payload?.message as string) || `HTTP ${response.status}`);
|
||||||
|
return { ...fallback, error: reason };
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestTag = String(payload.tag_name || `v${APP_VERSION}`).trim();
|
||||||
|
const latestVersion = latestTag.replace(/^v/i, "") || APP_VERSION;
|
||||||
|
const releaseUrl = String(payload.html_url || fallback.releaseUrl);
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateAvailable: isRemoteNewer(APP_VERSION, latestVersion),
|
||||||
|
currentVersion: APP_VERSION,
|
||||||
|
latestVersion,
|
||||||
|
latestTag,
|
||||||
|
releaseUrl
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
...fallback,
|
||||||
|
error: compactErrorText(error)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,12 @@
|
|||||||
import { contextBridge, ipcRenderer } from "electron";
|
import { contextBridge, ipcRenderer } from "electron";
|
||||||
import { AddLinksPayload, AppSettings, UiSnapshot } from "../shared/types";
|
import { AddLinksPayload, AppSettings, UiSnapshot, UpdateCheckResult } from "../shared/types";
|
||||||
import { IPC_CHANNELS } from "../shared/ipc";
|
import { IPC_CHANNELS } from "../shared/ipc";
|
||||||
import { ElectronApi } from "../shared/preload-api";
|
import { ElectronApi } from "../shared/preload-api";
|
||||||
|
|
||||||
const api: ElectronApi = {
|
const api: ElectronApi = {
|
||||||
getSnapshot: (): Promise<UiSnapshot> => ipcRenderer.invoke(IPC_CHANNELS.GET_SNAPSHOT),
|
getSnapshot: (): Promise<UiSnapshot> => ipcRenderer.invoke(IPC_CHANNELS.GET_SNAPSHOT),
|
||||||
getVersion: (): Promise<string> => ipcRenderer.invoke(IPC_CHANNELS.GET_VERSION),
|
getVersion: (): Promise<string> => ipcRenderer.invoke(IPC_CHANNELS.GET_VERSION),
|
||||||
|
checkUpdates: (): Promise<UpdateCheckResult> => ipcRenderer.invoke(IPC_CHANNELS.CHECK_UPDATES),
|
||||||
updateSettings: (settings: Partial<AppSettings>): Promise<AppSettings> => ipcRenderer.invoke(IPC_CHANNELS.UPDATE_SETTINGS, settings),
|
updateSettings: (settings: Partial<AppSettings>): Promise<AppSettings> => ipcRenderer.invoke(IPC_CHANNELS.UPDATE_SETTINGS, settings),
|
||||||
addLinks: (payload: AddLinksPayload): Promise<{ addedPackages: number; addedLinks: number; invalidCount: number }> =>
|
addLinks: (payload: AddLinksPayload): Promise<{ addedPackages: number; addedLinks: number; invalidCount: number }> =>
|
||||||
ipcRenderer.invoke(IPC_CHANNELS.ADD_LINKS, payload),
|
ipcRenderer.invoke(IPC_CHANNELS.ADD_LINKS, payload),
|
||||||
|
|||||||
@ -84,6 +84,14 @@ export function App(): ReactElement {
|
|||||||
void window.rd.getSnapshot().then((state) => {
|
void window.rd.getSnapshot().then((state) => {
|
||||||
setSnapshot(state);
|
setSnapshot(state);
|
||||||
setSettingsDraft(state.settings);
|
setSettingsDraft(state.settings);
|
||||||
|
if (state.settings.autoUpdateCheck) {
|
||||||
|
void window.rd.checkUpdates().then((result) => {
|
||||||
|
if (result.updateAvailable) {
|
||||||
|
setStatusToast(`Update verfügbar: ${result.latestTag} (aktuell v${result.currentVersion})`);
|
||||||
|
setTimeout(() => setStatusToast(""), 3800);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
unsubscribe = window.rd.onStateUpdate((state) => {
|
unsubscribe = window.rd.onStateUpdate((state) => {
|
||||||
setSnapshot(state);
|
setSnapshot(state);
|
||||||
@ -105,6 +113,22 @@ export function App(): ReactElement {
|
|||||||
setTimeout(() => setStatusToast(""), 1800);
|
setTimeout(() => setStatusToast(""), 1800);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onCheckUpdates = async (): Promise<void> => {
|
||||||
|
const result = await window.rd.checkUpdates();
|
||||||
|
if (result.error) {
|
||||||
|
setStatusToast(`Update-Check fehlgeschlagen: ${result.error}`);
|
||||||
|
setTimeout(() => setStatusToast(""), 2800);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.updateAvailable) {
|
||||||
|
setStatusToast(`Update verfügbar: ${result.latestTag} (aktuell v${result.currentVersion})`);
|
||||||
|
setTimeout(() => setStatusToast(""), 3200);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setStatusToast(`Kein Update verfügbar (v${result.currentVersion})`);
|
||||||
|
setTimeout(() => setStatusToast(""), 2000);
|
||||||
|
};
|
||||||
|
|
||||||
const onAddLinks = async (): Promise<void> => {
|
const onAddLinks = async (): Promise<void> => {
|
||||||
await window.rd.updateSettings(settingsDraft);
|
await window.rd.updateSettings(settingsDraft);
|
||||||
const result = await window.rd.addLinks({ rawText: linksRaw, packageName: settingsDraft.packageName });
|
const result = await window.rd.addLinks({ rawText: linksRaw, packageName: settingsDraft.packageName });
|
||||||
@ -198,6 +222,7 @@ export function App(): ReactElement {
|
|||||||
<option value="per_download">per_download</option>
|
<option value="per_download">per_download</option>
|
||||||
</select>
|
</select>
|
||||||
<button className="btn" onClick={onSaveSettings}>Live speichern</button>
|
<button className="btn" onClick={onSaveSettings}>Live speichern</button>
|
||||||
|
<button className="btn" onClick={onCheckUpdates}>Updates prüfen</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
export const IPC_CHANNELS = {
|
export const IPC_CHANNELS = {
|
||||||
GET_SNAPSHOT: "app:get-snapshot",
|
GET_SNAPSHOT: "app:get-snapshot",
|
||||||
GET_VERSION: "app:get-version",
|
GET_VERSION: "app:get-version",
|
||||||
|
CHECK_UPDATES: "app:check-updates",
|
||||||
UPDATE_SETTINGS: "app:update-settings",
|
UPDATE_SETTINGS: "app:update-settings",
|
||||||
ADD_LINKS: "queue:add-links",
|
ADD_LINKS: "queue:add-links",
|
||||||
ADD_CONTAINERS: "queue:add-containers",
|
ADD_CONTAINERS: "queue:add-containers",
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import type { AddLinksPayload, AppSettings, UiSnapshot } from "./types";
|
import type { AddLinksPayload, AppSettings, UiSnapshot, UpdateCheckResult } from "./types";
|
||||||
|
|
||||||
export interface ElectronApi {
|
export interface ElectronApi {
|
||||||
getSnapshot: () => Promise<UiSnapshot>;
|
getSnapshot: () => Promise<UiSnapshot>;
|
||||||
getVersion: () => Promise<string>;
|
getVersion: () => Promise<string>;
|
||||||
|
checkUpdates: () => Promise<UpdateCheckResult>;
|
||||||
updateSettings: (settings: Partial<AppSettings>) => Promise<AppSettings>;
|
updateSettings: (settings: Partial<AppSettings>) => Promise<AppSettings>;
|
||||||
addLinks: (payload: AddLinksPayload) => Promise<{ addedPackages: number; addedLinks: number; invalidCount: number }>;
|
addLinks: (payload: AddLinksPayload) => Promise<{ addedPackages: number; addedLinks: number; invalidCount: number }>;
|
||||||
addContainers: (filePaths: string[]) => Promise<{ addedPackages: number; addedLinks: number }>;
|
addContainers: (filePaths: string[]) => Promise<{ addedPackages: number; addedLinks: number }>;
|
||||||
|
|||||||
@ -137,6 +137,15 @@ export interface AddContainerPayload {
|
|||||||
filePaths: string[];
|
filePaths: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UpdateCheckResult {
|
||||||
|
updateAvailable: boolean;
|
||||||
|
currentVersion: string;
|
||||||
|
latestVersion: string;
|
||||||
|
latestTag: string;
|
||||||
|
releaseUrl: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ParsedHashEntry {
|
export interface ParsedHashEntry {
|
||||||
fileName: string;
|
fileName: string;
|
||||||
algorithm: "crc32" | "md5" | "sha1";
|
algorithm: "crc32" | "md5" | "sha1";
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user