0.2.54 (2026-03-09)

  • Added canonical repository class names: PostQuerySnapshot, PostDetailContext, BlogIndexContext, FeedContext, and EpisodeFeedContext.

  • Added canonical repository serialization APIs: serialize_audio/deserialize_audio, serialize_video/deserialize_video, serialize_image/deserialize_image, serialize_blog/deserialize_blog, serialize_post/deserialize_post, serialize_episode/deserialize_episode, and serialize_transcript/deserialize_transcript.

  • Breaking change: removed legacy repository class aliases QuerysetData, PostDetailRepository, BlogIndexRepository, FeedRepository, and EpisodeFeedRepository.

  • Breaking change: removed legacy serialization aliases audio_to_dict, video_to_dict, image_to_dict, blog_to_dict/blog_from_data, post_to_dict, episode_to_dict, and transcript_to_dict.

  • Canonical names are now the only supported API surface for repository classes and serialization helpers.

  • Repository hardening pass: - removed dead PostQuerySnapshot.create_from_post_queryset(…, is_podcast=…) API surface and dead branch - fixed repository-layer import coupling (HtmxHttpRequest/PostFilterset) by moving to type-only/local imports - guarded add_site_raw SQL fallback for empty-site rows (fetchone() is None) - corrected media ID aliases to use set[int] - removed dead CastBlock type alias and Site package re-export - narrowed transcript exception handling to avoid masking AttributeError - added EpisodeFeedContext to model reference docs - removed duplicate URL computation in data_for_blog_cachable by serializing URL maps in add_queryset_data - fixed FeedContext.create_from_django_models to handle Site.find_for_request(request) is None

  • Secured media detail API authorization: - VideoDetailView and AudioDetailView now scope object lookup to request.user, preventing cross-user retrieve/delete. - Added regression tests for owner and non-owner GET/DELETE behavior, including unauthenticated DELETE responses.

  • Hardened styleguide helper account setup: - the styleguide user is now forced to set_unusable_password(), is_active=False, and removed from the Moderators group. - added regression tests to guarantee the styleguide account remains non-login and de-privileged.

  • Fixed request-state leakage in Post.get_context() by avoiding mutation of the page instance (owner/page_url); request-specific values are now applied to a context-local page object instead.

  • Fixed rich-text internal page-link cache isolation by replacing the process-global PageLinkHandlerWithCache.cache dict with context-local storage, preventing cross-request/thread cache leakage during concurrent rendering while preserving existing link-resolution behavior.

  • Hardened Audio.save() persistence flow by wrapping enrichment in a database transaction and collapsing up to three writes into one initial save plus at most one targeted follow-up update, reducing partial-update risk while preserving duration and file-size metadata behavior.

  • Fixed Video._create_poster() file-handle lifecycle by closing the mkstemp descriptor, context-managing poster file reads, and always cleaning up the temporary poster file; added regression tests for success, save-failure, and missing-temp-file cleanup paths.

  • Fixed stale settings reads in cast.appsettings and cast.renditions by resolving settings at runtime so @override_settings changes are respected after module import.

  • Aligned test INSTALLED_APPS with shipped CAST_APPS by reusing cast.apps.CAST_APPS, ensuring rest_framework, django_htmx, and wagtail.api.v2 are present in tests and adding regression checks to prevent future app-list drift.

  • Hardened media_replace with safe defaults: it now requires explicit --yes confirmation for destructive writes, supports non-destructive previews via --dry-run, and reports per-file operations plus summary counts for planned/replaced/skipped/errors.

  • Added new Django system checks for configuration safety: cast.E002 validates cast.comments.apps.CastCommentsConfig is listed before django_comments in INSTALLED_APPS, and cast.E003 validates all middleware listed in cast.apps.CAST_MIDDLEWARE are present in settings.MIDDLEWARE.

  • Hardened select_theme POST redirects by validating next with url_has_allowed_host_and_scheme against the current host/scheme and falling back to the local select-theme URL when next is empty or unsafe.

  • Fixed runtime comment setting lookups so cast.comments form/helper CSS and exclude-field settings are resolved lazily instead of freezing at import time.

  • Fixed the comments fallback CRISPY_TEMPLATE_PACK default to bootstrap4 so AJAX validation errors still render when projects rely on django-cast’s built-in crispy setup without redefining that setting.

  • Pinned the CI uv installation step to 0.10.2 for more predictable workflow behavior.

  • Fixed Python 3.11-3.13 compatibility for Post model imports by replacing a runtime-evaluated "Blog" | None annotation with Optional["Blog"].

  • Typed Django system check callables in cast.checks so uv run mypy no longer emits annotation-unchecked notes during the release gate.

  • Fixed migration 0063_gallery_signature for current Django versions by providing an explicit chunk_size when iterating a prefetched queryset.

  • Hardened recalc_video_posters with progress output, per-video error handling, and completion summaries so one broken video no longer aborts the entire command.

  • Replaced mutable class-level fallback repository dicts in blocks.py with per-instance mappings.

  • Reduced settings drift by making tests.settings import shared defaults from cast.settings and keep only explicit test-specific overrides.

  • Hardened media_stale so cleanup now preserves live Audio and Transcript assets instead of misclassifying them as stale.

  • Made the optional theme-template contract more forgiving by falling back to cast/plain when a discovered theme omits transcript.html or gallery_modal.html.

  • Added just test-slow so contributors can run only the intentionally slower integration/dev-surface tests that complement just test-fast.

  • Restricted GET /api/comment_training_data/ to staff users so comment training exports are no longer exposed to arbitrary authenticated users.

  • Made slug-based feed, transcript, and meta endpoints site-scoped so multisite installs resolve pages within the current site tree instead of failing on duplicate slugs. These lookups now intentionally stay limited to live pages.

  • Hardened sync_renditions --blog-slug to raise CommandError when a slug is missing or ambiguous instead of silently targeting the wrong blog.

  • Fixed Video.save() follow-up poster persistence so force_insert and objects.create() paths no longer trigger a duplicate insert.

  • Hardened the gallery modal view to return a controlled 404 when the requested image was deleted after the page HTML was rendered.

Migration mapping (old -> new):

Removed

Replacement

QuerysetData

PostQuerySnapshot

PostDetailRepository

PostDetailContext

BlogIndexRepository

BlogIndexContext

FeedRepository

FeedContext

EpisodeFeedRepository

EpisodeFeedContext

audio_to_dict

serialize_audio

video_to_dict

serialize_video

image_to_dict

serialize_image

blog_to_dict

serialize_blog

blog_from_data

deserialize_blog

post_to_dict

serialize_post

episode_to_dict

serialize_episode

transcript_to_dict

serialize_transcript