Bug #954: The initialize_superuser endpoint only checked is_superuser=True, missing admin accounts created via the API (user_level=10). This caused users to intermittently see the "Create Super User" page instead of login.
Feature #1004: Admins can now disable/enable user accounts. Disabled users are blocked from JWT login, token refresh, XC API access, and all
authenticated endpoints. The last active admin cannot be disabled.
Files changed:
Backend:
- apps/accounts/api_views.py — Superuser check uses user_level>=10; token refresh blocks disabled users
- apps/accounts/models.py — CustomUserManager ensures create_superuser() always sets user_level=10
- apps/accounts/serializers.py — Last-admin protection guard; respect is_active on user creation
- apps/accounts/tests.py — new tests covering superuser detection, token
refresh blocking, last-admin protection, disabled user login/access
- apps/backups/api_views.py — Switched from DRF's IsAdminUser (checks is_staff) to app's IsAdmin (checks user_level) on all 8 endpoints
- apps/backups/tests.py — new tests verifying user_level-based admin permission on backup endpoints
- apps/output/views.py — is_active check on xc_get_user(), xc_movie_stream(), xc_series_stream()
- apps/proxy/ts_proxy/views.py — is_active check on stream_xc()
- core/api_views.py — Admin notification filter uses user_level>=10
- core/developer_notifications.py — Admin checks use user_level>=10
Frontend:
- frontend/src/App.jsx — Null-safe superuser existence check
- frontend/src/components/forms/User.jsx — Account Enabled switch with
self-disable prevention and tooltip
- frontend/src/components/tables/UsersTable.jsx — Status column (Active/Disabled badge)
- The User-Agent field was not correctly loaded from the existing profile when opening the edit modal. (Fixes#650)
- Replaced the plain command text field with a dropdown listing built-in tools (FFmpeg, Streamlink, VLC, yt-dlp) plus a Custom option that reveals a free-text input. Each built-in now shows its default parameter string as a live example in the Parameters field description, updating as the command selection changes. Added descriptive help text to all fields to improve clarity.
Add cron expression support for M3U and EPG refreshes with interactive
builder modal and quick reference examples. Refactor backup scheduling
to use shared ScheduleInput component for consistency.
- New CronBuilder modal with preset buttons and custom field editors
- Info popover with common cron expression examples
- Shared ScheduleInput component for interval/cron toggle
- Fix credential extraction in get_transformed_credentials() using negative
indices anchored to the known tail structure instead of hardcoded indices
that break when server URLs contain sub-paths
- Fix EPG URL construction in M3U form to normalize server URL to origin
before appending xmltv.php endpoint
XC accounts with sub-paths in their server URL (e.g., http://server.com/portal/a/) caused two failures: profile refresh extracted wrong credentials from the URL path due to hardcoded array indices, and EPG auto-creation appended xmltv.php under the sub-path instead of at the domain root. Both fixes normalize away the sub-path using the same approach the backend's XCClient already uses for API calls.
Real-time notifications for system events and alerts
Per-user notification management and dismissal
Update check on startup and every 24 hours to notify users of available versions
Notification center UI component
Automatic cleanup of expired notifications
Enhanced EPG matching UX by adding radio button toggle to more clearly define optional advanced options. Also added test coverage for stability.
Frontend Changes:
- Added radio button UI to EPGMatchModal for default/advanced mode selection
- Default mode: Uses built-in normalization
- Advanced mode: Shows TagsInput fields for custom prefixes/suffixes/strings
- Mode persists across sessions and settings are preserved when switching modes
Test Coverage:
- Created EPGMatchModal.test.jsx with test cases covering:
- Component rendering and mode switching
- Form submission and settings persistence
- Error handling and UI text variations
- Added test cases to SettingsUtils.test.js for EPG mode handling:
- epg_match_mode routing to epg_settings group
- Settings creation and preservation
Since advanced settings are saved persistently but ignored when default settings are used, this adds an easy way in the future to add additional advanced settings to EPG matching like region influence, match aggression, and match preview, since users can easily switch between modes.
- Add three-dot menu for toggling column visibility (Name, Group, M3U, TVG-ID, Source Info)
- Add Source Info column showing stream resolution and codec with detailed tooltip
- Column visibility persists to localStorage with proper migration handling for existing users
- Default hidden columns: TVG-ID, Source Info
- Auto-refresh table data when video preview closes to show updated stream stats
- Fix state spreading bug in CustomTable/index.jsx (was spreading options.state instead of state)
I think the people are gonna like this one!
Changes:
- frontend/src/components/tables/StreamsTable.jsx
Added TVG-ID column to streams table
Added search filter input in column header
Added sort button for ascending/descending order
Column follows same pattern as Name, Group, and M3U columns
- apps/channels/api_views.py
Added TVG-ID to filterable fields
Added TVG-ID to sortable fields
Enables backend support for search and sorting
Users can now view, search, and sort streams by their TVG-ID directly in the Streams table. Fulfills #866
Relocate EPG matching configuration (ignore prefixes, suffixes, and custom
strings) from the Settings page to a dedicated modal that opens when triggering auto-match.
Changes:
- frontend/src/components/modals/EPGMatchModal.jsx: Added new modal that combines EPG match settings configuration with the auto-match trigger.
- frontend/src/components/tables/ChannelsTable/ChannelTableHeader.jsx: Added EPGMatchModal import and state. Changed menu item to open modal instead of directly calling API. Removed inline matchEpg function (logic moved to modal)
- frontend/src/pages/Settings.jsx: Removed EPG Settings accordion (now accessible via modal)
- frontend/src/components/forms/settings/EPGSettingsForm.jsx: Deleted - functionality replaced by EPGMatchModal
- frontend/src/utils/forms/settings/EPGSettingsFormUtils.js: Deleted - was only used by EPGSettingsForm (now defunct)