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 :ref:`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+.