Finalize dev-1.3 upgrades and Seerr updates
This commit is contained in:
@@ -22,7 +22,8 @@ const SECTION_LABELS: Record<string, string> = {
|
||||
magent: 'Magent',
|
||||
general: 'General',
|
||||
notifications: 'Notifications',
|
||||
jellyseerr: 'Jellyseerr',
|
||||
seerr: 'Seerr',
|
||||
jellyseerr: 'Seerr',
|
||||
jellyfin: 'Jellyfin',
|
||||
artwork: 'Artwork cache',
|
||||
cache: 'Cache Control',
|
||||
@@ -89,7 +90,8 @@ const SECTION_DESCRIPTIONS: Record<string, string> = {
|
||||
'Application runtime, binding, reverse proxy, and manual SSL settings for the Magent UI/API.',
|
||||
notifications:
|
||||
'Notification providers and delivery channel settings used by Magent messaging features.',
|
||||
jellyseerr: 'Connect the request system where users submit content.',
|
||||
seerr: 'Connect Seerr where users submit content requests.',
|
||||
jellyseerr: 'Connect Seerr where users submit content requests.',
|
||||
jellyfin: 'Control Jellyfin login and availability checks.',
|
||||
artwork: 'Cache posters/backdrops and review artwork coverage.',
|
||||
cache: 'Manage saved requests cache and refresh behavior.',
|
||||
@@ -106,6 +108,7 @@ const SETTINGS_SECTION_MAP: Record<string, string | null> = {
|
||||
magent: 'magent',
|
||||
general: 'magent',
|
||||
notifications: 'magent',
|
||||
seerr: 'jellyseerr',
|
||||
jellyseerr: 'jellyseerr',
|
||||
jellyfin: 'jellyfin',
|
||||
artwork: null,
|
||||
@@ -234,6 +237,8 @@ const MAGENT_GROUPS_BY_SECTION: Record<string, Set<string>> = {
|
||||
}
|
||||
|
||||
const SETTING_LABEL_OVERRIDES: Record<string, string> = {
|
||||
jellyseerr_base_url: 'Seerr base URL',
|
||||
jellyseerr_api_key: 'Seerr API key',
|
||||
magent_application_url: 'Application URL',
|
||||
magent_application_port: 'Application port',
|
||||
magent_api_url: 'API URL',
|
||||
@@ -278,6 +283,7 @@ const labelFromKey = (key: string) =>
|
||||
SETTING_LABEL_OVERRIDES[key] ??
|
||||
key
|
||||
.replaceAll('_', ' ')
|
||||
.replace('jellyseerr', 'Seerr')
|
||||
.replace('base url', 'URL')
|
||||
.replace('api key', 'API key')
|
||||
.replace('quality profile id', 'Quality profile ID')
|
||||
@@ -289,7 +295,7 @@ const labelFromKey = (key: string) =>
|
||||
.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('requests data source', 'Request source (cache vs Seerr)')
|
||||
.replace('jellyfin public url', 'Jellyfin public URL')
|
||||
.replace('jellyfin sync to arr', 'Sync Jellyfin to Sonarr/Radarr')
|
||||
.replace('artwork cache mode', 'Artwork cache mode')
|
||||
@@ -352,6 +358,21 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
const [liveStreamConnected, setLiveStreamConnected] = useState(false)
|
||||
const requestsSyncRef = useRef<any | null>(null)
|
||||
const artworkPrefetchRef = useRef<any | null>(null)
|
||||
const computeProgressPercent = (
|
||||
completedValue: unknown,
|
||||
totalValue: unknown,
|
||||
statusValue: unknown
|
||||
): number => {
|
||||
if (String(statusValue).toLowerCase() === 'completed') {
|
||||
return 100
|
||||
}
|
||||
const completed = Number(completedValue)
|
||||
const total = Number(totalValue)
|
||||
if (!Number.isFinite(completed) || !Number.isFinite(total) || total <= 0 || completed <= 0) {
|
||||
return 0
|
||||
}
|
||||
return Math.max(0, Math.min(100, Math.round((completed / total) * 100)))
|
||||
}
|
||||
|
||||
const loadSettings = useCallback(async () => {
|
||||
const baseUrl = getApiBase()
|
||||
@@ -642,7 +663,7 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
magent_notify_webhook_url:
|
||||
'Generic webhook endpoint for custom integrations or automation flows.',
|
||||
jellyseerr_base_url:
|
||||
'Base URL for your Jellyseerr server (FQDN or IP). Scheme is optional.',
|
||||
'Base URL for your Seerr server (FQDN or IP). Scheme is optional.',
|
||||
jellyseerr_api_key: 'API key used to read requests and status.',
|
||||
jellyfin_base_url:
|
||||
'Jellyfin server URL for logins and lookups (FQDN or IP). Scheme is optional.',
|
||||
@@ -677,7 +698,7 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
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.',
|
||||
'Pick where Magent should read requests from. Cache-only avoids Seerr lookups on reads.',
|
||||
log_level: 'How much detail is written to the activity log.',
|
||||
log_file: 'Where the activity log is stored.',
|
||||
site_build_number: 'Build number shown in the account menu (auto-set from releases).',
|
||||
@@ -805,6 +826,13 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
|
||||
const syncRequests = async () => {
|
||||
setRequestsSyncStatus(null)
|
||||
setRequestsSync({
|
||||
status: 'running',
|
||||
stored: 0,
|
||||
total: 0,
|
||||
skip: 0,
|
||||
message: 'Starting sync',
|
||||
})
|
||||
try {
|
||||
const baseUrl = getApiBase()
|
||||
const response = await authFetch(`${baseUrl}/admin/requests/sync`, {
|
||||
@@ -829,6 +857,13 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
|
||||
const syncRequestsDelta = async () => {
|
||||
setRequestsSyncStatus(null)
|
||||
setRequestsSync({
|
||||
status: 'running',
|
||||
stored: 0,
|
||||
total: 0,
|
||||
skip: 0,
|
||||
message: 'Starting delta sync',
|
||||
})
|
||||
try {
|
||||
const baseUrl = getApiBase()
|
||||
const response = await authFetch(`${baseUrl}/admin/requests/sync/delta`, {
|
||||
@@ -853,6 +888,12 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
|
||||
const prefetchArtwork = async () => {
|
||||
setArtworkPrefetchStatus(null)
|
||||
setArtworkPrefetch({
|
||||
status: 'running',
|
||||
processed: 0,
|
||||
total: 0,
|
||||
message: 'Starting artwork caching',
|
||||
})
|
||||
try {
|
||||
const baseUrl = getApiBase()
|
||||
const response = await authFetch(`${baseUrl}/admin/requests/artwork/prefetch`, {
|
||||
@@ -877,6 +918,12 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
|
||||
const prefetchArtworkMissing = async () => {
|
||||
setArtworkPrefetchStatus(null)
|
||||
setArtworkPrefetch({
|
||||
status: 'running',
|
||||
processed: 0,
|
||||
total: 0,
|
||||
message: 'Starting missing artwork caching',
|
||||
})
|
||||
try {
|
||||
const baseUrl = getApiBase()
|
||||
const response = await authFetch(
|
||||
@@ -1202,7 +1249,7 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
setMaintenanceBusy(true)
|
||||
if (typeof window !== 'undefined') {
|
||||
const ok = window.confirm(
|
||||
'This will perform a nuclear reset: clear cached requests/history, wipe non-admin users, invites, and profiles, then re-sync users and requests from Jellyseerr. Continue?'
|
||||
'This will perform a nuclear reset: clear cached requests/history, wipe non-admin users, invites, and profiles, then re-sync users and requests from Seerr. Continue?'
|
||||
)
|
||||
if (!ok) {
|
||||
setMaintenanceBusy(false)
|
||||
@@ -1264,7 +1311,7 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
|
||||
const cacheSourceLabel =
|
||||
formValues.requests_data_source === 'always_js'
|
||||
? 'Jellyseerr direct'
|
||||
? 'Seerr direct'
|
||||
: formValues.requests_data_source === 'prefer_cache'
|
||||
? 'Saved requests only'
|
||||
: 'Saved requests only'
|
||||
@@ -1485,22 +1532,16 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={`progress ${artworkPrefetch.total ? '' : 'progress-indeterminate'} ${
|
||||
artworkPrefetch.status === 'completed' ? 'progress-complete' : ''
|
||||
}`}
|
||||
className={`progress ${artworkPrefetch.status === 'completed' ? 'progress-complete' : ''}`}
|
||||
>
|
||||
<div
|
||||
className="progress-fill"
|
||||
style={{
|
||||
width:
|
||||
artworkPrefetch.status === 'completed'
|
||||
? '100%'
|
||||
: artworkPrefetch.total
|
||||
? `${Math.min(
|
||||
100,
|
||||
Math.round((artworkPrefetch.processed / artworkPrefetch.total) * 100)
|
||||
)}%`
|
||||
: '30%',
|
||||
width: `${computeProgressPercent(
|
||||
artworkPrefetch.processed,
|
||||
artworkPrefetch.total,
|
||||
artworkPrefetch.status
|
||||
)}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -1517,22 +1558,16 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={`progress ${requestsSync.total ? '' : 'progress-indeterminate'} ${
|
||||
requestsSync.status === 'completed' ? 'progress-complete' : ''
|
||||
}`}
|
||||
className={`progress ${requestsSync.status === 'completed' ? 'progress-complete' : ''}`}
|
||||
>
|
||||
<div
|
||||
className="progress-fill"
|
||||
style={{
|
||||
width:
|
||||
requestsSync.status === 'completed'
|
||||
? '100%'
|
||||
: requestsSync.total
|
||||
? `${Math.min(
|
||||
100,
|
||||
Math.round((requestsSync.stored / requestsSync.total) * 100)
|
||||
)}%`
|
||||
: '30%',
|
||||
width: `${computeProgressPercent(
|
||||
requestsSync.stored,
|
||||
requestsSync.total,
|
||||
requestsSync.status
|
||||
)}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -1860,7 +1895,7 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
}))
|
||||
}
|
||||
>
|
||||
<option value="always_js">Always use Jellyseerr (slower)</option>
|
||||
<option value="always_js">Always use Seerr (slower)</option>
|
||||
<option value="prefer_cache">
|
||||
Use saved requests only (fastest)
|
||||
</option>
|
||||
@@ -2005,7 +2040,7 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
<h2>Maintenance</h2>
|
||||
</div>
|
||||
<div className="status-banner">
|
||||
Emergency tools. Use with care: flush + resync now performs a nuclear wipe of non-admin users, invite links, profiles, cached requests, and history before re-syncing Jellyseerr users/requests.
|
||||
Emergency tools. Use with care: flush + resync now performs a nuclear wipe of non-admin users, invite links, profiles, cached requests, and history before re-syncing Seerr users/requests.
|
||||
</div>
|
||||
{maintenanceStatus && <div className="status-banner">{maintenanceStatus}</div>}
|
||||
<div className="maintenance-grid">
|
||||
|
||||
@@ -2,6 +2,7 @@ import { notFound } from 'next/navigation'
|
||||
import SettingsPage from '../SettingsPage'
|
||||
|
||||
const ALLOWED_SECTIONS = new Set([
|
||||
'seerr',
|
||||
'jellyseerr',
|
||||
'jellyfin',
|
||||
'artwork',
|
||||
@@ -20,12 +21,13 @@ const ALLOWED_SECTIONS = new Set([
|
||||
])
|
||||
|
||||
type PageProps = {
|
||||
params: { section: string }
|
||||
params: Promise<{ section: string }>
|
||||
}
|
||||
|
||||
export default function AdminSectionPage({ params }: PageProps) {
|
||||
if (!ALLOWED_SECTIONS.has(params.section)) {
|
||||
export default async function AdminSectionPage({ params }: PageProps) {
|
||||
const { section } = await params
|
||||
if (!ALLOWED_SECTIONS.has(section)) {
|
||||
notFound()
|
||||
}
|
||||
return <SettingsPage section={params.section} />
|
||||
return <SettingsPage section={section} />
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ const REQUEST_FLOW: FlowStage[] = [
|
||||
},
|
||||
{
|
||||
title: 'Request intake',
|
||||
input: 'Jellyseerr request ID',
|
||||
input: 'Seerr request ID',
|
||||
action: 'Magent snapshots request + media metadata',
|
||||
output: 'Unified request state',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user