feat: implement hardware and software upscaling

This commit is contained in:
Trevor Mears
2026-01-19 01:34:45 -08:00
parent 877c6f8df9
commit 237d11cf00
8 changed files with 169 additions and 30 deletions
+14 -11
View File
@@ -814,16 +814,17 @@ class VideoPlayer {
}
}
if (info.needsTranscode) {
// Incompatible audio (AC3/EAC3/DTS) - use transcode session
console.log('[Player] Auto: Using HLS transcode session');
if (info.needsTranscode || this.settings.upscaleEnabled) {
// Incompatible audio (AC3/EAC3/DTS) or Upscaling enabled - use transcode session
console.log(`[Player] Auto: Using HLS transcode session (${this.settings.upscaleEnabled ? 'Upscaling' : 'Incompatible audio/video'})`);
// Heuristic: If video is h264, it's likely compatible, so only copy video (audio transcode only)
// If video is hevc/other, we need full transcode
const videoMode = (info.video && info.video.includes('h264')) ? 'copy' : 'encode';
const statusText = videoMode === 'copy' ? 'Transcoding (Audio)' : 'Transcoding (Video)';
// BUT: If upscaling is enabled, we MUST encode.
const videoMode = (info.video && info.video.includes('h264') && !this.settings.upscaleEnabled) ? 'copy' : 'encode';
const statusText = videoMode === 'copy' ? 'Transcoding (Audio)' : (this.settings.upscaleEnabled ? 'Upscaling' : 'Transcoding (Video)');
const statusMode = this.settings.upscaleEnabled ? 'upscaling' : 'transcoding';
this.updateTranscodeStatus('transcoding', statusText);
this.updateTranscodeStatus(statusMode, statusText);
const playlistUrl = await this.startTranscodeSession(streamUrl, {
videoMode,
videoCodec: info.video,
@@ -863,10 +864,12 @@ class VideoPlayer {
}
}
// CHECK: Force Video Transcode (Full)
if (this.settings.forceVideoTranscode) {
console.log('[Player] Force Video Transcode enabled. Starting session (encode)...');
this.updateTranscodeStatus('transcoding', 'Transcoding (Video)');
// CHECK: Force Video Transcode (Full) or Upscaling
if (this.settings.forceVideoTranscode || this.settings.upscaleEnabled) {
const statusText = this.settings.upscaleEnabled ? 'Upscaling' : 'Transcoding (Video)';
const statusMode = this.settings.upscaleEnabled ? 'upscaling' : 'transcoding';
console.log(`[Player] ${statusText} enabled. Starting session (encode)...`);
this.updateTranscodeStatus(statusMode, statusText);
const playlistUrl = await this.startTranscodeSession(streamUrl, { videoMode: 'encode' });
this.currentUrl = playlistUrl;
+38
View File
@@ -163,6 +163,44 @@ class SettingsPage {
});
}
// Upscaling Settings
const upscaleEnabledToggle = document.getElementById('setting-upscale-enabled');
const upscaleMethodSelect = document.getElementById('setting-upscale-method');
const upscaleTargetSelect = document.getElementById('setting-upscale-target');
const upscaleMethodContainer = document.getElementById('upscale-method-container');
const upscaleTargetContainer = document.getElementById('upscale-target-container');
// Helper to toggle upscale options visibility
const toggleUpscaleOptions = (enabled) => {
if (upscaleMethodContainer) upscaleMethodContainer.style.display = enabled ? 'flex' : 'none';
if (upscaleTargetContainer) upscaleTargetContainer.style.display = enabled ? 'flex' : 'none';
};
// Load upscaling settings
if (upscaleEnabledToggle) {
upscaleEnabledToggle.checked = s.upscaleEnabled || false;
toggleUpscaleOptions(upscaleEnabledToggle.checked);
}
if (upscaleMethodSelect) upscaleMethodSelect.value = s.upscaleMethod || 'hardware';
if (upscaleTargetSelect) upscaleTargetSelect.value = s.upscaleTarget || '1080p';
// Upscaling event handlers
upscaleEnabledToggle?.addEventListener('change', () => {
this.app.player.settings.upscaleEnabled = upscaleEnabledToggle.checked;
this.app.player.saveSettings();
toggleUpscaleOptions(upscaleEnabledToggle.checked);
});
upscaleMethodSelect?.addEventListener('change', () => {
this.app.player.settings.upscaleMethod = upscaleMethodSelect.value;
this.app.player.saveSettings();
});
upscaleTargetSelect?.addEventListener('change', () => {
this.app.player.settings.upscaleTarget = upscaleTargetSelect.value;
this.app.player.saveSettings();
});
// Stream processing toggles
forceProxyToggle?.addEventListener('change', () => {
this.app.player.settings.forceProxy = forceProxyToggle.checked;
+14 -10
View File
@@ -395,14 +395,16 @@ class WatchPage {
this.currentStreamInfo = info;
this.updateQualityBadge();
if (info.needsTranscode) {
console.log('[WatchPage] Auto: Using HLS transcode session (incompatible audio/video)');
if (info.needsTranscode || settings.upscaleEnabled) {
console.log(`[WatchPage] Auto: Using HLS transcode session (${settings.upscaleEnabled ? 'Upscaling' : 'Incompatible audio/video'})`);
// Heuristic: If video is h264/compat, copy video. Usage: Audio fix.
const videoMode = (info.video && info.video.includes('h264')) ? 'copy' : 'encode';
const statusText = videoMode === 'copy' ? 'Transcoding (Audio)' : 'Transcoding (Video)';
// Heuristic: If video is h264/compat, copy video. Usage: Audio fix.
// BUT: If upscaling is enabled, we MUST encode.
const videoMode = (info.video && info.video.includes('h264') && !settings.upscaleEnabled) ? 'copy' : 'encode';
const statusText = videoMode === 'copy' ? 'Transcoding (Audio)' : (settings.upscaleEnabled ? 'Upscaling' : 'Transcoding (Video)');
const statusMode = settings.upscaleEnabled ? 'upscaling' : 'transcoding';
this.updateTranscodeStatus('transcoding', statusText);
this.updateTranscodeStatus(statusMode, statusText);
const playlistUrl = await this.startTranscodeSession(url, {
videoMode,
seekOffset: this.resumeTime, // Ensure seekOffset is passed
@@ -434,10 +436,12 @@ class WatchPage {
}
}
// Priority 1: Force Video Transcode (Full)
if (settings.forceVideoTranscode) {
console.log('[WatchPage] Force Video Transcode enabled. Starting session (encode)...');
this.updateTranscodeStatus('transcoding', 'Transcoding (Video)');
// Priority 1: Force Video Transcode (Full) or Upscaling
if (settings.forceVideoTranscode || settings.upscaleEnabled) {
const statusText = settings.upscaleEnabled ? 'Upscaling' : 'Transcoding (Video)';
const statusMode = settings.upscaleEnabled ? 'upscaling' : 'transcoding';
console.log(`[WatchPage] ${statusText} enabled. Starting session (encode)...`);
this.updateTranscodeStatus(statusMode, statusText);
const playlistUrl = await this.startTranscodeSession(url, {
videoMode: 'encode',
seekOffset: this.resumeTime