diff --git a/.build_number b/.build_number index 4d3f0fe..1c7363e 100644 --- a/.build_number +++ b/.build_number @@ -1 +1 @@ -271261149 +271261159 diff --git a/frontend/app/admin/SettingsPage.tsx b/frontend/app/admin/SettingsPage.tsx index f7e536b..0575884 100644 --- a/frontend/app/admin/SettingsPage.tsx +++ b/frontend/app/admin/SettingsPage.tsx @@ -28,7 +28,7 @@ const SECTION_LABELS: Record = { prowlarr: 'Prowlarr', qbittorrent: 'qBittorrent', log: 'Activity log', - requests: 'Request syncing', + requests: 'Request sync', site: 'Site', } @@ -45,7 +45,7 @@ const SECTION_DESCRIPTIONS: Record = { radarr: 'Movie automation settings.', prowlarr: 'Indexer search settings.', qbittorrent: 'Downloader connection settings.', - requests: 'Sync and refresh cadence for requests.', + requests: 'Control how often requests are refreshed and cleaned up.', log: 'Activity log for troubleshooting.', site: 'Sitewide banner, version, and changelog details.', } @@ -73,13 +73,13 @@ const labelFromKey = (key: string) => .replace('quality profile id', 'Quality profile ID') .replace('root folder', 'Root folder') .replace('qbittorrent', 'qBittorrent') - .replace('requests sync ttl minutes', 'Refresh saved requests if older than (minutes)') - .replace('requests poll interval seconds', 'Background refresh check (seconds)') - .replace('requests delta sync interval minutes', 'Check for new or updated requests every (minutes)') - .replace('requests full sync time', 'Full refresh time (24h)') - .replace('requests cleanup time', 'Clean up old history time (24h)') - .replace('requests cleanup days', 'Remove history older than (days)') - .replace('requests data source', 'Where requests are loaded from') + .replace('requests sync ttl minutes', 'Saved request refresh TTL (minutes)') + .replace('requests poll interval seconds', 'Full refresh check interval (seconds)') + .replace('requests delta sync interval minutes', 'Delta sync interval (minutes)') + .replace('requests full sync time', 'Daily full refresh time (24h)') + .replace('requests cleanup time', 'Daily history cleanup time (24h)') + .replace('requests cleanup days', 'History retention window (days)') + .replace('requests data source', 'Request source (cache vs Jellyseerr)') .replace('jellyfin public url', 'Jellyfin public URL') .replace('jellyfin sync to arr', 'Sync Jellyfin to Sonarr/Radarr') .replace('artwork cache mode', 'Artwork cache mode') @@ -277,6 +277,22 @@ export default function SettingsPage({ section }: SettingsPageProps) { const cacheSettingKeys = new Set(['requests_sync_ttl_minutes', 'requests_data_source']) const artworkSettingKeys = new Set(['artwork_cache_mode']) const hiddenSettingKeys = new Set([...cacheSettingKeys, ...artworkSettingKeys]) + const requestSettingOrder = [ + 'requests_poll_interval_seconds', + 'requests_delta_sync_interval_minutes', + 'requests_full_sync_time', + 'requests_cleanup_time', + 'requests_cleanup_days', + ] + const sortByOrder = (items: AdminSetting[], order: string[]) => { + const position = new Map(order.map((key, index) => [key, index])) + return [...items].sort((a, b) => { + const aIndex = position.get(a.key) ?? Number.POSITIVE_INFINITY + const bIndex = position.get(b.key) ?? Number.POSITIVE_INFINITY + if (aIndex !== bIndex) return aIndex - bIndex + return a.key.localeCompare(b.key) + }) + } const cacheSettings = settings.filter((setting) => cacheSettingKeys.has(setting.key)) const artworkSettings = settings.filter((setting) => artworkSettingKeys.has(setting.key)) const settingsSections = isCacheSection @@ -287,12 +303,17 @@ export default function SettingsPage({ section }: SettingsPageProps) { : visibleSections.map((sectionKey) => ({ key: sectionKey, title: SECTION_LABELS[sectionKey] ?? sectionKey, - items: - sectionKey === 'requests' || sectionKey === 'artwork' - ? (groupedSettings[sectionKey] ?? []).filter( - (setting) => !hiddenSettingKeys.has(setting.key) - ) - : groupedSettings[sectionKey] ?? [], + items: (() => { + const sectionItems = groupedSettings[sectionKey] ?? [] + const filtered = + sectionKey === 'requests' || sectionKey === 'artwork' + ? sectionItems.filter((setting) => !hiddenSettingKeys.has(setting.key)) + : sectionItems + if (sectionKey === 'requests') { + return sortByOrder(filtered, requestSettingOrder) + } + return filtered + })(), })) const showLogs = section === 'logs' const showMaintenance = section === 'maintenance' @@ -332,11 +353,11 @@ export default function SettingsPage({ section }: SettingsPageProps) { qbittorrent_password: 'qBittorrent login password.', requests_sync_ttl_minutes: 'How long saved requests stay fresh before a refresh is needed.', requests_poll_interval_seconds: - 'How often Magent wakes up to check if the cache is stale and needs a full refresh.', + 'How often Magent checks if a full refresh should run.', requests_delta_sync_interval_minutes: - 'How often we actively poll for new or updated requests (delta sync).', - requests_full_sync_time: 'Daily time to refresh the full request list.', - requests_cleanup_time: 'Daily time to trim old history.', + 'How often we poll for new or updated requests.', + requests_full_sync_time: 'Daily time to rebuild the full request cache.', + requests_cleanup_time: 'Daily time to trim old request history.', requests_cleanup_days: 'History older than this is removed during cleanup.', requests_data_source: 'Pick where Magent should read requests from. Cache-only avoids Jellyseerr lookups on reads.', @@ -782,7 +803,9 @@ export default function SettingsPage({ section }: SettingsPageProps) { .map((sectionGroup) => (
-

{sectionGroup.key === 'requests' ? 'Sync controls' : sectionGroup.title}

+

+ {sectionGroup.key === 'requests' ? 'Request sync controls' : sectionGroup.title} +

{sectionGroup.key === 'sonarr' && (
- Full refresh reloads the entire list. Quick refresh only checks recent changes. + Full refresh rebuilds the entire cache. Delta sync only checks new or updated + requests.
)} @@ -877,8 +901,8 @@ export default function SettingsPage({ section }: SettingsPageProps) { )} {showRequestsExtras && sectionGroup.key === 'requests' && (
- Background refresh checks only decide when to run a full refresh. The delta sync - interval actively polls for new or updated requests. + Full refresh checks only decide when to run a full refresh. The delta sync interval + polls for new or updated requests.
)} {showArtworkExtras && sectionGroup.key === 'artwork' && artworkPrefetch && ( diff --git a/frontend/app/ui/AdminSidebar.tsx b/frontend/app/ui/AdminSidebar.tsx index d877f0a..a3bcd2f 100644 --- a/frontend/app/ui/AdminSidebar.tsx +++ b/frontend/app/ui/AdminSidebar.tsx @@ -17,7 +17,7 @@ const NAV_GROUPS = [ { title: 'Requests', items: [ - { href: '/admin/requests', label: 'Request syncing' }, + { href: '/admin/requests', label: 'Request sync' }, { href: '/admin/cache', label: 'Cache Control' }, ], },