Video¶
Django Cast provides video file management with poster frame extraction, dimension detection, and integration with Wagtail’s StreamField editor. The built-in upload workflows generate posters automatically.
Video Model¶
Videos are represented by the Video model. Each video has the following
fields:
user– The user who uploaded the video (foreign key).title– A short title for the video (up to 255 characters).original– The uploaded video file (stored undercast_videos/).poster– An auto-generated or manually uploaded poster image (stored undercast_videos/poster/). May be blank.poster_seconds– The timestamp (in seconds) from which the poster frame is extracted. Defaults to1.tags– Tags for organising and searching videos.
The model also inherits Wagtail CollectionMember (so videos belong to
collections) and TimeStampedModel (providing created and modified
timestamps).
Poster Generation¶
The built-in admin and API upload workflows check whether a poster image
already exists and, when needed, extract a single frame to use as the poster.
Programmatic callers opt into the same workflow with
save_video_with_derivations().
Requirements¶
Both FFmpeg and FFprobe must be installed and available on the
system PATH:
# Ubuntu / Debian
sudo apt-get install ffmpeg
# macOS
brew install ffmpeg
How It Works¶
cast.media_derivation.save_video_with_derivations(video)persists the video and callscreate_poster(). A plainVideo.save()only persists fields.If a poster already exists or the class attribute
calc_posterisFalse, the step is skipped.FFprobeis used to detect the original video dimensions (width and height), including portrait orientation handling.FFmpegextracts a single frame at the timestamp given byposter_seconds(default 1 second) and writes it to a temporary JPEG file.The temporary file is saved to the
posterfield and then cleaned up.
If FFmpeg or FFprobe is not installed (or the command fails for any other reason), the error is logged and the video is saved without a poster. Poster generation never raises an exception to the caller.
You can durably regenerate the poster for a video by clearing the existing
poster and saving through save_video_with_derivations():
from cast.media_derivation import save_video_with_derivations
video.poster = None
video.poster_seconds = 5.0 # extract at 5 seconds instead
save_video_with_derivations(video)
To create posters for all videos that are currently missing one, use the management command:
python manage.py recalc_video_posters
Note
This command only fills in missing posters. If a poster already
exists it is kept. To force regeneration for a specific video, clear
its poster first, persist that change, and then re-run the command or call
save_video_with_derivations(video).
Dimension Detection¶
Video dimensions are detected via FFprobe. The parser understands
H.264, HEVC, and SAR-based dimension lines and automatically swaps width
and height for portrait videos (detected by rotation metadata or a 9:16
display aspect ratio).
MIME Types¶
The get_mime_type() method returns a MIME type based on the file
extension:
.mp4–video/mp4.mov–video/quicktime.avi–video/x-msvideo
All other extensions default to video/mp4.
Using Videos in StreamField¶
Videos are added to posts via the VideoChooserBlock in Wagtail’s
StreamField editor. The block uses a modal chooser that lets editors search
and select existing videos or upload new ones.
In the post model the block is declared as part of the body StreamField:
from cast.blocks import VideoChooserBlock
body = StreamField([
# ...
("video", VideoChooserBlock()),
# ...
])
The VideoChooserBlock integrates with the repository pattern so that
video data is prefetched efficiently when rendering pages.