feat(downloads): ASCII-slug names, universal browser playability, force re-download
Mini-epic "Downloads correctness & UX" (S1–S3), on top of the iOS-AV1 + 720p fixes.
S1 — naming & titles:
- ASCII-fold on-disk dir/file names AND the served (Content-Disposition) filename: no
spaces (underscore-joined), no accents (á→a, ő→o, ß→ss), pure ASCII — portable and free
of the `?` mojibake accented names show in non-UTF-8 tools. (Forward-only; existing files
clean up via re-download.)
- Strip a leading social engagement prefix ("1.6M views · 66K reactions | …") from titles.
S2 — universal browser playability (generalizes the iOS re-encode):
- ensure_browser_playable guarantees the stored file is H.264 + AAC/MP3 in mp4 — the only
combo every browser decodes. Re-encode just the offending stream(s) (video for AV1/VP9/HEVC,
audio for Opus/etc), or remux when only the container is wrong. Gated on the mp4 compat
profile (an explicit vcodec / non-mp4 custom profile opts out).
S3 — force re-download (preserves share links):
- POST /downloads/{job_id}/redownload keeps the job row so its public DownloadLink survives
and resolves to the freshly-downloaded file; deletes the old file and re-fetches under the
current rules. A failed re-download leaves the job in error (the intended broken-link case).
- Library "Re-download" action (icon + confirm), api method, hu/en strings.
Tests: naming/title-strip (test_naming), browser_transcode_flags matrix; existing
download_filename expectation updated for the underscore policy.
This commit is contained in:
@@ -1673,6 +1673,10 @@ export const api = {
|
||||
req(`/api/downloads/${id}/resume`, { method: "POST" }, { idempotent: true }),
|
||||
cancelDownload: (id: number): Promise<DownloadJob> =>
|
||||
req(`/api/downloads/${id}/cancel`, { method: "POST" }, { idempotent: true }),
|
||||
// Force a fresh re-download of a finished/failed job — keeps the job (and its share links),
|
||||
// deletes the old file, and rebuilds it under the current format/naming rules.
|
||||
redownloadDownload: (id: number): Promise<DownloadJob> =>
|
||||
req(`/api/downloads/${id}/redownload`, { method: "POST" }, { idempotent: true }),
|
||||
deleteDownload: (id: number) => req(`/api/downloads/${id}`, { method: "DELETE" }),
|
||||
// Update a download's display metadata (title / channel name+link / extra reference URLs).
|
||||
updateDownloadMeta: (
|
||||
|
||||
Reference in New Issue
Block a user