Repository navigation
fix: play the new crop straight after cropping or re-cropping - #757
Merged
Merged
Conversation
Cropping a video that had just been played could fail with a format error until the page was reloaded. nginx's open_file_cache had remembered that the crop did not exist yet, since /_content/video/ looks for it on every play, and kept answering 404 for up to two minutes after it was made. The same cache kept serving a deleted crop after a re-crop. The cache is gone. A re-crop could also keep playing the previous crop until a force refresh. The crop and the transcodes keep their names when a video is re-cropped, but nginx told the browser to keep them for a year. They are now revalidated instead, as is /_content/video/, whose content depends on whether a crop exists. The player and the card hover preview also add the crop bounds to their URLs, because Chromium reuses media it already has in memory for an unchanged URL no matter what the headers say. Crops and posters are now written to a temp file and renamed into place, as transcodes already are, so nothing serves one that is half written. A failed poster attempt leaves the old poster in place.
This was referenced Sep 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #755
Fixes #756
Problem
#755: format error right after cropping.
/_content/video/looks for<id>-cropped.mp4withtry_fileson every play of an uncropped video. Withopen_file_cache_errors on, nginx remembered "no such file" for up to 120 s (open_file_cache_valid). The player asked for the crop as soon as it was made, got a 404, and showedMEDIA_ELEMENT_ERROR: Format error. A freshly uploaded video has usually just been played, which is why it showed up mostly then. Thesendfile() ... truncatedalert onposter.jpgin the report comes from the same cache: ffmpeg rewrites the poster in place while nginx still holds its old size.#756: re-crop plays the old crop. The crop and the transcodes keep their names when a video is re-cropped, but
/_content/derived/and/_content/video/sentCache-Control: public, max-age=31536000, immutable, so the browser kept the old files. nginx's file cache made it worse: after a re-crop it could keep serving the deleted crop from a cached descriptor.Change
prod.conf)open_file_cacheis removed, with a comment explaining why it stays off./_content/derived/<id>/*.mp4(the crop and the transcodes) sendsno-cache, with the same auth gate as before. Other derived files (images, their thumbnails) keep the one-year immutable header./_content/video/sendsno-cache: it serves the crop when there is one and the original when there is not, so its content changes with the crop.og:videoembeds use this URL too.utils.js,CompactVideoCard.js)getMediaVersion(info)returns<start>-<end>for a cropped video and nothing otherwise.?v=to the crop and transcode URLs. The headers alone are not enough: Chromium reuses media it already has in memory for an unchanged URL, which fits the report that only a new tab helped.util.py)create_video_cropandcreate_posterwrite to a.tmpfile and rename it into place, astranscode_video_qualityalready does.try_filespicks up the crop as soon as the name exists, and+faststartrewrites the whole file in a second pass, so a half-written crop could previously reach viewers.Trade-offs
/_content/video/, now check with nginx each time. When nothing changed that is a small304 Not Modified. Password-protected videos also go through the auth gate on each of these checks now.-cropped.tmp.mp4, matches the**/*.tmp.mp4sweep that runs when a bulk transcode starts. If a bulk transcode starts during the second or so a crop takes, that crop fails and has to be saved again. The same was already true of transcodes.Testing
prod.confinnginx:stable-alpinewith a stand-in for the Flask login check, and replayed play → play → crop → play → re-crop → play.nginx -tpasses on both configs./_content/video/still serving the deleted crop after the re-crop.create_video_cropandcreate_posteragainst real ffmpeg..tmpname exists while it is written.getVideoSourcesbuilds for uncropped, cropped, re-cropped, mid-crop and editor states.vite buildpasses.