# {{ widget:recently_added_media }} — Wizarr custom widget patch

This is real, currently-running code, pulled directly out of the live `wizarr` container's
`/app/app/services/` on 2026-08-28. It only exists in that container's writable layer, not in
`/data` and not in stock Wizarr — it will be lost the next time the Wizarr image gets updated
unless it's reapplied. Saving it here so it survives regardless.

## Files

- **`wizard_widgets.py`** — full file, 475 lines. The widget engine itself: `WIDGET_REGISTRY`,
  the `{{ widget:... }}` template syntax parser, and `RecentlyAddedMediaWidget` (the class that
  renders the actual carousel — pulls a pool of items via a media client's `get_random_items()`
  or `get_recent_items()`, then builds the HTML). This is the piece that makes
  `{{ widget:recently_added_media source=audiobookshelf limit=8 }}` work at all inside a wizard
  step's markdown.
- **`audiobookshelf_get_random_items.py`** — one method, pulled from
  `app/services/media/audiobookshelf.py`. Samples randomly across the *whole* Audiobookshelf
  library (not just the "recently added" shelf) so the carousel doesn't end up dominated by
  whatever was bulk-imported most recently, and tags each item with author/series so the widget
  can diversify by author.

## What's genuinely custom vs. stock Wizarr

`wizard_widgets.py` and `get_random_items()` are the parts built specifically for this: neither
exists in a plain Wizarr install. The widget also calls each media client's `get_recent_items()`
(Jellyfin, Plex, Audiobookshelf all have one) for the non-diversified path — those methods
appear to be Wizarr's own pre-existing functionality, reused by this widget rather than written
for it. No clean diff against upstream was done to confirm that line-for-line, so treat that
distinction as a strong likelihood, not a verified fact.

## Where it lives live right now

Container `wizarr`, path `/app/app/services/wizard_widgets.py` and
`/app/app/services/media/audiobookshelf.py`. Bind-mounted config is at
`M:\Docker\config\wizarr\`, but this patch is NOT in that mount — it's only in the container's
own writable layer, which is why it needs to be reapplied by hand after any
`docker compose pull && docker compose up -d` on this service.
