- Files toolbar link box (paste button, Enter); a lone http(s) link pasted onto any page opens the one app-wide download dialog, which now names the link; the palette's "Download from a link…" focuses the box on its history entry. - The download tray, bottom right on every page: what waits, runs, is paused or failed (reason + Retry, the queue's right-click / "…" menu), and what finished in this tab as "Done · Open" until closed; folds to a pill (per-account pref); closed, it stays shut — across a reload — until the work changes or something finishes. Back to top steps up over it, the page end keeps clear. - A finish is announced once: "Downloaded: …", whether a phone plays it, Copy link + Open (toasts and notices may carry a second action). - The inbox's ready notice opens the file in its folder: download_done carries job_id + folder_id. - GET /api/downloads is the queue only (in flight, failed, finished in the last 10 minutes), with each row's source key.
60 lines
2.9 KiB
Python
60 lines
2.9 KiB
Python
"""Canonical download-job status values — the ONE source of truth for the lifecycle literals.
|
|
|
|
A job's status is written from several places: the worker (claim → running, terminal done/error,
|
|
requeue), the routes (pause/cancel/redownload/delete), the service (enqueue), and the GC. Keeping the
|
|
literals here is a data-safety measure, not tidiness. A British/American drift once silently broke
|
|
cancellation: the supervisor compared the live status against ``"cancelled"`` while the cancel route
|
|
wrote ``"canceled"`` (models.py), so the supervisor's "a route stopped it → kill the child, free the
|
|
asset" branch was DEAD. A canceled job's child then ran to completion and wrote ``"done"``
|
|
unconditionally over the route's ``"canceled"``; because ``done`` counts as a holding state but
|
|
``canceled`` does not (see ``service.release_asset``), a later delete released the asset a SECOND time,
|
|
dropping ``ref_count`` below the live holder count and deleting a file still in use. Import these names
|
|
instead of typing the strings.
|
|
"""
|
|
|
|
from enum import StrEnum
|
|
|
|
|
|
class JobStatus(StrEnum):
|
|
QUEUED = "queued"
|
|
RUNNING = "running"
|
|
PAUSED = "paused"
|
|
DONE = "done"
|
|
ERROR = "error"
|
|
CANCELED = "canceled"
|
|
|
|
|
|
# A job "holds" its media asset (the +1 on ``ref_count`` from enqueue) while in any of these states;
|
|
# ``release_asset`` on a status OUTSIDE this set is a no-op. ``CANCELED`` is deliberately excluded: the
|
|
# cancel route releases once while the job is still holding, then sets ``CANCELED``, so a later delete
|
|
# does not double-release. This invariant only holds while a finishing child cannot flip ``CANCELED``
|
|
# back into the set — hence the worker's terminal writes are conditional on the row still being RUNNING.
|
|
HOLDING = frozenset(
|
|
{JobStatus.QUEUED, JobStatus.RUNNING, JobStatus.PAUSED, JobStatus.DONE, JobStatus.ERROR}
|
|
)
|
|
|
|
|
|
class JobKind(StrEnum):
|
|
"""What a job produces, which decides the worker path that runs it and what the library may do
|
|
with the result (only a DOWNLOAD has a web source to fetch again)."""
|
|
|
|
DOWNLOAD = "download" # yt-dlp fetches a source into a shared, deduplicated asset
|
|
EDIT = "edit" # a per-user trim/crop of a file already in the library
|
|
UPLOAD = "upload" # a file the user sent; the worker only converts it
|
|
CLIP = (
|
|
"clip" # a per-user range of a YouTube video or a Plex item, cut straight from the source
|
|
)
|
|
|
|
|
|
# A job kind with no web source to fetch again: Re-download refuses these.
|
|
NOT_REFETCHABLE = frozenset({JobKind.EDIT, JobKind.UPLOAD, JobKind.CLIP})
|
|
|
|
|
|
# States a route sets to STOP a running job. The worker's supervisor must step in (kill the child, free
|
|
# the asset, leave the route's status intact) when it observes one of these on a job it is running.
|
|
STOPPED_BY_ROUTE = frozenset({JobStatus.PAUSED, JobStatus.CANCELED})
|
|
|
|
|
|
# A job that will not move again on its own: the queue lists it only briefly after it got here.
|
|
FINISHED = frozenset({JobStatus.DONE, JobStatus.CANCELED})
|