feat: add dedicated transcoding settings tab
This commit is contained in:
@@ -4025,4 +4025,51 @@ kbd {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Hardware Info Grid (Transcoding Tab) */
|
||||
.hw-info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.hw-info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-xs);
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-bg-secondary);
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.hw-info-item.hw-available {
|
||||
border-color: var(--color-success);
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
.hw-info-item.hw-unavailable {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.hw-badge {
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.hw-info-item.hw-available .hw-badge {
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.hw-info-item.hw-unavailable .hw-badge {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.hw-name {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-secondary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
+88
-31
@@ -368,6 +368,7 @@
|
||||
<div class="tabs">
|
||||
<button class="tab active" data-tab="sources">Sources</button>
|
||||
<button class="tab" data-tab="player">Player</button>
|
||||
<button class="tab" data-tab="transcode">Transcoding</button>
|
||||
<button class="tab" data-tab="content">Manage Content</button>
|
||||
<button class="tab" data-tab="users" id="users-tab" style="display:none;">Users</button>
|
||||
</div>
|
||||
@@ -484,21 +485,75 @@
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h3>Streaming</h3>
|
||||
<h3>Keyboard Shortcuts</h3>
|
||||
<div class="shortcuts-grid">
|
||||
<div class="shortcut"><kbd>↑</kbd> / <kbd>↓</kbd> Channel up/down (or volume)</div>
|
||||
<div class="shortcut"><kbd>←</kbd> / <kbd>→</kbd> Volume down/up (when arrows = channels)</div>
|
||||
<div class="shortcut"><kbd>Space</kbd> / <kbd>K</kbd> Play/Pause</div>
|
||||
<div class="shortcut"><kbd>F</kbd> Fullscreen</div>
|
||||
<div class="shortcut"><kbd>M</kbd> Mute/Unmute</div>
|
||||
<div class="shortcut"><kbd>I</kbd> Toggle info overlay</div>
|
||||
<div class="shortcut"><kbd>PgUp</kbd> / <kbd>PgDn</kbd> Channel up/down</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transcoding Tab -->
|
||||
<div id="tab-transcode" class="tab-content">
|
||||
<div class="settings-section">
|
||||
<h3>Detected Hardware</h3>
|
||||
<div id="hw-info-container" class="hw-info-container">
|
||||
<p class="hint">Loading hardware information...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h3>Encoder Settings</h3>
|
||||
<div class="setting-item">
|
||||
<label class="setting-toggle">
|
||||
<input type="checkbox" id="setting-auto-transcode">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<div class="setting-info">
|
||||
<span class="setting-label">Auto Transcode (Smart)</span>
|
||||
<span class="setting-hint">Automatically detect stream codecs and only transcode/remux when needed.
|
||||
Adds ~1-3s probe delay on first play.</span>
|
||||
<div class="setting-info" style="flex: 1;">
|
||||
<span class="setting-label">Hardware Encoder</span>
|
||||
<span class="setting-hint">Select which GPU encoder to use for transcoding. Auto will use the best
|
||||
available.</span>
|
||||
</div>
|
||||
<select id="setting-hw-encoder" class="source-select" style="width: auto; min-width: 200px;">
|
||||
<option value="auto">Auto (Recommended)</option>
|
||||
<option value="nvenc">NVIDIA NVENC</option>
|
||||
<option value="amf">AMD AMF</option>
|
||||
<option value="qsv">Intel QuickSync</option>
|
||||
<option value="vaapi">VAAPI (Linux)</option>
|
||||
<option value="software">Software (CPU)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="setting-info" style="flex: 1;">
|
||||
<span class="setting-label">Max Resolution</span>
|
||||
<span class="setting-hint">Limit transcoded output resolution. Lower = faster, more compatible.</span>
|
||||
</div>
|
||||
<select id="setting-max-resolution" class="source-select" style="width: auto; min-width: 200px;">
|
||||
<option value="4k">4K (2160p)</option>
|
||||
<option value="1080p" selected>1080p (Full HD)</option>
|
||||
<option value="720p">720p (HD)</option>
|
||||
<option value="480p">480p (SD)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="setting-info" style="flex: 1;">
|
||||
<span class="setting-label">Quality Preset</span>
|
||||
<span class="setting-hint">Higher quality uses more bandwidth and CPU/GPU.</span>
|
||||
</div>
|
||||
<select id="setting-quality" class="source-select" style="width: auto; min-width: 200px;">
|
||||
<option value="high">High</option>
|
||||
<option value="medium" selected>Medium</option>
|
||||
<option value="low">Low (Fastest)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h3>Stream Processing</h3>
|
||||
<div class="setting-item">
|
||||
<label class="setting-toggle">
|
||||
<input type="checkbox" id="setting-force-proxy">
|
||||
<input type="checkbox" id="setting-force-proxy-tc">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<div class="setting-info">
|
||||
@@ -509,7 +564,18 @@
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<label class="setting-toggle">
|
||||
<input type="checkbox" id="setting-force-transcode">
|
||||
<input type="checkbox" id="setting-auto-transcode-tc">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<div class="setting-info">
|
||||
<span class="setting-label">Auto Transcode (Smart)</span>
|
||||
<span class="setting-hint">Automatically detect stream codecs and only transcode/remux when needed.
|
||||
Adds ~1-3s probe delay on first play.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<label class="setting-toggle">
|
||||
<input type="checkbox" id="setting-force-transcode-tc">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<div class="setting-info">
|
||||
@@ -520,13 +586,13 @@
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<label class="setting-toggle">
|
||||
<input type="checkbox" id="setting-force-remux">
|
||||
<input type="checkbox" id="setting-force-remux-tc">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<div class="setting-info">
|
||||
<span class="setting-label">Force Remux</span>
|
||||
<span class="setting-hint">Remux streams to MP4 container. Enable for raw .ts streams
|
||||
from IPTV middleware like m3u-editor or dispatcharr.</span>
|
||||
<span class="setting-hint">Remux streams to MP4 container. Enable for raw .ts streams from IPTV
|
||||
middleware like m3u-editor or dispatcharr.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
@@ -535,46 +601,37 @@
|
||||
<span class="setting-hint">Container format for Xtream streams. Try TS if you experience buffering
|
||||
issues.</span>
|
||||
</div>
|
||||
<select id="setting-stream-format" class="source-select" style="width: auto; min-width: 200px;">
|
||||
<select id="setting-stream-format-tc" class="source-select" style="width: auto; min-width: 200px;">
|
||||
<option value="m3u8">HLS (m3u8) - Recommended</option>
|
||||
<option value="ts">MPEG-TS (ts) - Better buffer</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h3>HTTP Settings</h3>
|
||||
<div class="setting-item">
|
||||
<div class="setting-info" style="flex: 1;">
|
||||
<span class="setting-label">User-Agent</span>
|
||||
<span class="setting-hint">HTTP User-Agent for stream requests. Try VLC if your provider blocks
|
||||
Chrome.</span>
|
||||
</div>
|
||||
<select id="setting-user-agent" class="source-select" style="width: auto; min-width: 200px;">
|
||||
<select id="setting-user-agent-tc" class="source-select" style="width: auto; min-width: 200px;">
|
||||
<option value="chrome">Chrome (Default)</option>
|
||||
<option value="vlc">VLC</option>
|
||||
<option value="tivimate">TiviMate</option>
|
||||
<option value="custom">Custom...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-item" id="custom-user-agent-container" style="display: none;">
|
||||
<div class="setting-item" id="custom-user-agent-container-tc" style="display: none;">
|
||||
<div class="setting-info" style="flex: 1;">
|
||||
<span class="setting-label">Custom User-Agent</span>
|
||||
<span class="setting-hint">Enter your own User-Agent string.</span>
|
||||
</div>
|
||||
<input type="text" id="setting-user-agent-custom" class="search-input" style="width: 300px;"
|
||||
<input type="text" id="setting-user-agent-custom-tc" class="search-input" style="width: 300px;"
|
||||
placeholder="Custom User-Agent string...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h3>Keyboard Shortcuts</h3>
|
||||
<div class="shortcuts-grid">
|
||||
<div class="shortcut"><kbd>↑</kbd> / <kbd>↓</kbd> Channel up/down (or volume)</div>
|
||||
<div class="shortcut"><kbd>←</kbd> / <kbd>→</kbd> Volume down/up (when arrows = channels)</div>
|
||||
<div class="shortcut"><kbd>Space</kbd> / <kbd>K</kbd> Play/Pause</div>
|
||||
<div class="shortcut"><kbd>F</kbd> Fullscreen</div>
|
||||
<div class="shortcut"><kbd>M</kbd> Mute/Unmute</div>
|
||||
<div class="shortcut"><kbd>I</kbd> Toggle info overlay</div>
|
||||
<div class="shortcut"><kbd>PgUp</kbd> / <kbd>PgDn</kbd> Channel up/down</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Manage Content Tab -->
|
||||
|
||||
+160
-85
@@ -20,6 +20,9 @@ class SettingsPage {
|
||||
// Player settings
|
||||
this.initPlayerSettings();
|
||||
|
||||
// Transcoding settings
|
||||
this.initTranscodingSettings();
|
||||
|
||||
// User management (admin only)
|
||||
this.initUserManagement();
|
||||
}
|
||||
@@ -31,10 +34,6 @@ class SettingsPage {
|
||||
const volumeValueDisplay = document.getElementById('volume-value');
|
||||
const rememberVolumeToggle = document.getElementById('setting-remember-volume');
|
||||
const autoPlayNextToggle = document.getElementById('setting-autoplay-next');
|
||||
const forceProxyToggle = document.getElementById('setting-force-proxy');
|
||||
const forceTranscodeToggle = document.getElementById('setting-force-transcode');
|
||||
const forceRemuxToggle = document.getElementById('setting-force-remux');
|
||||
const autoTranscodeToggle = document.getElementById('setting-auto-transcode');
|
||||
|
||||
// Load current settings
|
||||
if (this.app.player?.settings) {
|
||||
@@ -44,18 +43,6 @@ class SettingsPage {
|
||||
volumeValueDisplay.textContent = this.app.player.settings.defaultVolume + '%';
|
||||
rememberVolumeToggle.checked = this.app.player.settings.rememberVolume;
|
||||
autoPlayNextToggle.checked = this.app.player.settings.autoPlayNextEpisode;
|
||||
if (forceProxyToggle) {
|
||||
forceProxyToggle.checked = this.app.player.settings.forceProxy || false;
|
||||
}
|
||||
if (forceTranscodeToggle) {
|
||||
forceTranscodeToggle.checked = this.app.player.settings.forceTranscode || false;
|
||||
}
|
||||
if (forceRemuxToggle) {
|
||||
forceRemuxToggle.checked = this.app.player.settings.forceRemux || false;
|
||||
}
|
||||
if (autoTranscodeToggle) {
|
||||
autoTranscodeToggle.checked = this.app.player.settings.autoTranscode || false;
|
||||
}
|
||||
}
|
||||
|
||||
// Arrow keys toggle
|
||||
@@ -66,56 +53,30 @@ class SettingsPage {
|
||||
|
||||
// Overlay duration
|
||||
overlayDurationInput.addEventListener('change', () => {
|
||||
const value = Math.min(30, Math.max(1, parseInt(overlayDurationInput.value) || 5));
|
||||
overlayDurationInput.value = value;
|
||||
this.app.player.settings.overlayDuration = value;
|
||||
this.app.player.settings.overlayDuration = parseInt(overlayDurationInput.value) || 5;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
// Default volume slider
|
||||
defaultVolumeSlider?.addEventListener('input', () => {
|
||||
const value = parseInt(defaultVolumeSlider.value);
|
||||
defaultVolumeSlider.addEventListener('input', () => {
|
||||
const value = defaultVolumeSlider.value;
|
||||
volumeValueDisplay.textContent = value + '%';
|
||||
this.app.player.settings.defaultVolume = value;
|
||||
this.app.player.settings.defaultVolume = parseInt(value);
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
// Remember volume toggle
|
||||
rememberVolumeToggle?.addEventListener('change', () => {
|
||||
rememberVolumeToggle.addEventListener('change', () => {
|
||||
this.app.player.settings.rememberVolume = rememberVolumeToggle.checked;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
// Auto-play next episode toggle
|
||||
autoPlayNextToggle?.addEventListener('change', () => {
|
||||
autoPlayNextToggle.addEventListener('change', () => {
|
||||
this.app.player.settings.autoPlayNextEpisode = autoPlayNextToggle.checked;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
// Force proxy toggle
|
||||
forceProxyToggle?.addEventListener('change', () => {
|
||||
this.app.player.settings.forceProxy = forceProxyToggle.checked;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
// Force transcode toggle
|
||||
forceTranscodeToggle?.addEventListener('change', () => {
|
||||
this.app.player.settings.forceTranscode = forceTranscodeToggle.checked;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
// Force remux toggle
|
||||
forceRemuxToggle?.addEventListener('change', () => {
|
||||
this.app.player.settings.forceRemux = forceRemuxToggle.checked;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
// Auto transcode (smart) toggle
|
||||
autoTranscodeToggle?.addEventListener('change', () => {
|
||||
this.app.player.settings.autoTranscode = autoTranscodeToggle.checked;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
// EPG refresh interval
|
||||
const epgRefreshSelect = document.getElementById('epg-refresh-interval');
|
||||
if (epgRefreshSelect && this.app.player?.settings) {
|
||||
@@ -126,57 +87,166 @@ class SettingsPage {
|
||||
epgRefreshSelect.addEventListener('change', () => {
|
||||
this.app.player.settings.epgRefreshInterval = epgRefreshSelect.value;
|
||||
this.app.player.saveSettings();
|
||||
// Server-side sync timer is restarted automatically via settings API
|
||||
});
|
||||
}
|
||||
|
||||
// Update last refreshed display
|
||||
this.updateEpgLastRefreshed();
|
||||
}
|
||||
|
||||
// Stream output format
|
||||
const streamFormatSelect = document.getElementById('setting-stream-format');
|
||||
if (streamFormatSelect && this.app.player?.settings) {
|
||||
// Load saved value from player settings
|
||||
streamFormatSelect.value = this.app.player.settings.streamFormat || 'm3u8';
|
||||
initTranscodingSettings() {
|
||||
// Encoder settings
|
||||
const hwEncoderSelect = document.getElementById('setting-hw-encoder');
|
||||
const maxResolutionSelect = document.getElementById('setting-max-resolution');
|
||||
const qualitySelect = document.getElementById('setting-quality');
|
||||
|
||||
// Save on change
|
||||
streamFormatSelect.addEventListener('change', () => {
|
||||
this.app.player.settings.streamFormat = streamFormatSelect.value;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
// Stream processing (use -tc suffix IDs from Transcoding tab)
|
||||
const forceProxyToggle = document.getElementById('setting-force-proxy-tc');
|
||||
const autoTranscodeToggle = document.getElementById('setting-auto-transcode-tc');
|
||||
const forceTranscodeToggle = document.getElementById('setting-force-transcode-tc');
|
||||
const forceRemuxToggle = document.getElementById('setting-force-remux-tc');
|
||||
const streamFormatSelect = document.getElementById('setting-stream-format-tc');
|
||||
|
||||
// User-Agent (Transcoding tab versions)
|
||||
const userAgentSelect = document.getElementById('setting-user-agent-tc');
|
||||
const userAgentCustomInput = document.getElementById('setting-user-agent-custom-tc');
|
||||
const customUaContainer = document.getElementById('custom-user-agent-container-tc');
|
||||
|
||||
// Load current settings
|
||||
if (this.app.player?.settings) {
|
||||
const s = this.app.player.settings;
|
||||
|
||||
if (hwEncoderSelect) hwEncoderSelect.value = s.hwEncoder || 'auto';
|
||||
if (maxResolutionSelect) maxResolutionSelect.value = s.maxResolution || '1080p';
|
||||
if (qualitySelect) qualitySelect.value = s.quality || 'medium';
|
||||
if (forceProxyToggle) forceProxyToggle.checked = s.forceProxy === true;
|
||||
if (autoTranscodeToggle) autoTranscodeToggle.checked = s.autoTranscode !== false;
|
||||
if (forceTranscodeToggle) forceTranscodeToggle.checked = s.forceTranscode === true;
|
||||
if (forceRemuxToggle) forceRemuxToggle.checked = s.forceRemux || false;
|
||||
if (streamFormatSelect) streamFormatSelect.value = s.streamFormat || 'm3u8';
|
||||
if (userAgentSelect) userAgentSelect.value = s.userAgentPreset || 'chrome';
|
||||
if (userAgentCustomInput) userAgentCustomInput.value = s.userAgentCustom || '';
|
||||
if (customUaContainer) {
|
||||
customUaContainer.style.display = userAgentSelect?.value === 'custom' ? 'flex' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// User-Agent preset
|
||||
const userAgentSelect = document.getElementById('setting-user-agent');
|
||||
const userAgentCustomInput = document.getElementById('setting-user-agent-custom');
|
||||
const customUaContainer = document.getElementById('custom-user-agent-container');
|
||||
// Event listeners for encoder settings
|
||||
hwEncoderSelect?.addEventListener('change', () => {
|
||||
this.app.player.settings.hwEncoder = hwEncoderSelect.value;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
if (userAgentSelect && this.app.player?.settings) {
|
||||
// Load saved values
|
||||
userAgentSelect.value = this.app.player.settings.userAgentPreset || 'chrome';
|
||||
if (userAgentCustomInput) {
|
||||
userAgentCustomInput.value = this.app.player.settings.userAgentCustom || '';
|
||||
maxResolutionSelect?.addEventListener('change', () => {
|
||||
this.app.player.settings.maxResolution = maxResolutionSelect.value;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
qualitySelect?.addEventListener('change', () => {
|
||||
this.app.player.settings.quality = qualitySelect.value;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
// Stream processing toggles
|
||||
forceProxyToggle?.addEventListener('change', () => {
|
||||
this.app.player.settings.forceProxy = forceProxyToggle.checked;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
autoTranscodeToggle?.addEventListener('change', () => {
|
||||
this.app.player.settings.autoTranscode = autoTranscodeToggle.checked;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
forceTranscodeToggle?.addEventListener('change', () => {
|
||||
this.app.player.settings.forceTranscode = forceTranscodeToggle.checked;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
forceRemuxToggle?.addEventListener('change', () => {
|
||||
this.app.player.settings.forceRemux = forceRemuxToggle.checked;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
streamFormatSelect?.addEventListener('change', () => {
|
||||
this.app.player.settings.streamFormat = streamFormatSelect.value;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
|
||||
// User-Agent handlers
|
||||
const toggleCustomInput = () => {
|
||||
if (customUaContainer) {
|
||||
customUaContainer.style.display = userAgentSelect?.value === 'custom' ? 'flex' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
userAgentSelect?.addEventListener('change', () => {
|
||||
this.app.player.settings.userAgentPreset = userAgentSelect.value;
|
||||
this.app.player.saveSettings();
|
||||
toggleCustomInput();
|
||||
});
|
||||
|
||||
userAgentCustomInput?.addEventListener('change', () => {
|
||||
this.app.player.settings.userAgentCustom = userAgentCustomInput.value;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and display hardware info in Transcoding tab
|
||||
*/
|
||||
async loadHardwareInfo() {
|
||||
const container = document.getElementById('hw-info-container');
|
||||
if (!container) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/settings/hw-info');
|
||||
if (!response.ok) throw new Error('Failed to fetch hardware info');
|
||||
const hwInfo = await response.json();
|
||||
|
||||
const detected = [];
|
||||
|
||||
// Only show detected hardware
|
||||
if (hwInfo.nvidia?.available) {
|
||||
detected.push(`<div class="hw-info-item hw-available">
|
||||
<span class="hw-badge">✓ NVIDIA</span>
|
||||
<span class="hw-name">${hwInfo.nvidia.name}</span>
|
||||
</div>`);
|
||||
}
|
||||
|
||||
// Show/hide custom input
|
||||
const toggleCustomInput = () => {
|
||||
if (customUaContainer) {
|
||||
customUaContainer.style.display = userAgentSelect.value === 'custom' ? 'flex' : 'none';
|
||||
}
|
||||
};
|
||||
toggleCustomInput();
|
||||
if (hwInfo.amf?.available) {
|
||||
detected.push(`<div class="hw-info-item hw-available">
|
||||
<span class="hw-badge">✓ AMD</span>
|
||||
<span class="hw-name">${hwInfo.amf.name || 'Available'}</span>
|
||||
</div>`);
|
||||
}
|
||||
|
||||
// Save on change
|
||||
userAgentSelect.addEventListener('change', () => {
|
||||
this.app.player.settings.userAgentPreset = userAgentSelect.value;
|
||||
this.app.player.saveSettings();
|
||||
toggleCustomInput();
|
||||
});
|
||||
if (hwInfo.qsv?.available) {
|
||||
detected.push(`<div class="hw-info-item hw-available">
|
||||
<span class="hw-badge">✓ Intel QSV</span>
|
||||
<span class="hw-name">Available</span>
|
||||
</div>`);
|
||||
}
|
||||
|
||||
userAgentCustomInput?.addEventListener('change', () => {
|
||||
this.app.player.settings.userAgentCustom = userAgentCustomInput.value;
|
||||
this.app.player.saveSettings();
|
||||
});
|
||||
if (hwInfo.vaapi?.available) {
|
||||
detected.push(`<div class="hw-info-item hw-available">
|
||||
<span class="hw-badge">✓ VAAPI</span>
|
||||
<span class="hw-name">${hwInfo.vaapi.device || 'Available'}</span>
|
||||
</div>`);
|
||||
}
|
||||
|
||||
let html;
|
||||
if (detected.length > 0) {
|
||||
html = `<div class="hw-info-grid">${detected.join('')}</div>`;
|
||||
html += `<p class="hint" style="margin-top: var(--space-sm);">Recommended encoder: <strong>${hwInfo.recommended}</strong></p>`;
|
||||
} else {
|
||||
html = `<p class="hint">No GPU acceleration detected. Using software encoding.</p>`;
|
||||
}
|
||||
|
||||
container.innerHTML = html;
|
||||
} catch (err) {
|
||||
console.error('Error loading hardware info:', err);
|
||||
container.innerHTML = '<p class="hint error">Failed to load hardware info</p>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +356,11 @@ class SettingsPage {
|
||||
if (tabName === 'users') {
|
||||
this.loadUsers();
|
||||
}
|
||||
|
||||
// Load hardware info when switching to transcode tab
|
||||
if (tabName === 'transcode') {
|
||||
this.loadHardwareInfo();
|
||||
}
|
||||
}
|
||||
|
||||
async show() {
|
||||
|
||||
@@ -71,6 +71,10 @@ function getDefaultSettings() {
|
||||
// User-Agent settings
|
||||
userAgentPreset: 'chrome', // chrome | vlc | tivimate | custom
|
||||
userAgentCustom: '', // Custom UA string when preset is 'custom'
|
||||
// Transcoding settings
|
||||
hwEncoder: 'auto', // auto | nvenc | amf | qsv | vaapi | software
|
||||
maxResolution: '1080p', // 4k | 1080p | 720p | 480p
|
||||
quality: 'medium', // high | medium | low
|
||||
// Probe cache settings
|
||||
probeCacheTTL: 300, // 5 minutes for URL probe cache
|
||||
seriesProbeCacheDays: 7 // 7 days for series episode probe cache
|
||||
|
||||
Reference in New Issue
Block a user