Migrate transcoding page to React
This commit is contained in:
@@ -1,407 +0,0 @@
|
||||
<div id="encodingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage" data-title="${TitlePlayback}">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<form class="encodingSettingsForm">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${Transcoding}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectVideoDecoder" label="${LabelHardwareAccelerationType}">
|
||||
<option value="none">${None}</option>
|
||||
<option value="amf">AMD AMF</option>
|
||||
<option value="nvenc">Nvidia NVENC</option>
|
||||
<option value="qsv">Intel QuickSync (QSV)</option>
|
||||
<option value="vaapi">Video Acceleration API (VAAPI)</option>
|
||||
<option value="rkmpp">Rockchip MPP (RKMPP)</option>
|
||||
<option value="videotoolbox">Apple VideoToolBox</option>
|
||||
<option value="v4l2m2m">Video4Linux2 (V4L2)</option>
|
||||
</select>
|
||||
<div class="fieldDescription">
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/" target="_blank">${LabelHardwareAccelerationTypeHelp}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer hide fldVaapiDevice">
|
||||
<input is="emby-input" type="text" id="txtVaapiDevice" label="${LabelVaapiDevice}" />
|
||||
<div class="fieldDescription">${LabelVaapiDeviceHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer hide fldQsvDevice">
|
||||
<input is="emby-input" type="text" id="txtQsvDevice" label="${LabelQsvDevice}" />
|
||||
<div class="fieldDescription">${LabelQsvDeviceHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="hardwareAccelerationOptions hide">
|
||||
<div class="checkboxListContainer decodingCodecsList">
|
||||
<h3 class="checkboxListLabel">${LabelEnableHardwareDecodingFor}</h3>
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="h264" data-types="amf,nvenc,qsv,vaapi,rkmpp,videotoolbox,v4l2m2m" />
|
||||
<span>H264</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="hevc" data-types="amf,nvenc,qsv,vaapi,rkmpp,videotoolbox" />
|
||||
<span>HEVC</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="mpeg1video" data-types="rkmpp" />
|
||||
<span>MPEG1</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="mpeg2video" data-types="amf,nvenc,qsv,vaapi,rkmpp" />
|
||||
<span>MPEG2</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="mpeg4" data-types="nvenc,rkmpp" />
|
||||
<span>MPEG4</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="vc1" data-types="amf,nvenc,qsv,vaapi" />
|
||||
<span>VC1</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="vp8" data-types="nvenc,qsv,vaapi,rkmpp,videotoolbox" />
|
||||
<span>VP8</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="vp9" data-types="amf,nvenc,qsv,vaapi,rkmpp,videotoolbox" />
|
||||
<span>VP9</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkDecodeCodec" data-codec="av1" data-types="amf,nvenc,qsv,vaapi,rkmpp,videotoolbox" />
|
||||
<span>AV1</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkboxList hide fld10bitHevcVp9HwDecoding">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkDecodingColorDepth10Hevc" />
|
||||
<span>HEVC 10bit</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkDecodingColorDepth10Vp9" />
|
||||
<span>VP9 10bit</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkboxList hide fldHevcRextHwDecoding">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkDecodingColorDepth10HevcRext" />
|
||||
<span>HEVC RExt 8/10bit</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkDecodingColorDepth12HevcRext" />
|
||||
<span>HEVC RExt 12bit</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxListContainer hide fldEnhancedNvdec">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnhancedNvdecDecoder" />
|
||||
<span>${EnableEnhancedNvdecDecoder}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${EnableEnhancedNvdecDecoderHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxListContainer hide fldSysNativeHwDecoder">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkSystemNativeHwDecoder" />
|
||||
<span>${PreferSystemNativeHwDecoder}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkboxListContainer">
|
||||
<h3 class="checkboxListLabel">${LabelHardwareEncodingOptions}</h3>
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkHardwareEncoding" />
|
||||
<span>${EnableHardwareEncoding}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkboxList hide fldIntelLp">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkIntelLpH264HwEncoder" />
|
||||
<span>${EnableIntelLowPowerH264HwEncoder}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkIntelLpHevcHwEncoder" />
|
||||
<span>${EnableIntelLowPowerHevcHwEncoder}</span>
|
||||
</label>
|
||||
<div class="fieldDescription">
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#configure-and-verify-lp-mode-on-linux" target="_blank">${IntelLowPowerEncHelp}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxListContainer">
|
||||
<h3 class="checkboxListLabel">${LabelEncodingFormatOptions}</h3>
|
||||
<div class="fieldDescription">${EncodingFormatHelp}</div>
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkAllowHevcEncoding" />
|
||||
<span>${AllowHevcEncoding}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkAllowAv1Encoding" />
|
||||
<span>${AllowAv1Encoding}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vppTonemappingOptions hide">
|
||||
<div class="checkboxListContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkVppTonemapping" />
|
||||
<span>${EnableVppTonemapping}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${AllowVppTonemappingHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtVppTonemappingBrightness" pattern="[0-9]*" min="0" max="100" step=".00001" label="${LabelVppTonemappingBrightness}" />
|
||||
<div class="fieldDescription">${LabelVppTonemappingBrightnessHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtVppTonemappingContrast" pattern="[0-9]*" min="1" max="2" step=".00001" label="${LabelVppTonemappingContrast}" />
|
||||
<div class="fieldDescription">${LabelVppTonemappingContrastHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="videoToolboxTonemappingOptions hide">
|
||||
<div class="checkboxListContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkVideoToolboxTonemapping" />
|
||||
<span>${EnableVideoToolboxTonemapping}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${AllowVideoToolboxTonemappingHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tonemappingOptions hide">
|
||||
<div class="checkboxListContainer checkboxContainer-withDescription fldTonemapCheckbox hide">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkTonemapping" />
|
||||
<span>${EnableTonemapping}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription allowTonemappingHardwareHelp">${AllowTonemappingHelp}</div>
|
||||
<div class="fieldDescription checkboxFieldDescription allowTonemappingSoftwareHelp">${AllowTonemappingSoftwareHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectTonemappingAlgorithm" label="${LabelTonemappingAlgorithm}">
|
||||
<option value="none">${None}</option>
|
||||
<option value="clip">Clip</option>
|
||||
<option value="linear">Linear</option>
|
||||
<option value="gamma">Gamma</option>
|
||||
<option value="reinhard">Reinhard</option>
|
||||
<option value="hable">Hable</option>
|
||||
<option value="mobius">Mobius</option>
|
||||
<option value="bt2390">BT.2390</option>
|
||||
</select>
|
||||
<div class="fieldDescription">
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="http://ffmpeg.org/ffmpeg-all.html#tonemap_005fopencl" target="_blank">${TonemappingAlgorithmHelp}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tonemappingModeOptions selectContainer">
|
||||
<select is="emby-select" id="selectTonemappingMode" label="${LabelTonemappingMode}">
|
||||
<option value="auto">${Auto}</option>
|
||||
<option value="max">MAX</option>
|
||||
<option value="rgb">RGB</option>
|
||||
<option value="lum">LUM</option>
|
||||
<option value="itp">ITP</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${TonemappingModeHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectTonemappingRange" label="${LabelTonemappingRange}">
|
||||
<option value="auto">${Auto}</option>
|
||||
<option value="tv">TV</option>
|
||||
<option value="pc">PC</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${TonemappingRangeHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtTonemappingDesat" pattern="[0-9]*" min="0" max="1.79769e+308" step=".00001" label="${LabelTonemappingDesat}" />
|
||||
<div class="fieldDescription">${LabelTonemappingDesatHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtTonemappingPeak" pattern="[0-9]*" min="0" max="1.79769e+308" step=".00001" label="${LabelTonemappingPeak}" />
|
||||
<div class="fieldDescription">${LabelTonemappingPeakHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtTonemappingParam" pattern="[0-9]*" min="2.22507e-308" max="1.79769e+308" step=".00001" label="${LabelTonemappingParam}" />
|
||||
<div class="fieldDescription">${LabelTonemappingParamHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectThreadCount" label="${LabelTranscodingThreadCount}">
|
||||
<option value="-1">${Auto}</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="10">10</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="14">14</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="0">${OptionMax}</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${LabelTranscodingThreadCountHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer fldEncoderPath">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="flex-grow:1;">
|
||||
<input is="emby-input" class="txtEncoderPath" label="${LabelffmpegPath}" autocomplete="off" dir="ltr" disabled/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fieldDescription">
|
||||
<div>${LabelffmpegPathHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="flex-grow:1;">
|
||||
<input is="emby-input" id="txtTranscodingTempPath" label="${LabelTranscodePath}" autocomplete="off" dir="ltr" />
|
||||
</div>
|
||||
<button type="button" is="paper-icon-button-light" id="btnSelectTranscodingTempPath" class="emby-input-iconbutton"><span class="material-icons search" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
<div class="fieldDescription">${LabelTranscodingTempPathHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="flex-grow:1;">
|
||||
<input is="emby-input" id="txtFallbackFontPath" label="${LabelFallbackFontPath}" autocomplete="off" dir="ltr" />
|
||||
</div>
|
||||
<button type="button" is="paper-icon-button-light" id="btnSelectFallbackFontPath" class="emby-input-iconbutton"><span class="material-icons search" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
<div class="fieldDescription">
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="https://jellyfin.org/docs/general/administration/configuration#fonts" target="_blank">${LabelFallbackFontPathHelp}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableFallbackFont" />
|
||||
<span>${EnableFallbackFont}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${EnableFallbackFontHelp}</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableAudioVbr" />
|
||||
<span>${LabelEnableAudioVbr}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableAudioVbrHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtDownMixAudioBoost" pattern="[0-9]*" required="required" min=".5" max="3" step=".1" label="${LabelDownMixAudioScale}" />
|
||||
<div class="fieldDescription">${LabelDownMixAudioScaleHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectStereoDownmixAlgorithm" label="${LabelStereoDownmixAlgorithm}">
|
||||
<option value="None">${None}</option>
|
||||
<option value="Dave750">Dave750</option>
|
||||
<option value="NightmodeDialogue">NightmodeDialogue</option>
|
||||
<option value="Rfc7845">RFC7845</option>
|
||||
<option value="Ac4">AC-4</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${StereoDownmixAlgorithmHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtMaxMuxingQueueSize" pattern="[0-9]*" required="required" min="128" max="2147483647" step="1" label="${LabelMaxMuxingQueueSize}" />
|
||||
<div class="fieldDescription">${LabelMaxMuxingQueueSizeHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectEncoderPreset" label="${LabelEncoderPreset}">
|
||||
<option value="auto">${Auto}</option>
|
||||
<option value="veryslow">veryslow</option>
|
||||
<option value="slower">slower</option>
|
||||
<option value="slow">slow</option>
|
||||
<option value="medium">medium</option>
|
||||
<option value="fast">fast</option>
|
||||
<option value="faster">faster</option>
|
||||
<option value="veryfast">veryfast</option>
|
||||
<option value="superfast">superfast</option>
|
||||
<option value="ultrafast">ultrafast</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${EncoderPresetHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtH265Crf" pattern="[0-9]*" min="0" max="51" step="1" label="${LabelH265Crf}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtH264Crf" pattern="[0-9]*" min="0" max="51" step="1" label="${LabelH264Crf}" />
|
||||
<div class="fieldDescription">${H264CrfHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectDeinterlaceMethod" label="${LabelDeinterlaceMethod}">
|
||||
<option value="yadif">${Yadif}</option>
|
||||
<option value="bwdif">${Bwdif}</option>
|
||||
</select>
|
||||
<div class="fieldDescription">${DeinterlaceMethodHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkDoubleRateDeinterlacing" />
|
||||
<span>${UseDoubleRateDeinterlacing}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${UseDoubleRateDeinterlacingHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableSubtitleExtraction" />
|
||||
<span>${AllowOnTheFlySubtitleExtraction}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${AllowOnTheFlySubtitleExtractionHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableThrottling" />
|
||||
<span>${AllowFfmpegThrottling}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${AllowFfmpegThrottlingHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableSegmentDeletion" />
|
||||
<span>${AllowSegmentDeletion}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${AllowSegmentDeletionHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtThrottleDelaySeconds" pattern="[0-9]*" min="10" max="3600" step="1" label="${LabelThrottleDelaySeconds}" />
|
||||
<div class="fieldDescription">${LabelThrottleDelaySecondsHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtSegmentKeepSeconds" pattern="[0-9]*" min="15" max="3600" step="1" label="${LabelSegmentKeepSeconds}" />
|
||||
<div class="fieldDescription">${LabelSegmentKeepSecondsHelp}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,309 +0,0 @@
|
||||
import 'jquery';
|
||||
import loading from 'components/loading/loading';
|
||||
import globalize from 'lib/globalize';
|
||||
import dom from 'scripts/dom';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
import alert from 'components/alert';
|
||||
|
||||
function loadPage(page, config, systemInfo) {
|
||||
Array.prototype.forEach.call(page.querySelectorAll('.chkDecodeCodec'), function (c) {
|
||||
c.checked = (config.HardwareDecodingCodecs || []).indexOf(c.getAttribute('data-codec')) !== -1;
|
||||
});
|
||||
page.querySelector('#chkDecodingColorDepth10Hevc').checked = config.EnableDecodingColorDepth10Hevc;
|
||||
page.querySelector('#chkDecodingColorDepth10Vp9').checked = config.EnableDecodingColorDepth10Vp9;
|
||||
page.querySelector('#chkDecodingColorDepth10HevcRext').checked = config.EnableDecodingColorDepth10HevcRext;
|
||||
page.querySelector('#chkDecodingColorDepth12HevcRext').checked = config.EnableDecodingColorDepth12HevcRext;
|
||||
page.querySelector('#chkEnhancedNvdecDecoder').checked = config.EnableEnhancedNvdecDecoder;
|
||||
page.querySelector('#chkSystemNativeHwDecoder').checked = config.PreferSystemNativeHwDecoder;
|
||||
page.querySelector('#chkIntelLpH264HwEncoder').checked = config.EnableIntelLowPowerH264HwEncoder;
|
||||
page.querySelector('#chkIntelLpHevcHwEncoder').checked = config.EnableIntelLowPowerHevcHwEncoder;
|
||||
page.querySelector('#chkHardwareEncoding').checked = config.EnableHardwareEncoding;
|
||||
page.querySelector('#chkAllowHevcEncoding').checked = config.AllowHevcEncoding;
|
||||
page.querySelector('#chkAllowAv1Encoding').checked = config.AllowAv1Encoding;
|
||||
page.querySelector('#selectVideoDecoder').value = config.HardwareAccelerationType || 'none';
|
||||
page.querySelector('#selectThreadCount').value = config.EncodingThreadCount;
|
||||
page.querySelector('#chkEnableAudioVbr').checked = config.EnableAudioVbr;
|
||||
page.querySelector('#txtDownMixAudioBoost').value = config.DownMixAudioBoost;
|
||||
page.querySelector('#selectStereoDownmixAlgorithm').value = config.DownMixStereoAlgorithm || 'None';
|
||||
page.querySelector('#txtMaxMuxingQueueSize').value = config.MaxMuxingQueueSize || '';
|
||||
page.querySelector('.txtEncoderPath').value = config.EncoderAppPathDisplay || '';
|
||||
page.querySelector('#txtTranscodingTempPath').value = systemInfo.TranscodingTempPath || '';
|
||||
page.querySelector('#txtFallbackFontPath').value = config.FallbackFontPath || '';
|
||||
page.querySelector('#chkEnableFallbackFont').checked = config.EnableFallbackFont;
|
||||
page.querySelector('#txtVaapiDevice').value = config.VaapiDevice || '';
|
||||
page.querySelector('#txtQsvDevice').value = config.QsvDevice || '';
|
||||
page.querySelector('#chkTonemapping').checked = config.EnableTonemapping;
|
||||
page.querySelector('#chkVppTonemapping').checked = config.EnableVppTonemapping;
|
||||
page.querySelector('#chkVideoToolboxTonemapping').checked = config.EnableVideoToolboxTonemapping;
|
||||
page.querySelector('#selectTonemappingAlgorithm').value = config.TonemappingAlgorithm || 'none';
|
||||
page.querySelector('#selectTonemappingMode').value = config.TonemappingMode || 'auto';
|
||||
page.querySelector('#selectTonemappingRange').value = config.TonemappingRange || 'auto';
|
||||
page.querySelector('#txtTonemappingDesat').value = config.TonemappingDesat;
|
||||
page.querySelector('#txtTonemappingPeak').value = config.TonemappingPeak;
|
||||
page.querySelector('#txtTonemappingParam').value = config.TonemappingParam || '';
|
||||
page.querySelector('#txtVppTonemappingBrightness').value = config.VppTonemappingBrightness;
|
||||
page.querySelector('#txtVppTonemappingContrast').value = config.VppTonemappingContrast;
|
||||
page.querySelector('#selectEncoderPreset').value = config.EncoderPreset || 'auto';
|
||||
page.querySelector('#txtH264Crf').value = config.H264Crf || '';
|
||||
page.querySelector('#txtH265Crf').value = config.H265Crf || '';
|
||||
page.querySelector('#selectDeinterlaceMethod').value = config.DeinterlaceMethod || 'yadif';
|
||||
page.querySelector('#chkDoubleRateDeinterlacing').checked = config.DeinterlaceDoubleRate;
|
||||
page.querySelector('#chkEnableSubtitleExtraction').checked = config.EnableSubtitleExtraction || false;
|
||||
page.querySelector('#chkEnableThrottling').checked = config.EnableThrottling || false;
|
||||
page.querySelector('#chkEnableSegmentDeletion').checked = config.EnableSegmentDeletion || false;
|
||||
page.querySelector('#txtThrottleDelaySeconds').value = config.ThrottleDelaySeconds || '';
|
||||
page.querySelector('#txtSegmentKeepSeconds').value = config.SegmentKeepSeconds || '';
|
||||
page.querySelector('#selectVideoDecoder').dispatchEvent(new CustomEvent('change', {
|
||||
bubbles: true
|
||||
}));
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function onSaveEncodingPathFailure() {
|
||||
loading.hide();
|
||||
alert(globalize.translate('FFmpegSavePathNotFound'));
|
||||
}
|
||||
|
||||
function updateEncoder(form) {
|
||||
return ApiClient.getSystemInfo().then(function () {
|
||||
return ApiClient.ajax({
|
||||
url: ApiClient.getUrl('System/MediaEncoder/Path'),
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
Path: form.querySelector('.txtEncoderPath').value,
|
||||
PathType: 'Custom'
|
||||
}),
|
||||
contentType: 'application/json'
|
||||
}).then(Dashboard.processServerConfigurationUpdateResult, onSaveEncodingPathFailure);
|
||||
});
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
const form = this;
|
||||
|
||||
const onDecoderConfirmed = function () {
|
||||
loading.show();
|
||||
ApiClient.getNamedConfiguration('encoding').then(function (config) {
|
||||
config.EnableAudioVbr = form.querySelector('#chkEnableAudioVbr').checked;
|
||||
config.DownMixAudioBoost = form.querySelector('#txtDownMixAudioBoost').value;
|
||||
config.DownMixStereoAlgorithm = form.querySelector('#selectStereoDownmixAlgorithm').value || 'None';
|
||||
config.MaxMuxingQueueSize = form.querySelector('#txtMaxMuxingQueueSize').value;
|
||||
config.TranscodingTempPath = form.querySelector('#txtTranscodingTempPath').value;
|
||||
config.FallbackFontPath = form.querySelector('#txtFallbackFontPath').value;
|
||||
config.EnableFallbackFont = form.querySelector('#txtFallbackFontPath').value ? form.querySelector('#chkEnableFallbackFont').checked : false;
|
||||
config.EncodingThreadCount = form.querySelector('#selectThreadCount').value;
|
||||
config.HardwareAccelerationType = form.querySelector('#selectVideoDecoder').value;
|
||||
config.VaapiDevice = form.querySelector('#txtVaapiDevice').value;
|
||||
config.QsvDevice = form.querySelector('#txtQsvDevice').value;
|
||||
config.EnableTonemapping = form.querySelector('#chkTonemapping').checked;
|
||||
config.EnableVppTonemapping = form.querySelector('#chkVppTonemapping').checked;
|
||||
config.EnableVideoToolboxTonemapping = form.querySelector('#chkVideoToolboxTonemapping').checked;
|
||||
config.TonemappingAlgorithm = form.querySelector('#selectTonemappingAlgorithm').value;
|
||||
config.TonemappingMode = form.querySelector('#selectTonemappingMode').value;
|
||||
config.TonemappingRange = form.querySelector('#selectTonemappingRange').value;
|
||||
config.TonemappingDesat = form.querySelector('#txtTonemappingDesat').value;
|
||||
config.TonemappingPeak = form.querySelector('#txtTonemappingPeak').value;
|
||||
config.TonemappingParam = form.querySelector('#txtTonemappingParam').value || '0';
|
||||
config.VppTonemappingBrightness = form.querySelector('#txtVppTonemappingBrightness').value;
|
||||
config.VppTonemappingContrast = form.querySelector('#txtVppTonemappingContrast').value;
|
||||
config.EncoderPreset = form.querySelector('#selectEncoderPreset').value;
|
||||
config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0', 10);
|
||||
config.H265Crf = parseInt(form.querySelector('#txtH265Crf').value || '0', 10);
|
||||
config.DeinterlaceMethod = form.querySelector('#selectDeinterlaceMethod').value;
|
||||
config.DeinterlaceDoubleRate = form.querySelector('#chkDoubleRateDeinterlacing').checked;
|
||||
config.EnableSubtitleExtraction = form.querySelector('#chkEnableSubtitleExtraction').checked;
|
||||
config.EnableThrottling = form.querySelector('#chkEnableThrottling').checked;
|
||||
config.EnableSegmentDeletion = form.querySelector('#chkEnableSegmentDeletion').checked;
|
||||
config.ThrottleDelaySeconds = parseInt(form.querySelector('#txtThrottleDelaySeconds').value || '0', 10);
|
||||
config.SegmentKeepSeconds = parseInt(form.querySelector('#txtSegmentKeepSeconds').value || '0', 10);
|
||||
config.HardwareDecodingCodecs = Array.prototype.map.call(Array.prototype.filter.call(form.querySelectorAll('.chkDecodeCodec'), function (c) {
|
||||
return c.checked;
|
||||
}), function (c) {
|
||||
return c.getAttribute('data-codec');
|
||||
});
|
||||
config.EnableDecodingColorDepth10Hevc = form.querySelector('#chkDecodingColorDepth10Hevc').checked;
|
||||
config.EnableDecodingColorDepth10Vp9 = form.querySelector('#chkDecodingColorDepth10Vp9').checked;
|
||||
config.EnableDecodingColorDepth10HevcRext = form.querySelector('#chkDecodingColorDepth10HevcRext').checked;
|
||||
config.EnableDecodingColorDepth12HevcRext = form.querySelector('#chkDecodingColorDepth12HevcRext').checked;
|
||||
config.EnableEnhancedNvdecDecoder = form.querySelector('#chkEnhancedNvdecDecoder').checked;
|
||||
config.PreferSystemNativeHwDecoder = form.querySelector('#chkSystemNativeHwDecoder').checked;
|
||||
config.EnableIntelLowPowerH264HwEncoder = form.querySelector('#chkIntelLpH264HwEncoder').checked;
|
||||
config.EnableIntelLowPowerHevcHwEncoder = form.querySelector('#chkIntelLpHevcHwEncoder').checked;
|
||||
config.EnableHardwareEncoding = form.querySelector('#chkHardwareEncoding').checked;
|
||||
config.AllowHevcEncoding = form.querySelector('#chkAllowHevcEncoding').checked;
|
||||
config.AllowAv1Encoding = form.querySelector('#chkAllowAv1Encoding').checked;
|
||||
ApiClient.updateNamedConfiguration('encoding', config).then(function () {
|
||||
updateEncoder(form);
|
||||
}, function () {
|
||||
alert(globalize.translate('ErrorDefault'));
|
||||
Dashboard.processServerConfigurationUpdateResult();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (form.querySelector('#selectVideoDecoder').value !== 'none') {
|
||||
alert({
|
||||
title: globalize.translate('TitleHardwareAcceleration'),
|
||||
text: globalize.translate('HardwareAccelerationWarning')
|
||||
}).then(onDecoderConfirmed);
|
||||
} else {
|
||||
onDecoderConfirmed();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function setDecodingCodecsVisible(context, value) {
|
||||
value = value || '';
|
||||
let any;
|
||||
Array.prototype.forEach.call(context.querySelectorAll('.chkDecodeCodec'), function (c) {
|
||||
if (c.getAttribute('data-types').split(',').indexOf(value) === -1) {
|
||||
dom.parentWithTag(c, 'LABEL').classList.add('hide');
|
||||
} else {
|
||||
dom.parentWithTag(c, 'LABEL').classList.remove('hide');
|
||||
any = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (any) {
|
||||
context.querySelector('.decodingCodecsList').classList.remove('hide');
|
||||
} else {
|
||||
context.querySelector('.decodingCodecsList').classList.add('hide');
|
||||
}
|
||||
}
|
||||
|
||||
let systemInfo;
|
||||
function getSystemInfo() {
|
||||
return systemInfo ? Promise.resolve(systemInfo) : ApiClient.getPublicSystemInfo().then(
|
||||
info => {
|
||||
systemInfo = info;
|
||||
return info;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$(document).on('pageinit', '#encodingSettingsPage', function () {
|
||||
const page = this;
|
||||
getSystemInfo();
|
||||
page.querySelector('#selectVideoDecoder').addEventListener('change', function () {
|
||||
if (this.value == 'vaapi') {
|
||||
page.querySelector('.fldVaapiDevice').classList.remove('hide');
|
||||
page.querySelector('#txtVaapiDevice').setAttribute('required', 'required');
|
||||
} else {
|
||||
page.querySelector('.fldVaapiDevice').classList.add('hide');
|
||||
page.querySelector('#txtVaapiDevice').removeAttribute('required');
|
||||
}
|
||||
|
||||
if (this.value == 'amf' || this.value == 'nvenc' || this.value == 'qsv' || this.value == 'vaapi' || this.value == 'rkmpp') {
|
||||
page.querySelector('.fld10bitHevcVp9HwDecoding').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.fld10bitHevcVp9HwDecoding').classList.add('hide');
|
||||
}
|
||||
|
||||
if (this.value == 'nvenc' || this.value == 'qsv' || this.value == 'vaapi') {
|
||||
page.querySelector('.fldHevcRextHwDecoding').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.fldHevcRextHwDecoding').classList.add('hide');
|
||||
}
|
||||
|
||||
const isHwaSelected = [ 'amf', 'nvenc', 'qsv', 'vaapi', 'rkmpp', 'videotoolbox' ].includes(this.value);
|
||||
if (this.value === 'none') {
|
||||
page.querySelector('.tonemappingOptions').classList.remove('hide');
|
||||
page.querySelector('.fldTonemapCheckbox').classList.add('hide');
|
||||
} else if (isHwaSelected) {
|
||||
page.querySelector('.tonemappingOptions').classList.remove('hide');
|
||||
page.querySelector('.fldTonemapCheckbox').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.tonemappingOptions').classList.add('hide');
|
||||
page.querySelector('.fldTonemapCheckbox').classList.add('hide');
|
||||
}
|
||||
|
||||
page.querySelector('.tonemappingModeOptions').classList.toggle('hide', !isHwaSelected);
|
||||
page.querySelector('.allowTonemappingHardwareHelp').classList.toggle('hide', !isHwaSelected);
|
||||
page.querySelector('.allowTonemappingSoftwareHelp').classList.toggle('hide', isHwaSelected);
|
||||
|
||||
if (this.value == 'qsv' || this.value == 'vaapi') {
|
||||
page.querySelector('.fldIntelLp').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.fldIntelLp').classList.add('hide');
|
||||
}
|
||||
|
||||
if (this.value === 'videotoolbox') {
|
||||
page.querySelector('.videoToolboxTonemappingOptions').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.videoToolboxTonemappingOptions').classList.add('hide');
|
||||
}
|
||||
|
||||
if (this.value == 'qsv' || this.value == 'vaapi') {
|
||||
page.querySelector('.vppTonemappingOptions').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.vppTonemappingOptions').classList.add('hide');
|
||||
}
|
||||
|
||||
if (this.value == 'qsv') {
|
||||
page.querySelector('.fldSysNativeHwDecoder').classList.remove('hide');
|
||||
page.querySelector('.fldQsvDevice').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.fldSysNativeHwDecoder').classList.add('hide');
|
||||
page.querySelector('.fldQsvDevice').classList.add('hide');
|
||||
}
|
||||
|
||||
if (this.value == 'nvenc') {
|
||||
page.querySelector('.fldEnhancedNvdec').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.fldEnhancedNvdec').classList.add('hide');
|
||||
}
|
||||
|
||||
if (this.value !== 'none') {
|
||||
page.querySelector('.hardwareAccelerationOptions').classList.remove('hide');
|
||||
} else {
|
||||
page.querySelector('.hardwareAccelerationOptions').classList.add('hide');
|
||||
}
|
||||
|
||||
setDecodingCodecsVisible(page, this.value);
|
||||
});
|
||||
$('#btnSelectTranscodingTempPath', page).on('click.selectDirectory', function () {
|
||||
import('components/directorybrowser/directorybrowser').then(({ default: DirectoryBrowser }) => {
|
||||
const picker = new DirectoryBrowser();
|
||||
picker.show({
|
||||
callback: function (path) {
|
||||
if (path) {
|
||||
page.querySelector('#txtTranscodingTempPath').value = path;
|
||||
}
|
||||
|
||||
picker.close();
|
||||
},
|
||||
validateWriteable: true,
|
||||
header: globalize.translate('HeaderSelectTranscodingPath'),
|
||||
instruction: globalize.translate('HeaderSelectTranscodingPathHelp')
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#btnSelectFallbackFontPath', page).on('click.selectDirectory', function () {
|
||||
import('components/directorybrowser/directorybrowser').then(({ default: DirectoryBrowser }) => {
|
||||
const picker = new DirectoryBrowser();
|
||||
picker.show({
|
||||
includeDirectories: true,
|
||||
callback: function (path) {
|
||||
if (path) {
|
||||
page.querySelector('#txtFallbackFontPath').value = path;
|
||||
}
|
||||
|
||||
picker.close();
|
||||
},
|
||||
header: globalize.translate('HeaderSelectFallbackFontPath'),
|
||||
instruction: globalize.translate('HeaderSelectFallbackFontPathHelp')
|
||||
});
|
||||
});
|
||||
});
|
||||
$('.encodingSettingsForm').off('submit', onSubmit).on('submit', onSubmit);
|
||||
}).on('pageshow', '#encodingSettingsPage', function () {
|
||||
loading.show();
|
||||
const page = this;
|
||||
ApiClient.getNamedConfiguration('encoding').then(function (config) {
|
||||
ApiClient.getSystemInfo().then(function (fetchedSystemInfo) {
|
||||
loadPage(page, config, fetchedSystemInfo);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
113
src/apps/dashboard/features/playback/constants/codecs.ts
Normal file
113
src/apps/dashboard/features/playback/constants/codecs.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
/** List of codecs and their supported hardware acceleration types */
|
||||
export const CODECS = [
|
||||
{
|
||||
name: 'H264',
|
||||
codec: 'h264',
|
||||
types: [
|
||||
'amf',
|
||||
'nvenc',
|
||||
'qsv',
|
||||
'vaapi',
|
||||
'rkmpp',
|
||||
'videotoolbox',
|
||||
'v4l2m2m'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'HEVC',
|
||||
codec: 'hevc',
|
||||
types: [
|
||||
'amf',
|
||||
'nvenc',
|
||||
'qsv',
|
||||
'vaapi',
|
||||
'rkmpp',
|
||||
'videotoolbox'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'MPEG1',
|
||||
codec: 'mpeg1video',
|
||||
types: [ 'rkmpp' ]
|
||||
},
|
||||
{
|
||||
name: 'MPEG2',
|
||||
codec: 'mpeg2video',
|
||||
types: [
|
||||
'amf',
|
||||
'nvenc',
|
||||
'qsv',
|
||||
'vaapi',
|
||||
'rkmpp'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'MPEG4',
|
||||
codec: 'mpeg4',
|
||||
types: [
|
||||
'nvenc',
|
||||
'rkmpp'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'VC1',
|
||||
codec: 'vc1',
|
||||
types: [
|
||||
'amf',
|
||||
'nvenc',
|
||||
'qsv',
|
||||
'vaapi'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'VP8',
|
||||
codec: 'vp8',
|
||||
types: [
|
||||
'nvenc',
|
||||
'qsv',
|
||||
'vaapi',
|
||||
'rkmpp',
|
||||
'videotoolbox'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'VP9',
|
||||
codec: 'vp9',
|
||||
types: [
|
||||
'amf',
|
||||
'nvenc',
|
||||
'qsv',
|
||||
'vaapi',
|
||||
'rkmpp',
|
||||
'videotoolbox'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'AV1',
|
||||
codec: 'av1',
|
||||
types: [
|
||||
'amf',
|
||||
'nvenc',
|
||||
'qsv',
|
||||
'vaapi',
|
||||
'rkmpp',
|
||||
'videotoolbox'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
/** Hardware decoders which support 10-bit HEVC & VP9 */
|
||||
export const HEVC_VP9_HW_DECODING_TYPES = [
|
||||
'amf',
|
||||
'nvenc',
|
||||
'qsv',
|
||||
'vaapi',
|
||||
'rkmpp'
|
||||
];
|
||||
|
||||
/** Hardware decoders which support HEVC RExt */
|
||||
export const HEVC_REXT_DECODING_TYPES = [
|
||||
'nvenc',
|
||||
'qsv',
|
||||
'vaapi'
|
||||
];
|
||||
@@ -14,6 +14,7 @@ export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
||||
{ path: 'logs/:file', page: 'logs/file', type: AppType.Dashboard },
|
||||
{ path: 'playback/resume', type: AppType.Dashboard },
|
||||
{ path: 'playback/streaming', type: AppType.Dashboard },
|
||||
{ path: 'playback/transcoding', type: AppType.Dashboard },
|
||||
{ path: 'playback/trickplay', type: AppType.Dashboard },
|
||||
{ path: 'plugins/:pluginId', page: 'plugins/plugin', type: AppType.Dashboard },
|
||||
{ path: 'tasks', type: AppType.Dashboard },
|
||||
|
||||
@@ -23,13 +23,6 @@ export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [
|
||||
controller: 'library',
|
||||
view: 'library.html'
|
||||
}
|
||||
}, {
|
||||
path: 'playback/transcoding',
|
||||
pageProps: {
|
||||
appType: AppType.Dashboard,
|
||||
controller: 'encodingsettings',
|
||||
view: 'encodingsettings.html'
|
||||
}
|
||||
}, {
|
||||
path: 'plugins/catalog',
|
||||
pageProps: {
|
||||
|
||||
@@ -14,12 +14,13 @@ import Loading from 'components/loading/LoadingComponent';
|
||||
import Page from 'components/Page';
|
||||
import { getConfigurationApi } from '@jellyfin/sdk/lib/utils/api/configuration-api';
|
||||
import { QUERY_KEY as CONFIG_QUERY_KEY, useConfiguration } from 'hooks/useConfiguration';
|
||||
import { QUERY_KEY as NAMED_CONFIG_QUERY_KEY, NamedConfiguration, useNamedConfiguration } from 'hooks/useNamedConfiguration';
|
||||
import { QUERY_KEY as NAMED_CONFIG_QUERY_KEY, useNamedConfiguration } from 'hooks/useNamedConfiguration';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'react-router-dom';
|
||||
import { ActionData } from 'types/actionData';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
import type { MetadataConfiguration } from '@jellyfin/sdk/lib/generated-client/models/metadata-configuration';
|
||||
|
||||
const CONFIG_KEY = 'metadata';
|
||||
|
||||
@@ -32,7 +33,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
|
||||
|
||||
const { data: config } = await getConfigurationApi(api).getConfiguration();
|
||||
|
||||
const metadataConfig: NamedConfiguration = {
|
||||
const metadataConfig: MetadataConfiguration = {
|
||||
UseFileCreationTimeForDateAdded: data.DateAddedBehavior.toString() === '1'
|
||||
};
|
||||
|
||||
@@ -70,7 +71,7 @@ export const Component = () => {
|
||||
data: namedConfig,
|
||||
isPending: isNamedConfigPending,
|
||||
isError: isNamedConfigError
|
||||
} = useNamedConfiguration(CONFIG_KEY);
|
||||
} = useNamedConfiguration<MetadataConfiguration>(CONFIG_KEY);
|
||||
|
||||
const navigation = useNavigation();
|
||||
const actionData = useActionData() as ActionData | undefined;
|
||||
|
||||
@@ -21,17 +21,10 @@ import React, { useCallback, useState } from 'react';
|
||||
import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'react-router-dom';
|
||||
import { ActionData } from 'types/actionData';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
import type { XbmcMetadataOptions } from '@jellyfin/sdk/lib/generated-client/models/xbmc-metadata-options';
|
||||
|
||||
const CONFIG_KEY = 'xbmcmetadata';
|
||||
|
||||
interface NFOSettingsConfig {
|
||||
UserId?: string;
|
||||
EnableExtraThumbsDuplication?: boolean;
|
||||
EnablePathSubstitution?: boolean;
|
||||
ReleaseDateFormat?: string;
|
||||
SaveImagePathsInNfo?: boolean;
|
||||
};
|
||||
|
||||
export const action = async ({ request }: ActionFunctionArgs) => {
|
||||
const api = ServerConnections.getCurrentApi();
|
||||
if (!api) throw new Error('No Api instance available');
|
||||
@@ -39,7 +32,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
|
||||
const formData = await request.formData();
|
||||
const data = Object.fromEntries(formData);
|
||||
|
||||
const newConfig: NFOSettingsConfig = {
|
||||
const newConfig: XbmcMetadataOptions = {
|
||||
UserId: data.UserId?.toString(),
|
||||
ReleaseDateFormat: 'yyyy-MM-dd',
|
||||
SaveImagePathsInNfo: data.SaveImagePathsInNfo?.toString() === 'on',
|
||||
@@ -64,7 +57,7 @@ export const Component = () => {
|
||||
data: config,
|
||||
isPending: isConfigPending,
|
||||
isError: isConfigError
|
||||
} = useNamedConfiguration(CONFIG_KEY);
|
||||
} = useNamedConfiguration<XbmcMetadataOptions>(CONFIG_KEY);
|
||||
const {
|
||||
data: users,
|
||||
isPending: isUsersPending,
|
||||
@@ -75,8 +68,6 @@ export const Component = () => {
|
||||
const isSubmitting = navigation.state === 'submitting';
|
||||
const [isAlertOpen, setIsAlertOpen] = useState(false);
|
||||
|
||||
const nfoConfig = config as NFOSettingsConfig;
|
||||
|
||||
const onAlertClose = useCallback(() => {
|
||||
setIsAlertOpen(false);
|
||||
}, []);
|
||||
@@ -117,7 +108,7 @@ export const Component = () => {
|
||||
<TextField
|
||||
name={'UserId'}
|
||||
label={globalize.translate('LabelKodiMetadataUser')}
|
||||
defaultValue={nfoConfig.UserId || ''}
|
||||
defaultValue={config.UserId || ''}
|
||||
select
|
||||
helperText={globalize.translate('LabelKodiMetadataUserHelp')}
|
||||
slotProps={{
|
||||
@@ -141,7 +132,7 @@ export const Component = () => {
|
||||
control={
|
||||
<Checkbox
|
||||
name={'SaveImagePathsInNfo'}
|
||||
defaultChecked={nfoConfig.SaveImagePathsInNfo}
|
||||
defaultChecked={config.SaveImagePathsInNfo}
|
||||
/>
|
||||
}
|
||||
label={globalize.translate('LabelKodiMetadataSaveImagePaths')}
|
||||
@@ -154,7 +145,7 @@ export const Component = () => {
|
||||
control={
|
||||
<Checkbox
|
||||
name={'EnablePathSubstitution'}
|
||||
defaultChecked={nfoConfig.EnablePathSubstitution}
|
||||
defaultChecked={config.EnablePathSubstitution}
|
||||
/>
|
||||
}
|
||||
label={globalize.translate('LabelKodiMetadataEnablePathSubstitution')}
|
||||
@@ -167,7 +158,7 @@ export const Component = () => {
|
||||
control={
|
||||
<Checkbox
|
||||
name={'EnableExtraThumbsDuplication'}
|
||||
defaultChecked={nfoConfig.EnableExtraThumbsDuplication}
|
||||
defaultChecked={config.EnableExtraThumbsDuplication}
|
||||
/>
|
||||
}
|
||||
label={globalize.translate('LabelKodiMetadataEnableExtraThumbs')}
|
||||
|
||||
@@ -66,7 +66,7 @@ export const Component = () => {
|
||||
<Box className='content-primary'>
|
||||
<Form method='POST'>
|
||||
<Stack spacing={3}>
|
||||
<Typography variant='h2'>
|
||||
<Typography variant='h1'>
|
||||
{globalize.translate('ButtonResume')}
|
||||
</Typography>
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export const Component = () => {
|
||||
<Box className='content-primary'>
|
||||
<Form method='POST'>
|
||||
<Stack spacing={3}>
|
||||
<Typography variant='h2'>
|
||||
<Typography variant='h1'>
|
||||
{globalize.translate('TabStreaming')}
|
||||
</Typography>
|
||||
|
||||
|
||||
899
src/apps/dashboard/routes/playback/transcoding.tsx
Normal file
899
src/apps/dashboard/routes/playback/transcoding.tsx
Normal file
@@ -0,0 +1,899 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Link from '@mui/material/Link';
|
||||
import Page from 'components/Page';
|
||||
import globalize from 'lib/globalize';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import FormGroup from '@mui/material/FormGroup';
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
import FormHelperText from '@mui/material/FormHelperText';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import DirectoryBrowser from 'components/directorybrowser/directorybrowser';
|
||||
import InputAdornment from '@mui/material/InputAdornment';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import Button from '@mui/material/Button';
|
||||
import { type ActionFunctionArgs, Form, useActionData, useNavigation, useSubmit } from 'react-router-dom';
|
||||
import { QUERY_KEY, useNamedConfiguration } from 'hooks/useNamedConfiguration';
|
||||
import type { EncodingOptions } from '@jellyfin/sdk/lib/generated-client/models/encoding-options';
|
||||
import { HardwareAccelerationType } from '@jellyfin/sdk/lib/generated-client/models/hardware-acceleration-type';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { getConfigurationApi } from '@jellyfin/sdk/lib/utils/api/configuration-api';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
import { ActionData } from 'types/actionData';
|
||||
import { CODECS, HEVC_REXT_DECODING_TYPES, HEVC_VP9_HW_DECODING_TYPES } from 'apps/dashboard/features/playback/constants/codecs';
|
||||
import SimpleAlert from 'components/SimpleAlert';
|
||||
|
||||
const CONFIG_KEY = 'encoding';
|
||||
|
||||
export const action = async ({ request }: ActionFunctionArgs) => {
|
||||
const api = ServerConnections.getCurrentApi();
|
||||
if (!api) throw new Error('No Api instance available');
|
||||
|
||||
const data = await request.json() as EncodingOptions;
|
||||
|
||||
await getConfigurationApi(api)
|
||||
.updateNamedConfiguration({ key: CONFIG_KEY, body: data });
|
||||
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [QUERY_KEY, CONFIG_KEY]
|
||||
});
|
||||
|
||||
return {
|
||||
isSaved: true
|
||||
};
|
||||
};
|
||||
|
||||
export const Component = () => {
|
||||
const { data: initialConfig, isPending, isError } = useNamedConfiguration<EncodingOptions>(CONFIG_KEY);
|
||||
const [ config, setConfig ] = useState<EncodingOptions | null>(null);
|
||||
const navigation = useNavigation();
|
||||
const actionData = useActionData() as ActionData | undefined;
|
||||
const submit = useSubmit();
|
||||
const isSubmitting = navigation.state === 'submitting';
|
||||
const [ isAlertOpen, setIsAlertOpen ] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialConfig && config == null) {
|
||||
setConfig(initialConfig);
|
||||
}
|
||||
}, [ initialConfig, config ]);
|
||||
|
||||
const onConfigChange = useCallback((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
||||
setConfig({
|
||||
...config,
|
||||
[e.target.name]: e.target.value
|
||||
});
|
||||
}, [ config ]);
|
||||
|
||||
const onCheckboxChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setConfig({
|
||||
...config,
|
||||
[e.target.name]: e.target.checked
|
||||
});
|
||||
}, [ config ]);
|
||||
|
||||
const onCodecChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (config?.HardwareDecodingCodecs) {
|
||||
if (e.target.checked) {
|
||||
setConfig({
|
||||
...config,
|
||||
HardwareDecodingCodecs: [
|
||||
...config.HardwareDecodingCodecs,
|
||||
e.target.name
|
||||
]
|
||||
});
|
||||
} else {
|
||||
setConfig({
|
||||
...config,
|
||||
HardwareDecodingCodecs: config.HardwareDecodingCodecs.filter(v => v !== e.target.name)
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [ config ]);
|
||||
|
||||
const onAlertClose = useCallback(() => {
|
||||
setIsAlertOpen(false);
|
||||
}, []);
|
||||
|
||||
const onSubmit = useCallback((e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (config) {
|
||||
setIsAlertOpen(true);
|
||||
submit(
|
||||
{ ...config },
|
||||
{ method: 'post', encType: 'application/json' }
|
||||
);
|
||||
}
|
||||
}, [ config, submit ]);
|
||||
|
||||
const showTranscodingPathPicker = useCallback(() => {
|
||||
const picker = new DirectoryBrowser();
|
||||
|
||||
picker.show({
|
||||
callback: (path: string) => {
|
||||
setConfig({
|
||||
...config,
|
||||
TranscodingTempPath: path
|
||||
});
|
||||
|
||||
picker.close();
|
||||
},
|
||||
validateWriteable: true,
|
||||
header: globalize.translate('HeaderSelectTranscodingPath'),
|
||||
instruction: globalize.translate('HeaderSelectTranscodingPathHelp')
|
||||
});
|
||||
}, [ config ]);
|
||||
|
||||
const showFallbackFontPathPicker = useCallback(() => {
|
||||
const picker = new DirectoryBrowser();
|
||||
|
||||
picker.show({
|
||||
callback: (path: string) => {
|
||||
setConfig({
|
||||
...config,
|
||||
FallbackFontPath: path
|
||||
});
|
||||
|
||||
picker.close();
|
||||
},
|
||||
header: globalize.translate('HeaderSelectFallbackFontPath'),
|
||||
instruction: globalize.translate('HeaderSelectFallbackFontPathHelp')
|
||||
});
|
||||
}, [ config ]);
|
||||
|
||||
const hardwareAccelType = config?.HardwareAccelerationType || HardwareAccelerationType.None;
|
||||
const isHwaSelected = [ 'amf', 'nvenc', 'qsv', 'vaapi', 'rkmpp', 'videotoolbox' ].includes(hardwareAccelType);
|
||||
|
||||
const availableCodecs = useMemo(() => (
|
||||
CODECS.filter(codec => codec.types.includes(hardwareAccelType))
|
||||
), [hardwareAccelType]);
|
||||
|
||||
if (isPending || !config) return <Loading />;
|
||||
|
||||
return (
|
||||
<Page
|
||||
id='encodingSettingsPage'
|
||||
className='mainAnimatedPage type-interior'
|
||||
title={globalize.translate('TitlePlayback')}
|
||||
>
|
||||
<SimpleAlert
|
||||
open={isAlertOpen}
|
||||
onClose={onAlertClose}
|
||||
title={globalize.translate('TitleHardwareAcceleration')}
|
||||
text={globalize.translate('HardwareAccelerationWarning')}
|
||||
/>
|
||||
<Box className='content-primary'>
|
||||
{isError ? (
|
||||
<Alert severity='error'>{globalize.translate('TranscodingLoadError')}</Alert>
|
||||
) : (
|
||||
<Form method='POST' onSubmit={onSubmit}>
|
||||
<Stack spacing={3}>
|
||||
<Typography variant='h1'>{globalize.translate('Transcoding')}</Typography>
|
||||
|
||||
{!isSubmitting && actionData?.isSaved && (
|
||||
<Alert severity='success'>
|
||||
{globalize.translate('SettingsSaved')}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<TextField
|
||||
name='HardwareAccelerationType'
|
||||
select
|
||||
label={globalize.translate('LabelHardwareAccelerationType')}
|
||||
value={config.HardwareAccelerationType}
|
||||
onChange={onConfigChange}
|
||||
helperText={(
|
||||
<Link href='https://jellyfin.org/docs/general/administration/hardware-acceleration' target='_blank'>
|
||||
{globalize.translate('LabelHardwareAccelerationTypeHelp')}
|
||||
</Link>
|
||||
)}
|
||||
>
|
||||
<MenuItem value='none'>{globalize.translate('None')}</MenuItem>
|
||||
<MenuItem value='amf'>AMD AMF</MenuItem>
|
||||
<MenuItem value='nvenc'>Nvidia NVENC</MenuItem>
|
||||
<MenuItem value='qsv'>Intel Quicksync (QSV)</MenuItem>
|
||||
<MenuItem value='vaapi'>Video Acceleration API (VAAPI)</MenuItem>
|
||||
<MenuItem value='rkmpp'>Rockchip MPP (RKMPP)</MenuItem>
|
||||
<MenuItem value='videotoolbox'>Apple VideoToolBox</MenuItem>
|
||||
<MenuItem value='v4l2m2m'>Video4Linux2 (V4L2)</MenuItem>
|
||||
</TextField>
|
||||
|
||||
{hardwareAccelType === 'vaapi' && (
|
||||
<TextField
|
||||
name='VaapiDevice'
|
||||
label={globalize.translate('LabelVaapiDevice')}
|
||||
value={config.VaapiDevice}
|
||||
onChange={onConfigChange}
|
||||
helperText={globalize.translate('LabelVaapiDeviceHelp')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{hardwareAccelType === 'qsv' && (
|
||||
<TextField
|
||||
name='QsvDevice'
|
||||
label={globalize.translate('LabelQsvDevice')}
|
||||
value={config.QsvDevice}
|
||||
onChange={onConfigChange}
|
||||
helperText={globalize.translate('LabelQsvDeviceHelp')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{hardwareAccelType !== 'none' && (
|
||||
<>
|
||||
<Typography variant='h3'>{globalize.translate('LabelEnableHardwareDecodingFor')}</Typography>
|
||||
<FormGroup>
|
||||
{availableCodecs.map(codec => (
|
||||
<FormControlLabel
|
||||
key={codec.name}
|
||||
label={codec.name}
|
||||
control={
|
||||
<Checkbox
|
||||
name={codec.codec}
|
||||
checked={(config.HardwareDecodingCodecs || []).includes(codec.codec)}
|
||||
onChange={onCodecChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
|
||||
{HEVC_VP9_HW_DECODING_TYPES.includes(hardwareAccelType) && (
|
||||
<FormControlLabel
|
||||
label={'HEVC 10bit'}
|
||||
control={
|
||||
<Checkbox
|
||||
name={'EnableDecodingColorDepth10Hevc'}
|
||||
checked={config.EnableDecodingColorDepth10Hevc}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{HEVC_VP9_HW_DECODING_TYPES.includes(hardwareAccelType) && (
|
||||
<FormControlLabel
|
||||
label={'VP9 10bit'}
|
||||
control={
|
||||
<Checkbox
|
||||
name={'EnableDecodingColorDepth10Vp9'}
|
||||
checked={config.EnableDecodingColorDepth10Vp9}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{HEVC_REXT_DECODING_TYPES.includes(hardwareAccelType) && (
|
||||
<FormControlLabel
|
||||
label={'HEVC RExt 8/10bit'}
|
||||
control={
|
||||
<Checkbox
|
||||
name={'EnableDecodingColorDepth10HevcRext'}
|
||||
checked={config.EnableDecodingColorDepth10HevcRext}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{HEVC_REXT_DECODING_TYPES.includes(hardwareAccelType) && (
|
||||
<FormControlLabel
|
||||
label={'HEVC RExt 12bit'}
|
||||
control={
|
||||
<Checkbox
|
||||
name={'EnableDecodingColorDepth12HevcRext'}
|
||||
checked={config.EnableDecodingColorDepth12HevcRext}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
{hardwareAccelType === 'nvenc' && (
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('EnableEnhancedNvdecDecoder')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableEnhancedNvdecDecoder'
|
||||
checked={config.EnableEnhancedNvdecDecoder}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>{globalize.translate('EnableEnhancedNvdecDecoderHelp')}</FormHelperText>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{hardwareAccelType === 'qsv' && (
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('PreferSystemNativeHwDecoder')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='PreferSystemNativeHwDecoder'
|
||||
checked={config.PreferSystemNativeHwDecoder}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{hardwareAccelType !== 'none' && (
|
||||
<FormControl variant='standard'>
|
||||
<Typography variant='h3'>{globalize.translate('LabelHardwareEncodingOptions')}</Typography>
|
||||
<FormGroup>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('EnableHardwareEncoding')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableHardwareEncoding'
|
||||
checked={config.EnableHardwareEncoding}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{(hardwareAccelType === 'qsv' || hardwareAccelType === 'vaapi') && (
|
||||
<>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('EnableIntelLowPowerH264HwEncoder')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableIntelLowPowerH264HwEncoder'
|
||||
checked={config.EnableIntelLowPowerH264HwEncoder}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('EnableIntelLowPowerHevcHwEncoder')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableIntelLowPowerHevcHwEncoder'
|
||||
checked={config.EnableIntelLowPowerHevcHwEncoder}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<Link href='https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#configure-and-verify-lp-mode-on-linux' target='_blank'>
|
||||
{globalize.translate('IntelLowPowerEncHelp')}
|
||||
</Link>
|
||||
</FormHelperText>
|
||||
</>
|
||||
)}
|
||||
</FormGroup>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
<FormControl variant='standard'>
|
||||
<Typography variant='h3'>{globalize.translate('LabelEncodingFormatOptions')}</Typography>
|
||||
<FormHelperText>{globalize.translate('EncodingFormatHelp')}</FormHelperText>
|
||||
<FormGroup>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('AllowHevcEncoding')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='AllowHevcEncoding'
|
||||
checked={config.AllowHevcEncoding}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('AllowAv1Encoding')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='AllowAv1Encoding'
|
||||
checked={config.AllowAv1Encoding}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FormControl>
|
||||
|
||||
{(hardwareAccelType === 'qsv' || hardwareAccelType === 'vaapi') && (
|
||||
<>
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('EnableVppTonemapping')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableVppTonemapping'
|
||||
checked={config.EnableVppTonemapping}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>{globalize.translate('AllowVppTonemappingHelp')}</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<TextField
|
||||
name='VppTonemappingBrightness'
|
||||
value={config.VppTonemappingBrightness}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelVppTonemappingBrightness')}
|
||||
helperText={globalize.translate('LabelVppTonemappingBrightnessHelp')}
|
||||
type='number'
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 0.00001
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name='VppTonemappingContrast'
|
||||
value={config.VppTonemappingContrast}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelVppTonemappingContrast')}
|
||||
helperText={globalize.translate('LabelVppTonemappingContrastHelp')}
|
||||
type='number'
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: 1,
|
||||
max: 2,
|
||||
step: 0.00001
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{hardwareAccelType === 'videotoolbox' && (
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('EnableVideoToolboxTonemapping')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableVideoToolboxTonemapping'
|
||||
checked={config.EnableVideoToolboxTonemapping}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>{globalize.translate('AllowVideoToolboxTonemappingHelp')}</FormHelperText>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{(hardwareAccelType === 'none' || isHwaSelected) && (
|
||||
<>
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('EnableTonemapping')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableTonemapping'
|
||||
checked={config.EnableTonemapping}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>{globalize.translate(isHwaSelected ? 'AllowTonemappingHelp' : 'AllowTonemappingSoftwareHelp')}</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<TextField
|
||||
name='TonemappingAlgorithm'
|
||||
select
|
||||
label={globalize.translate('LabelTonemappingAlgorithm')}
|
||||
value={config.TonemappingAlgorithm}
|
||||
onChange={onConfigChange}
|
||||
helperText={(
|
||||
<Link href='https://ffmpeg.org/ffmpeg-all.html#tonemap_005fopencl' target='_blank'>
|
||||
{globalize.translate('TonemappingAlgorithmHelp')}
|
||||
</Link>
|
||||
)}
|
||||
>
|
||||
<MenuItem value='none'>{globalize.translate('None')}</MenuItem>
|
||||
<MenuItem value='clip'>Clip</MenuItem>
|
||||
<MenuItem value='linear'>Linear</MenuItem>
|
||||
<MenuItem value='gamma'>Gamma</MenuItem>
|
||||
<MenuItem value='reinhard'>Reinhard</MenuItem>
|
||||
<MenuItem value='hable'>Hable</MenuItem>
|
||||
<MenuItem value='mobius'>Mobius</MenuItem>
|
||||
<MenuItem value='bt2390'>BT.2390</MenuItem>
|
||||
</TextField>
|
||||
|
||||
{isHwaSelected && (
|
||||
<TextField
|
||||
name='TonemappingMode'
|
||||
select
|
||||
value={config.TonemappingMode}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelTonemappingMode')}
|
||||
helperText={globalize.translate('TonemappingModeHelp')}
|
||||
>
|
||||
<MenuItem value='auto'>{globalize.translate('Auto')}</MenuItem>
|
||||
<MenuItem value='max'>MAX</MenuItem>
|
||||
<MenuItem value='rgb'>RGB</MenuItem>
|
||||
<MenuItem value='lum'>LUM</MenuItem>
|
||||
<MenuItem value='itp'>ITP</MenuItem>
|
||||
</TextField>
|
||||
)}
|
||||
|
||||
<TextField
|
||||
name='TonemappingRange'
|
||||
select
|
||||
value={config.TonemappingRange}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelTonemappingRange')}
|
||||
helperText={globalize.translate('TonemappingRangeHelp')}
|
||||
>
|
||||
<MenuItem value='auto'>{globalize.translate('Auto')}</MenuItem>
|
||||
<MenuItem value='tv'>TV</MenuItem>
|
||||
<MenuItem value='pc'>PC</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
name='TonemappingDesat'
|
||||
value={config.TonemappingDesat}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelTonemappingDesat')}
|
||||
helperText={globalize.translate('LabelTonemappingDesatHelp')}
|
||||
type='number'
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: 0,
|
||||
max: 1.79769e+308,
|
||||
step: 0.00001
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name='TonemappingPeak'
|
||||
value={config.TonemappingPeak}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelTonemappingPeak')}
|
||||
helperText={globalize.translate('LabelTonemappingPeakHelp')}
|
||||
type='number'
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: 0,
|
||||
max: 1.79769e+308,
|
||||
step: 0.00001
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name='TonemappingParam'
|
||||
value={config.TonemappingParam || ''}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelTonemappingParam')}
|
||||
helperText={globalize.translate('LabelTonemappingParamHelp')}
|
||||
type='number'
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: 2.22507e-308,
|
||||
max: 1.79769e+308,
|
||||
step: 0.00001
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<TextField
|
||||
name='EncodingThreadCount'
|
||||
value={config.EncodingThreadCount}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelTranscodingThreadCount')}
|
||||
helperText={globalize.translate('LabelTranscodingThreadCountHelp')}
|
||||
select
|
||||
>
|
||||
<MenuItem value='-1'>{globalize.translate('Auto')}</MenuItem>
|
||||
<MenuItem value='1'>1</MenuItem>
|
||||
<MenuItem value='2'>2</MenuItem>
|
||||
<MenuItem value='3'>3</MenuItem>
|
||||
<MenuItem value='4'>4</MenuItem>
|
||||
<MenuItem value='5'>5</MenuItem>
|
||||
<MenuItem value='6'>6</MenuItem>
|
||||
<MenuItem value='7'>7</MenuItem>
|
||||
<MenuItem value='8'>8</MenuItem>
|
||||
<MenuItem value='9'>9</MenuItem>
|
||||
<MenuItem value='10'>10</MenuItem>
|
||||
<MenuItem value='11'>11</MenuItem>
|
||||
<MenuItem value='12'>12</MenuItem>
|
||||
<MenuItem value='13'>13</MenuItem>
|
||||
<MenuItem value='14'>14</MenuItem>
|
||||
<MenuItem value='15'>15</MenuItem>
|
||||
<MenuItem value='16'>16</MenuItem>
|
||||
<MenuItem value='0'>{globalize.translate('OptionMax')}</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
name='FFmpegPath'
|
||||
value={config.EncoderAppPathDisplay}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelffmpegPath')}
|
||||
helperText={globalize.translate('LabelffmpegPathHelp')}
|
||||
disabled
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name='TranscodingTempPath'
|
||||
value={config.TranscodingTempPath}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelTranscodePath')}
|
||||
helperText={globalize.translate('LabelTranscodingTempPathHelp')}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton edge='end' onClick={showTranscodingPathPicker}>
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name='FallbackFontPath'
|
||||
value={config.FallbackFontPath}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelFallbackFontPath')}
|
||||
helperText={
|
||||
<Link href='https://jellyfin.org/docs/general/administration/configuration#fonts' target='_blank'>
|
||||
{globalize.translate('LabelFallbackFontPathHelp')}
|
||||
</Link>
|
||||
}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton edge='end' onClick={showFallbackFontPathPicker}>
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('EnableFallbackFont')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableFallbackFont'
|
||||
checked={config.EnableFallbackFont}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>{globalize.translate('EnableFallbackFontHelp')}</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('LabelEnableAudioVbr')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableAudioVbr'
|
||||
checked={config.EnableAudioVbr}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>{globalize.translate('LabelEnableAudioVbrHelp')}</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<TextField
|
||||
name='DownMixAudioBoost'
|
||||
value={config.DownMixAudioBoost}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelDownMixAudioScale')}
|
||||
helperText={globalize.translate('LabelDownMixAudioScaleHelp')}
|
||||
type='number'
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
required: true,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name='DownMixStereoAlgorithm'
|
||||
value={config.DownMixStereoAlgorithm}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelStereoDownmixAlgorithm')}
|
||||
helperText={globalize.translate('StereoDownmixAlgorithmHelp')}
|
||||
select
|
||||
>
|
||||
<MenuItem value='None'>{globalize.translate('None')}</MenuItem>
|
||||
<MenuItem value='Dave750'>Dave750</MenuItem>
|
||||
<MenuItem value='NightmodeDialogue'>NightmodeDialogue</MenuItem>
|
||||
<MenuItem value='Rfc7845'>RFC7845</MenuItem>
|
||||
<MenuItem value='Ac4'>AC-4</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
name='MaxMuxingQueueSize'
|
||||
value={config.MaxMuxingQueueSize}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelMaxMuxingQueueSize')}
|
||||
helperText={globalize.translate('LabelMaxMuxingQueueSizeHelp')}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name='EncoderPreset'
|
||||
value={config.EncoderPreset}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelEncoderPreset')}
|
||||
helperText={globalize.translate('EncoderPresetHelp')}
|
||||
select
|
||||
>
|
||||
<MenuItem value='auto'>{globalize.translate('Auto')}</MenuItem>
|
||||
<MenuItem value='veryslow'>veryslow</MenuItem>
|
||||
<MenuItem value='slower'>slower</MenuItem>
|
||||
<MenuItem value='slow'>slow</MenuItem>
|
||||
<MenuItem value='medium'>medium</MenuItem>
|
||||
<MenuItem value='fast'>fast</MenuItem>
|
||||
<MenuItem value='faster'>faster</MenuItem>
|
||||
<MenuItem value='veryfast'>veryfast</MenuItem>
|
||||
<MenuItem value='superfast'>superfast</MenuItem>
|
||||
<MenuItem value='ultrafast'>ultrafast</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
name='H265Crf'
|
||||
value={config.H265Crf}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelH265Crf')}
|
||||
type='number'
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: 0,
|
||||
max: 51,
|
||||
step: 1
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name='H264Crf'
|
||||
value={config.H264Crf}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelH264Crf')}
|
||||
helperText={globalize.translate('H264CrfHelp')}
|
||||
type='number'
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: 0,
|
||||
max: 51,
|
||||
step: 1
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name='DeinterlaceMethod'
|
||||
value={config.DeinterlaceMethod}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelDeinterlaceMethod')}
|
||||
helperText={globalize.translate('DeinterlaceMethodHelp')}
|
||||
select
|
||||
>
|
||||
<MenuItem value='yadif'>{globalize.translate('Yadif')}</MenuItem>
|
||||
<MenuItem value='bwdif'>{globalize.translate('Bwdif')}</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('UseDoubleRateDeinterlacing')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='DeinterlaceDoubleRate'
|
||||
checked={config.DeinterlaceDoubleRate}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>{globalize.translate('UseDoubleRateDeinterlacingHelp')}</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('AllowOnTheFlySubtitleExtraction')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableSubtitleExtraction'
|
||||
checked={config.EnableSubtitleExtraction}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>{globalize.translate('AllowOnTheFlySubtitleExtractionHelp')}</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('AllowFfmpegThrottling')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableThrottling'
|
||||
checked={config.EnableThrottling}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>{globalize.translate('AllowFfmpegThrottlingHelp')}</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
label={globalize.translate('AllowSegmentDeletion')}
|
||||
control={
|
||||
<Checkbox
|
||||
name='EnableSegmentDeletion'
|
||||
checked={config.EnableSegmentDeletion}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormHelperText>{globalize.translate('AllowSegmentDeletionHelp')}</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<TextField
|
||||
name='ThrottleDelaySeconds'
|
||||
value={config.ThrottleDelaySeconds}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelThrottleDelaySeconds')}
|
||||
helperText={globalize.translate('LabelThrottleDelaySecondsHelp')}
|
||||
type='number'
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: 10,
|
||||
max: 3600,
|
||||
step: 1
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name='SegmentKeepSeconds'
|
||||
value={config.SegmentKeepSeconds}
|
||||
onChange={onConfigChange}
|
||||
label={globalize.translate('LabelSegmentKeepSeconds')}
|
||||
helperText={globalize.translate('LabelSegmentKeepSecondsHelp')}
|
||||
type='number'
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: 10,
|
||||
max: 3600,
|
||||
step: 1
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button type='submit' size='large'>
|
||||
{globalize.translate('Save')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Form>
|
||||
)}
|
||||
</Box>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
Component.displayName = 'TranscodingPage';
|
||||
@@ -13,15 +13,15 @@ export interface NamedConfiguration {
|
||||
const fetchNamedConfiguration = async (api: Api, key: string, options?: AxiosRequestConfig) => {
|
||||
const response = await getConfigurationApi(api).getNamedConfiguration({ key }, options);
|
||||
|
||||
return response.data as unknown as NamedConfiguration;
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const useNamedConfiguration = (key: string) => {
|
||||
export const useNamedConfiguration = <ConfigType = NamedConfiguration>(key: string) => {
|
||||
const { api } = useApi();
|
||||
|
||||
return useQuery({
|
||||
queryKey: [ QUERY_KEY, key ],
|
||||
queryFn: ({ signal }) => fetchNamedConfiguration(api!, key, { signal }),
|
||||
queryFn: ({ signal }) => fetchNamedConfiguration(api!, key, { signal }) as ConfigType,
|
||||
enabled: !!api
|
||||
});
|
||||
};
|
||||
|
||||
@@ -977,7 +977,7 @@
|
||||
"LabelTonemappingDesat": "Tone mapping desat",
|
||||
"LabelTonemappingDesatHelp": "Apply desaturation for highlights that exceed this level of brightness. The recommended value is 0 (disable).",
|
||||
"LabelTonemappingParam": "Tone mapping param",
|
||||
"LabelTonemappingParamHelp": "Tune the tone mapping algorithm. The recommended and default values are NaN. Generally leave it blank.",
|
||||
"LabelTonemappingParamHelp": "Tune the tone mapping algorithm. Generally leave it blank.",
|
||||
"LabelTonemappingPeak": "Tone mapping peak",
|
||||
"LabelTonemappingPeakHelp": "Override the embedded metadata value for the input signal with this peak value instead. The default value is 100 (1000nit).",
|
||||
"LabelTonemappingRange": "Tone mapping range",
|
||||
@@ -1608,6 +1608,7 @@
|
||||
"TrackCount": "{0} tracks",
|
||||
"Trailers": "Trailers",
|
||||
"Transcoding": "Transcoding",
|
||||
"TranscodingLoadError": "Failed to load transcoding settings",
|
||||
"Translator": "Translator",
|
||||
"Tuesday": "Tuesday",
|
||||
"TV": "TV",
|
||||
|
||||
Reference in New Issue
Block a user