feat(settings): move per-hoster upload settings into the Accounts tab
Declutters the Einstellungen tab: the per-hoster panels (Retries, Max Speed, Parallel Uploads, Restart-below, Interval, Max Size, log-to-file) are no longer listed there. Each hoster group in the Accounts tab now has a collapsible "Upload-Einstellungen" section with exactly those controls, so everything about a hoster lives in one place. The Einstellungen tab shows a short pointer to where they moved. Per-hoster edits in Accounts save via a dedicated lightweight handler (scheduleHosterSettingsSave → saveHosterSettings) that touches only hosterSettings — it deliberately does NOT route through the global saveSettings, so tweaking a hoster slider can't re-run the folder-monitor start/stop or clobber global fields. The global saveSettings still reads .hs-input[data-hoster] wherever they live, and global inputs (which have no data-hoster) are unaffected. The new inputs drop the .settings-autosave class and bind via delegation on the persistent accounts container so they survive re-renders. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
68c4007c82
commit
b1826eae00
170
renderer/app.js
170
renderer/app.js
@ -3281,81 +3281,10 @@ function renderSettings() {
|
|||||||
document.getElementById('exportBackupBtn').addEventListener('click', () => doBackupExport());
|
document.getElementById('exportBackupBtn').addEventListener('click', () => doBackupExport());
|
||||||
document.getElementById('importBackupBtn').addEventListener('click', () => doBackupImport());
|
document.getElementById('importBackupBtn').addEventListener('click', () => doBackupImport());
|
||||||
|
|
||||||
// --- Separator before hoster panels ---
|
const hosterPointer = document.createElement('div');
|
||||||
const separator = document.createElement('div');
|
hosterPointer.className = 'settings-hoster-pointer';
|
||||||
separator.style.cssText = 'height:16px';
|
hosterPointer.innerHTML = 'Die <strong>Upload-Einstellungen pro Hoster</strong> (Retries, Speed, Parallel, Max-Größe, Log) sind jetzt im <strong>Accounts</strong>-Tab — direkt bei den jeweiligen Hostern unter „Upload-Einstellungen".';
|
||||||
container.appendChild(separator);
|
container.appendChild(hosterPointer);
|
||||||
|
|
||||||
if (configuredAccounts.length === 0) {
|
|
||||||
const empty = document.createElement('div');
|
|
||||||
empty.className = 'settings-empty';
|
|
||||||
empty.innerHTML = '<p>Noch keine Account-Einstellungen vorhanden.</p><span class="hint">Sobald du einen Account anlegst, erscheinen hier die passenden Upload-Einstellungen.</span>';
|
|
||||||
container.appendChild(empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const { name } of configuredAccounts) {
|
|
||||||
const hs = hosterSettings[name] || {};
|
|
||||||
const maxSpeedMbs = hs.maxSpeedKbs > 0 ? (hs.maxSpeedKbs / 1024).toFixed(2).replace(/\.00$/, '') : '0';
|
|
||||||
|
|
||||||
const panel = document.createElement('div');
|
|
||||||
panel.className = 'hoster-settings-panel';
|
|
||||||
|
|
||||||
panel.innerHTML = `
|
|
||||||
<div class="hoster-panel-header" data-hoster="${name}">
|
|
||||||
<span class="panel-arrow">▶</span>
|
|
||||||
<span class="panel-title">${escapeHtml(getHosterLabel(name))}</span>
|
|
||||||
<span class="panel-status active">Aktiv</span>
|
|
||||||
</div>
|
|
||||||
<div class="hoster-panel-body" data-panel="${name}" style="display:none">
|
|
||||||
<h4>Upload-Einstellungen</h4>
|
|
||||||
<div class="settings-grid-mini">
|
|
||||||
<div class="settings-row">
|
|
||||||
<label>Retries</label>
|
|
||||||
<input type="number" class="hs-input settings-autosave" data-hoster="${name}" data-hs="retries" value="${hs.retries ?? 3}" min="0" max="500">
|
|
||||||
</div>
|
|
||||||
<div class="settings-row">
|
|
||||||
<label>Max Speed (MB/s)</label>
|
|
||||||
<input type="number" class="hs-input settings-autosave" data-hoster="${name}" data-hs="maxSpeedMbs" value="${maxSpeedMbs}" min="0" step="0.1">
|
|
||||||
<span class="hint">0 = unbegrenzt</span>
|
|
||||||
</div>
|
|
||||||
<div class="settings-row">
|
|
||||||
<label>Parallele Uploads</label>
|
|
||||||
<input type="number" class="hs-input settings-autosave" data-hoster="${name}" data-hs="parallelCount" value="${hs.parallelCount ?? 2}" min="1" max="100">
|
|
||||||
</div>
|
|
||||||
<div class="settings-row">
|
|
||||||
<label>Restart unter (kB/s)</label>
|
|
||||||
<input type="number" class="hs-input settings-autosave" data-hoster="${name}" data-hs="restartBelowKbs" value="${hs.restartBelowKbs ?? 0}" min="0">
|
|
||||||
<span class="hint">0 = aus</span>
|
|
||||||
</div>
|
|
||||||
<div class="settings-row">
|
|
||||||
<label>Intervall (s)</label>
|
|
||||||
<input type="number" class="hs-input settings-autosave" data-hoster="${name}" data-hs="timeIntervalSec" value="${hs.timeIntervalSec ?? 0}" min="0">
|
|
||||||
</div>
|
|
||||||
<div class="settings-row">
|
|
||||||
<label>Max Size (MB)</label>
|
|
||||||
<input type="number" class="hs-input settings-autosave" data-hoster="${name}" data-hs="maxSizeMb" value="${hs.maxSizeMb ?? 0}" min="0">
|
|
||||||
<span class="hint">0 = unbegrenzt</span>
|
|
||||||
</div>
|
|
||||||
<div class="settings-row">
|
|
||||||
<label>Links in Log schreiben</label>
|
|
||||||
<input type="checkbox" class="hs-input settings-autosave" data-hoster="${name}" data-hs="logToFile" ${hs.logToFile !== false ? 'checked' : ''}>
|
|
||||||
<span class="hint">Erfolgreiche Links in fileuploader.log. Aus = auch kein Doppel-Upload-Schutz beim Neustart für diesen Hoster.</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
container.appendChild(panel);
|
|
||||||
|
|
||||||
// Toggle panel
|
|
||||||
panel.querySelector('.hoster-panel-header').addEventListener('click', () => {
|
|
||||||
const body = panel.querySelector('.hoster-panel-body');
|
|
||||||
const arrow = panel.querySelector('.panel-arrow');
|
|
||||||
const isOpen = body.style.display !== 'none';
|
|
||||||
body.style.display = isOpen ? 'none' : 'block';
|
|
||||||
arrow.innerHTML = isOpen ? '▶' : '▼';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('chooseLogFilePathBtn')?.addEventListener('click', chooseLogFilePath);
|
document.getElementById('chooseLogFilePathBtn')?.addEventListener('click', chooseLogFilePath);
|
||||||
document.getElementById('openLogFolderBtn')?.addEventListener('click', () => window.api.openLogFolder());
|
document.getElementById('openLogFolderBtn')?.addEventListener('click', () => window.api.openLogFolder());
|
||||||
@ -3708,6 +3637,78 @@ function _hosterGroupOpenState(name, summary) {
|
|||||||
|
|
||||||
const _hosterGroupOpenMemory = new Map();
|
const _hosterGroupOpenMemory = new Map();
|
||||||
|
|
||||||
|
let _hosterSettingsSaveTimer = null;
|
||||||
|
function scheduleHosterSettingsSave() {
|
||||||
|
clearTimeout(_hosterSettingsSaveTimer);
|
||||||
|
_hosterSettingsSaveTimer = setTimeout(() => { saveHosterSettingsFromDom().catch(() => {}); }, 350);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveHosterSettingsFromDom() {
|
||||||
|
const newHosterSettings = { ...(config.hosterSettings || {}) };
|
||||||
|
for (const name of HOSTERS) {
|
||||||
|
const inputs = document.querySelectorAll(`.account-hoster-settings-body .hs-input[data-hoster="${name}"]`);
|
||||||
|
if (!inputs.length) continue;
|
||||||
|
const hs = { ...(newHosterSettings[name] || {}) };
|
||||||
|
inputs.forEach(input => {
|
||||||
|
const field = input.dataset.hs;
|
||||||
|
if (input.type === 'checkbox') hs[field] = input.checked;
|
||||||
|
else if (field === 'maxSpeedMbs') hs.maxSpeedKbs = Math.max(0, Math.round((parseFloat(input.value) || 0) * 1024));
|
||||||
|
else hs[field] = parseInt(input.value, 10) || 0;
|
||||||
|
});
|
||||||
|
newHosterSettings[name] = hs;
|
||||||
|
}
|
||||||
|
await window.api.saveHosterSettings(newHosterSettings);
|
||||||
|
config.hosterSettings = newHosterSettings;
|
||||||
|
hosterSettings = newHosterSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _buildHosterSettingsHtml(name) {
|
||||||
|
const hs = (config.hosterSettings && config.hosterSettings[name]) || {};
|
||||||
|
const maxSpeedMbs = hs.maxSpeedKbs > 0 ? String(+(hs.maxSpeedKbs / 1024).toFixed(2)) : '0';
|
||||||
|
return `<div class="account-hoster-settings">
|
||||||
|
<div class="account-hoster-settings-header" data-hoster-settings-toggle="${name}">
|
||||||
|
<span class="panel-arrow">▶</span>
|
||||||
|
<span>Upload-Einstellungen</span>
|
||||||
|
</div>
|
||||||
|
<div class="account-hoster-settings-body" style="display:none">
|
||||||
|
<div class="settings-grid-mini">
|
||||||
|
<div class="settings-row">
|
||||||
|
<label>Retries</label>
|
||||||
|
<input type="number" class="hs-input" data-hoster="${name}" data-hs="retries" value="${hs.retries ?? 3}" min="0" max="500">
|
||||||
|
</div>
|
||||||
|
<div class="settings-row">
|
||||||
|
<label>Max Speed (MB/s)</label>
|
||||||
|
<input type="number" class="hs-input" data-hoster="${name}" data-hs="maxSpeedMbs" value="${maxSpeedMbs}" min="0" step="0.1">
|
||||||
|
<span class="hint">0 = unbegrenzt</span>
|
||||||
|
</div>
|
||||||
|
<div class="settings-row">
|
||||||
|
<label>Parallele Uploads</label>
|
||||||
|
<input type="number" class="hs-input" data-hoster="${name}" data-hs="parallelCount" value="${hs.parallelCount ?? 2}" min="1" max="100">
|
||||||
|
</div>
|
||||||
|
<div class="settings-row">
|
||||||
|
<label>Restart unter (kB/s)</label>
|
||||||
|
<input type="number" class="hs-input" data-hoster="${name}" data-hs="restartBelowKbs" value="${hs.restartBelowKbs ?? 0}" min="0">
|
||||||
|
<span class="hint">0 = aus</span>
|
||||||
|
</div>
|
||||||
|
<div class="settings-row">
|
||||||
|
<label>Intervall (s)</label>
|
||||||
|
<input type="number" class="hs-input" data-hoster="${name}" data-hs="timeIntervalSec" value="${hs.timeIntervalSec ?? 0}" min="0">
|
||||||
|
</div>
|
||||||
|
<div class="settings-row">
|
||||||
|
<label>Max Size (MB)</label>
|
||||||
|
<input type="number" class="hs-input" data-hoster="${name}" data-hs="maxSizeMb" value="${hs.maxSizeMb ?? 0}" min="0">
|
||||||
|
<span class="hint">0 = unbegrenzt</span>
|
||||||
|
</div>
|
||||||
|
<div class="settings-row">
|
||||||
|
<label>Links in Log schreiben</label>
|
||||||
|
<input type="checkbox" class="hs-input" data-hoster="${name}" data-hs="logToFile" ${hs.logToFile !== false ? 'checked' : ''}>
|
||||||
|
<span class="hint">Erfolgreiche Links in fileuploader.log.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
function _buildAccountHosterGroupHtml(name, accounts) {
|
function _buildAccountHosterGroupHtml(name, accounts) {
|
||||||
const summary = _summarizeHosterGroup(accounts);
|
const summary = _summarizeHosterGroup(accounts);
|
||||||
const isOpen = _hosterGroupOpenState(name, summary);
|
const isOpen = _hosterGroupOpenState(name, summary);
|
||||||
@ -3734,7 +3735,7 @@ function _buildAccountHosterGroupHtml(name, accounts) {
|
|||||||
${summary.error ? `<span class="account-hoster-group-meta error">${summary.error} Fehler</span>` : ''}
|
${summary.error ? `<span class="account-hoster-group-meta error">${summary.error} Fehler</span>` : ''}
|
||||||
${lifeMeta}
|
${lifeMeta}
|
||||||
</div>
|
</div>
|
||||||
<div class="account-hoster-group-body" ${bodyStyle}>${cardsHtml}</div>
|
<div class="account-hoster-group-body" ${bodyStyle}>${cardsHtml}${_buildHosterSettingsHtml(name)}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3799,6 +3800,17 @@ function bindAccountListeners(container) {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const settingsHeader = e.target.closest('[data-hoster-settings-toggle]');
|
||||||
|
if (settingsHeader) {
|
||||||
|
const body = settingsHeader.nextElementSibling;
|
||||||
|
const arrow = settingsHeader.querySelector('.panel-arrow');
|
||||||
|
if (body) {
|
||||||
|
const isOpen = body.style.display !== 'none';
|
||||||
|
body.style.display = isOpen ? 'none' : '';
|
||||||
|
if (arrow) arrow.innerHTML = isOpen ? '▶' : '▼';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
const btn = e.target.closest('button');
|
const btn = e.target.closest('button');
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
if (btn.dataset.accountToggle) return toggleAccount(btn.dataset.accountToggle);
|
if (btn.dataset.accountToggle) return toggleAccount(btn.dataset.accountToggle);
|
||||||
@ -3819,6 +3831,12 @@ function bindAccountListeners(container) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onHosterSettingInput = (e) => {
|
||||||
|
if (e.target && e.target.classList && e.target.classList.contains('hs-input')) scheduleHosterSettingsSave();
|
||||||
|
};
|
||||||
|
container.addEventListener('input', onHosterSettingInput);
|
||||||
|
container.addEventListener('change', onHosterSettingInput);
|
||||||
|
|
||||||
let draggedCard = null;
|
let draggedCard = null;
|
||||||
container.addEventListener('dragstart', (e) => {
|
container.addEventListener('dragstart', (e) => {
|
||||||
const card = e.target.closest('.account-card[draggable]');
|
const card = e.target.closest('.account-card[draggable]');
|
||||||
|
|||||||
@ -1024,6 +1024,39 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
.account-card.drag-over-above { border-top: 2px solid var(--accent); }
|
.account-card.drag-over-above { border-top: 2px solid var(--accent); }
|
||||||
.account-card.drag-over-below { border-bottom: 2px solid var(--accent); }
|
.account-card.drag-over-below { border-bottom: 2px solid var(--accent); }
|
||||||
|
|
||||||
|
.account-hoster-settings {
|
||||||
|
margin-top: 8px;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
.account-hoster-settings-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 6px 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background 0.1s, color 0.1s;
|
||||||
|
}
|
||||||
|
.account-hoster-settings-header:hover { background: rgba(255,255,255,0.03); color: var(--text); }
|
||||||
|
.account-hoster-settings-header .panel-arrow { font-size: 10px; color: var(--text-dim); }
|
||||||
|
.account-hoster-settings-body { padding: 6px 4px 4px; }
|
||||||
|
|
||||||
|
.settings-hoster-pointer {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--text-muted);
|
||||||
|
background: rgba(62, 167, 255, 0.06);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.account-priority-badge {
|
.account-priority-badge {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user