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.
27 lines
1.1 KiB
Python
27 lines
1.1 KiB
Python
"""add download_jobs.error_code — a category for a failed download (S2)
|
|
|
|
The worker already stores the raw yt-dlp/ffmpeg failure text in `download_jobs.error`. This adds a
|
|
small category code (`unavailable` / `login_required` / `geo_blocked` / `format_unavailable` /
|
|
`drm` / `postprocess` / `source_missing` / `network` / `unknown`) computed by
|
|
`app.downloads.errors.classify`, so the UI can show a clean localized explanation and decide whether
|
|
a retry makes sense, keeping the raw text as a technical-detail disclosure. Nullable; only set on the
|
|
error path. Forward-only — an existing errored row simply has NULL until its next failure/retry.
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "0061_dl_job_error_code"
|
|
down_revision: Union[str, None] = "0060_plex_jsonb_null_normalize"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column("download_jobs", sa.Column("error_code", sa.String(length=24), nullable=True))
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_column("download_jobs", "error_code")
|