0.2.61 (2026-07-01)¶
Added optional podcast publishing metadata: reusable podcast-scoped seasons, episode numbers, and explicit episode types. Podcast feeds now emit matching iTunes and Podcasting 2.0 item tags when metadata is present while preserving existing UUID-based RSS GUIDs and omitting episode type for blank values.
Added an optional podcast-level Apple Podcasts ordering type. Podcast pages can now set
itunes_typetoepisodicorserial; blank remains the default and omits the channel-levelitunes:typefeed tag.Added opt-in automatic podcast episode numbering per podcast. Blank full episodes can now receive the next podcast-scoped number on first real publish; draft saves and future scheduling approvals do not consume numbers, manual numbers stay authoritative, and RSS GUIDs remain UUID-based.
Added opt-in anonymous comment self-editing and deletion. With
CAST_COMMENTS_ALLOW_AUTHOR_EDITS = Trueand a server-side session backend, an author can edit or delete their own comment from the same browser until someone replies or the session expires. Edits are re-moderated (and flagged(edited)) and deletion is a soft delete that staff can restore from the Django admin. Enabling the feature sets a functional session cookie for previously cookieless commenters. See CAST_COMMENTS_ALLOW_AUTHOR_EDITS.Added a session-authenticated programmatic content editing API (
/api/editor/). Trusted clients can list the blogs and podcasts they may add to (GET /api/editor/parents/), create a draftPostfrom a structuredoverviewblock list (POST /api/editor/posts/), and read the draft back with normalized authoring source (GET /api/editor/posts/{id}/). Drafts can also be revised withPATCH /api/editor/posts/{id}/by sending the returnedlatest_revision_idasbase_revision_id; stale bases return409 revision_conflictinstead of overwriting newer edits. The API stays authentication-mechanism agnostic — it requires an authenticated user and authorizes every action with Wagtail page permissions. Create and update stay draft-only and rejectpublish: true; draft posts can now be published through the explicitPOST /api/editor/posts/{id}/publish/action, which requires Wagtail publish permission and publishes the latest draft revision through Wagtail’s revision path. Body blocks supported in this slice: heading, paragraph, code, image, gallery, audio, and video (media blocks reference media the caller may choose). The editor API now also supports thedetailbody section, editor media list/upload endpoints for images, audio, and video, and upload collection discovery at/api/editor/media/collections/. Audio/video editor uploads share a one-in-flight per-user lock, and editor audio probing is capped by a request-path budget so slow files fail withprobe_timeoutinstead of hanging the request.Extended the editor API to podcast episodes with draft-only
POST /api/editor/episodes/,GET /api/editor/episodes/{id}/, andPATCH /api/editor/episodes/{id}/. Episodes reuse the post body, tags, categories, cover image, and revision/conflict handling, and add the episode-specific fieldspodcast_audio,episode_number,episode_type,season,keywords,explicit, andblock. The parent must be acast.Podcast(a non-podcast parent is rejected), theseasonmust belong to that podcast, andpodcast_audiomust be choosable by the caller.GET /api/editor/parents/now points podcasts at the episode create endpoint. Create and update stay draft-only and rejectpublish: true; draft episodes are published through the explicitPOST /api/editor/episodes/{id}/publish/action, which mirrors the post publish action and additionally requires a non-nullpodcast_audioto publish. That audio requirement is also enforced when an episode id is published throughPOST /api/editor/posts/{id}/publish/, so it cannot be bypassed.The editor API now enforces optional per-action scopes for scoped-token authentication: reads need no scope, create/update/media uploads need a
writescope, and the publish actions need apublishscope, so a token can be restricted to draft-only. Session auth and unscoped tokens fall back to pure Wagtail permissions. Accepted scope strings are configurable viaCAST_EDITOR_SCOPES; a token missing the required scope gets403 insufficient_scope. django-cast still imports no authentication provider.Added rendered draft preview endpoints for token-only editor workflows:
GET /api/editor/posts/{id}/preview/andGET /api/editor/episodes/{id}/preview/return the latest editable revision as full themedtext/htmlwhen the caller has edit permission, while errors keep the editor API JSON envelopes.Tightened Wagtail admin permissions for custom audio, video, and transcript views. Index, chooser, edit, and delete endpoints now use collection-scoped permission policies, and new
choose_audio,choose_video, andchoose_transcriptpermissions control chooser access.Added a dedicated
STORAGES["cast_public_transcripts"]alias for public transcript artifacts (Podlove JSON, DOTe JSON, and WebVTT). Those files are publishable transcript output and no longer needcast_private_media. For compatibility, django-cast still reads/writes them through an explicitly configuredcast_private_mediaalias whencast_public_transcriptsis omitted; otherwise they stay on default storage. Public transcript URLs continue to use the existing authorized and sanitized views.Voice-reference clips and known-speaker sidecar files no longer fall back to default public media storage when
STORAGES["cast_voice_references"]is omitted. They now use the private media backend, and a migration copies existing default-storage files into private storage. Voxhelm known-speaker handoff now skips uploaded clips on no-URL private storage instead of exposing or crashing on direct storage URLs; source-range references continue to work.Audio and video uploads are now rejected before ffprobe/ffmpeg when their extension, content type, container signature, or configured size limit is not supported. New
CAST_AUDIO_UPLOAD_MAX_BYTESandCAST_VIDEO_UPLOAD_MAX_BYTESsettings control the size limits.The editor media upload lock now uses owner tokens, defaults to a 2-hour TTL, and can be tuned with
CAST_EDITOR_MEDIA_UPLOAD_LOCK_SECONDS. Multi-worker deployments should use a shared Django cache backend for process-wide upload throttling; a worker crash can leave the per-user lock in place until the TTL expires.Editor media probing can be tuned with
CAST_EDITOR_MEDIA_PROBE_SECONDS. Required audio probe failures now returnprobe_failed/HTTP 422 with cleanup instead of escaping as server errors. Optional chapter extraction during audio saves is now best-effort in both the editor API and Wagtail admin: failed or timed-out chapter probes are logged and the audio is saved without auto-imported chapter marks, while malformed individual chapter entries are skipped and valid entries are kept.media_stale --deletenow only reports and deletes files under known django-cast/Wagtail-managed media prefixes, and its referenced-file scan includes private transcript speaker sidecars and contributor voice clips.media_replace --yesnow stages replacements before writing the requested production key and no longer deletes an existing production object before the replacement save succeeds. If a storage backend would auto-save an existing target as a different name, the generated file is removed and the original is left untouched.The Podlove player web component no longer falls back to a mutable third-party CDN script when
data-embedis missing. Themes must provide a first-party embed script URL; missing configuration now fails closed in the player UI.Updated the JavaScript toolchain to Vite 8, removing the vulnerable esbuild dev-server dependency reported by
npm audit. Frontend builds now require Node.js 20.19+ or 22.12+.