diff --git a/.claude/memory-bank.md b/.claude/memory-bank.md new file mode 100644 index 0000000..8720488 --- /dev/null +++ b/.claude/memory-bank.md @@ -0,0 +1,126 @@ +# Memory Bank - Multi Debrid Downloader + +## Projekt-Überblick + +**Name:** Multi Debrid Downloader (MDD) +**Typ:** Electron Desktop App für Windows 10/11 +**Repository:** +- Codeberg: https://codeberg.org/Sucukdeluxe/real-debrid-downloader.git +- GitHub: https://github.com/Sucukdeluxe/real-debrid-downloader.git + +## Technologie-Stack + +- **Runtime:** Electron 31.x +- **Frontend:** React 18.x + TypeScript 5.x +- **Build:** Vite (Renderer) + tsup (Main/Preload) +- **Tests:** Vitest (262+ Tests) +- **Installer:** NSIS via electron-builder + +## Unterstützte Debrid-Provider + +| Provider | Auth | Priorität | +|----------|------|-----------| +| Real-Debrid | API Token | Primär | +| Mega-Debrid | Login + Passwort | Fallback 1 | +| BestDebrid | API Token | Fallback 2 | +| AllDebrid | API Key | Fallback 3 | + +## Kernfeatures + +- **Queue-Management:** Package-basierte Organisation mit Drag & Drop +- **Auto-Extract:** RAR, ZIP, 7z mit Passwortliste +- **Auto-Rename:** Scene-Release Muster (4sf/4sj) → saubere Namen +- **Integritätsprüfung:** CRC32, MD5, SHA1 via SFV-Dateien +- **Provider-Fallback:** Automatischer Wechsel bei Fehlern/Fair-Use +- **Session-Persistenz:** Queue überlebt App-Neustart +- **Clipboard-Watcher:** Automatische Link-Erkennung +- **System-Tray:** Minimize to Tray +- **Speed-Limit:** Global oder per Download + Bandwidth-Schedules +- **MKV-Sammelordner:** Automatisches Verschieben nach Paketabschluss +- **Update-System:** Automatische Updates via Codeberg Releases + +## Projektstruktur + +``` +src/ +├── main/ # Electron Main Process +│ ├── main.ts # Entry Point, IPC Handler, Window Management +│ ├── app-controller.ts # Koordiniert DownloadManager + Settings +│ ├── download-manager.ts # Core: Queue, Downloads, Retry-Logic +│ ├── debrid.ts # Debrid-Service Abstraktion +│ ├── realdebrid.ts # Real-Debrid API Client +│ ├── extractor.ts # Archiv-Entpackung +│ ├── integrity.ts # CRC32/Hash-Validierung +│ ├── storage.ts # Session/Settings Persistenz +│ ├── update.ts # Update-Check & Installation +│ └── ... +├── renderer/ # React UI +│ ├── App.tsx # Hauptkomponente mit allen Tabs +│ └── styles.css # Styling +├── preload/ # Preload Script (IPC Bridge) +│ └── preload.ts +└── shared/ # Geteilte Types + ├── types.ts # Alle TypeScript Interfaces + ├── ipc.ts # IPC Channel Konstanten + └── preload-api.ts # window.rd API Definition +``` + +## Wichtige Types (src/shared/types.ts) + +- `DownloadItem`: Einzelner Download mit Status, Progress, Speed +- `PackageEntry`: Gruppe von Downloads mit OutputDir, ExtractDir +- `SessionState`: Gesamter Queue-Zustand (persistiert) +- `AppSettings`: Alle Einstellungen +- `UiSnapshot`: Kompletter UI-State für Renderer + +## IPC Channels (src/shared/ipc.ts) + +Hauptchannels für Renderer ↔ Main Kommunikation: +- `GET_SNAPSHOT`, `STATE_UPDATE`: State-Sync +- `ADD_LINKS`, `ADD_CONTAINERS`: Queue befüllen +- `START`, `STOP`, `TOGGLE_PAUSE`: Download-Kontrolle +- `UPDATE_SETTINGS`: Einstellungen ändern + +## Aktuelle Version + +**Version:** 1.5.27 +**Letztes Release:** 1.4.68 (2026-03-01) + +### Letzte Änderungen (CHANGELOG) +- Session-Backup für Queue-Zustand +- Start-Konflikt-Behandlung verbessert +- Mega-Web Unrestrict abort-fähig +- DLC-Import gehärtet +- Auto-Renamer erweitert + +## Offene Pläne + +1. **Native Menüleiste** (`.claude/plans/agile-watching-lampson.md`) + - JDownloader 2 Style Menü + - Electron Menu API nutzen + - Bestehende React Menu-Bar ersetzen + +## Coding-Conventions + +- TypeScript strict mode +- Async/Await über Promises +- Deutsche UI-Texte +- Ausführliche Error-Logs via `logger` +- Retry-Logic mit exponential backoff +- AbortController für abbrechbare Operationen + +## Build & Release + +```bash +npm run build # TypeScript + Vite Build +npm run dist # electron-builder (NSIS + Portable) +npm test # Vitest Tests +npm run self-check # Vollständiger Check (Typecheck + Tests) +``` + +## Wichtige Dateien + +- `CHANGELOG.md` - Detaillierte Versionshistorie +- `.claude/plans/` - Feature-Pläne +- `tests/` - Umfangreiche Test-Suite +- `installer/RealDebridDownloader.iss` - Inno Setup Script \ No newline at end of file diff --git a/.claude/plans/agile-watching-lampson.md b/.claude/plans/agile-watching-lampson.md new file mode 100644 index 0000000..e46a889 --- /dev/null +++ b/.claude/plans/agile-watching-lampson.md @@ -0,0 +1,66 @@ +# Native Menüleiste (JDownloader 2 Style) + +## Context +Die App hat aktuell keine native Menüleiste (nur ein Tray-Kontextmenü). Der User möchte eine Menüleiste oben links wie bei JDownloader 2 mit Datei-Menü, Shortcuts und Sicherungs-Funktion. + +## Features + +### "Datei"-Menü (oben links) +| Menüpunkt | Shortcut | Aktion | +|-----------|----------|--------| +| Text mit Links analysieren | Ctrl+L | Wechselt zum Linksammler-Tab | +| Linkcontainer laden | Ctrl+O | Öffnet DLC-Dateiauswahl (existiert bereits) | +| --- Separator --- | | | +| Sicherung → Backup erstellen | | Exportiert Queue als JSON (existiert: `exportQueue`) | +| Sicherung → Backup laden | | Importiert Queue-JSON (existiert: `importQueue`) | +| --- Separator --- | | | +| Neustart | Ctrl+Shift+R | `app.relaunch()` + `app.quit()` | +| Beenden | Ctrl+Q | `app.quit()` | + +## Implementation + +### Step 1: Neue IPC-Channels +**Datei:** `src/shared/ipc.ts` +- `NAVIGATE_TAB: "app:navigate-tab"` — Renderer wechselt Tab +- `RESTART: "app:restart"` — App neustarten +- `SAVE_BACKUP: "dialog:save-backup"` — Save-Dialog + Export +- `LOAD_BACKUP: "dialog:load-backup"` — Open-Dialog + Import + +### Step 2: Preload-API erweitern +**Datei:** `src/shared/preload-api.ts` + `src/preload/preload.ts` +- `onNavigateTab(callback)` — Event-Listener für Tab-Wechsel +- `saveBackup()` — Backup über nativen Save-Dialog speichern +- `loadBackup()` — Backup über nativen Open-Dialog laden + +### Step 3: Menüleiste erstellen +**Datei:** `src/main/main.ts` + +Neue Funktion `createApplicationMenu()` nach `createTray()`: +- Nutzt `Menu.buildFromTemplate()` + `Menu.setApplicationMenu()` +- "Datei"-Menü mit allen Punkten aus der Tabelle +- Accelerators für Shortcuts (Electron handelt die automatisch) +- Menü-Clicks senden IPC-Events an den Renderer oder rufen direkt Main-Process-Funktionen auf + +**Backup erstellen:** `dialog.showSaveDialog()` → `controller.exportQueue()` → `fs.writeFile()` +**Backup laden:** `dialog.showOpenDialog()` → `fs.readFile()` → `controller.importQueue()` +**Neustart:** `app.relaunch()` → `app.quit()` +**Beenden:** `app.quit()` +**Linksammler/DLC:** IPC-Event an Renderer senden + +### Step 4: Renderer reagiert auf Menü-Events +**Datei:** `src/renderer/App.tsx` +- `onNavigateTab` Listener registrieren im `useEffect` +- Bei `"collector"` → `setTab("collector")` +- DLC-Import: `pickContainers` + `addContainers` (bestehendes Pattern) + +## Dateien +- `src/shared/ipc.ts` — Neue Channels +- `src/shared/preload-api.ts` — Neue API-Methoden +- `src/preload/preload.ts` — IPC-Bridge +- `src/main/main.ts` — Menüleiste + IPC-Handler + Backup-Logik +- `src/renderer/App.tsx` — Tab-Navigation Listener + +## Verification +1. `npm run build` +2. `npx vitest run` (schnelle Tests) +3. Manuell: App starten, Datei-Menü prüfen, Shortcuts testen diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..b82dbf1 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,6 @@ +{ + "enabledPlugins": { + "frontend-design@claude-plugins-official": true, + "code-review@claude-plugins-official": true + } +} diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..e56d14e --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "WebFetch(domain:github.com)" + ] + } +} diff --git a/.claude/worktrees/nifty-vaughan b/.claude/worktrees/nifty-vaughan new file mode 160000 index 0000000..f1e132b --- /dev/null +++ b/.claude/worktrees/nifty-vaughan @@ -0,0 +1 @@ +Subproject commit f1e132b2ed4717667fd7318ecab22e5ef52da0cc diff --git a/CHANGELOG.md b/CHANGELOG.md index dac185f..0c7c3e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ Alle nennenswerten Aenderungen werden in dieser Datei dokumentiert. +## 1.5.28 - 2026-03-02 + +UI-Verbesserung: Visuelle Fortschrittsanzeigen in der Download-Liste (JDownloader 2 Style). + +### Features + +- **Visuelle Fortschrittsbalken:** + - Paket-Fortschritt wird jetzt als grafische Progress-Bar in der Spalte "Fortschritt" angezeigt. + - Einzelne Items haben ebenfalls eine kleinere Progress-Bar. + - Grüner Gradient (#22c55e → #4ade80) für bessere Text-Lesbarkeit. + - Prozentanzeige als Overlay-Text auf der Bar. + ## 1.4.68 - 2026-03-01 Stabilitaets-Hotfix fuer Session-Verlust nach Update/Neustart: Session-Dateien haben jetzt ein robustes Backup-/Restore-Fallback. diff --git a/_upload_release.mjs b/_upload_release.mjs new file mode 100644 index 0000000..5fd2c28 --- /dev/null +++ b/_upload_release.mjs @@ -0,0 +1,75 @@ +import fs from "node:fs"; +import path from "node:path"; +import { spawnSync } from "node:child_process"; + +const credResult = spawnSync("git", ["credential", "fill"], { + input: "protocol=https\nhost=codeberg.org\n\n", + encoding: "utf8", + stdio: ["pipe", "pipe", "pipe"] +}); +const creds = new Map(); +for (const line of credResult.stdout.split(/\r?\n/)) { + if (line.includes("=")) { + const [k, v] = line.split("=", 2); + creds.set(k, v); + } +} +const auth = "Basic " + Buffer.from(creds.get("username") + ":" + creds.get("password")).toString("base64"); +const owner = "Sucukdeluxe"; +const repo = "real-debrid-downloader"; +const tag = "v1.5.27"; +const baseApi = `https://codeberg.org/api/v1/repos/${owner}/${repo}`; + +async function main() { + await fetch(baseApi, { + method: "PATCH", + headers: { Authorization: auth, "Content-Type": "application/json" }, + body: JSON.stringify({ has_releases: true }) + }); + + const createRes = await fetch(`${baseApi}/releases`, { + method: "POST", + headers: { Authorization: auth, "Content-Type": "application/json", Accept: "application/json" }, + body: JSON.stringify({ + tag_name: tag, + target_commitish: "main", + name: tag, + body: "- Increase column spacing for Fortschritt/Größe/Geladen", + draft: false, + prerelease: false + }) + }); + const release = await createRes.json(); + if (!createRes.ok) { + console.error("Create failed:", JSON.stringify(release)); + process.exit(1); + } + console.log("Release created:", release.id); + + const files = [ + "Real-Debrid-Downloader Setup 1.5.27.exe", + "Real-Debrid-Downloader 1.5.27.exe", + "latest.yml", + "Real-Debrid-Downloader Setup 1.5.27.exe.blockmap" + ]; + for (const f of files) { + const filePath = path.join("release", f); + const data = fs.readFileSync(filePath); + const uploadUrl = `${baseApi}/releases/${release.id}/assets?name=${encodeURIComponent(f)}`; + const res = await fetch(uploadUrl, { + method: "POST", + headers: { Authorization: auth, "Content-Type": "application/octet-stream" }, + body: data + }); + if (res.ok) { + console.log("Uploaded:", f); + } else if (res.status === 409 || res.status === 422) { + console.log("Skipped existing:", f); + } else { + console.error("Upload failed for", f, ":", res.status); + } + } + console.log(`Done! https://codeberg.org/${owner}/${repo}/releases/tag/${tag}`); +} + +main().catch(e => { console.error(e.message); process.exit(1); }); diff --git a/package.json b/package.json index fbd0b49..c354e4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "real-debrid-downloader", - "version": "1.5.27", + "version": "1.5.28", "description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)", "main": "build/main/main/main.js", "author": "Sucukdeluxe", diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index f1d835a..67a861c 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -2718,7 +2718,12 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, isFirs