Files
Huntarr.io/frontend/templates/components/settings_sizes_section.html
2026-02-18 09:14:02 -05:00

851 lines
33 KiB
HTML

<section id="settingsSizesSection" class="content-section" style="display: none;">
<div id="sizesManagementContainer" class="app-content-panel">
<div id="settings-sizes-no-indexers" class="settings-no-indexers" style="display: none;">
<i class="fas fa-satellite-dish" aria-hidden="true"></i>
<p class="no-instances-title">No indexers configured</p>
<p class="no-instances-desc">Configure at least one indexer in the Index Master Pool to use this section.</p>
<a href="./#indexer-hunt" class="no-instances-action-btn"><i class="fas fa-plus"></i> Add Indexer</a>
</div>
<div id="settings-sizes-no-clients" class="settings-no-clients" style="display: none;">
<i class="fas fa-download" aria-hidden="true"></i>
<p class="no-instances-title">No clients configured</p>
<p class="no-instances-desc">Configure at least one download client in Settings → Clients to use this section.</p>
<a href="./#settings-clients" class="no-instances-action-btn"><i class="fas fa-cog"></i> Configure Clients</a>
</div>
<div id="settings-sizes-content-wrapper" style="display: none;">
{% from 'components/page_header_partial.html' import page_header %}
{{ page_header(back_href='./#media-hunt-settings', parent_icon='fas fa-weight-hanging', parent_name='Media Hunt', current_name='Sizes', save_button_id='sizes-save-btn') }}
<div class="media-hunt-settings-instance-bar" style="margin-bottom: 16px;">
<select id="settings-sizes-instance-select" class="control-select" aria-label="Instance">
<option value="">Loading...</option>
</select>
</div>
<div class="settings-group sizes-settings-group">
<div class="sizes-header">
<div class="sizes-header-left">
<h3><i class="fas fa-weight-hanging"></i> Sizes</h3>
<p class="sizes-subtitle">Set file size limits per quality. Values are in <strong>MB per minute</strong> of runtime. Limits are automatically adjusted for the media length.</p>
</div>
<div class="sizes-header-actions">
<button type="button" class="btn-sizes-reset" id="sizes-reset-btn" title="Reset all to defaults">
<i class="fas fa-undo"></i> Reset Defaults
</button>
</div>
</div>
<div id="sizes-quality-list" class="sizes-quality-list">
<!-- Populated by JS -->
<div class="sizes-loading"><i class="fas fa-spinner fa-spin"></i> Loading...</div>
</div>
</div>
</div>
</div>
</section>
<style>
#settingsSizesSection {
width: 100%;
min-height: 100vh;
overflow-y: auto;
overflow-x: hidden;
}
#settingsSizesSection.active {
display: block !important;
}
#sizesManagementContainer {
width: 100%;
padding: 20px;
margin: 0;
background-color: transparent;
box-shadow: none;
border: none;
}
.sizes-settings-group {
background: rgba(15, 23, 42, 0.4);
border: 1px solid rgba(148, 163, 184, 0.08);
border-radius: 12px;
padding: 24px;
margin: 12px 0 20px 0;
}
.sizes-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid rgba(148, 163, 184, 0.12);
flex-wrap: wrap;
}
.sizes-header-left h3 {
margin: 0 0 6px 0;
font-size: 1.1rem;
font-weight: 600;
color: #f1f5f9;
background: none;
box-shadow: none;
border: none;
padding: 0;
}
.sizes-header-left h3 i {
margin-right: 8px;
color: #6366f1;
}
.sizes-subtitle {
margin: 0;
font-size: 0.82rem;
color: #94a3b8;
max-width: 600px;
line-height: 1.4;
}
.sizes-header-actions {
display: flex;
gap: 10px;
align-items: center;
flex-shrink: 0;
}
.btn-sizes-reset {
background: rgba(71, 85, 105, 0.4);
border: 1px solid rgba(148, 163, 184, 0.2);
color: #94a3b8;
padding: 8px 14px;
border-radius: 8px;
font-size: 0.82rem;
cursor: pointer;
transition: all 0.2s;
}
.btn-sizes-reset:hover {
background: rgba(71, 85, 105, 0.6);
color: #f1f5f9;
}
/* Group headers */
.sizes-group-header {
display: flex;
align-items: center;
gap: 10px;
margin: 20px 0 10px 0;
padding: 0;
}
.sizes-group-header:first-child {
margin-top: 0;
}
.sizes-group-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 12px;
border-radius: 6px;
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.02em;
text-transform: uppercase;
color: #f1f5f9;
}
.sizes-group-badge-low { background: rgba(100, 116, 139, 0.4); color: #94a3b8; }
.sizes-group-badge-sd { background: rgba(234, 179, 8, 0.2); color: #fbbf24; }
.sizes-group-badge-720 { background: rgba(34, 197, 94, 0.2); color: #4ade80; }
.sizes-group-badge-1080 { background: rgba(99, 102, 241, 0.2); color: #a5b4fc; }
.sizes-group-badge-2160 { background: rgba(168, 85, 247, 0.2); color: #c084fc; }
.sizes-group-badge-ultra { background: rgba(236, 72, 153, 0.2); color: #f472b6; }
.sizes-group-line {
flex: 1;
height: 1px;
background: rgba(148, 163, 184, 0.1);
}
/* Quality rows */
.sizes-quality-row {
display: grid;
grid-template-columns: 140px 1fr;
align-items: center;
gap: 16px;
padding: 10px 14px;
border-radius: 8px;
margin-bottom: 4px;
transition: background 0.15s;
}
.sizes-quality-row:hover {
background: rgba(30, 41, 59, 0.5);
}
.sizes-quality-name {
font-size: 0.88rem;
font-weight: 500;
color: #e2e8f0;
white-space: nowrap;
}
.sizes-slider-group {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
}
.sizes-input-pair {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.sizes-input-label {
font-size: 0.7rem;
color: #64748b;
text-transform: uppercase;
font-weight: 600;
letter-spacing: 0.04em;
width: 26px;
text-align: right;
}
.sizes-input {
width: 60px;
padding: 5px 6px;
font-size: 0.82rem;
color: #f1f5f9;
background: rgba(15, 23, 42, 0.6);
border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 6px;
text-align: center;
-moz-appearance: textfield;
}
.sizes-input::-webkit-outer-spin-button,
.sizes-input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.sizes-input:focus {
outline: none;
border-color: #6366f1;
}
/* Range track - the visual bar between min and max */
.sizes-range-container {
flex: 1;
position: relative;
height: 28px;
min-width: 120px;
}
.sizes-range-track {
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 6px;
transform: translateY(-50%);
background: rgba(51, 65, 85, 0.6);
border-radius: 3px;
overflow: hidden;
}
.sizes-range-fill {
position: absolute;
top: 0;
height: 100%;
border-radius: 3px;
transition: left 0.1s, width 0.1s;
}
.sizes-range-fill-active {
background: linear-gradient(90deg, #6366f1, #818cf8);
}
.sizes-range-fill-preferred {
position: absolute;
top: 0;
width: 3px;
height: 100%;
background: #fbbf24;
border-radius: 2px;
transition: left 0.1s;
z-index: 2;
}
/* Thumb markers */
.sizes-range-thumb {
position: absolute;
top: 50%;
width: 14px;
height: 14px;
border-radius: 50%;
transform: translate(-50%, -50%);
cursor: grab;
z-index: 3;
transition: left 0.1s, box-shadow 0.15s;
border: 2px solid;
}
.sizes-range-thumb:active {
cursor: grabbing;
}
.sizes-range-thumb-min {
background: #1e293b;
border-color: #6366f1;
}
.sizes-range-thumb-min:hover {
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.25);
}
.sizes-range-thumb-preferred {
background: #fbbf24;
border-color: #f59e0b;
width: 12px;
height: 12px;
z-index: 4;
}
.sizes-range-thumb-preferred:hover {
box-shadow: 0 0 0 4px rgba(251, 191, 36, 0.25);
}
.sizes-range-thumb-max {
background: #1e293b;
border-color: #6366f1;
}
.sizes-range-thumb-max:hover {
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.25);
}
/* Legend */
.sizes-legend {
display: flex;
align-items: center;
gap: 18px;
margin-bottom: 16px;
padding: 8px 14px;
border-radius: 8px;
background: rgba(30, 41, 59, 0.3);
}
.sizes-legend-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 0.75rem;
color: #94a3b8;
}
.sizes-legend-dot {
width: 10px;
height: 10px;
border-radius: 50%;
}
.sizes-legend-dot-min { background: #6366f1; }
.sizes-legend-dot-preferred { background: #fbbf24; }
.sizes-legend-dot-max { background: #6366f1; }
.sizes-legend-dot-range { background: linear-gradient(90deg, #6366f1, #818cf8); width: 24px; border-radius: 3px; height: 6px; }
.sizes-loading {
text-align: center;
padding: 40px;
color: #64748b;
font-size: 0.9rem;
}
/* Column labels row */
.sizes-column-labels {
display: grid;
grid-template-columns: 140px 1fr;
gap: 16px;
padding: 0 14px 6px 14px;
}
.sizes-column-labels-right {
display: flex;
align-items: center;
gap: 12px;
}
.sizes-column-label {
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: #64748b;
}
.sizes-column-label-min { width: 86px; text-align: center; flex-shrink: 0; }
.sizes-column-label-slider { flex: 1; text-align: center; min-width: 120px; }
.sizes-column-label-pref { width: 86px; text-align: center; flex-shrink: 0; }
.sizes-column-label-max { width: 86px; text-align: center; flex-shrink: 0; }
/* Responsive */
@media (max-width: 768px) {
.sizes-quality-row {
grid-template-columns: 1fr;
gap: 8px;
padding: 12px 10px;
}
.sizes-slider-group {
flex-wrap: wrap;
}
.sizes-range-container {
min-width: 100%;
order: -1;
}
.sizes-header {
flex-direction: column;
}
.sizes-column-labels {
display: none;
}
}
</style>
<script>
(function() {
'use strict';
var _sizesData = [];
var _sizesDirty = false;
var _sizesMax = 400;
var _sizesMode = 'movie';
var _sizesInstanceId = null;
function _getSelectedInstance() {
var sel = document.getElementById('settings-sizes-instance-select');
if (!sel || !sel.value) return { mode: _sizesMode, id: null };
var parts = sel.value.split(':');
return { mode: parts[0] || 'movie', id: parts[1] || null };
}
function _getApiBase() {
var inst = _getSelectedInstance();
_sizesMode = inst.mode;
_sizesInstanceId = inst.id;
if (inst.mode === 'tv') return './api/tv-hunt/sizes';
return './api/sizes';
}
function _appendInstanceParam(url) {
var inst = _getSelectedInstance();
if (!inst.id) return url;
var sep = url.indexOf('?') === -1 ? '?' : '&';
return url + sep + 'instance_id=' + encodeURIComponent(inst.id);
}
function markDirty() {
_sizesDirty = true;
var btn = document.getElementById('sizes-save-btn');
if (btn) btn.disabled = false;
}
function markClean() {
_sizesDirty = false;
var btn = document.getElementById('sizes-save-btn');
if (btn) btn.disabled = true;
}
function groupBadgeClass(group) {
var g = (group || '').toLowerCase().replace(/\s+/g, '');
if (g === 'lowquality') return 'sizes-group-badge-low';
if (g === 'sd') return 'sizes-group-badge-sd';
if (g === '720p') return 'sizes-group-badge-720';
if (g === '1080p') return 'sizes-group-badge-1080';
if (g === '2160p') return 'sizes-group-badge-2160';
if (g === 'ultra') return 'sizes-group-badge-ultra';
return 'sizes-group-badge-low';
}
function clamp(val, lo, hi) {
return Math.max(lo, Math.min(hi, val));
}
function pctOf(val) {
return (val / _sizesMax) * 100;
}
function updateVisual(row, item) {
var track = row.querySelector('.sizes-range-fill-active');
var prefMark = row.querySelector('.sizes-range-fill-preferred');
var thumbMin = row.querySelector('.sizes-range-thumb-min');
var thumbPref = row.querySelector('.sizes-range-thumb-preferred');
var thumbMax = row.querySelector('.sizes-range-thumb-max');
var minPct = pctOf(item.min);
var prefPct = pctOf(item.preferred);
var maxPct = pctOf(item.max);
if (track) {
track.style.left = minPct + '%';
track.style.width = (maxPct - minPct) + '%';
}
if (prefMark) prefMark.style.left = prefPct + '%';
if (thumbMin) thumbMin.style.left = minPct + '%';
if (thumbPref) thumbPref.style.left = prefPct + '%';
if (thumbMax) thumbMax.style.left = maxPct + '%';
}
function makeDraggable(thumb, item, field, row, minInput, prefInput, maxInput) {
var dragging = false;
var container = row.querySelector('.sizes-range-container');
function onMove(clientX) {
var rect = container.getBoundingClientRect();
var pct = ((clientX - rect.left) / rect.width);
var val = Math.round(pct * _sizesMax);
if (field === 'min') {
val = clamp(val, 0, item.preferred);
item.min = val;
if (minInput) minInput.value = val;
} else if (field === 'preferred') {
val = clamp(val, item.min, item.max);
item.preferred = val;
if (prefInput) prefInput.value = val;
} else {
val = clamp(val, item.preferred, _sizesMax);
item.max = val;
if (maxInput) maxInput.value = val;
}
updateVisual(row, item);
markDirty();
}
thumb.addEventListener('mousedown', function(e) {
e.preventDefault();
dragging = true;
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
});
thumb.addEventListener('touchstart', function(e) {
dragging = true;
document.addEventListener('touchmove', handleTouchMove, {passive: false});
document.addEventListener('touchend', handleTouchEnd);
});
function handleMouseMove(e) { if (dragging) onMove(e.clientX); }
function handleMouseUp() { dragging = false; document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }
function handleTouchMove(e) { if (dragging) { e.preventDefault(); onMove(e.touches[0].clientX); } }
function handleTouchEnd() { dragging = false; document.removeEventListener('touchmove', handleTouchMove); document.removeEventListener('touchend', handleTouchEnd); }
}
function renderSizes(sizes) {
_sizesData = sizes;
var container = document.getElementById('sizes-quality-list');
if (!container) return;
var html = '';
html += '<div class="sizes-legend">' +
'<div class="sizes-legend-item"><span class="sizes-legend-dot sizes-legend-dot-range"></span> Accepted range</div>' +
'<div class="sizes-legend-item"><span class="sizes-legend-dot sizes-legend-dot-preferred"></span> Preferred</div>' +
'<span style="flex:1"></span>' +
'<div class="sizes-legend-item" style="color: #64748b; font-size: 0.72rem;">MB per minute of runtime &middot; Max scale: ' + _sizesMax + '</div>' +
'</div>';
html += '<div class="sizes-column-labels"><div></div><div class="sizes-column-labels-right">' +
'<div class="sizes-column-label sizes-column-label-min">Min</div>' +
'<div class="sizes-column-label sizes-column-label-slider">Range</div>' +
'<div class="sizes-column-label sizes-column-label-pref">Preferred</div>' +
'<div class="sizes-column-label sizes-column-label-max">Max</div>' +
'</div></div>';
var lastGroup = '';
for (var i = 0; i < sizes.length; i++) {
var s = sizes[i];
if (s.group && s.group !== lastGroup) {
lastGroup = s.group;
html += '<div class="sizes-group-header">' +
'<span class="sizes-group-badge ' + groupBadgeClass(s.group) + '">' + escapeHtml(s.group) + '</span>' +
'<span class="sizes-group-line"></span></div>';
}
var minPct = pctOf(s.min);
var prefPct = pctOf(s.preferred);
var maxPct = pctOf(s.max);
html += '<div class="sizes-quality-row" data-index="' + i + '">' +
'<div class="sizes-quality-name">' + escapeHtml(s.name) + '</div>' +
'<div class="sizes-slider-group">' +
'<div class="sizes-input-pair"><span class="sizes-input-label">Min</span>' +
'<input type="number" class="sizes-input sizes-input-min" min="0" max="' + _sizesMax + '" value="' + s.min + '"></div>' +
'<div class="sizes-range-container">' +
'<div class="sizes-range-track">' +
'<div class="sizes-range-fill sizes-range-fill-active" style="left:' + minPct + '%;width:' + (maxPct - minPct) + '%"></div>' +
'<div class="sizes-range-fill-preferred" style="left:' + prefPct + '%"></div>' +
'</div>' +
'<div class="sizes-range-thumb sizes-range-thumb-min" style="left:' + minPct + '%"></div>' +
'<div class="sizes-range-thumb sizes-range-thumb-preferred" style="left:' + prefPct + '%"></div>' +
'<div class="sizes-range-thumb sizes-range-thumb-max" style="left:' + maxPct + '%"></div>' +
'</div>' +
'<div class="sizes-input-pair"><span class="sizes-input-label">Pref</span>' +
'<input type="number" class="sizes-input sizes-input-preferred" min="0" max="' + _sizesMax + '" value="' + s.preferred + '"></div>' +
'<div class="sizes-input-pair"><span class="sizes-input-label">Max</span>' +
'<input type="number" class="sizes-input sizes-input-max" min="1" max="' + _sizesMax + '" value="' + s.max + '"></div>' +
'</div>' +
'</div>';
}
container.innerHTML = html;
var rows = container.querySelectorAll('.sizes-quality-row');
rows.forEach(function(row) {
var idx = parseInt(row.getAttribute('data-index'), 10);
var item = _sizesData[idx];
if (!item) return;
var minInput = row.querySelector('.sizes-input-min');
var prefInput = row.querySelector('.sizes-input-preferred');
var maxInput = row.querySelector('.sizes-input-max');
var thumbMin = row.querySelector('.sizes-range-thumb-min');
var thumbPref = row.querySelector('.sizes-range-thumb-preferred');
var thumbMax = row.querySelector('.sizes-range-thumb-max');
function syncFromInputs() {
var mn = parseInt(minInput.value, 10); if (isNaN(mn)) mn = 0;
var pf = parseInt(prefInput.value, 10); if (isNaN(pf)) pf = 0;
var mx = parseInt(maxInput.value, 10); if (isNaN(mx)) mx = 1;
mn = clamp(mn, 0, _sizesMax);
mx = clamp(mx, 1, _sizesMax);
if (mn > mx) mn = mx;
pf = clamp(pf, mn, mx);
item.min = mn; item.preferred = pf; item.max = mx;
minInput.value = mn; prefInput.value = pf; maxInput.value = mx;
updateVisual(row, item);
markDirty();
}
[minInput, prefInput, maxInput].forEach(function(inp) {
if (inp) {
inp.addEventListener('change', syncFromInputs);
inp.addEventListener('input', function() {
var mn = parseInt(minInput.value, 10) || 0;
var pf = parseInt(prefInput.value, 10) || 0;
var mx = parseInt(maxInput.value, 10) || 1;
mn = clamp(mn, 0, _sizesMax);
mx = clamp(mx, 1, _sizesMax);
pf = clamp(pf, mn, mx);
var tempItem = {min: mn, preferred: pf, max: mx};
updateVisual(row, tempItem);
markDirty();
});
}
});
if (thumbMin) makeDraggable(thumbMin, item, 'min', row, minInput, prefInput, maxInput);
if (thumbPref) makeDraggable(thumbPref, item, 'preferred', row, minInput, prefInput, maxInput);
if (thumbMax) makeDraggable(thumbMax, item, 'max', row, minInput, prefInput, maxInput);
});
}
function escapeHtml(s) {
if (s == null) return '';
return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function safeJsonFetch(url, fallback) {
return fetch(url, { cache: 'no-store' }).then(function(r) { return r.json(); }).catch(function() { return fallback || {}; });
}
function populateCombinedInstanceDropdown(preferMode) {
var selectEl = document.getElementById('settings-sizes-instance-select');
if (!selectEl) return;
selectEl.innerHTML = '<option value="">Loading...</option>';
var ts = Date.now();
Promise.all([
safeJsonFetch('./api/movie-hunt/instances?t=' + ts, { instances: [] }),
safeJsonFetch('./api/tv-hunt/instances?t=' + ts, { instances: [] }),
safeJsonFetch('./api/movie-hunt/instances/current?t=' + ts, { current_instance_id: null }),
safeJsonFetch('./api/tv-hunt/instances/current?t=' + ts, { current_instance_id: null })
]).then(function(results) {
var movieList = (results[0].instances || []).map(function(inst) {
return { value: 'movie:' + inst.id, label: 'Movie - ' + (inst.name || 'Instance ' + inst.id) };
});
var tvList = (results[1].instances || []).map(function(inst) {
return { value: 'tv:' + inst.id, label: 'TV - ' + (inst.name || 'Instance ' + inst.id) };
});
var combined = movieList.concat(tvList);
combined.sort(function(a, b) { return (a.label || '').localeCompare(b.label || '', undefined, { sensitivity: 'base' }); });
var currentMovie = results[2].current_instance_id != null ? Number(results[2].current_instance_id) : null;
var currentTv = results[3].current_instance_id != null ? Number(results[3].current_instance_id) : null;
selectEl.innerHTML = '';
if (combined.length === 0) {
var emptyOpt = document.createElement('option');
emptyOpt.value = '';
emptyOpt.textContent = 'No Movie or TV Hunt instances';
selectEl.appendChild(emptyOpt);
return;
}
combined.forEach(function(item) {
var opt = document.createElement('option');
opt.value = item.value;
opt.textContent = item.label;
selectEl.appendChild(opt);
});
var saved = '';
try { saved = localStorage.getItem('media-hunt-sizes-last-instance') || ''; } catch (e) {}
var selected = '';
if (preferMode === 'movie' && currentMovie != null) {
selected = 'movie:' + currentMovie;
if (!combined.some(function(i) { return i.value === selected; })) selected = combined[0].value;
} else if (preferMode === 'tv' && currentTv != null) {
selected = 'tv:' + currentTv;
if (!combined.some(function(i) { return i.value === selected; })) selected = combined[0].value;
} else if (saved && combined.some(function(i) { return i.value === saved; })) {
selected = saved;
} else if (currentMovie != null && combined.some(function(i) { return i.value === 'movie:' + currentMovie; })) {
selected = 'movie:' + currentMovie;
} else if (currentTv != null && combined.some(function(i) { return i.value === 'tv:' + currentTv; })) {
selected = 'tv:' + currentTv;
} else {
selected = combined[0].value;
}
selectEl.value = selected;
selected = selectEl.value || selected;
try { localStorage.setItem('media-hunt-sizes-last-instance', selected); } catch (e) {}
var inst = _getSelectedInstance();
_sizesMode = inst.mode;
_sizesInstanceId = inst.id;
doLoadSizes();
});
}
function onInstanceChange() {
var sel = document.getElementById('settings-sizes-instance-select');
if (sel) {
try { localStorage.setItem('media-hunt-sizes-last-instance', sel.value); } catch (e) {}
}
var inst = _getSelectedInstance();
_sizesMode = inst.mode;
_sizesInstanceId = inst.id;
markClean();
doLoadSizes();
}
function loadSizes(preferMode) {
var noIdxEl = document.getElementById('settings-sizes-no-indexers');
var noCliEl = document.getElementById('settings-sizes-no-clients');
var wrapperEl = document.getElementById('settings-sizes-content-wrapper');
Promise.all([
safeJsonFetch('./api/movie-hunt/instances', { instances: [] }),
safeJsonFetch('./api/tv-hunt/instances', { instances: [] })
]).then(function(results) {
var movieCount = (results[0].instances || []).length;
var tvCount = (results[1].instances || []).length;
if (movieCount === 0 && tvCount === 0) {
var selectEl = document.getElementById('settings-sizes-instance-select');
if (selectEl) {
selectEl.innerHTML = '';
var emptyOpt = document.createElement('option');
emptyOpt.value = '';
emptyOpt.textContent = 'No Movie or TV Hunt instances';
selectEl.appendChild(emptyOpt);
}
if (noIdxEl) noIdxEl.style.display = 'none';
if (noCliEl) noCliEl.style.display = 'none';
if (wrapperEl) wrapperEl.style.display = '';
return;
}
if (noIdxEl) noIdxEl.style.display = 'none';
if (noCliEl) noCliEl.style.display = 'none';
if (wrapperEl) wrapperEl.style.display = '';
populateCombinedInstanceDropdown(preferMode || 'movie');
}).catch(function() {
if (noIdxEl) noIdxEl.style.display = 'none';
if (noCliEl) noCliEl.style.display = '';
if (wrapperEl) wrapperEl.style.display = 'none';
});
}
function doLoadSizes() {
var url = _appendInstanceParam(_getApiBase());
fetch(url, { cache: 'no-store' })
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success && data.sizes) {
renderSizes(data.sizes);
markClean();
} else if (data.error) {
var container = document.getElementById('sizes-quality-list');
if (container) container.innerHTML = '<div class="sizes-loading" style="color:#ef4444;">' + escapeHtml(data.error) + '</div>';
}
})
.catch(function(err) {
console.error('[Sizes] Failed to load:', err);
var container = document.getElementById('sizes-quality-list');
if (container) container.innerHTML = '<div class="sizes-loading" style="color:#ef4444;">Failed to load sizes. <a href="#" onclick="location.reload()" style="color:#6366f1;">Retry</a></div>';
});
}
function saveSizes() {
var btn = document.getElementById('sizes-save-btn');
if (btn) btn.disabled = true;
var url = _appendInstanceParam(_getApiBase());
fetch(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sizes: _sizesData })
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success) {
_sizesData = data.sizes;
markClean();
if (window.huntarrUI && window.huntarrUI.showNotification) {
window.huntarrUI.showNotification('Sizes saved.', 'success');
}
} else {
if (btn) btn.disabled = false;
if (window.huntarrUI && window.huntarrUI.showNotification) {
window.huntarrUI.showNotification(data.error || 'Failed to save sizes.', 'error');
}
}
})
.catch(function() {
if (btn) btn.disabled = false;
if (window.huntarrUI && window.huntarrUI.showNotification) {
window.huntarrUI.showNotification('Failed to save sizes.', 'error');
}
});
}
function resetSizes() {
function doReset() {
var url = _appendInstanceParam(_getApiBase() + '/reset');
fetch(url, { method: 'POST' })
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success && data.sizes) {
renderSizes(data.sizes);
markClean();
if (window.huntarrUI && window.huntarrUI.showNotification) {
window.huntarrUI.showNotification('Sizes reset to defaults.', 'success');
}
}
})
.catch(function() {
if (window.huntarrUI && window.huntarrUI.showNotification) {
window.huntarrUI.showNotification('Failed to reset sizes.', 'error');
}
});
}
if (window.HuntarrConfirm && window.HuntarrConfirm.show) {
window.HuntarrConfirm.show({
title: 'Reset Size Limits',
message: 'Reset all size limits to defaults for this instance?',
confirmLabel: 'Reset',
onConfirm: doReset
});
} else {
if (!confirm('Reset all size limits to defaults for this instance?')) return;
doReset();
}
}
function initSizes() {
var saveBtn = document.getElementById('sizes-save-btn');
var resetBtn = document.getElementById('sizes-reset-btn');
if (saveBtn && !saveBtn._sizesBound) {
saveBtn._sizesBound = true;
saveBtn.addEventListener('click', saveSizes);
}
if (resetBtn && !resetBtn._sizesBound) {
resetBtn._sizesBound = true;
resetBtn.addEventListener('click', resetSizes);
}
var selectEl = document.getElementById('settings-sizes-instance-select');
if (selectEl && !selectEl._sizesChangeBound) {
selectEl._sizesChangeBound = true;
selectEl.addEventListener('change', onInstanceChange);
}
}
window.SizesModule = {
load: loadSizes,
init: initSizes,
initOrRefresh: function(preferMode) {
_sizesMode = (preferMode === 'tv') ? 'tv' : 'movie';
initSizes();
loadSizes(preferMode);
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSizes);
} else {
initSizes();
}
})();
</script>