Formats like .mkv, .avi, .wmv, .ts, .m2ts and .tiff are not natively
supported by browsers and would stall silently in Doom Scroll mode.
Add src/lib/browser-media.ts with BROWSER_VIDEO_EXTENSIONS (.mp4,
.webm, .mov, .m4v), BROWSER_IMAGE_EXTENSIONS (.jpg, .jpeg, .png, .gif,
.webp, .bmp), and an isBrowserPlayable() helper that extracts the
extension without importing Node's path module.
Filter doomScrollItems in MixedView, MoviesView, and TvView using this
helper so only natively renderable files are passed to DoomScrollView.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
media_key was a lossy shortening of item_key (libraryId:lastSegment) that
introduced a real collision bug: two TV episodes from different series with
the same filename would share the same media_key and each other's tags.
- DB migration converts existing media_tags rows from short format to full
item_key by joining against media_items; ambiguous/orphaned rows are dropped
- media_tags column renamed media_key → item_key
- Removed itemKeyToMediaKey() from scanner; reconcileAndPrune now passes
item_key directly to reKeyMediaItem
- DB reader functions (tv, movies, games) now expose item_key on returned
entities; frontend components use entity.item_key instead of constructing
the short libraryId:id form
- MixedView now constructs the full mixed_file: item_key format
- Tag API renamed mediaKey param → itemKey throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add prev/next arrow buttons and ArrowLeft/ArrowRight keyboard shortcuts to ImageLightbox and VideoPlayerModal
- Wire prev/next navigation in MixedView (through filtered media entries), TvView (through season episodes), and MoviesView/MovieDetailModal (through filtered movie list)
- Add new DoomScrollView component: fullscreen random-media mode with scroll/swipe/keyboard navigation, 100-item back-history, and per-library mute settings
- Add Doom Scroll button to mixed, movies, and TV library views
- Doom scroll respects active filters: mixed uses filtered entries, movies uses filtered movie list, TV fetches episodes from matching series only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a toggle button to show/hide the filter panel in Movies, Games,
Mixed, and TV views. On mobile the layout stacks vertically (filter
above content); on md+ it returns to the side-by-side layout. The
toggle button highlights when filters are active so hidden filters
remain discoverable. Also fixes a layout bug where items-start on the
flex-col container caused MixedView thumbnails to collapse on narrow
screens.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add left sidebar filter panel to MixedView and GamesView with name
search and tag toggles; only shows tags/categories used in the current
library; AND logic when multiple tags are selected
- Add GET /api/tags/library-assignments endpoint returning all tag
assignments for a library keyed by mediaKey
- Add getTagAssignmentsForLibrary() and getTagsSortedByUsage() to tags lib
- Support ?sort=usage on GET /api/tags/items to order by assignment count
- Tag selector: per-category search, top-25-by-usage display, inline add
tag (auto-assigned to current item) and add category flows
- Tag selector: group assigned tags by category into nested pills
- Fix nested <button> hydration error in EntryTile (outer element is now
a div with role="button")
- Keep filter panel assignments in sync when tags are toggled or created
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces user-defined tag categories and tags with a many-to-many
relationship to media items. Tags are stored in a SQLite database
(medialore.db via better-sqlite3) with ON DELETE CASCADE for automatic
cleanup. Users can manage categories and tags at /manage/tags, assign
tags to games in the detail modal, and tag mixed media files via a
hover button on each tile.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>