Tidy release picker profile toggle and repair display text
Magent CI/CD / verify (push) Successful in 2m15s
Magent CI/CD / deploy-prod (push) Skipped
Magent CI/CD / deploy-beta (push) Skipped

This commit is contained in:
2026-09-11 22:56:21 +12:00
parent 956fb3ecb1
commit 835d9c8de3
3 changed files with 54 additions and 5 deletions
+1 -1
View File
@@ -3401,7 +3401,7 @@ async def action_search(request_id: str, user: Dict[str, str] = Depends(get_curr
if len(results) > len(releases): if len(results) > len(releases):
message += ' Duplicate results are combined; up to 200 ranked releases are shown.' message += ' Duplicate results are combined; up to 200 ranked releases are shown.'
if total_missing: if total_missing:
message += f' Searched episodes {offset + 1}?{min(offset + 20, total_missing)} of {total_missing} missing monitored episodes.' message += f' Searched {min(20, max(0, total_missing - offset))} of {total_missing} missing monitored episodes.'
await asyncio.to_thread(save_action, request_id, 'search_releases', 'Search and choose a download', 'ok', message) await asyncio.to_thread(save_action, request_id, 'search_releases', 'Search and choose a download', 'ok', message)
return {'status': 'ok', 'collector': collector, 'qualityFiltered': False, 'message': message, return {'status': 'ok', 'collector': collector, 'qualityFiltered': False, 'message': message,
'outcome': 'matches' if approved else 'attention', 'rejectionReasons': rejection_reasons, 'outcome': 'matches' if approved else 'attention', 'rejectionReasons': rejection_reasons,
+49
View File
@@ -4912,3 +4912,52 @@ textarea:focus {
max-height: calc(100vh - 84px); max-height: calc(100vh - 84px);
} }
} }
/* Release picker control: independent of the user-management page stylesheet. */
.request-release-profile-toggle {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 20px;
padding: 14px 16px;
border: 1px solid var(--ops-border, #393941);
border-radius: 8px;
background: rgba(255, 255, 255, 0.025);
cursor: pointer;
}
.request-release-profile-toggle > span { display: grid; gap: 5px; min-width: 0; }
.request-release-profile-toggle strong { font-size: 0.85rem; line-height: 1.4; }
.request-release-profile-toggle small { color: var(--ops-muted); font-size: 0.75rem; line-height: 1.5; }
.request-release-profile-toggle input[type="checkbox"] {
appearance: none;
position: relative;
display: block;
width: 38px;
min-width: 38px;
height: 22px;
min-height: 22px;
margin: 0;
padding: 0;
border: 1px solid #62616d !important;
border-radius: 999px !important;
background: #45444e !important;
box-shadow: none;
cursor: pointer;
transition: background 0.15s;
}
.request-release-profile-toggle input[type="checkbox"]::before {
content: "";
position: absolute;
width: 14px;
height: 14px;
left: 3px;
top: 3px;
border-radius: 50%;
background: #eeedf5;
transition: transform 0.15s;
}
.request-release-profile-toggle input[type="checkbox"]:checked { background: #c7bdff !important; border-color: #c7bdff !important; }
.request-release-profile-toggle input[type="checkbox"]:checked::before { transform: translateX(16px); background: #181529; }
.request-release-profile-toggle input[type="checkbox"]:focus-visible { outline: 2px solid #c7bdff; outline-offset: 4px; }
.request-release-profile-toggle input[type="checkbox"]:disabled { cursor: wait; opacity: 0.5; }
+4 -4
View File
@@ -968,7 +968,7 @@ export default function RequestTimelinePage() {
<div> <div>
<span className="section-kicker">Results from {releaseCollector ?? (snapshot.request_type === 'tv' ? 'Sonarr' : 'Radarr')}</span> <span className="section-kicker">Results from {releaseCollector ?? (snapshot.request_type === 'tv' ? 'Sonarr' : 'Radarr')}</span>
<h2 id="release-picker-title">Choose an available download</h2> <h2 id="release-picker-title">Choose an available download</h2>
<p>Search results include the collector?s rejection reasons. The assigned profile stays unchanged.</p> <p>Review available releases and any rejection reasons. The assigned profile stays unchanged.</p>
</div> </div>
<button type="button" className="ghost-button" onClick={closeReleasePicker} disabled={busyAction?.startsWith('grab:')}>Close</button> <button type="button" className="ghost-button" onClick={closeReleasePicker} disabled={busyAction?.startsWith('grab:')}>Close</button>
</header> </header>
@@ -1005,9 +1005,9 @@ export default function RequestTimelinePage() {
</div> </div>
)} )}
{canIgnoreProfileLimits && <label className="feature-access-row"> {canIgnoreProfileLimits && <label className="request-release-profile-toggle">
<span><strong>Ignore profile limits</strong><small>Allow releases outside this title&apos;s profile. You&apos;ll confirm each download.</small></span>
<input type="checkbox" checked={ignoreProfileLimits} disabled={Boolean(busyAction)} onChange={event => setIgnoreProfileLimits(event.target.checked)} /> <input type="checkbox" checked={ignoreProfileLimits} disabled={Boolean(busyAction)} onChange={event => setIgnoreProfileLimits(event.target.checked)} />
<span><strong>Ignore profile limits</strong><small>Enable selection outside quality, size, language and custom-format limits for this search. Each download requires confirmation. Other rejection reasons remain blocked.</small></span>
</label>} </label>}
{nextSearchOffset !== null && <button type="button" className="ghost-button" disabled={Boolean(busyAction)} onClick={() => void runAction({ id: 'search_releases', label: 'Search next missing episodes', risk: 'low', requires_confirmation: false }, nextSearchOffset)}>Search next missing episodes</button>} {nextSearchOffset !== null && <button type="button" className="ghost-button" disabled={Boolean(busyAction)} onClick={() => void runAction({ id: 'search_releases', label: 'Search next missing episodes', risk: 'low', requires_confirmation: false }, nextSearchOffset)}>Search next missing episodes</button>}
{releaseOptions.length > 0 && ( {releaseOptions.length > 0 && (
@@ -1029,7 +1029,7 @@ export default function RequestTimelinePage() {
{release.indexer ?? 'Unknown indexer'} · {release.seeders ?? 0} seeders · {formatBytes(release.size)} {release.indexer ?? 'Unknown indexer'} · {release.seeders ?? 0} seeders · {formatBytes(release.size)}
{typeof release.customFormatScore === 'number' ? ` · Score ${release.customFormatScore}` : ''} {typeof release.customFormatScore === 'number' ? ` · Score ${release.customFormatScore}` : ''}
</span> </span>
{!!release.rejections?.length && <small className="release-rejection-reasons">{release.rejections.join(' ? ')}</small>} {!!release.rejections?.length && <small className="release-rejection-reasons">{release.rejections.join('; ')}</small>}
{isBestPick && <small>This is the highest-ranked release approved by {releaseCollector ?? 'the collector'}.</small>} {isBestPick && <small>This is the highest-ranked release approved by {releaseCollector ?? 'the collector'}.</small>}
</div> </div>
<button <button