0.2.61 (unreleased)

  • 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 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 = True and 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 draft Post from a structured overview block list (POST /api/editor/posts/), and read the draft back with normalized authoring source (GET /api/editor/posts/{id}/). Drafts can also be revised with PATCH /api/editor/posts/{id}/ by sending the returned latest_revision_id as base_revision_id; stale bases return 409 revision_conflict instead of overwriting newer edits. The API stays authentication-mechanism agnostic — it requires an authenticated user and authorizes every action with Wagtail page permissions — and never publishes: pages are saved as drafts via Wagtail revisions. 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 the detail body 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 with probe_timeout instead of hanging the request.

  • 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, and choose_transcript permissions 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 need cast_private_media. For compatibility, django-cast still reads/writes them through an explicitly configured cast_private_media alias when cast_public_transcripts is 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_BYTES and CAST_VIDEO_UPLOAD_MAX_BYTES settings 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 return probe_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 --delete now 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.

  • The Podlove player web component no longer falls back to a mutable third-party CDN script when data-embed is 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+.