Failed downloads stored the raw yt-dlp/ffmpeg text and dumped it as a truncated red one-liner in the queue. Classify the failure into a category (unavailable / login_required / geo_blocked / format_unavailable / drm / postprocess / source_missing / network / unknown) and surface it properly: - backend: app/downloads/errors.classify maps the raw message to a category code; the worker stores it in the new download_jobs.error_code (migration 0061) at both failure sites; resume clears it; the serializer exposes it. Raw text kept as technical detail. - frontend: the queue row shows a clean localized reason + a "details" link opening a modal with the explanation, the raw text behind a disclosure, and a Retry only for transient categories (network/postprocess/unknown). - tests: classify() unit coverage over verbatim yt-dlp/ffmpeg message shapes.
255 lines
8.4 KiB
JSON
255 lines
8.4 KiB
JSON
{
|
||
"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",
|
||
"paste": "Paste link from clipboard",
|
||
"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"
|
||
},
|
||
"phase": {
|
||
"video": "Downloading video",
|
||
"audio": "Downloading audio",
|
||
"merging": "Merging",
|
||
"audio_extract": "Extracting audio",
|
||
"thumbnail": "Embedding thumbnail",
|
||
"sponsorblock": "Removing sponsors",
|
||
"metadata": "Writing metadata",
|
||
"processing": "Processing",
|
||
"editing": "Editing"
|
||
},
|
||
"actions": {
|
||
"pause": "Pause",
|
||
"resume": "Resume",
|
||
"cancel": "Cancel",
|
||
"delete": "Remove",
|
||
"retry": "Retry",
|
||
"download": "Save to my device",
|
||
"rename": "Rename",
|
||
"editDetails": "Edit details",
|
||
"share": "Share",
|
||
"edit": "Edit / trim",
|
||
"redownload": "Re-download (rebuild file)",
|
||
"removeShared": "Remove from my list"
|
||
},
|
||
"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"
|
||
},
|
||
"source": {
|
||
"open": "Open the original page in a new tab",
|
||
"copy": "Copy source link",
|
||
"copied": "Source link copied to clipboard",
|
||
"copyFailed": "Couldn't copy the link"
|
||
},
|
||
"edit": {
|
||
"title": "Edit details",
|
||
"name": "Title",
|
||
"channel": "Channel",
|
||
"channelUrl": "Channel link",
|
||
"extraLinks": "Extra links",
|
||
"addLink": "Add link",
|
||
"removeLink": "Remove link",
|
||
"hint": "Display only — the stored file keeps its own name. The channel and links also show on the card and on shared watch pages.",
|
||
"save": "Save"
|
||
},
|
||
"share": {
|
||
"title": "Share download",
|
||
"label": "Recipient email",
|
||
"placeholder": "user@example.com",
|
||
"submit": "Share",
|
||
"done": "Shared with {{email}}",
|
||
"failed": "Couldn’t share.",
|
||
"userSection": "Share with a user",
|
||
"userHint": "They’ll find it under “Shared with me”.",
|
||
"pickPlaceholder": "Search a user…",
|
||
"noUsers": "No other users yet.",
|
||
"linkSection": "Share a link",
|
||
"linkHint": "Anyone with the link can watch — no account needed.",
|
||
"noLinks": "No links yet.",
|
||
"newLink": "Create a link",
|
||
"createLink": "Create link",
|
||
"allowDownload": "Allow download",
|
||
"expiry": "Expires",
|
||
"expiryNever": "Never",
|
||
"days": "{{n}} days",
|
||
"password": "Password (optional)",
|
||
"copy": "Copy link",
|
||
"copyFailed": "Couldn’t copy.",
|
||
"revoke": "Revoke",
|
||
"revokeConfirm": "This link will stop working immediately.",
|
||
"views": "{{n}} views",
|
||
"expiresOn": "expires {{date}}",
|
||
"hasPassword": "password",
|
||
"downloadable": "downloadable",
|
||
"setPassword": "Set a password",
|
||
"changePassword": "Change password",
|
||
"newPassword": "New password",
|
||
"savePassword": "Save",
|
||
"removePassword": "Remove",
|
||
"removePasswordConfirm": "This makes the link public — anyone with the URL can watch it without a password."
|
||
},
|
||
"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.",
|
||
"removeSharedTitle": "Remove from your list?",
|
||
"removeSharedBody": "This removes it from your shared list only — it won't delete the owner's file.",
|
||
"redownloadTitle": "Re-download this file?",
|
||
"redownloadBody": "The current file is deleted and downloaded again under the latest format and naming rules. Any share links you've sent keep working and will point to the new file (a failed re-download leaves the link broken)."
|
||
},
|
||
"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",
|
||
"quotaPickPlaceholder": "Set a user's quota…",
|
||
"noFootprint": "No downloads yet."
|
||
},
|
||
"cols": {
|
||
"title": "Title",
|
||
"channel": "Channel",
|
||
"format": "Format",
|
||
"size": "Size",
|
||
"status": "Status",
|
||
"added": "Added",
|
||
"user": "User"
|
||
},
|
||
"clipBadge": "Clip",
|
||
"errors": {
|
||
"quota": {
|
||
"max_bytes": "You've used your download storage quota ({{size}} GB). Free up space first.",
|
||
"max_jobs": "You've reached your download limit ({{limit}} items). Remove some first.",
|
||
"generic": "Download quota exceeded."
|
||
},
|
||
"edit": {
|
||
"empty_edit": "Choose a trim range or crop area first.",
|
||
"source_not_ready": "The source download isn't ready to edit.",
|
||
"generic": "That edit couldn't be created."
|
||
},
|
||
"fail": {
|
||
"title": "Download failed",
|
||
"detailsLink": "details",
|
||
"detailsToggle": "Technical details",
|
||
"cat": {
|
||
"unavailable": {
|
||
"short": "Video unavailable",
|
||
"body": "This video is private, deleted, or otherwise no longer available at the source, so it can't be downloaded."
|
||
},
|
||
"login_required": {
|
||
"short": "Sign-in required",
|
||
"body": "This video needs a signed-in, age-verified, or members-only account to view, so it can't be downloaded here."
|
||
},
|
||
"geo_blocked": {
|
||
"short": "Region-blocked",
|
||
"body": "The source blocks this video in the server's region, so it can't be downloaded."
|
||
},
|
||
"format_unavailable": {
|
||
"short": "No downloadable format",
|
||
"body": "No downloadable video format was offered for this link — it may be images-only or an unsupported page."
|
||
},
|
||
"drm": {
|
||
"short": "DRM-protected",
|
||
"body": "This video is DRM-protected and cannot be downloaded."
|
||
},
|
||
"postprocess": {
|
||
"short": "Processing failed",
|
||
"body": "The video downloaded but a processing step (merge or convert) failed. A retry often fixes this."
|
||
},
|
||
"source_missing": {
|
||
"short": "Source unavailable",
|
||
"body": "The original download this clip is based on is no longer available."
|
||
},
|
||
"network": {
|
||
"short": "Network error",
|
||
"body": "A network error interrupted the download. A retry usually fixes this."
|
||
},
|
||
"unknown": {
|
||
"short": "Download failed",
|
||
"body": "Something went wrong while downloading this item. The technical details below may help."
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|