feat(downloads): M5 — Download Center frontend

- api.ts: download types + methods (profiles, enqueue, list/manage, file url, share,
  usage, index, admin storage/quota)
- format.ts: formatBytes / formatSpeed
- i18n: en/hu/de downloads.json (trilingual)
- Downloads nav module (Download icon, active-count badge, hidden for demo) placed after
  Messages; urlState page + App render + Header title
- DownloadButton: self-contained per-card/player affordance (demo-hidden, reflects the
  per-user download index: downloaded / in-queue), opens the preset dialog
- DownloadDialog: preset picker + optional display name -> enqueue + 'View' toast
- ProfileEditor: manage built-in + custom formats (full spec form incl. SponsorBlock)
- DownloadCenter: Queue / Library / Shared / (admin) System tabs, live progress via
  useLiveQuery, usage bar, pause/resume/cancel/delete, save-to-device, rename, share,
  admin storage dashboard + per-user quota editor
- wired DownloadButton into VideoCard + PlayerModal
- lib/nav.ts: decoupled navigator so the download toast can jump to the page

tsc + vite build clean. Verified in a headless authed browser: page + tabs render, Library
shows a real download with usage bar + actions, no console errors.
This commit is contained in:
npeter83 2026-07-03 01:52:58 +02:00
parent 51158ad9cf
commit 7d1ed24fea
17 changed files with 1551 additions and 1 deletions

View file

@ -0,0 +1,136 @@
{
"navLabel": "Downloads",
"button": {
"label": "Herunterladen",
"queued": "In Warteschlange",
"downloaded": "Heruntergeladen"
},
"dialog": {
"title": "Video herunterladen",
"profile": "Format",
"nameLabel": "Dateiname (optional)",
"namePlaceholder": "Leer lassen, um den Videotitel zu verwenden",
"submit": "Zu Downloads hinzufügen",
"added": "Zu Downloads hinzugefügt",
"view": "Öffnen",
"manage": "Formate verwalten"
},
"page": {
"title": "Downloads",
"subtitle": "Videos auf den Server laden und dann auf dein Gerät speichern.",
"addUrl": "YouTube-Link oder Video-ID einfügen…",
"add": "Hinzufügen",
"manage": "Formate verwalten"
},
"tabs": {
"queue": "Warteschlange",
"done": "Bibliothek",
"shared": "Mit mir geteilt",
"system": "System"
},
"status": {
"queued": "Wartet",
"running": "Lädt",
"paused": "Pausiert",
"done": "Fertig",
"error": "Fehlgeschlagen",
"canceled": "Abgebrochen",
"expired": "Abgelaufen"
},
"actions": {
"pause": "Pause",
"resume": "Fortsetzen",
"cancel": "Abbrechen",
"delete": "Entfernen",
"retry": "Erneut",
"download": "Auf mein Gerät speichern",
"rename": "Umbenennen",
"share": "Teilen"
},
"empty": {
"queue": "Die Warteschlange ist leer.",
"done": "Noch keine Downloads.",
"shared": "Es wurde nichts mit dir geteilt.",
"admin": "Auf dieser Instanz gibt es noch keine Downloads."
},
"usage": {
"title": "Dein Speicher",
"items": "{{used}} / {{max}} Elemente",
"unlimited": "Unbegrenzt"
},
"rename": {
"title": "Download umbenennen",
"label": "Anzeigename",
"hint": "Ändert nur den angezeigten und beim Speichern verwendeten Namen — die gespeicherte Datei behält ihren Namen.",
"save": "Speichern"
},
"share": {
"title": "Download teilen",
"label": "E-Mail des Empfängers",
"placeholder": "user@example.com",
"submit": "Teilen",
"done": "Geteilt mit {{email}}"
},
"confirm": {
"cancelTitle": "Download abbrechen?",
"cancelBody": "Dies stoppt den Download und entfernt ihn aus deiner Warteschlange.",
"deleteTitle": "Download entfernen?",
"deleteBody": "Dies entfernt ihn aus deiner Liste. Die Datei kann bis zum Ablauf für andere Nutzer im Cache bleiben."
},
"profiles": {
"manage": "Formate verwalten",
"title": "Download-Formate",
"builtin": "Integriert",
"yours": "Deine Formate",
"new": "Neues Format",
"name": "Name",
"namePlaceholder": "Mein Format",
"mode": "Inhalt",
"modeAv": "Video + Audio",
"modeV": "Nur Video",
"modeA": "Nur Audio",
"quality": "Max. Qualität",
"best": "Beste",
"container": "Container",
"audioFormat": "Audioformat",
"vcodec": "Video-Codec",
"any": "Beliebig",
"embedSubs": "Untertitel einbetten",
"embedChapters": "Kapitel einbetten",
"embedThumbnail": "Vorschaubild einbetten",
"sponsorblock": "Sponsor-Segmente überspringen (SponsorBlock)",
"save": "Speichern",
"create": "Erstellen",
"delete": "Löschen",
"deleteConfirm": "Das Format „{{name}}“ löschen?"
},
"admin": {
"storageTitle": "Speicher",
"readyFiles": "Fertige Dateien",
"totalSize": "Gesamtgröße",
"cap": "Cache-Limit",
"noCap": "kein Limit",
"perUser": "Speicher pro Nutzer",
"user": "Nutzer",
"footprint": "Belegung",
"editQuota": "Kontingent bearbeiten",
"quotaTitle": "Download-Kontingent — {{email}}",
"maxBytes": "Speicherlimit (GB)",
"maxJobs": "Max. Downloads",
"maxConcurrent": "Max. gleichzeitig",
"unlimited": "Unbegrenzt (Speicherlimit umgehen)",
"reset": "Auf Standard zurücksetzen",
"save": "Speichern",
"custom": "individuell",
"default": "Standard"
},
"cols": {
"title": "Titel",
"channel": "Kanal",
"format": "Format",
"size": "Größe",
"status": "Status",
"added": "Hinzugefügt",
"user": "Nutzer"
}
}

View file

@ -0,0 +1,136 @@
{
"navLabel": "Downloads",
"button": {
"label": "Download",
"queued": "In queue",
"downloaded": "Downloaded"
},
"dialog": {
"title": "Download video",
"profile": "Format",
"nameLabel": "File name (optional)",
"namePlaceholder": "Leave blank to use the video title",
"submit": "Add to downloads",
"added": "Added to downloads",
"view": "View",
"manage": "Manage formats"
},
"page": {
"title": "Downloads",
"subtitle": "Download videos to the server, then save them to your device.",
"addUrl": "Paste a YouTube link or video id…",
"add": "Add",
"manage": "Manage formats"
},
"tabs": {
"queue": "Queue",
"done": "Library",
"shared": "Shared with me",
"system": "System"
},
"status": {
"queued": "Queued",
"running": "Downloading",
"paused": "Paused",
"done": "Ready",
"error": "Failed",
"canceled": "Canceled",
"expired": "Expired"
},
"actions": {
"pause": "Pause",
"resume": "Resume",
"cancel": "Cancel",
"delete": "Remove",
"retry": "Retry",
"download": "Save to my device",
"rename": "Rename",
"share": "Share"
},
"empty": {
"queue": "Nothing in the queue.",
"done": "No downloads yet.",
"shared": "Nothing has been shared with you.",
"admin": "No downloads on this instance yet."
},
"usage": {
"title": "Your storage",
"items": "{{used}} / {{max}} items",
"unlimited": "Unlimited"
},
"rename": {
"title": "Rename download",
"label": "Display name",
"hint": "Only changes the name you see and download with — the stored file keeps its own name.",
"save": "Save"
},
"share": {
"title": "Share download",
"label": "Recipient email",
"placeholder": "user@example.com",
"submit": "Share",
"done": "Shared with {{email}}"
},
"confirm": {
"cancelTitle": "Cancel download?",
"cancelBody": "This stops the download and removes it from your queue.",
"deleteTitle": "Remove download?",
"deleteBody": "This removes it from your list. The file may stay cached for other users until it expires."
},
"profiles": {
"manage": "Manage formats",
"title": "Download formats",
"builtin": "Built-in",
"yours": "Your formats",
"new": "New format",
"name": "Name",
"namePlaceholder": "My format",
"mode": "Content",
"modeAv": "Video + audio",
"modeV": "Video only",
"modeA": "Audio only",
"quality": "Max quality",
"best": "Best",
"container": "Container",
"audioFormat": "Audio format",
"vcodec": "Video codec",
"any": "Any",
"embedSubs": "Embed subtitles",
"embedChapters": "Embed chapters",
"embedThumbnail": "Embed thumbnail",
"sponsorblock": "Skip sponsor segments (SponsorBlock)",
"save": "Save",
"create": "Create",
"delete": "Delete",
"deleteConfirm": "Delete the format “{{name}}”?"
},
"admin": {
"storageTitle": "Storage",
"readyFiles": "Ready files",
"totalSize": "Total size",
"cap": "Cache cap",
"noCap": "no cap",
"perUser": "Per-user footprint",
"user": "User",
"footprint": "Footprint",
"editQuota": "Edit quota",
"quotaTitle": "Download quota — {{email}}",
"maxBytes": "Storage limit (GB)",
"maxJobs": "Max downloads",
"maxConcurrent": "Max concurrent",
"unlimited": "Unlimited (bypass storage limit)",
"reset": "Reset to default",
"save": "Save",
"custom": "custom",
"default": "default"
},
"cols": {
"title": "Title",
"channel": "Channel",
"format": "Format",
"size": "Size",
"status": "Status",
"added": "Added",
"user": "User"
}
}

View file

@ -0,0 +1,136 @@
{
"navLabel": "Letöltések",
"button": {
"label": "Letöltés",
"queued": "Sorban",
"downloaded": "Letöltve"
},
"dialog": {
"title": "Videó letöltése",
"profile": "Formátum",
"nameLabel": "Fájlnév (opcionális)",
"namePlaceholder": "Üresen hagyva a videó címét használja",
"submit": "Hozzáadás a letöltésekhez",
"added": "Hozzáadva a letöltésekhez",
"view": "Megnyitás",
"manage": "Formátumok kezelése"
},
"page": {
"title": "Letöltések",
"subtitle": "Töltsd le a videókat a szerverre, majd mentsd a saját gépedre.",
"addUrl": "Illessz be egy YouTube linket vagy videó azonosítót…",
"add": "Hozzáad",
"manage": "Formátumok kezelése"
},
"tabs": {
"queue": "Sor",
"done": "Könyvtár",
"shared": "Velem megosztva",
"system": "Rendszer"
},
"status": {
"queued": "Várakozik",
"running": "Letöltés",
"paused": "Szüneteltetve",
"done": "Kész",
"error": "Sikertelen",
"canceled": "Megszakítva",
"expired": "Lejárt"
},
"actions": {
"pause": "Szünet",
"resume": "Folytatás",
"cancel": "Megszakítás",
"delete": "Eltávolítás",
"retry": "Újra",
"download": "Mentés a gépemre",
"rename": "Átnevezés",
"share": "Megosztás"
},
"empty": {
"queue": "A sor üres.",
"done": "Még nincs letöltés.",
"shared": "Még nem osztottak meg veled semmit.",
"admin": "Ezen a példányon még nincs letöltés."
},
"usage": {
"title": "Tárhelyed",
"items": "{{used}} / {{max}} elem",
"unlimited": "Korlátlan"
},
"rename": {
"title": "Letöltés átnevezése",
"label": "Megjelenített név",
"hint": "Csak a megjelenített és letöltéskor használt nevet módosítja — a tárolt fájl neve nem változik.",
"save": "Mentés"
},
"share": {
"title": "Letöltés megosztása",
"label": "Címzett e-mail címe",
"placeholder": "user@example.com",
"submit": "Megosztás",
"done": "Megosztva vele: {{email}}"
},
"confirm": {
"cancelTitle": "Megszakítod a letöltést?",
"cancelBody": "Ez leállítja a letöltést és eltávolítja a sorból.",
"deleteTitle": "Eltávolítod a letöltést?",
"deleteBody": "Ez eltávolítja a listádról. A fájl a lejáratáig még a gyorsítótárban maradhat mások számára."
},
"profiles": {
"manage": "Formátumok kezelése",
"title": "Letöltési formátumok",
"builtin": "Beépített",
"yours": "Saját formátumaid",
"new": "Új formátum",
"name": "Név",
"namePlaceholder": "Saját formátum",
"mode": "Tartalom",
"modeAv": "Videó + hang",
"modeV": "Csak videó",
"modeA": "Csak hang",
"quality": "Max. minőség",
"best": "Legjobb",
"container": "Konténer",
"audioFormat": "Hangformátum",
"vcodec": "Videó codec",
"any": "Bármelyik",
"embedSubs": "Feliratok beágyazása",
"embedChapters": "Fejezetek beágyazása",
"embedThumbnail": "Bélyegkép beágyazása",
"sponsorblock": "Szponzorált szakaszok kihagyása (SponsorBlock)",
"save": "Mentés",
"create": "Létrehozás",
"delete": "Törlés",
"deleteConfirm": "Törlöd a(z) „{{name}}” formátumot?"
},
"admin": {
"storageTitle": "Tárhely",
"readyFiles": "Kész fájlok",
"totalSize": "Teljes méret",
"cap": "Gyorsítótár-korlát",
"noCap": "nincs korlát",
"perUser": "Felhasználónkénti tárhelyhasználat",
"user": "Felhasználó",
"footprint": "Használat",
"editQuota": "Kvóta szerkesztése",
"quotaTitle": "Letöltési kvóta — {{email}}",
"maxBytes": "Tárhelykorlát (GB)",
"maxJobs": "Max. letöltés",
"maxConcurrent": "Max. egyidejű",
"unlimited": "Korlátlan (tárhelykorlát kikapcsolása)",
"reset": "Visszaállítás alapértékre",
"save": "Mentés",
"custom": "egyedi",
"default": "alapértelmezett"
},
"cols": {
"title": "Cím",
"channel": "Csatorna",
"format": "Formátum",
"size": "Méret",
"status": "Állapot",
"added": "Hozzáadva",
"user": "Felhasználó"
}
}