1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge branch 'master' into remux-translation

This commit is contained in:
Nyanmisaka 2020-11-21 04:36:48 +00:00 committed by GitHub
commit 0962e8d0ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 285 additions and 61 deletions

View file

@ -34,7 +34,6 @@ function getDeviceProfile(item, options = {}) {
profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder);
} else { } else {
const builderOpts = getBaseProfileOptions(item); const builderOpts = getBaseProfileOptions(item);
builderOpts.enableSsaRender = (item && !options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats');
profile = profileBuilder(builderOpts); profile = profileBuilder(builderOpts);
} }

View file

@ -3,7 +3,9 @@ export function getDisplayPlayMethod(session) {
return null; return null;
} }
if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) { if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect && session.TranscodingInfo.IsAudioDirect) {
return 'Remux';
} else if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) {
return 'DirectStream'; return 'DirectStream';
} else if (session.PlayState.PlayMethod === 'Transcode') { } else if (session.PlayState.PlayMethod === 'Transcode') {
return 'Transcode'; return 'Transcode';

View file

@ -147,6 +147,8 @@ import toast from '../toast/toast';
showHideQualityFields(context, user, apiClient); showHideQualityFields(context, user, apiClient);
context.querySelector('#selectAllowedAudioChannels').value = userSettings.allowedAudioChannels();
apiClient.getCultures().then(allCultures => { apiClient.getCultures().then(allCultures => {
populateLanguages(context.querySelector('#selectAudioLanguage'), allCultures); populateLanguages(context.querySelector('#selectAudioLanguage'), allCultures);
@ -189,6 +191,7 @@ import toast from '../toast/toast';
} }
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false; context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode(); context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay(); context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
context.querySelector('.chkExternalVideoPlayer').checked = appSettings.enableSystemExternalPlayers(); context.querySelector('.chkExternalVideoPlayer').checked = appSettings.enableSystemExternalPlayers();
@ -224,10 +227,11 @@ import toast from '../toast/toast';
setMaxBitrateFromField(context.querySelector('.selectVideoInternetQuality'), false, 'Video'); setMaxBitrateFromField(context.querySelector('.selectVideoInternetQuality'), false, 'Video');
setMaxBitrateFromField(context.querySelector('.selectMusicInternetQuality'), false, 'Audio'); setMaxBitrateFromField(context.querySelector('.selectMusicInternetQuality'), false, 'Audio');
userSettingsInstance.allowedAudioChannels(context.querySelector('#selectAllowedAudioChannels').value);
user.Configuration.AudioLanguagePreference = context.querySelector('#selectAudioLanguage').value; user.Configuration.AudioLanguagePreference = context.querySelector('#selectAudioLanguage').value;
user.Configuration.PlayDefaultAudioTrack = context.querySelector('.chkPlayDefaultAudioTrack').checked; user.Configuration.PlayDefaultAudioTrack = context.querySelector('.chkPlayDefaultAudioTrack').checked;
user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked; user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked;
userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked);
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked); userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked); userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);

View file

@ -4,6 +4,16 @@
${HeaderAudioSettings} ${HeaderAudioSettings}
</h2> </h2>
<div class="selectContainer">
<select is="emby-select" id="selectAllowedAudioChannels" label="${LabelAllowedAudioChannels}">
<option value="-1">${Auto}</option>
<option value="1">${LabelSelectMono}</option>
<option value="2">${LabelSelectStereo}</option>
<option value="6">5.1 ${LabelSelectAudioChannels}</option>
<option value="8">7.1 ${LabelSelectAudioChannels}</option>
</select>
</div>
<div class="selectContainer"> <div class="selectContainer">
<select is="emby-select" id="selectAudioLanguage" label="${LabelAudioLanguagePreference}"></select> <select is="emby-select" id="selectAudioLanguage" label="${LabelAudioLanguagePreference}"></select>
</div> </div>
@ -49,6 +59,14 @@
${TabAdvanced} ${TabAdvanced}
</h2> </h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" class="chkPreferFmp4HlsContainer" />
<span>${PreferFmp4HlsContainer}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${PreferFmp4HlsContainerHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription cinemaModeOptions"> <div class="checkboxContainer checkboxContainer-withDescription cinemaModeOptions">
<label> <label>
<input type="checkbox" is="emby-checkbox" class="chkEnableCinemaMode" /> <input type="checkbox" is="emby-checkbox" class="chkEnableCinemaMode" />

View file

@ -94,6 +94,15 @@
</div> </div>
</div> </div>
<div class="checkboxListContainer">
<div class="checkboxList">
<label>
<input type="checkbox" is="emby-checkbox" id="chkAllowHevcEncoding" />
<span>${AllowHevcEncoding}</span>
</label>
</div>
</div>
<div class="tonemappingOptions hide"> <div class="tonemappingOptions hide">
<div class="checkboxListContainer checkboxContainer-withDescription"> <div class="checkboxListContainer checkboxContainer-withDescription">
<label> <label>
@ -218,11 +227,13 @@
</select> </select>
<div class="fieldDescription">${EncoderPresetHelp}</div> <div class="fieldDescription">${EncoderPresetHelp}</div>
</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"> <div class="inputContainer">
<input is="emby-input" type="number" id="txtH264Crf" pattern="[0-9]*" min="0" max="51" step="1" label="${LabelH264Crf}" /> <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 class="fieldDescription">${H264CrfHelp}</div>
</div> </div>
<div class="selectContainer"> <div class="selectContainer">
<select is="emby-select" id="selectDeinterlaceMethod" label="${LabelDeinterlaceMethod}"> <select is="emby-select" id="selectDeinterlaceMethod" label="${LabelDeinterlaceMethod}">
<option value="yadif">${Yadif}</option> <option value="yadif">${Yadif}</option>

View file

@ -15,6 +15,7 @@ import alert from '../../components/alert';
page.querySelector('#chkDecodingColorDepth10Hevc').checked = config.EnableDecodingColorDepth10Hevc; page.querySelector('#chkDecodingColorDepth10Hevc').checked = config.EnableDecodingColorDepth10Hevc;
page.querySelector('#chkDecodingColorDepth10Vp9').checked = config.EnableDecodingColorDepth10Vp9; page.querySelector('#chkDecodingColorDepth10Vp9').checked = config.EnableDecodingColorDepth10Vp9;
page.querySelector('#chkHardwareEncoding').checked = config.EnableHardwareEncoding; page.querySelector('#chkHardwareEncoding').checked = config.EnableHardwareEncoding;
page.querySelector('#chkAllowHevcEncoding').checked = config.AllowHevcEncoding;
$('#selectVideoDecoder', page).val(config.HardwareAccelerationType); $('#selectVideoDecoder', page).val(config.HardwareAccelerationType);
$('#selectThreadCount', page).val(config.EncodingThreadCount); $('#selectThreadCount', page).val(config.EncodingThreadCount);
$('#txtDownMixAudioBoost', page).val(config.DownMixAudioBoost); $('#txtDownMixAudioBoost', page).val(config.DownMixAudioBoost);
@ -34,6 +35,7 @@ import alert from '../../components/alert';
page.querySelector('#txtTonemappingParam').value = config.TonemappingParam || ''; page.querySelector('#txtTonemappingParam').value = config.TonemappingParam || '';
page.querySelector('#selectEncoderPreset').value = config.EncoderPreset || ''; page.querySelector('#selectEncoderPreset').value = config.EncoderPreset || '';
page.querySelector('#txtH264Crf').value = config.H264Crf || ''; page.querySelector('#txtH264Crf').value = config.H264Crf || '';
page.querySelector('#txtH265Crf').value = config.H265Crf || '';
page.querySelector('#selectDeinterlaceMethod').value = config.DeinterlaceMethod || ''; page.querySelector('#selectDeinterlaceMethod').value = config.DeinterlaceMethod || '';
page.querySelector('#chkDoubleRateDeinterlacing').checked = config.DeinterlaceDoubleRate; page.querySelector('#chkDoubleRateDeinterlacing').checked = config.DeinterlaceDoubleRate;
page.querySelector('#chkEnableSubtitleExtraction').checked = config.EnableSubtitleExtraction || false; page.querySelector('#chkEnableSubtitleExtraction').checked = config.EnableSubtitleExtraction || false;
@ -87,6 +89,7 @@ import alert from '../../components/alert';
config.TonemappingParam = form.querySelector('#txtTonemappingParam').value || '0'; config.TonemappingParam = form.querySelector('#txtTonemappingParam').value || '0';
config.EncoderPreset = form.querySelector('#selectEncoderPreset').value; config.EncoderPreset = form.querySelector('#selectEncoderPreset').value;
config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0'); config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0');
config.H265Crf = parseInt(form.querySelector('#txtH265Crf').value || '0');
config.DeinterlaceMethod = form.querySelector('#selectDeinterlaceMethod').value; config.DeinterlaceMethod = form.querySelector('#selectDeinterlaceMethod').value;
config.DeinterlaceDoubleRate = form.querySelector('#chkDoubleRateDeinterlacing').checked; config.DeinterlaceDoubleRate = form.querySelector('#chkDoubleRateDeinterlacing').checked;
config.EnableSubtitleExtraction = form.querySelector('#chkEnableSubtitleExtraction').checked; config.EnableSubtitleExtraction = form.querySelector('#chkEnableSubtitleExtraction').checked;
@ -99,6 +102,7 @@ import alert from '../../components/alert';
config.EnableDecodingColorDepth10Hevc = form.querySelector('#chkDecodingColorDepth10Hevc').checked; config.EnableDecodingColorDepth10Hevc = form.querySelector('#chkDecodingColorDepth10Hevc').checked;
config.EnableDecodingColorDepth10Vp9 = form.querySelector('#chkDecodingColorDepth10Vp9').checked; config.EnableDecodingColorDepth10Vp9 = form.querySelector('#chkDecodingColorDepth10Vp9').checked;
config.EnableHardwareEncoding = form.querySelector('#chkHardwareEncoding').checked; config.EnableHardwareEncoding = form.querySelector('#chkHardwareEncoding').checked;
config.AllowHevcEncoding = form.querySelector('#chkAllowHevcEncoding').checked;
ApiClient.updateNamedConfiguration('encoding', config).then(function () { ApiClient.updateNamedConfiguration('encoding', config).then(function () {
updateEncoder(form); updateEncoder(form);
}, function () { }, function () {

View file

@ -1,3 +1,5 @@
import appSettings from './settings/appSettings';
import * as userSettings from './settings/userSettings';
import browser from './browser'; import browser from './browser';
/* eslint-disable indent */ /* eslint-disable indent */
@ -5,7 +7,7 @@ import browser from './browser';
return !!(videoTestElement.canPlayType && videoTestElement.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, '')); return !!(videoTestElement.canPlayType && videoTestElement.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, ''));
} }
function canPlayH265(videoTestElement, options) { function canPlayHevc(videoTestElement, options) {
if (browser.tizen || browser.xboxOne || browser.web0s || options.supportsHevc) { if (browser.tizen || browser.xboxOne || browser.web0s || options.supportsHevc) {
return true; return true;
} }
@ -14,6 +16,7 @@ import browser from './browser';
return false; return false;
} }
// hevc main level 4.0
return !!videoTestElement.canPlayType && return !!videoTestElement.canPlayType &&
(videoTestElement.canPlayType('video/mp4; codecs="hvc1.1.L120"').replace(/no/, '') || (videoTestElement.canPlayType('video/mp4; codecs="hvc1.1.L120"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="hev1.1.L120"').replace(/no/, '') || videoTestElement.canPlayType('video/mp4; codecs="hev1.1.L120"').replace(/no/, '') ||
@ -205,8 +208,7 @@ import browser from './browser';
// Explicitly add supported codecs to make other codecs be transcoded // Explicitly add supported codecs to make other codecs be transcoded
if (browser.tizenVersion >= 4) { if (browser.tizenVersion >= 4) {
videoCodecs.push('h264'); videoCodecs.push('h264');
if (canPlayH265(videoTestElement, options)) { if (canPlayHevc(videoTestElement, options)) {
videoCodecs.push('h265');
videoCodecs.push('hevc'); videoCodecs.push('hevc');
} }
} }
@ -246,8 +248,8 @@ import browser from './browser';
case 'ts': case 'ts':
supported = testCanPlayTs(); supported = testCanPlayTs();
videoCodecs.push('h264'); videoCodecs.push('h264');
if (canPlayH265(videoTestElement, options)) { // safari doesn't support hevc in TS-HLS
videoCodecs.push('h265'); if ((browser.tizen || browser.web0s) && canPlayHevc(videoTestElement, options)) {
videoCodecs.push('hevc'); videoCodecs.push('hevc');
} }
if (supportsVc1(videoTestElement)) { if (supportsVc1(videoTestElement)) {
@ -295,7 +297,9 @@ import browser from './browser';
export default function (options) { export default function (options) {
options = options || {}; options = options || {};
const physicalAudioChannels = options.audioChannels || (browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2); const isSurroundSoundSupportedBrowser = browser.safari || browser.chrome || browser.edgeChromium || browser.firefox;
const allowedAudioChannels = parseInt(userSettings.allowedAudioChannels() || '-1');
const physicalAudioChannels = (allowedAudioChannels > 0 ? allowedAudioChannels : null) || options.audioChannels || (isSurroundSoundSupportedBrowser || browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2);
const bitrateSetting = getMaxBitrate(); const bitrateSetting = getMaxBitrate();
@ -311,12 +315,13 @@ import browser from './browser';
profile.MaxStreamingBitrate = bitrateSetting; profile.MaxStreamingBitrate = bitrateSetting;
profile.MaxStaticBitrate = 100000000; profile.MaxStaticBitrate = 100000000;
profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 192000); profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 384000);
profile.DirectPlayProfiles = []; profile.DirectPlayProfiles = [];
let videoAudioCodecs = []; let videoAudioCodecs = [];
let hlsVideoAudioCodecs = []; let hlsInTsVideoAudioCodecs = [];
let hlsInFmp4VideoAudioCodecs = [];
const supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, '') const supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, '')
|| videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, '') || videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, '')
@ -351,16 +356,19 @@ import browser from './browser';
// Transcoding codec is the first in hlsVideoAudioCodecs // Transcoding codec is the first in hlsVideoAudioCodecs
// Put ac3/eac3 first only when the audio channels > 2 and need transcoding // Put ac3/eac3 first only when the audio channels > 2 and need transcoding
if (canPlayAc3VideoAudioInHls && physicalAudioChannels > 2) { if (canPlayAc3VideoAudioInHls && physicalAudioChannels > 2) {
hlsVideoAudioCodecs.push('ac3'); hlsInTsVideoAudioCodecs.push('ac3');
hlsInFmp4VideoAudioCodecs.push('ac3');
if (canPlayEac3VideoAudio) { if (canPlayEac3VideoAudio) {
hlsVideoAudioCodecs.push('eac3'); hlsInTsVideoAudioCodecs.push('eac3');
hlsInFmp4VideoAudioCodecs.push('eac3');
} }
} }
} }
if (canPlayAacVideoAudio) { if (canPlayAacVideoAudio) {
videoAudioCodecs.push('aac'); videoAudioCodecs.push('aac');
hlsVideoAudioCodecs.push('aac'); hlsInTsVideoAudioCodecs.push('aac');
hlsInFmp4VideoAudioCodecs.push('aac');
} }
if (supportsMp3VideoAudio) { if (supportsMp3VideoAudio) {
@ -368,16 +376,31 @@ import browser from './browser';
// PS4 fails to load HLS with mp3 audio // PS4 fails to load HLS with mp3 audio
if (!browser.ps4) { if (!browser.ps4) {
hlsVideoAudioCodecs.push('mp3'); hlsInTsVideoAudioCodecs.push('mp3');
} }
hlsInFmp4VideoAudioCodecs.push('mp3');
} }
// For ac3/eac3 directstream // For ac3/eac3 directstream
if (canPlayAc3VideoAudio) { if (canPlayAc3VideoAudio) {
if (canPlayAc3VideoAudioInHls && hlsVideoAudioCodecs.indexOf('ac3') === -1) { if (canPlayAc3VideoAudioInHls) {
hlsVideoAudioCodecs.push('ac3'); if (hlsInTsVideoAudioCodecs.indexOf('ac3') === -1) {
if (canPlayEac3VideoAudio && hlsVideoAudioCodecs.indexOf('eac3') === -1) { hlsInTsVideoAudioCodecs.push('ac3');
hlsVideoAudioCodecs.push('eac3'); }
if (hlsInFmp4VideoAudioCodecs.indexOf('ac3') === -1) {
hlsInFmp4VideoAudioCodecs.push('ac3');
}
if (canPlayEac3VideoAudio) {
if (hlsInTsVideoAudioCodecs.indexOf('eac3') === -1) {
hlsInTsVideoAudioCodecs.push('eac3');
}
if (hlsInFmp4VideoAudioCodecs.indexOf('eac3') === -1) {
hlsInFmp4VideoAudioCodecs.push('eac3');
}
} }
} }
} }
@ -413,38 +436,54 @@ import browser from './browser';
if (canPlayAudioFormat('opus')) { if (canPlayAudioFormat('opus')) {
videoAudioCodecs.push('opus'); videoAudioCodecs.push('opus');
hlsVideoAudioCodecs.push('opus'); hlsInTsVideoAudioCodecs.push('opus');
webmAudioCodecs.push('opus'); webmAudioCodecs.push('opus');
} }
if (canPlayAudioFormat('flac')) { if (canPlayAudioFormat('flac')) {
videoAudioCodecs.push('flac'); videoAudioCodecs.push('flac');
hlsInFmp4VideoAudioCodecs.push('flac');
}
if (canPlayAudioFormat('alac')) {
videoAudioCodecs.push('alac');
hlsInFmp4VideoAudioCodecs.push('alac');
} }
videoAudioCodecs = videoAudioCodecs.filter(function (c) { videoAudioCodecs = videoAudioCodecs.filter(function (c) {
return (options.disableVideoAudioCodecs || []).indexOf(c) === -1; return (options.disableVideoAudioCodecs || []).indexOf(c) === -1;
}); });
hlsVideoAudioCodecs = hlsVideoAudioCodecs.filter(function (c) { hlsInTsVideoAudioCodecs = hlsInTsVideoAudioCodecs.filter(function (c) {
return (options.disableHlsVideoAudioCodecs || []).indexOf(c) === -1;
});
hlsInFmp4VideoAudioCodecs = hlsInFmp4VideoAudioCodecs.filter(function (c) {
return (options.disableHlsVideoAudioCodecs || []).indexOf(c) === -1; return (options.disableHlsVideoAudioCodecs || []).indexOf(c) === -1;
}); });
const mp4VideoCodecs = []; const mp4VideoCodecs = [];
const webmVideoCodecs = []; const webmVideoCodecs = [];
const hlsVideoCodecs = []; const hlsInTsVideoCodecs = [];
const hlsInFmp4VideoCodecs = [];
if ((browser.safari || browser.tizen || browser.web0s) && canPlayHevc(videoTestElement, options)) {
hlsInFmp4VideoCodecs.push('hevc');
}
if (canPlayH264(videoTestElement)) { if (canPlayH264(videoTestElement)) {
mp4VideoCodecs.push('h264'); mp4VideoCodecs.push('h264');
hlsVideoCodecs.push('h264'); hlsInTsVideoCodecs.push('h264');
if (browser.safari || browser.tizen || browser.web0s) {
hlsInFmp4VideoCodecs.push('h264');
}
} }
if (canPlayH265(videoTestElement, options)) { if (canPlayHevc(videoTestElement, options)) {
mp4VideoCodecs.push('h265'); // safari is lying on HDR and 60fps videos, use fMP4 instead
if (!browser.safari) {
mp4VideoCodecs.push('hevc'); mp4VideoCodecs.push('hevc');
if (browser.tizen || browser.web0s) {
hlsVideoCodecs.push('h265');
hlsVideoCodecs.push('hevc');
} }
} }
@ -604,12 +643,13 @@ import browser from './browser';
}); });
} }
if (canPlayHls() && hlsVideoAudioCodecs.length && options.enableHls !== false) { if (canPlayHls() && options.enableHls !== false) {
if (hlsInFmp4VideoCodecs.length && hlsInFmp4VideoAudioCodecs.length && userSettings.preferFmp4HlsContainer() && (browser.safari || browser.tizen || browser.web0s)) {
profile.TranscodingProfiles.push({ profile.TranscodingProfiles.push({
Container: 'ts', Container: 'mp4',
Type: 'Video', Type: 'Video',
AudioCodec: hlsVideoAudioCodecs.join(','), AudioCodec: hlsInFmp4VideoAudioCodecs.join(','),
VideoCodec: hlsVideoCodecs.join(','), VideoCodec: hlsInFmp4VideoCodecs.join(','),
Context: 'Streaming', Context: 'Streaming',
Protocol: 'hls', Protocol: 'hls',
MaxAudioChannels: physicalAudioChannels.toString(), MaxAudioChannels: physicalAudioChannels.toString(),
@ -618,6 +658,21 @@ import browser from './browser';
}); });
} }
if (hlsInTsVideoCodecs.length && hlsInTsVideoAudioCodecs.length) {
profile.TranscodingProfiles.push({
Container: 'ts',
Type: 'Video',
AudioCodec: hlsInTsVideoAudioCodecs.join(','),
VideoCodec: hlsInTsVideoCodecs.join(','),
Context: 'Streaming',
Protocol: 'hls',
MaxAudioChannels: physicalAudioChannels.toString(),
MinSegments: browser.iOS || browser.osx ? '2' : '1',
BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames
});
}
}
if (canPlayVp8) { if (canPlayVp8) {
profile.TranscodingProfiles.push({ profile.TranscodingProfiles.push({
Container: 'webm', Container: 'webm',
@ -711,6 +766,36 @@ import browser from './browser';
} }
} }
let maxHevcLevel = 120;
let hevcProfiles = 'main';
// hevc main level 4.1
if (videoTestElement.canPlayType('video/mp4; codecs="hvc1.1.4.L123"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="hev1.1.4.L123"').replace(/no/, '')) {
maxHevcLevel = 123;
}
// hevc main10 level 4.1
if (videoTestElement.canPlayType('video/mp4; codecs="hvc1.2.4.L123"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="hev1.2.4.L123"').replace(/no/, '')) {
maxHevcLevel = 123;
hevcProfiles = 'main|main 10';
}
// hevc main10 level 5.1
if (videoTestElement.canPlayType('video/mp4; codecs="hvc1.2.4.L153"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="hev1.2.4.L153"').replace(/no/, '')) {
maxHevcLevel = 153;
hevcProfiles = 'main|main 10';
}
// hevc main10 level 6.1
if (videoTestElement.canPlayType('video/mp4; codecs="hvc1.2.4.L183"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="hev1.2.4.L183"').replace(/no/, '')) {
maxHevcLevel = 183;
hevcProfiles = 'main|main 10';
}
const h264CodecProfileConditions = [ const h264CodecProfileConditions = [
{ {
Condition: 'NotEquals', Condition: 'NotEquals',
@ -732,6 +817,27 @@ import browser from './browser';
} }
]; ];
const hevcCodecProfileConditions = [
{
Condition: 'NotEquals',
Property: 'IsAnamorphic',
Value: 'true',
IsRequired: false
},
{
Condition: 'EqualsAny',
Property: 'VideoProfile',
Value: hevcProfiles,
IsRequired: false
},
{
Condition: 'LessThanEqual',
Property: 'VideoLevel',
Value: maxHevcLevel.toString(),
IsRequired: false
}
];
if (!browser.edgeUwp && !browser.tizen && !browser.web0s) { if (!browser.edgeUwp && !browser.tizen && !browser.web0s) {
h264CodecProfileConditions.push({ h264CodecProfileConditions.push({
Condition: 'NotEquals', Condition: 'NotEquals',
@ -739,6 +845,13 @@ import browser from './browser';
Value: 'true', Value: 'true',
IsRequired: false IsRequired: false
}); });
hevcCodecProfileConditions.push({
Condition: 'NotEquals',
Property: 'IsInterlaced',
Value: 'true',
IsRequired: false
});
} }
if (maxVideoWidth) { if (maxVideoWidth) {
@ -748,12 +861,21 @@ import browser from './browser';
Value: maxVideoWidth.toString(), Value: maxVideoWidth.toString(),
IsRequired: false IsRequired: false
}); });
hevcCodecProfileConditions.push({
Condition: 'LessThanEqual',
Property: 'Width',
Value: maxVideoWidth.toString(),
IsRequired: false
});
} }
const globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString(); const globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString();
const h264MaxVideoBitrate = globalMaxVideoBitrate; const h264MaxVideoBitrate = globalMaxVideoBitrate;
const hevcMaxVideoBitrate = globalMaxVideoBitrate;
if (h264MaxVideoBitrate) { if (h264MaxVideoBitrate) {
h264CodecProfileConditions.push({ h264CodecProfileConditions.push({
Condition: 'LessThanEqual', Condition: 'LessThanEqual',
@ -763,6 +885,15 @@ import browser from './browser';
}); });
} }
if (hevcMaxVideoBitrate) {
hevcCodecProfileConditions.push({
Condition: 'LessThanEqual',
Property: 'VideoBitrate',
Value: hevcMaxVideoBitrate,
IsRequired: true
});
}
// On iOS 12.x, for TS container max h264 level is 4.2 // On iOS 12.x, for TS container max h264 level is 4.2
if (browser.iOS && browser.iOSVersion < 13) { if (browser.iOS && browser.iOSVersion < 13) {
const codecProfile = { const codecProfile = {
@ -790,6 +921,12 @@ import browser from './browser';
Conditions: h264CodecProfileConditions Conditions: h264CodecProfileConditions
}); });
profile.CodecProfiles.push({
Type: 'Video',
Codec: 'hevc',
Conditions: hevcCodecProfileConditions
});
const globalVideoConditions = []; const globalVideoConditions = [];
if (globalMaxVideoBitrate) { if (globalMaxVideoBitrate) {
@ -825,7 +962,7 @@ import browser from './browser';
Method: 'External' Method: 'External'
}); });
} }
if (options.enableSsaRender) { if (options.enableSsaRender !== false && (!options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats')) {
profile.SubtitleProfiles.push({ profile.SubtitleProfiles.push({
Format: 'ass', Format: 'ass',
Method: 'External' Method: 'External'

View file

@ -114,6 +114,33 @@ export class UserSettings {
}); });
} }
/**
* Get or set 'Allowed Audio Channels'.
* @param {string|undefined} val - 'Allowed Audio Channels'.
* @return {string} 'Allowed Audio Channels'.
*/
allowedAudioChannels(val) {
if (val !== undefined) {
return this.set('allowedAudioChannels', val, false);
}
return this.get('allowedAudioChannels', false) || '-1';
}
/**
* Get or set 'Perfer fMP4-HLS Container' state.
* @param {boolean|undefined} val - Flag to enable 'Perfer fMP4-HLS Container' or undefined.
* @return {boolean} 'Prefer fMP4-HLS Container' state.
*/
preferFmp4HlsContainer(val) {
if (val !== undefined) {
return this.set('preferFmp4HlsContainer', val.toString(), false);
}
val = this.get('preferFmp4HlsContainer', false);
return val === 'true';
}
/** /**
* Get or set 'Cinema Mode' state. * Get or set 'Cinema Mode' state.
* @param {boolean|undefined} val - Flag to enable 'Cinema Mode' or undefined. * @param {boolean|undefined} val - Flag to enable 'Cinema Mode' or undefined.
@ -457,6 +484,8 @@ export const importFrom = currentSettings.importFrom.bind(currentSettings);
export const set = currentSettings.set.bind(currentSettings); export const set = currentSettings.set.bind(currentSettings);
export const get = currentSettings.get.bind(currentSettings); export const get = currentSettings.get.bind(currentSettings);
export const serverConfig = currentSettings.serverConfig.bind(currentSettings); export const serverConfig = currentSettings.serverConfig.bind(currentSettings);
export const allowedAudioChannels = currentSettings.allowedAudioChannels.bind(currentSettings);
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
export const enableCinemaMode = currentSettings.enableCinemaMode.bind(currentSettings); export const enableCinemaMode = currentSettings.enableCinemaMode.bind(currentSettings);
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings); export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings); export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);

View file

@ -162,8 +162,8 @@
"EnableHardwareEncoding": "Ενεργοποίηση αποκωδικοποίησης υλικού", "EnableHardwareEncoding": "Ενεργοποίηση αποκωδικοποίησης υλικού",
"EnableNextVideoInfoOverlay": "Εμφάνιση πληροφοριών επόμενου βίντεο κατά την αναπαραγωγή", "EnableNextVideoInfoOverlay": "Εμφάνιση πληροφοριών επόμενου βίντεο κατά την αναπαραγωγή",
"EnableNextVideoInfoOverlayHelp": "Στο τέλος ενός βίντεο, εμφανίστε πληροφορίες σχετικά με το επόμενο βίντεο που εμφανίζεται στην τρέχουσα λίστα αναπαραγωγής.", "EnableNextVideoInfoOverlayHelp": "Στο τέλος ενός βίντεο, εμφανίστε πληροφορίες σχετικά με το επόμενο βίντεο που εμφανίζεται στην τρέχουσα λίστα αναπαραγωγής.",
"EnableThemeSongsHelp": "Αν είναι ενεργοποιημένη, τα τραγούδια θεμάτων θα αναπαραχθούν στο παρασκήνιο κατά την περιήγηση στη βιβλιοθήκη.", "EnableThemeSongsHelp": "Αν είναι ενεργοποιημένη, τα τραγούδια θεμάτων θα αναπαράγονται στο παρασκήνιο κατά την περιήγηση στη βιβλιοθήκη.",
"EnableThemeVideosHelp": "Αν είναι ενεργοποιημένη, τα βίντεο θεμάτων θα αναπαραχθούν στο παρασκήνιο κατά την περιήγηση στη βιβλιοθήκη.", "EnableThemeVideosHelp": "Αν είναι ενεργοποιημένη, τα βίντεο θεμάτων θα αναπαράγονται στο παρασκήνιο κατά την περιήγηση στη βιβλιοθήκη.",
"Ended": "Τέλος", "Ended": "Τέλος",
"EndsAtValue": "Τελειώνει σε {0}", "EndsAtValue": "Τελειώνει σε {0}",
"Episodes": "Επεισόδια", "Episodes": "Επεισόδια",
@ -1056,5 +1056,7 @@
"EnableDecodingColorDepth10Vp9": "Ενεργοποίηση αποκωδικοποίησης υλικού 10-bit για το VP9", "EnableDecodingColorDepth10Vp9": "Ενεργοποίηση αποκωδικοποίησης υλικού 10-bit για το VP9",
"EnableDecodingColorDepth10Hevc": "Ενεργοποίηση αποκωδικοποίησης υλικού 10-bit για HEVC", "EnableDecodingColorDepth10Hevc": "Ενεργοποίηση αποκωδικοποίησης υλικού 10-bit για HEVC",
"EnableAutoCast": "Ορίσετε ως προεπιλογή", "EnableAutoCast": "Ορίσετε ως προεπιλογή",
"ButtonUseQuickConnect": "Χρήση γρήγορης σύνδεσης" "ButtonUseQuickConnect": "Χρήση γρήγορης σύνδεσης",
"EnableDetailsBanner": "Πανό Λεπτομερειών",
"DeleteAll": "Διαγραφή Ολων"
} }

View file

@ -252,7 +252,7 @@
"Guide": "Guide", "Guide": "Guide",
"GuideProviderLogin": "Login", "GuideProviderLogin": "Login",
"GuideProviderSelectListings": "Select Listings", "GuideProviderSelectListings": "Select Listings",
"H264CrfHelp": "The Constant Rate Factor (CRF) is the default quality setting for the x264 encoder. You can set the values between 0 and 51, where lower values would result in better quality (at the expense of higher file sizes). Sane values are between 18 and 28. The default for x264 is 23, so you can use this as a starting point.", "H264CrfHelp": "The Constant Rate Factor (CRF) is the default quality setting for the x264 and x265 encoder. You can set the values between 0 and 51, where lower values would result in better quality (at the expense of higher file sizes). Sane values are between 18 and 28. The default for x264 is 23, and for x265 is 28, so you can use this as a starting point.",
"EncoderPresetHelp": "Choose a faster value to improve performance, or a slower value to improve quality.", "EncoderPresetHelp": "Choose a faster value to improve performance, or a slower value to improve quality.",
"HDPrograms": "HD programs", "HDPrograms": "HD programs",
"HardwareAccelerationWarning": "Enabling hardware acceleration may cause instability in some environments. Ensure that your operating system and video drivers are fully up to date. If you have difficulty playing video after enabling this, you'll need to change the setting back to None.", "HardwareAccelerationWarning": "Enabling hardware acceleration may cause instability in some environments. Ensure that your operating system and video drivers are fully up to date. If you have difficulty playing video after enabling this, you'll need to change the setting back to None.",
@ -589,7 +589,8 @@
"LabelGroupMoviesIntoCollections": "Group movies into collections", "LabelGroupMoviesIntoCollections": "Group movies into collections",
"LabelGroupMoviesIntoCollectionsHelp": "When displaying movie lists, movies in a collection will be displayed as one grouped item.", "LabelGroupMoviesIntoCollectionsHelp": "When displaying movie lists, movies in a collection will be displayed as one grouped item.",
"LabelH264Crf": "H264 encoding CRF:", "LabelH264Crf": "H264 encoding CRF:",
"LabelEncoderPreset": "H264 and H265 encoding preset:", "LabelH265Crf": "H265 encoding CRF:",
"LabelEncoderPreset": "Encoding preset:",
"LabelHardwareAccelerationType": "Hardware acceleration:", "LabelHardwareAccelerationType": "Hardware acceleration:",
"LabelHardwareAccelerationTypeHelp": "Hardware acceleration requires additional configuration.", "LabelHardwareAccelerationTypeHelp": "Hardware acceleration requires additional configuration.",
"LabelHomeNetworkQuality": "Home network quality:", "LabelHomeNetworkQuality": "Home network quality:",
@ -1439,5 +1440,12 @@
"LabelDirectStreamingInfo": "Direct Streaming Info", "LabelDirectStreamingInfo": "Direct Streaming Info",
"LabelRemuxingInfo": "Remuxing Info", "LabelRemuxingInfo": "Remuxing Info",
"LabelOriginalMediaInfo": "Original Media Info", "LabelOriginalMediaInfo": "Original Media Info",
"LabelSyncPlayInfo": "SyncPlay Info" "LabelSyncPlayInfo": "SyncPlay Info",
"PreferFmp4HlsContainer": "Prefer fMP4-HLS Media Container",
"PreferFmp4HlsContainerHelp": "Prefer to use fMP4 as the default container for HLS, making it possible to direct stream HEVC content on supported devices.",
"AllowHevcEncoding": "Allow encoding in HEVC format",
"LabelAllowedAudioChannels": "Maximum Allowed Audio Channels",
"LabelSelectAudioChannels": "Channels",
"LabelSelectMono": "Mono",
"LabelSelectStereo": "Stereo"
} }

View file

@ -416,7 +416,7 @@
"Items": "Pozycje", "Items": "Pozycje",
"Kids": "Dla dzieci", "Kids": "Dla dzieci",
"Label3DFormat": "Format 3D:", "Label3DFormat": "Format 3D:",
"LabelAbortedByServerShutdown": "(Przerwano w skuter wyłączenia serwera)", "LabelAbortedByServerShutdown": "(Przerwano w skutek wyłączenia serwera)",
"LabelAccessDay": "Dzień tygodnia:", "LabelAccessDay": "Dzień tygodnia:",
"LabelAccessEnd": "Czas zakończenia:", "LabelAccessEnd": "Czas zakończenia:",
"LabelAccessStart": "Czas startu:", "LabelAccessStart": "Czas startu:",
@ -1381,5 +1381,9 @@
"Authorize": "Autoryzuj", "Authorize": "Autoryzuj",
"HeaderDeleteDevices": "Usuń wszystkie urządzenia", "HeaderDeleteDevices": "Usuń wszystkie urządzenia",
"DeleteAll": "Usuń wszystkie", "DeleteAll": "Usuń wszystkie",
"LabelKnownProxies": "Znane serwery proxy:" "LabelKnownProxies": "Znane serwery proxy:",
"MediaInfoColorSpace": "Przestrzeń kolorów",
"LabelColorSpace": "Przestrzeń kolorów:",
"KnownProxiesHelp": "Podzielona przecinkami lista znanych serwerów proxy, używana do łączenia z twoją instancją Jellyfin. Wymagana do poprawnego użycia nagłówka X-Forwarded-For. Wymaga restartu po zapisaniu.",
"DeleteDevicesConfirmation": "Na pewno usunąć wszystkie urządzenia? Wszystkie pozostałe sesje zostaną wylogowane. Urządzenia pojawią się ponownie po następnym zalogowaniu użytkownika."
} }

View file

@ -197,7 +197,7 @@
"GuestStar": "特邀明星", "GuestStar": "特邀明星",
"GuideProviderLogin": "登入", "GuideProviderLogin": "登入",
"GuideProviderSelectListings": "选择列表", "GuideProviderSelectListings": "选择列表",
"H264CrfHelp": "The Constant Rate Factor (CRF) 是 x264 编码器的默认质量设置。您可以设置介于0和51之间的值, 其中较低的值将导致更好的质量 (以更高的文件大小为代价)。正常值介于18和28之间。x264 的默认值为 23, 因此可以将其用作起始点。", "H264CrfHelp": "The Constant Rate Factor (CRF) 是 x264 编码器的默认质量设置。您可以设置介于0和51之间的值 其中较低的值将导致更好的质量 (以更高的文件大小为代价)。正常值介于 18 和 28 之间。x264 的默认值为 23x265 的默认值为 28因此可以将其用作起始点。",
"EncoderPresetHelp": "选择一个更快的值以提升性能,或者选择一个更慢的值以提升质量。", "EncoderPresetHelp": "选择一个更快的值以提升性能,或者选择一个更慢的值以提升质量。",
"HDPrograms": "高清节目", "HDPrograms": "高清节目",
"HardwareAccelerationWarning": "启动硬件加速可能在某些环境下导致系统不稳定。请确认你的操作系统和显卡驱动程序是最新的。如果你在开启此项后播放视频时遇到困难,那么你需要将此选项设置回“没有”。", "HardwareAccelerationWarning": "启动硬件加速可能在某些环境下导致系统不稳定。请确认你的操作系统和显卡驱动程序是最新的。如果你在开启此项后播放视频时遇到困难,那么你需要将此选项设置回“没有”。",
@ -493,7 +493,8 @@
"LabelGroupMoviesIntoCollections": "批量添加电影到集合", "LabelGroupMoviesIntoCollections": "批量添加电影到集合",
"LabelGroupMoviesIntoCollectionsHelp": "显示电影列表时,同一集合的电影将显示为一个分组。", "LabelGroupMoviesIntoCollectionsHelp": "显示电影列表时,同一集合的电影将显示为一个分组。",
"LabelH264Crf": "H264 CRF 编码质量等级:", "LabelH264Crf": "H264 CRF 编码质量等级:",
"LabelEncoderPreset": "H264 和 H265 编码预设:", "LabelH265Crf": "H265 CRF 编码质量等级:",
"LabelEncoderPreset": "编码预设:",
"LabelHardwareAccelerationType": "硬件加速:", "LabelHardwareAccelerationType": "硬件加速:",
"LabelHardwareAccelerationTypeHelp": "硬件加速需要额外配置。", "LabelHardwareAccelerationTypeHelp": "硬件加速需要额外配置。",
"LabelHomeNetworkQuality": "家庭网络质量:", "LabelHomeNetworkQuality": "家庭网络质量:",
@ -1443,4 +1444,11 @@
"LabelRemuxingInfo": "转封装信息", "LabelRemuxingInfo": "转封装信息",
"LabelOriginalMediaInfo": "媒体源信息", "LabelOriginalMediaInfo": "媒体源信息",
"LabelSyncPlayInfo": "同步播放信息" "LabelSyncPlayInfo": "同步播放信息"
"PreferFmp4HlsContainer": "优先使用 fMP4-HLS 媒体容器",
"PreferFmp4HlsContainerHelp": "优先使用 fMP4 作为 HLS 播放的默认容器,从而可以在支持的设备上直接串流 HEVC 格式的内容。",
"AllowHevcEncoding": "允许以 HEVC 格式编码",
"LabelAllowedAudioChannels": "允许的最大声道数量",
"LabelSelectAudioChannels": "声道",
"LabelSelectMono": "单声道",
"LabelSelectStereo": "立体声"
} }

View file

@ -3,6 +3,7 @@ const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin'); const CopyPlugin = require('copy-webpack-plugin');
const packageConfig = require('./package.json'); const packageConfig = require('./package.json');
const WorkboxPlugin = require('workbox-webpack-plugin'); const WorkboxPlugin = require('workbox-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = { module.exports = {
context: path.resolve(__dirname, 'src'), context: path.resolve(__dirname, 'src'),
@ -14,6 +15,10 @@ module.exports = {
}, },
plugins: [ plugins: [
new CleanWebpackPlugin(), new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html'
}),
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ {

View file

@ -1,7 +1,6 @@
const path = require('path'); const path = require('path');
const common = require('./webpack.common'); const common = require('./webpack.common');
const merge = require('webpack-merge'); const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = merge(common, { module.exports = merge(common, {
mode: 'development', mode: 'development',
@ -16,12 +15,6 @@ module.exports = merge(common, {
} }
] ]
}, },
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html'
})
],
devServer: { devServer: {
contentBase: path.join(__dirname, 'dist'), contentBase: path.join(__dirname, 'dist'),
compress: true compress: true