Releases: Serph91P/MediaCurator
Development Build 4.1.1-dev
What's Changed in 4.1.1-dev
Bug Fixes
- docker: Pin PGDATA so postgres:18 reuses existing data volume
Other Changes
- Merge pull request #57 from Serph91P/fix/auth-persistence-and-delete-reliability
fix(docker): pin PGDATA so postgres:18 reuses existing data volume
Docker Images
This release is available as a Docker image for amd64 architecture:
Pull the image:
# Specific version
docker pull ghcr.io/Serph91P/mediacurator:4.1.1-dev
# Or latest stable (main branch)
docker pull ghcr.io/Serph91P/mediacurator:latest
# Or development (develop branch)
docker pull ghcr.io/Serph91P/mediacurator:devRun with Docker Compose:
# Download docker-compose.yml
curl -O https://raw.githubusercontent.com/Serph91P/MediaCurator/main/docker-compose.yml
# Start services
docker compose up -dRegistry Information
| Property | Value |
|---|---|
| Registry | GitHub Container Registry (ghcr.io) |
| Image | ghcr.io/Serph91P/mediacurator |
| Version | 4.1.1-dev |
| Architecture | linux/amd64 |
Note: ARM64 builds are not currently provided. Open a feature request if you need ARM support.
Documentation
See the README.md for full configuration options.
Development Build 4.1.0-dev
What's Changed in 4.1.0-dev
Bug Fixes
- auth: Persist secret key across restarts to keep sessions valid
- sync: Make Sonarr/Radarr deletes two-phase and observable
Features
- ui: Warn user when refresh fails because no users exist
Other Changes
- Merge pull request #56 from Serph91P/fix/auth-persistence-and-delete-reliability
Fix/auth persistence and delete reliability
Docker Images
This release is available as a Docker image for amd64 architecture:
Pull the image:
# Specific version
docker pull ghcr.io/Serph91P/mediacurator:4.1.0-dev
# Or latest stable (main branch)
docker pull ghcr.io/Serph91P/mediacurator:latest
# Or development (develop branch)
docker pull ghcr.io/Serph91P/mediacurator:devRun with Docker Compose:
# Download docker-compose.yml
curl -O https://raw.githubusercontent.com/Serph91P/MediaCurator/main/docker-compose.yml
# Start services
docker compose up -dRegistry Information
| Property | Value |
|---|---|
| Registry | GitHub Container Registry (ghcr.io) |
| Image | ghcr.io/Serph91P/mediacurator |
| Version | 4.1.0-dev |
| Architecture | linux/amd64 |
Note: ARM64 builds are not currently provided. Open a feature request if you need ARM support.
Documentation
See the README.md for full configuration options.
Development Build 4.0.1-dev
What's Changed in 4.0.1-dev
Other Changes
- Develop (#55)
- feat: Complete Phase 2 - Users, Activity & Library Detail Views
- Add PlaybackActivity model for detailed playback session tracking
- Add activity API routes (/api/activity/) with list, stats, active sessions
- Add users API routes (/api/users/) with list, detail, activity endpoints
- Extend Emby sync with _sync_active_sessions() for real-time session tracking
- Add Users.tsx page with search, pagination, user stats
- Add UserDetail.tsx page with Overview/Activity tabs and time-based stats
- Add Activity.tsx page with global activity log, active sessions, stats cards
- Add LibraryDetail.tsx with Overview/Media/Activity tabs
- Extend libraries API with /details, /media, /activity endpoints
- Update navigation with Users and Activity menu items
- Library cards now link to detail view
Completes Phase 2 of PLANNED_FEATURES.md roadmap (Jellystat-style features)
- feat: Add PostgreSQL support and fix sync issues
Database:
- Add PostgreSQL support via POSTGRES_* environment variables
- Add asyncpg driver to requirements
- Create docker-compose.postgres.yml for PostgreSQL setup
- Keep SQLite as default for simple deployments
- Add connection pooling for PostgreSQL
Bug Fixes:
- Fix MediaItem.parent_id AttributeError by using series_id (external_id)
- Add parent_id column to MediaItem model for future use
- Fix Users API to fallback to UserWatchHistory when PlaybackActivity is empty
- Fix deprecation warning: regex -> pattern in Query parameters
- Remove 'Jellystat-style' comments from codebase
Improvements:
- Sync jobs now visible in Jobs view with execution logs
- Manual sync shows progress/errors in Jobs history
- Users page shows last watched from UserWatchHistory fallback
-
chore: Update PostgreSQL to version 18 (latest stable)
-
fix: Add PostgreSQL support to database migrations
- Add column_exists() helper for SQLite/PostgreSQL compatibility
- Add table_exists() helper for SQLite/PostgreSQL compatibility
- Use information_schema for PostgreSQL column/table checks
- Use pragma_table_info for SQLite column checks
- Add IF NOT EXISTS for PostgreSQL ALTER TABLE commands
- Use SERIAL PRIMARY KEY for PostgreSQL auto-increment
- chore: Remove ARM64 build to speed up CI
- Build only linux/amd64 for faster builds
- Remove QEMU setup (not needed without cross-platform builds)
- Update release notes to reflect single architecture
- Add note about opening feature request for ARM support
- refactor: Remove Jellystat service type
- MediaCurator now has built-in watch statistics tracking
- Jellystat integration no longer needed
- Removed from frontend ServiceType, service options
- Removed from backend models and schemas
- feat: Add individual service sync jobs in Jobs tab
- Add separate sync jobs for each enabled service
- Show service sync status with running indicator
- Add manual 'Sync Now' button per service
- Display last sync result and duration
- Auto-refresh job status every 5 seconds
- New API endpoint POST /jobs/sync/service/{id}
- Service sync jobs show in job history
- fix: Emby sync creates MediaItems and populates watch statistics
- Sync libraries, movies, series, episodes directly from Emby
- Path-based library association for proper linking
- Track user total_plays and total_watch_time_seconds
- Use external_id for watch data matching
- Include RunTimeTicks for watch time calculation
- refactor(sync): Emby now matches existing items by PATH instead of creating duplicates
BREAKING CHANGE: Emby sync architecture completely redesigned
The core insight:
- Sonarr/Radarr CREATE MediaItems with file paths and metadata
- Emby PROVIDES watch statistics (plays, progress, favorites)
- Previous code created DUPLICATE items from Emby with different external_ids
New behavior:
- _sync_emby() no longer creates MediaItems
- Loads ALL existing items and builds path -> item mapping
- Matches Emby items to Sonarr/Radarr items by normalized PATH
- Updates library_id based on Emby library paths
- Applies watch data (play count, is_watched, favorites, progress) to matched items
- Updates user statistics and watch history
Key changes:
- Removed: external_id_to_item mapping (was causing duplicates)
- Added: path_to_item mapping with normalized paths (/ instead of )
- track_watch_data() now uses paths, skips items not in database
- _sync_active_sessions() simplified to use only path_to_item
- currently_watching_ids -> currently_watching_paths
- Return value updated (Emby reports 0 added, only updated counts)
This fixes:
- Movies showing 0 plays (Emby data was on separate items)
- Libraries showing 0 counts
- Users showing no watch data
- Activity/history not tracking properly
- feat: WebSocket real-time job system + Setup Wizard
WebSocket System:
- Add ConnectionManager (backend/app/core/websocket.py)
- Add /api/ws/jobs endpoint with ping/pong support
- Add progress callbacks to Sonarr, Radarr, Emby sync
- Integrate WebSocket broadcasts in all scheduler jobs
- Add Zustand job store (frontend/src/stores/jobs.ts)
- Add global WebSocket hook with auto-reconnect & toasts
- Rewrite Jobs page with live progress bars & running panel
- Add animated job count badge in Layout sidebar
Setup Wizard:
- Add backend API: /api/setup/status, test-connection, add-service, complete, skip
- Add 5-step frontend wizard (Welcome → Arr → MediaServer → Sync → Complete)
- Add SetupGate redirect in App.tsx for first-time setup
- Enforce service order: Sonarr/Radarr before Emby/Jellyfin
Docs:
- Update DEVELOPMENT_STATUS.md with new features
- fix: resolve all priority 1-5 bugs from DEVELOPMENT_STATUS.md
BUG-001 (CRITICAL): LibraryDetail.tsx complete rewrite
- Fix double /api/ prefix causing 404 errors on all API calls
- Migrate from manual useState/useEffect to React Query (useQuery)
- Remove local formatBytes, formatDuration, formatDate, formatRelativeTime
- Import shared utilities from lib/utils.ts
- Add proper light/dark mode classes throughout
BUG-002 (HIGH): Fix Login.tsx & Register.tsx light-mode
- Replace hardcoded dark classes with light/dark pairs
- Fix: bg-dark-800 → bg-white dark:bg-dark-800
- Fix: text-white → text-gray-900 dark:text-white
- Fix: text-dark-200 → text-gray-700 dark:text-dark-200
- Fix: border-dark-700 → border-gray-200 dark:border-dark-700
- Fix inputs: bg-gray-50 dark:bg-dark-800, proper placeholder colors
BUG-003 (HIGH): Fix ConfirmDialog.tsx light-mode
- Modal background, title, message, footer, cancel button themed
BUG-004 (HIGH): Fix Skeleton.tsx light-mode
- Base skeleton: bg-gray-200 dark:bg-dark-700
- Card/Stats containers: bg-white dark:bg-dark-800
BUG-005 (MEDIUM): Fix Toast theming in main.tsx
- Replace hardcoded hex colors with Tailwind CSS classes
- Toasts now respect light/dark mode automatically
BUG-006 (MEDIUM): Fix German locale in utils.ts
- Change formatDate and formatDateTime from 'de-DE' to 'en-US'
- Add formatDuration utility function for shared use
BUG-008 (MEDIUM): Call fetchUser on app init
- ProtectedRoute now calls fetchUser() on mount when token exists
- Ensures fresh user data instead of potentially stale persisted state
BUG-009 (LOW): Remove duplicate formatBytes from Preview.tsx
- Import from shared lib/utils.ts instead
BUG-010 (LOW): Fix manual debounce in Users.tsx
- Replace setTimeout-based debounce with useDebounce hook
-
docs: update DEVELOPMENT_STATUS.md with Session 3 bugfix results
-
feat: UX improvements, recharts charts, code-splitting
- Layout.tsx: German strings → English (BUG-006 complete)
- ResponsiveTable: Add light mode classes (missing dark: variants)
- Activity.tsx: useDebounce hook, shared utils (formatDurationLong,
formatWatchTime), ResponsiveTable migration, 3× recharts charts
(Daily Plays area, Day-of-Week bar, Hour-of-Day bar) - UserDetail.tsx: shared utils, ResponsiveTable migration
- Staging.tsx: ResponsiveTable migration
- Jobs.tsx: Executions table → ResponsiveTable
- App.tsx: React.lazy code-splitting for 13 pages with Suspense
- utils.ts: Add formatDurationLong and formatWatchTime shared functions
- DEVELOPMENT_STATUS.md: Update for Session 4
- feat: Phase 2 enhancements - ResponsiveTable, filters, dashboard charts
- LibraryDetail.tsx: Migrate Media + Activity tabs to ResponsiveTable
- Activity.tsx: Add Library-Filter dropdown + Items-per-Page selector (10/25/50/100)
- UserDetail.tsx: Add Library-Filter on activity tab
- Dashboard.tsx: Add 3x recharts charts (Daily Plays, Day-of-Week, Hour-of-Day)
- PLANNED_FEATURES.md: Update Phase 2/3 status, add Implementation History
- DEVELOPMENT_STATUS.md: Update for Session 5
- fix: int32 overflow on position_ticks/runtime_ticks + expand-rows + accessibility
BUG-011: PlaybackActivity position_ticks/runtime_ticks Integer→BigInteger
- Emby tick values exceed int32 max (~2.1B), e.g. 70223183889
- Added PostgreSQL migration (ALTER COLUMN TYPE BIGINT)
- Added db.rollback() in _sync_active_sessions and services.py error handlers
ResponsiveTable: Expand-Row support
- New props: onRowClick, isExpanded, expandedContent
- Desktop: expanded content as extra with colSpan
- Mobile: expanded content as div below card
Preview.tsx: Migrated both tables (Series + Movies) to ResponsiveTable
- Series table with expandable season breakdown
- Last page to complete ResponsiveTable migration
Expand-Rows on Activity, UserDetail, LibraryDetail
- IP Address, Device, Play Method (color-coded), Progress bar
- Library Activity API: added ip_address, transcode_video, transcode_audio
ConfirmDialog accessibility
- role=dialog, aria-modal, aria-labelledby, focus trap, Escape key, click-outside
Documentation: DEVELOPMENT_STATUS.md + PLANNED_FEATURES.md updated
- docs: fix inconsistencies in DEVELOPMENT_STATUS.md
- Update commit hash from Pending to ea6e64c
- Mark Preview.tsx migration as complete in Priority 2
- Clean up strikethroughs in Priority 4 (Activity, Dashboard fully done)
- Update ResponsiveTable comment (now used on all pages)...
Release 4.0.0
What's Changed in 4.0.0
Other Changes
- Develop (#55)
- feat: Complete Phase 2 - Users, Activity & Library Detail Views
- Add PlaybackActivity model for detailed playback session tracking
- Add activity API routes (/api/activity/) with list, stats, active sessions
- Add users API routes (/api/users/) with list, detail, activity endpoints
- Extend Emby sync with _sync_active_sessions() for real-time session tracking
- Add Users.tsx page with search, pagination, user stats
- Add UserDetail.tsx page with Overview/Activity tabs and time-based stats
- Add Activity.tsx page with global activity log, active sessions, stats cards
- Add LibraryDetail.tsx with Overview/Media/Activity tabs
- Extend libraries API with /details, /media, /activity endpoints
- Update navigation with Users and Activity menu items
- Library cards now link to detail view
Completes Phase 2 of PLANNED_FEATURES.md roadmap (Jellystat-style features)
- feat: Add PostgreSQL support and fix sync issues
Database:
- Add PostgreSQL support via POSTGRES_* environment variables
- Add asyncpg driver to requirements
- Create docker-compose.postgres.yml for PostgreSQL setup
- Keep SQLite as default for simple deployments
- Add connection pooling for PostgreSQL
Bug Fixes:
- Fix MediaItem.parent_id AttributeError by using series_id (external_id)
- Add parent_id column to MediaItem model for future use
- Fix Users API to fallback to UserWatchHistory when PlaybackActivity is empty
- Fix deprecation warning: regex -> pattern in Query parameters
- Remove 'Jellystat-style' comments from codebase
Improvements:
- Sync jobs now visible in Jobs view with execution logs
- Manual sync shows progress/errors in Jobs history
- Users page shows last watched from UserWatchHistory fallback
-
chore: Update PostgreSQL to version 18 (latest stable)
-
fix: Add PostgreSQL support to database migrations
- Add column_exists() helper for SQLite/PostgreSQL compatibility
- Add table_exists() helper for SQLite/PostgreSQL compatibility
- Use information_schema for PostgreSQL column/table checks
- Use pragma_table_info for SQLite column checks
- Add IF NOT EXISTS for PostgreSQL ALTER TABLE commands
- Use SERIAL PRIMARY KEY for PostgreSQL auto-increment
- chore: Remove ARM64 build to speed up CI
- Build only linux/amd64 for faster builds
- Remove QEMU setup (not needed without cross-platform builds)
- Update release notes to reflect single architecture
- Add note about opening feature request for ARM support
- refactor: Remove Jellystat service type
- MediaCurator now has built-in watch statistics tracking
- Jellystat integration no longer needed
- Removed from frontend ServiceType, service options
- Removed from backend models and schemas
- feat: Add individual service sync jobs in Jobs tab
- Add separate sync jobs for each enabled service
- Show service sync status with running indicator
- Add manual 'Sync Now' button per service
- Display last sync result and duration
- Auto-refresh job status every 5 seconds
- New API endpoint POST /jobs/sync/service/{id}
- Service sync jobs show in job history
- fix: Emby sync creates MediaItems and populates watch statistics
- Sync libraries, movies, series, episodes directly from Emby
- Path-based library association for proper linking
- Track user total_plays and total_watch_time_seconds
- Use external_id for watch data matching
- Include RunTimeTicks for watch time calculation
- refactor(sync): Emby now matches existing items by PATH instead of creating duplicates
BREAKING CHANGE: Emby sync architecture completely redesigned
The core insight:
- Sonarr/Radarr CREATE MediaItems with file paths and metadata
- Emby PROVIDES watch statistics (plays, progress, favorites)
- Previous code created DUPLICATE items from Emby with different external_ids
New behavior:
- _sync_emby() no longer creates MediaItems
- Loads ALL existing items and builds path -> item mapping
- Matches Emby items to Sonarr/Radarr items by normalized PATH
- Updates library_id based on Emby library paths
- Applies watch data (play count, is_watched, favorites, progress) to matched items
- Updates user statistics and watch history
Key changes:
- Removed: external_id_to_item mapping (was causing duplicates)
- Added: path_to_item mapping with normalized paths (/ instead of )
- track_watch_data() now uses paths, skips items not in database
- _sync_active_sessions() simplified to use only path_to_item
- currently_watching_ids -> currently_watching_paths
- Return value updated (Emby reports 0 added, only updated counts)
This fixes:
- Movies showing 0 plays (Emby data was on separate items)
- Libraries showing 0 counts
- Users showing no watch data
- Activity/history not tracking properly
- feat: WebSocket real-time job system + Setup Wizard
WebSocket System:
- Add ConnectionManager (backend/app/core/websocket.py)
- Add /api/ws/jobs endpoint with ping/pong support
- Add progress callbacks to Sonarr, Radarr, Emby sync
- Integrate WebSocket broadcasts in all scheduler jobs
- Add Zustand job store (frontend/src/stores/jobs.ts)
- Add global WebSocket hook with auto-reconnect & toasts
- Rewrite Jobs page with live progress bars & running panel
- Add animated job count badge in Layout sidebar
Setup Wizard:
- Add backend API: /api/setup/status, test-connection, add-service, complete, skip
- Add 5-step frontend wizard (Welcome → Arr → MediaServer → Sync → Complete)
- Add SetupGate redirect in App.tsx for first-time setup
- Enforce service order: Sonarr/Radarr before Emby/Jellyfin
Docs:
- Update DEVELOPMENT_STATUS.md with new features
- fix: resolve all priority 1-5 bugs from DEVELOPMENT_STATUS.md
BUG-001 (CRITICAL): LibraryDetail.tsx complete rewrite
- Fix double /api/ prefix causing 404 errors on all API calls
- Migrate from manual useState/useEffect to React Query (useQuery)
- Remove local formatBytes, formatDuration, formatDate, formatRelativeTime
- Import shared utilities from lib/utils.ts
- Add proper light/dark mode classes throughout
BUG-002 (HIGH): Fix Login.tsx & Register.tsx light-mode
- Replace hardcoded dark classes with light/dark pairs
- Fix: bg-dark-800 → bg-white dark:bg-dark-800
- Fix: text-white → text-gray-900 dark:text-white
- Fix: text-dark-200 → text-gray-700 dark:text-dark-200
- Fix: border-dark-700 → border-gray-200 dark:border-dark-700
- Fix inputs: bg-gray-50 dark:bg-dark-800, proper placeholder colors
BUG-003 (HIGH): Fix ConfirmDialog.tsx light-mode
- Modal background, title, message, footer, cancel button themed
BUG-004 (HIGH): Fix Skeleton.tsx light-mode
- Base skeleton: bg-gray-200 dark:bg-dark-700
- Card/Stats containers: bg-white dark:bg-dark-800
BUG-005 (MEDIUM): Fix Toast theming in main.tsx
- Replace hardcoded hex colors with Tailwind CSS classes
- Toasts now respect light/dark mode automatically
BUG-006 (MEDIUM): Fix German locale in utils.ts
- Change formatDate and formatDateTime from 'de-DE' to 'en-US'
- Add formatDuration utility function for shared use
BUG-008 (MEDIUM): Call fetchUser on app init
- ProtectedRoute now calls fetchUser() on mount when token exists
- Ensures fresh user data instead of potentially stale persisted state
BUG-009 (LOW): Remove duplicate formatBytes from Preview.tsx
- Import from shared lib/utils.ts instead
BUG-010 (LOW): Fix manual debounce in Users.tsx
- Replace setTimeout-based debounce with useDebounce hook
-
docs: update DEVELOPMENT_STATUS.md with Session 3 bugfix results
-
feat: UX improvements, recharts charts, code-splitting
- Layout.tsx: German strings → English (BUG-006 complete)
- ResponsiveTable: Add light mode classes (missing dark: variants)
- Activity.tsx: useDebounce hook, shared utils (formatDurationLong,
formatWatchTime), ResponsiveTable migration, 3× recharts charts
(Daily Plays area, Day-of-Week bar, Hour-of-Day bar) - UserDetail.tsx: shared utils, ResponsiveTable migration
- Staging.tsx: ResponsiveTable migration
- Jobs.tsx: Executions table → ResponsiveTable
- App.tsx: React.lazy code-splitting for 13 pages with Suspense
- utils.ts: Add formatDurationLong and formatWatchTime shared functions
- DEVELOPMENT_STATUS.md: Update for Session 4
- feat: Phase 2 enhancements - ResponsiveTable, filters, dashboard charts
- LibraryDetail.tsx: Migrate Media + Activity tabs to ResponsiveTable
- Activity.tsx: Add Library-Filter dropdown + Items-per-Page selector (10/25/50/100)
- UserDetail.tsx: Add Library-Filter on activity tab
- Dashboard.tsx: Add 3x recharts charts (Daily Plays, Day-of-Week, Hour-of-Day)
- PLANNED_FEATURES.md: Update Phase 2/3 status, add Implementation History
- DEVELOPMENT_STATUS.md: Update for Session 5
- fix: int32 overflow on position_ticks/runtime_ticks + expand-rows + accessibility
BUG-011: PlaybackActivity position_ticks/runtime_ticks Integer→BigInteger
- Emby tick values exceed int32 max (~2.1B), e.g. 70223183889
- Added PostgreSQL migration (ALTER COLUMN TYPE BIGINT)
- Added db.rollback() in _sync_active_sessions and services.py error handlers
ResponsiveTable: Expand-Row support
- New props: onRowClick, isExpanded, expandedContent
- Desktop: expanded content as extra with colSpan
- Mobile: expanded content as div below card
Preview.tsx: Migrated both tables (Series + Movies) to ResponsiveTable
- Series table with expandable season breakdown
- Last page to complete ResponsiveTable migration
Expand-Rows on Activity, UserDetail, LibraryDetail
- IP Address, Device, Play Method (color-coded), Progress bar
- Library Activity API: added ip_address, transcode_video, transcode_audio
ConfirmDialog accessibility
- role=dialog, aria-modal, aria-labelledby, focus trap, Escape key, click-outside
Documentation: DEVELOPMENT_STATUS.md + PLANNED_FEATURES.md updated
- docs: fix inconsistencies in DEVELOPMENT_STATUS.md
- Update commit hash from Pending to ea6e64c
- Mark Preview.tsx migration as complete in Priority 2
- Clean up strikethroughs in Priority 4 (Activity, Dashboard fully done)
- Update ResponsiveTable comment (now used on all pages)
- C...
Development Build 3.0.0-dev
What's Changed in 3.0.0-dev
Other Changes
- Merge branch 'main' into develop
Other Changes
- Develop (#47)
- feat: Complete Phase 2 - Users, Activity & Library Detail Views
- Add PlaybackActivity model for detailed playback session tracking
- Add activity API routes (/api/activity/) with list, stats, active sessions
- Add users API routes (/api/users/) with list, detail, activity endpoints
- Extend Emby sync with _sync_active_sessions() for real-time session tracking
- Add Users.tsx page with search, pagination, user stats
- Add UserDetail.tsx page with Overview/Activity tabs and time-based stats
- Add Activity.tsx page with global activity log, active sessions, stats cards
- Add LibraryDetail.tsx with Overview/Media/Activity tabs
- Extend libraries API with /details, /media, /activity endpoints
- Update navigation with Users and Activity menu items
- Library cards now link to detail view
Completes Phase 2 of PLANNED_FEATURES.md roadmap (Jellystat-style features)
- feat: Add PostgreSQL support and fix sync issues
Database:
- Add PostgreSQL support via POSTGRES_* environment variables
- Add asyncpg driver to requirements
- Create docker-compose.postgres.yml for PostgreSQL setup
- Keep SQLite as default for simple deployments
- Add connection pooling for PostgreSQL
Bug Fixes:
- Fix MediaItem.parent_id AttributeError by using series_id (external_id)
- Add parent_id column to MediaItem model for future use
- Fix Users API to fallback to UserWatchHistory when PlaybackActivity is empty
- Fix deprecation warning: regex -> pattern in Query parameters
- Remove 'Jellystat-style' comments from codebase
Improvements:
- Sync jobs now visible in Jobs view with execution logs
- Manual sync shows progress/errors in Jobs history
- Users page shows last watched from UserWatchHistory fallback
-
chore: Update PostgreSQL to version 18 (latest stable)
-
fix: Add PostgreSQL support to database migrations
- Add column_exists() helper for SQLite/PostgreSQL compatibility
- Add table_exists() helper for SQLite/PostgreSQL compatibility
- Use information_schema for PostgreSQL column/table checks
- Use pragma_table_info for SQLite column checks
- Add IF NOT EXISTS for PostgreSQL ALTER TABLE commands
- Use SERIAL PRIMARY KEY for PostgreSQL auto-increment
- chore: Remove ARM64 build to speed up CI
- Build only linux/amd64 for faster builds
- Remove QEMU setup (not needed without cross-platform builds)
- Update release notes to reflect single architecture
- Add note about opening feature request for ARM support
- refactor: Remove Jellystat service type
- MediaCurator now has built-in watch statistics tracking
- Jellystat integration no longer needed
- Removed from frontend ServiceType, service options
- Removed from backend models and schemas
- feat: Add individual service sync jobs in Jobs tab
- Add separate sync jobs for each enabled service
- Show service sync status with running indicator
- Add manual 'Sync Now' button per service
- Display last sync result and duration
- Auto-refresh job status every 5 seconds
- New API endpoint POST /jobs/sync/service/{id}
- Service sync jobs show in job history
- fix: Emby sync creates MediaItems and populates watch statistics
- Sync libraries, movies, series, episodes directly from Emby
- Path-based library association for proper linking
- Track user total_plays and total_watch_time_seconds
- Use external_id for watch data matching
- Include RunTimeTicks for watch time calculation
- refactor(sync): Emby now matches existing items by PATH instead of creating duplicates
BREAKING CHANGE: Emby sync architecture completely redesigned
The core insight:
- Sonarr/Radarr CREATE MediaItems with file paths and metadata
- Emby PROVIDES watch statistics (plays, progress, favorites)
- Previous code created DUPLICATE items from Emby with different external_ids
New behavior:
- _sync_emby() no longer creates MediaItems
- Loads ALL existing items and builds path -> item mapping
- Matches Emby items to Sonarr/Radarr items by normalized PATH
- Updates library_id based on Emby library paths
- Applies watch data (play count, is_watched, favorites, progress) to matched items
- Updates user statistics and watch history
Key changes:
- Removed: external_id_to_item mapping (was causing duplicates)
- Added: path_to_item mapping with normalized paths (/ instead of )
- track_watch_data() now uses paths, skips items not in database
- _sync_active_sessions() simplified to use only path_to_item
- currently_watching_ids -> currently_watching_paths
- Return value updated (Emby reports 0 added, only updated counts)
This fixes:
- Movies showing 0 plays (Emby data was on separate items)
- Libraries showing 0 counts
- Users showing no watch data
- Activity/history not tracking properly
- feat: WebSocket real-time job system + Setup Wizard
WebSocket System:
- Add ConnectionManager (backend/app/core/websocket.py)
- Add /api/ws/jobs endpoint with ping/pong support
- Add progress callbacks to Sonarr, Radarr, Emby sync
- Integrate WebSocket broadcasts in all scheduler jobs
- Add Zustand job store (frontend/src/stores/jobs.ts)
- Add global WebSocket hook with auto-reconnect & toasts
- Rewrite Jobs page with live progress bars & running panel
- Add animated job count badge in Layout sidebar
Setup Wizard:
- Add backend API: /api/setup/status, test-connection, add-service, complete, skip
- Add 5-step frontend wizard (Welcome → Arr → MediaServer → Sync → Complete)
- Add SetupGate redirect in App.tsx for first-time setup
- Enforce service order: Sonarr/Radarr before Emby/Jellyfin
Docs:
- Update DEVELOPMENT_STATUS.md with new features
- fix: resolve all priority 1-5 bugs from DEVELOPMENT_STATUS.md
BUG-001 (CRITICAL): LibraryDetail.tsx complete rewrite
- Fix double /api/ prefix causing 404 errors on all API calls
- Migrate from manual useState/useEffect to React Query (useQuery)
- Remove local formatBytes, formatDuration, formatDate, formatRelativeTime
- Import shared utilities from lib/utils.ts
- Add proper light/dark mode classes throughout
BUG-002 (HIGH): Fix Login.tsx & Register.tsx light-mode
- Replace hardcoded dark classes with light/dark pairs
- Fix: bg-dark-800 → bg-white dark:bg-dark-800
- Fix: text-white → text-gray-900 dark:text-white
- Fix: text-dark-200 → text-gray-700 dark:text-dark-200
- Fix: border-dark-700 → border-gray-200 dark:border-dark-700
- Fix inputs: bg-gray-50 dark:bg-dark-800, proper placeholder colors
BUG-003 (HIGH): Fix ConfirmDialog.tsx light-mode
- Modal background, title, message, footer, cancel button themed
BUG-004 (HIGH): Fix Skeleton.tsx light-mode
- Base skeleton: bg-gray-200 dark:bg-dark-700
- Card/Stats containers: bg-white dark:bg-dark-800
BUG-005 (MEDIUM): Fix Toast theming in main.tsx
- Replace hardcoded hex colors with Tailwind CSS classes
- Toasts now respect light/dark mode automatically
BUG-006 (MEDIUM): Fix German locale in utils.ts
- Change formatDate and formatDateTime from 'de-DE' to 'en-US'
- Add formatDuration utility function for shared use
BUG-008 (MEDIUM): Call fetchUser on app init
- ProtectedRoute now calls fetchUser() on mount when token exists
- Ensures fresh user data instead of potentially stale persisted state
BUG-009 (LOW): Remove duplicate formatBytes from Preview.tsx
- Import from shared lib/utils.ts instead
BUG-010 (LOW): Fix manual debounce in Users.tsx
- Replace setTimeout-based debounce with useDebounce hook
-
docs: update DEVELOPMENT_STATUS.md with Session 3 bugfix results
-
feat: UX improvements, recharts charts, code-splitting
- Layout.tsx: German strings → English (BUG-006 complete)
- ResponsiveTable: Add light mode classes (missing dark: variants)
- Activity.tsx: useDebounce hook, shared utils (formatDurationLong,
formatWatchTime), ResponsiveTable migration, 3× recharts charts
(Daily Plays area, Day-of-Week bar, Hour-of-Day bar) - UserDetail.tsx: shared utils, ResponsiveTable migration
- Staging.tsx: ResponsiveTable migration
- Jobs.tsx: Executions table → ResponsiveTable
- App.tsx: React.lazy code-splitting for 13 pages with Suspense
- utils.ts: Add formatDurationLong and formatWatchTime shared functions
- DEVELOPMENT_STATUS.md: Update for Session 4
- feat: Phase 2 enhancements - ResponsiveTable, filters, dashboard charts
- LibraryDetail.tsx: Migrate Media + Activity tabs to ResponsiveTable
- Activity.tsx: Add Library-Filter dropdown + Items-per-Page selector (10/25/50/100)
- UserDetail.tsx: Add Library-Filter on activity tab
- Dashboard.tsx: Add 3x recharts charts (Daily Plays, Day-of-Week, Hour-of-Day)
- PLANNED_FEATURES.md: Update Phase 2/3 status, add Implementation History
- DEVELOPMENT_STATUS.md: Update for Session 5
- fix: int32 overflow on position_ticks/runtime_ticks + expand-rows + accessibility
BUG-011: PlaybackActivity position_ticks/runtime_ticks Integer→BigInteger
- Emby tick values exceed int32 max (~2.1B), e.g. 70223183889
- Added PostgreSQL migration (ALTER COLUMN TYPE BIGINT)
- Added db.rollback() in _sync_active_sessions and services.py error handlers
ResponsiveTable: Expand-Row support
- New props: onRowClick, isExpanded, expandedContent
- Desktop: expanded content as extra with colSpan
- Mobile: expanded content as div below card
Preview.tsx: Migrated both tables (Series + Movies) to ResponsiveTable
- Series table with expandable season breakdown
- Last page to complete ResponsiveTable migration
Expand-Rows on Activity, UserDetail, LibraryDetail
- IP Address, Device, Play Method (color-coded), Progress bar
- Library Activity API: added ip_address, transcode_video, transcode_audio
ConfirmDialog accessibility
- role=dialog, aria-modal, aria-labelledby, focus trap, Escape key, click-outside
Documentation: DEVELOPMENT_STATUS.md + PLANNED_FEATURES.md updated
- docs: fix inconsistencies in DEVELOPMENT_STATUS.md
- Update commit hash from Pending to ea6e64c
- Mark Preview.tsx migration as complete in Priority 2
- Clean up strikethroughs in Priority 4 (Activity, Dashboard fully done)
- ...
Development Build 2.0.1-dev
What's Changed in 2.0.1-dev
Docker Images
This release is available as a Docker image for amd64 architecture:
Pull the image:
# Specific version
docker pull ghcr.io/Serph91P/mediacurator:2.0.1-dev
# Or latest stable (main branch)
docker pull ghcr.io/Serph91P/mediacurator:latest
# Or development (develop branch)
docker pull ghcr.io/Serph91P/mediacurator:devRun with Docker Compose:
# Download docker-compose.yml
curl -O https://raw.githubusercontent.com/Serph91P/MediaCurator/main/docker-compose.yml
# Start services
docker compose up -dRegistry Information
| Property | Value |
|---|---|
| Registry | GitHub Container Registry (ghcr.io) |
| Image | ghcr.io/Serph91P/mediacurator |
| Version | 2.0.1-dev |
| Architecture | linux/amd64 |
Note: ARM64 builds are not currently provided. Open a feature request if you need ARM support.
Documentation
See the README.md for full configuration options.
Development Build 2.0.0-dev
What's Changed in 2.0.0-dev
Docker Images
This release is available as a Docker image for amd64 architecture:
Pull the image:
# Specific version
docker pull ghcr.io/Serph91P/mediacurator:2.0.0-dev
# Or latest stable (main branch)
docker pull ghcr.io/Serph91P/mediacurator:latest
# Or development (develop branch)
docker pull ghcr.io/Serph91P/mediacurator:devRun with Docker Compose:
# Download docker-compose.yml
curl -O https://raw.githubusercontent.com/Serph91P/MediaCurator/main/docker-compose.yml
# Start services
docker compose up -dRegistry Information
| Property | Value |
|---|---|
| Registry | GitHub Container Registry (ghcr.io) |
| Image | ghcr.io/Serph91P/mediacurator |
| Version | 2.0.0-dev |
| Architecture | linux/amd64 |
Note: ARM64 builds are not currently provided. Open a feature request if you need ARM support.
Documentation
See the README.md for full configuration options.
Release 1.0.0
What's Changed in 1.0.0
Other Changes
- Develop (#47)
- feat: Complete Phase 2 - Users, Activity & Library Detail Views
- Add PlaybackActivity model for detailed playback session tracking
- Add activity API routes (/api/activity/) with list, stats, active sessions
- Add users API routes (/api/users/) with list, detail, activity endpoints
- Extend Emby sync with _sync_active_sessions() for real-time session tracking
- Add Users.tsx page with search, pagination, user stats
- Add UserDetail.tsx page with Overview/Activity tabs and time-based stats
- Add Activity.tsx page with global activity log, active sessions, stats cards
- Add LibraryDetail.tsx with Overview/Media/Activity tabs
- Extend libraries API with /details, /media, /activity endpoints
- Update navigation with Users and Activity menu items
- Library cards now link to detail view
Completes Phase 2 of PLANNED_FEATURES.md roadmap (Jellystat-style features)
- feat: Add PostgreSQL support and fix sync issues
Database:
- Add PostgreSQL support via POSTGRES_* environment variables
- Add asyncpg driver to requirements
- Create docker-compose.postgres.yml for PostgreSQL setup
- Keep SQLite as default for simple deployments
- Add connection pooling for PostgreSQL
Bug Fixes:
- Fix MediaItem.parent_id AttributeError by using series_id (external_id)
- Add parent_id column to MediaItem model for future use
- Fix Users API to fallback to UserWatchHistory when PlaybackActivity is empty
- Fix deprecation warning: regex -> pattern in Query parameters
- Remove 'Jellystat-style' comments from codebase
Improvements:
- Sync jobs now visible in Jobs view with execution logs
- Manual sync shows progress/errors in Jobs history
- Users page shows last watched from UserWatchHistory fallback
-
chore: Update PostgreSQL to version 18 (latest stable)
-
fix: Add PostgreSQL support to database migrations
- Add column_exists() helper for SQLite/PostgreSQL compatibility
- Add table_exists() helper for SQLite/PostgreSQL compatibility
- Use information_schema for PostgreSQL column/table checks
- Use pragma_table_info for SQLite column checks
- Add IF NOT EXISTS for PostgreSQL ALTER TABLE commands
- Use SERIAL PRIMARY KEY for PostgreSQL auto-increment
- chore: Remove ARM64 build to speed up CI
- Build only linux/amd64 for faster builds
- Remove QEMU setup (not needed without cross-platform builds)
- Update release notes to reflect single architecture
- Add note about opening feature request for ARM support
- refactor: Remove Jellystat service type
- MediaCurator now has built-in watch statistics tracking
- Jellystat integration no longer needed
- Removed from frontend ServiceType, service options
- Removed from backend models and schemas
- feat: Add individual service sync jobs in Jobs tab
- Add separate sync jobs for each enabled service
- Show service sync status with running indicator
- Add manual 'Sync Now' button per service
- Display last sync result and duration
- Auto-refresh job status every 5 seconds
- New API endpoint POST /jobs/sync/service/{id}
- Service sync jobs show in job history
- fix: Emby sync creates MediaItems and populates watch statistics
- Sync libraries, movies, series, episodes directly from Emby
- Path-based library association for proper linking
- Track user total_plays and total_watch_time_seconds
- Use external_id for watch data matching
- Include RunTimeTicks for watch time calculation
- refactor(sync): Emby now matches existing items by PATH instead of creating duplicates
BREAKING CHANGE: Emby sync architecture completely redesigned
The core insight:
- Sonarr/Radarr CREATE MediaItems with file paths and metadata
- Emby PROVIDES watch statistics (plays, progress, favorites)
- Previous code created DUPLICATE items from Emby with different external_ids
New behavior:
- _sync_emby() no longer creates MediaItems
- Loads ALL existing items and builds path -> item mapping
- Matches Emby items to Sonarr/Radarr items by normalized PATH
- Updates library_id based on Emby library paths
- Applies watch data (play count, is_watched, favorites, progress) to matched items
- Updates user statistics and watch history
Key changes:
- Removed: external_id_to_item mapping (was causing duplicates)
- Added: path_to_item mapping with normalized paths (/ instead of )
- track_watch_data() now uses paths, skips items not in database
- _sync_active_sessions() simplified to use only path_to_item
- currently_watching_ids -> currently_watching_paths
- Return value updated (Emby reports 0 added, only updated counts)
This fixes:
- Movies showing 0 plays (Emby data was on separate items)
- Libraries showing 0 counts
- Users showing no watch data
- Activity/history not tracking properly
- feat: WebSocket real-time job system + Setup Wizard
WebSocket System:
- Add ConnectionManager (backend/app/core/websocket.py)
- Add /api/ws/jobs endpoint with ping/pong support
- Add progress callbacks to Sonarr, Radarr, Emby sync
- Integrate WebSocket broadcasts in all scheduler jobs
- Add Zustand job store (frontend/src/stores/jobs.ts)
- Add global WebSocket hook with auto-reconnect & toasts
- Rewrite Jobs page with live progress bars & running panel
- Add animated job count badge in Layout sidebar
Setup Wizard:
- Add backend API: /api/setup/status, test-connection, add-service, complete, skip
- Add 5-step frontend wizard (Welcome → Arr → MediaServer → Sync → Complete)
- Add SetupGate redirect in App.tsx for first-time setup
- Enforce service order: Sonarr/Radarr before Emby/Jellyfin
Docs:
- Update DEVELOPMENT_STATUS.md with new features
- fix: resolve all priority 1-5 bugs from DEVELOPMENT_STATUS.md
BUG-001 (CRITICAL): LibraryDetail.tsx complete rewrite
- Fix double /api/ prefix causing 404 errors on all API calls
- Migrate from manual useState/useEffect to React Query (useQuery)
- Remove local formatBytes, formatDuration, formatDate, formatRelativeTime
- Import shared utilities from lib/utils.ts
- Add proper light/dark mode classes throughout
BUG-002 (HIGH): Fix Login.tsx & Register.tsx light-mode
- Replace hardcoded dark classes with light/dark pairs
- Fix: bg-dark-800 → bg-white dark:bg-dark-800
- Fix: text-white → text-gray-900 dark:text-white
- Fix: text-dark-200 → text-gray-700 dark:text-dark-200
- Fix: border-dark-700 → border-gray-200 dark:border-dark-700
- Fix inputs: bg-gray-50 dark:bg-dark-800, proper placeholder colors
BUG-003 (HIGH): Fix ConfirmDialog.tsx light-mode
- Modal background, title, message, footer, cancel button themed
BUG-004 (HIGH): Fix Skeleton.tsx light-mode
- Base skeleton: bg-gray-200 dark:bg-dark-700
- Card/Stats containers: bg-white dark:bg-dark-800
BUG-005 (MEDIUM): Fix Toast theming in main.tsx
- Replace hardcoded hex colors with Tailwind CSS classes
- Toasts now respect light/dark mode automatically
BUG-006 (MEDIUM): Fix German locale in utils.ts
- Change formatDate and formatDateTime from 'de-DE' to 'en-US'
- Add formatDuration utility function for shared use
BUG-008 (MEDIUM): Call fetchUser on app init
- ProtectedRoute now calls fetchUser() on mount when token exists
- Ensures fresh user data instead of potentially stale persisted state
BUG-009 (LOW): Remove duplicate formatBytes from Preview.tsx
- Import from shared lib/utils.ts instead
BUG-010 (LOW): Fix manual debounce in Users.tsx
- Replace setTimeout-based debounce with useDebounce hook
-
docs: update DEVELOPMENT_STATUS.md with Session 3 bugfix results
-
feat: UX improvements, recharts charts, code-splitting
- Layout.tsx: German strings → English (BUG-006 complete)
- ResponsiveTable: Add light mode classes (missing dark: variants)
- Activity.tsx: useDebounce hook, shared utils (formatDurationLong,
formatWatchTime), ResponsiveTable migration, 3× recharts charts
(Daily Plays area, Day-of-Week bar, Hour-of-Day bar) - UserDetail.tsx: shared utils, ResponsiveTable migration
- Staging.tsx: ResponsiveTable migration
- Jobs.tsx: Executions table → ResponsiveTable
- App.tsx: React.lazy code-splitting for 13 pages with Suspense
- utils.ts: Add formatDurationLong and formatWatchTime shared functions
- DEVELOPMENT_STATUS.md: Update for Session 4
- feat: Phase 2 enhancements - ResponsiveTable, filters, dashboard charts
- LibraryDetail.tsx: Migrate Media + Activity tabs to ResponsiveTable
- Activity.tsx: Add Library-Filter dropdown + Items-per-Page selector (10/25/50/100)
- UserDetail.tsx: Add Library-Filter on activity tab
- Dashboard.tsx: Add 3x recharts charts (Daily Plays, Day-of-Week, Hour-of-Day)
- PLANNED_FEATURES.md: Update Phase 2/3 status, add Implementation History
- DEVELOPMENT_STATUS.md: Update for Session 5
- fix: int32 overflow on position_ticks/runtime_ticks + expand-rows + accessibility
BUG-011: PlaybackActivity position_ticks/runtime_ticks Integer→BigInteger
- Emby tick values exceed int32 max (~2.1B), e.g. 70223183889
- Added PostgreSQL migration (ALTER COLUMN TYPE BIGINT)
- Added db.rollback() in _sync_active_sessions and services.py error handlers
ResponsiveTable: Expand-Row support
- New props: onRowClick, isExpanded, expandedContent
- Desktop: expanded content as extra with colSpan
- Mobile: expanded content as div below card
Preview.tsx: Migrated both tables (Series + Movies) to ResponsiveTable
- Series table with expandable season breakdown
- Last page to complete ResponsiveTable migration
Expand-Rows on Activity, UserDetail, LibraryDetail
- IP Address, Device, Play Method (color-coded), Progress bar
- Library Activity API: added ip_address, transcode_video, transcode_audio
ConfirmDialog accessibility
- role=dialog, aria-modal, aria-labelledby, focus trap, Escape key, click-outside
Documentation: DEVELOPMENT_STATUS.md + PLANNED_FEATURES.md updated
- docs: fix inconsistencies in DEVELOPMENT_STATUS.md
- Update commit hash from Pending to ea6e64c
- Mark Preview.tsx migration as complete in Priority 2
- Clean up strikethroughs in Priority 4 (Activity, Dashboard fully done)
- Update ResponsiveTable comment (now used on all pages)
- C...
Development Build 0.0.282-dev
What's Changed in 0.0.282-dev
CI/CD
- Unify versioning with conventional commits auto-bump (#46)
Docker Images
This release is available as a Docker image for amd64 architecture:
Pull the image:
# Specific version
docker pull ghcr.io/Serph91P/mediacurator:0.0.282-dev
# Or latest stable (main branch)
docker pull ghcr.io/Serph91P/mediacurator:latest
# Or development (develop branch)
docker pull ghcr.io/Serph91P/mediacurator:devRun with Docker Compose:
# Download docker-compose.yml
curl -O https://raw.githubusercontent.com/Serph91P/MediaCurator/main/docker-compose.yml
# Start services
docker compose up -dRegistry Information
| Property | Value |
|---|---|
| Registry | GitHub Container Registry (ghcr.io) |
| Image | ghcr.io/Serph91P/mediacurator |
| Version | 0.0.282-dev |
| Architecture | linux/amd64 |
Note: ARM64 builds are not currently provided. Open a feature request if you need ARM support.
Documentation
See the README.md for full configuration options.
Release 0.0.136
What's Changed in 0.0.136
Other Changes
- Develop (#45)
- feat: Complete Phase 2 - Users, Activity & Library Detail Views
- Add PlaybackActivity model for detailed playback session tracking
- Add activity API routes (/api/activity/) with list, stats, active sessions
- Add users API routes (/api/users/) with list, detail, activity endpoints
- Extend Emby sync with _sync_active_sessions() for real-time session tracking
- Add Users.tsx page with search, pagination, user stats
- Add UserDetail.tsx page with Overview/Activity tabs and time-based stats
- Add Activity.tsx page with global activity log, active sessions, stats cards
- Add LibraryDetail.tsx with Overview/Media/Activity tabs
- Extend libraries API with /details, /media, /activity endpoints
- Update navigation with Users and Activity menu items
- Library cards now link to detail view
Completes Phase 2 of PLANNED_FEATURES.md roadmap (Jellystat-style features)
- feat: Add PostgreSQL support and fix sync issues
Database:
- Add PostgreSQL support via POSTGRES_* environment variables
- Add asyncpg driver to requirements
- Create docker-compose.postgres.yml for PostgreSQL setup
- Keep SQLite as default for simple deployments
- Add connection pooling for PostgreSQL
Bug Fixes:
- Fix MediaItem.parent_id AttributeError by using series_id (external_id)
- Add parent_id column to MediaItem model for future use
- Fix Users API to fallback to UserWatchHistory when PlaybackActivity is empty
- Fix deprecation warning: regex -> pattern in Query parameters
- Remove 'Jellystat-style' comments from codebase
Improvements:
- Sync jobs now visible in Jobs view with execution logs
- Manual sync shows progress/errors in Jobs history
- Users page shows last watched from UserWatchHistory fallback
-
chore: Update PostgreSQL to version 18 (latest stable)
-
fix: Add PostgreSQL support to database migrations
- Add column_exists() helper for SQLite/PostgreSQL compatibility
- Add table_exists() helper for SQLite/PostgreSQL compatibility
- Use information_schema for PostgreSQL column/table checks
- Use pragma_table_info for SQLite column checks
- Add IF NOT EXISTS for PostgreSQL ALTER TABLE commands
- Use SERIAL PRIMARY KEY for PostgreSQL auto-increment
- chore: Remove ARM64 build to speed up CI
- Build only linux/amd64 for faster builds
- Remove QEMU setup (not needed without cross-platform builds)
- Update release notes to reflect single architecture
- Add note about opening feature request for ARM support
- refactor: Remove Jellystat service type
- MediaCurator now has built-in watch statistics tracking
- Jellystat integration no longer needed
- Removed from frontend ServiceType, service options
- Removed from backend models and schemas
- feat: Add individual service sync jobs in Jobs tab
- Add separate sync jobs for each enabled service
- Show service sync status with running indicator
- Add manual 'Sync Now' button per service
- Display last sync result and duration
- Auto-refresh job status every 5 seconds
- New API endpoint POST /jobs/sync/service/{id}
- Service sync jobs show in job history
- fix: Emby sync creates MediaItems and populates watch statistics
- Sync libraries, movies, series, episodes directly from Emby
- Path-based library association for proper linking
- Track user total_plays and total_watch_time_seconds
- Use external_id for watch data matching
- Include RunTimeTicks for watch time calculation
- refactor(sync): Emby now matches existing items by PATH instead of creating duplicates
BREAKING CHANGE: Emby sync architecture completely redesigned
The core insight:
- Sonarr/Radarr CREATE MediaItems with file paths and metadata
- Emby PROVIDES watch statistics (plays, progress, favorites)
- Previous code created DUPLICATE items from Emby with different external_ids
New behavior:
- _sync_emby() no longer creates MediaItems
- Loads ALL existing items and builds path -> item mapping
- Matches Emby items to Sonarr/Radarr items by normalized PATH
- Updates library_id based on Emby library paths
- Applies watch data (play count, is_watched, favorites, progress) to matched items
- Updates user statistics and watch history
Key changes:
- Removed: external_id_to_item mapping (was causing duplicates)
- Added: path_to_item mapping with normalized paths (/ instead of )
- track_watch_data() now uses paths, skips items not in database
- _sync_active_sessions() simplified to use only path_to_item
- currently_watching_ids -> currently_watching_paths
- Return value updated (Emby reports 0 added, only updated counts)
This fixes:
- Movies showing 0 plays (Emby data was on separate items)
- Libraries showing 0 counts
- Users showing no watch data
- Activity/history not tracking properly
- feat: WebSocket real-time job system + Setup Wizard
WebSocket System:
- Add ConnectionManager (backend/app/core/websocket.py)
- Add /api/ws/jobs endpoint with ping/pong support
- Add progress callbacks to Sonarr, Radarr, Emby sync
- Integrate WebSocket broadcasts in all scheduler jobs
- Add Zustand job store (frontend/src/stores/jobs.ts)
- Add global WebSocket hook with auto-reconnect & toasts
- Rewrite Jobs page with live progress bars & running panel
- Add animated job count badge in Layout sidebar
Setup Wizard:
- Add backend API: /api/setup/status, test-connection, add-service, complete, skip
- Add 5-step frontend wizard (Welcome → Arr → MediaServer → Sync → Complete)
- Add SetupGate redirect in App.tsx for first-time setup
- Enforce service order: Sonarr/Radarr before Emby/Jellyfin
Docs:
- Update DEVELOPMENT_STATUS.md with new features
- fix: resolve all priority 1-5 bugs from DEVELOPMENT_STATUS.md
BUG-001 (CRITICAL): LibraryDetail.tsx complete rewrite
- Fix double /api/ prefix causing 404 errors on all API calls
- Migrate from manual useState/useEffect to React Query (useQuery)
- Remove local formatBytes, formatDuration, formatDate, formatRelativeTime
- Import shared utilities from lib/utils.ts
- Add proper light/dark mode classes throughout
BUG-002 (HIGH): Fix Login.tsx & Register.tsx light-mode
- Replace hardcoded dark classes with light/dark pairs
- Fix: bg-dark-800 → bg-white dark:bg-dark-800
- Fix: text-white → text-gray-900 dark:text-white
- Fix: text-dark-200 → text-gray-700 dark:text-dark-200
- Fix: border-dark-700 → border-gray-200 dark:border-dark-700
- Fix inputs: bg-gray-50 dark:bg-dark-800, proper placeholder colors
BUG-003 (HIGH): Fix ConfirmDialog.tsx light-mode
- Modal background, title, message, footer, cancel button themed
BUG-004 (HIGH): Fix Skeleton.tsx light-mode
- Base skeleton: bg-gray-200 dark:bg-dark-700
- Card/Stats containers: bg-white dark:bg-dark-800
BUG-005 (MEDIUM): Fix Toast theming in main.tsx
- Replace hardcoded hex colors with Tailwind CSS classes
- Toasts now respect light/dark mode automatically
BUG-006 (MEDIUM): Fix German locale in utils.ts
- Change formatDate and formatDateTime from 'de-DE' to 'en-US'
- Add formatDuration utility function for shared use
BUG-008 (MEDIUM): Call fetchUser on app init
- ProtectedRoute now calls fetchUser() on mount when token exists
- Ensures fresh user data instead of potentially stale persisted state
BUG-009 (LOW): Remove duplicate formatBytes from Preview.tsx
- Import from shared lib/utils.ts instead
BUG-010 (LOW): Fix manual debounce in Users.tsx
- Replace setTimeout-based debounce with useDebounce hook
-
docs: update DEVELOPMENT_STATUS.md with Session 3 bugfix results
-
feat: UX improvements, recharts charts, code-splitting
- Layout.tsx: German strings → English (BUG-006 complete)
- ResponsiveTable: Add light mode classes (missing dark: variants)
- Activity.tsx: useDebounce hook, shared utils (formatDurationLong,
formatWatchTime), ResponsiveTable migration, 3× recharts charts
(Daily Plays area, Day-of-Week bar, Hour-of-Day bar) - UserDetail.tsx: shared utils, ResponsiveTable migration
- Staging.tsx: ResponsiveTable migration
- Jobs.tsx: Executions table → ResponsiveTable
- App.tsx: React.lazy code-splitting for 13 pages with Suspense
- utils.ts: Add formatDurationLong and formatWatchTime shared functions
- DEVELOPMENT_STATUS.md: Update for Session 4
- feat: Phase 2 enhancements - ResponsiveTable, filters, dashboard charts
- LibraryDetail.tsx: Migrate Media + Activity tabs to ResponsiveTable
- Activity.tsx: Add Library-Filter dropdown + Items-per-Page selector (10/25/50/100)
- UserDetail.tsx: Add Library-Filter on activity tab
- Dashboard.tsx: Add 3x recharts charts (Daily Plays, Day-of-Week, Hour-of-Day)
- PLANNED_FEATURES.md: Update Phase 2/3 status, add Implementation History
- DEVELOPMENT_STATUS.md: Update for Session 5
- fix: int32 overflow on position_ticks/runtime_ticks + expand-rows + accessibility
BUG-011: PlaybackActivity position_ticks/runtime_ticks Integer→BigInteger
- Emby tick values exceed int32 max (~2.1B), e.g. 70223183889
- Added PostgreSQL migration (ALTER COLUMN TYPE BIGINT)
- Added db.rollback() in _sync_active_sessions and services.py error handlers
ResponsiveTable: Expand-Row support
- New props: onRowClick, isExpanded, expandedContent
- Desktop: expanded content as extra with colSpan
- Mobile: expanded content as div below card
Preview.tsx: Migrated both tables (Series + Movies) to ResponsiveTable
- Series table with expandable season breakdown
- Last page to complete ResponsiveTable migration
Expand-Rows on Activity, UserDetail, LibraryDetail
- IP Address, Device, Play Method (color-coded), Progress bar
- Library Activity API: added ip_address, transcode_video, transcode_audio
ConfirmDialog accessibility
- role=dialog, aria-modal, aria-labelledby, focus trap, Escape key, click-outside
Documentation: DEVELOPMENT_STATUS.md + PLANNED_FEATURES.md updated
- docs: fix inconsistencies in DEVELOPMENT_STATUS.md
- Update commit hash from Pending to ea6e64c
- Mark Preview.tsx migration as complete in Priority 2
- Clean up strikethroughs in Priority 4 (Activity, Dashboard fully done)
- Update ResponsiveTable comment (now used on all pages)
-...