mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge branch 'jellyfin:master' into cast-with-localhost-server
This commit is contained in:
commit
8d3e8eb5b2
19 changed files with 179 additions and 85 deletions
12
package-lock.json
generated
12
package-lock.json
generated
|
@ -38,7 +38,7 @@
|
||||||
"flv.js": "1.6.2",
|
"flv.js": "1.6.2",
|
||||||
"headroom.js": "0.12.0",
|
"headroom.js": "0.12.0",
|
||||||
"history": "5.3.0",
|
"history": "5.3.0",
|
||||||
"hls.js": "1.4.4",
|
"hls.js": "github:nyanmisaka/hls.js#v1.5.0-fix-firefox-av1",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
"jassub": "1.7.1",
|
"jassub": "1.7.1",
|
||||||
"jellyfin-apiclient": "1.10.0",
|
"jellyfin-apiclient": "1.10.0",
|
||||||
|
@ -9460,9 +9460,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hls.js": {
|
"node_modules/hls.js": {
|
||||||
"version": "1.4.4",
|
"resolved": "git+ssh://git@github.com/nyanmisaka/hls.js.git#2ca771e97a15d8078a3850c4c4cf225f497dcaa7",
|
||||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.4.4.tgz",
|
"license": "Apache-2.0"
|
||||||
"integrity": "sha512-Yix3i1klHtTWIj8Fa/yoN5mFreY3eLKGrmVldpkQ+2Bb1PmEok9Ah/VfAzlJnwtInCo4rs5l6/W2tUh76DaSNA=="
|
|
||||||
},
|
},
|
||||||
"node_modules/hoist-non-react-statics": {
|
"node_modules/hoist-non-react-statics": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
|
@ -27345,9 +27344,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hls.js": {
|
"hls.js": {
|
||||||
"version": "1.4.4",
|
"version": "git+ssh://git@github.com/nyanmisaka/hls.js.git#2ca771e97a15d8078a3850c4c4cf225f497dcaa7",
|
||||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.4.4.tgz",
|
"from": "hls.js@github:nyanmisaka/hls.js#v1.5.0-fix-firefox-av1"
|
||||||
"integrity": "sha512-Yix3i1klHtTWIj8Fa/yoN5mFreY3eLKGrmVldpkQ+2Bb1PmEok9Ah/VfAzlJnwtInCo4rs5l6/W2tUh76DaSNA=="
|
|
||||||
},
|
},
|
||||||
"hoist-non-react-statics": {
|
"hoist-non-react-statics": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
|
|
|
@ -93,7 +93,7 @@
|
||||||
"flv.js": "1.6.2",
|
"flv.js": "1.6.2",
|
||||||
"headroom.js": "0.12.0",
|
"headroom.js": "0.12.0",
|
||||||
"history": "5.3.0",
|
"history": "5.3.0",
|
||||||
"hls.js": "1.4.4",
|
"hls.js": "github:nyanmisaka/hls.js#v1.5.0-fix-firefox-av1",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
"jassub": "1.7.1",
|
"jassub": "1.7.1",
|
||||||
"jellyfin-apiclient": "1.10.0",
|
"jellyfin-apiclient": "1.10.0",
|
||||||
|
|
|
@ -3,41 +3,32 @@ import browser from '../scripts/browser';
|
||||||
import dialog from './dialog/dialog';
|
import dialog from './dialog/dialog';
|
||||||
import globalize from '../scripts/globalize';
|
import globalize from '../scripts/globalize';
|
||||||
|
|
||||||
function useNativeAlert() {
|
export default async function (text, title) {
|
||||||
// webOS seems to block modals
|
// Modals seem to be blocked on Web OS and Tizen 2.x
|
||||||
// Tizen 2.x seems to block modals
|
const canUseNativeAlert = !!(
|
||||||
return !browser.web0s
|
!browser.web0s
|
||||||
&& !(browser.tizenVersion && browser.tizenVersion < 3)
|
&& !(browser.tizenVersion && browser.tizenVersion < 3)
|
||||||
&& browser.tv
|
&& browser.tv
|
||||||
&& window.alert;
|
&& window.alert
|
||||||
}
|
);
|
||||||
|
|
||||||
export default async function (text, title) {
|
const options = typeof text === 'string' ? { title, text } : text;
|
||||||
let options;
|
|
||||||
if (typeof text === 'string') {
|
|
||||||
options = {
|
|
||||||
title: title,
|
|
||||||
text: text
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
options = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
await appRouter.ready();
|
await appRouter.ready();
|
||||||
|
|
||||||
if (useNativeAlert()) {
|
if (canUseNativeAlert) {
|
||||||
alert((options.text || '').replaceAll('<br/>', '\n'));
|
alert((options.text || '').replaceAll('<br/>', '\n'));
|
||||||
return Promise.resolve();
|
|
||||||
} else {
|
|
||||||
const items = [];
|
|
||||||
|
|
||||||
items.push({
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
options.buttons = [
|
||||||
|
{
|
||||||
name: globalize.translate('ButtonGotIt'),
|
name: globalize.translate('ButtonGotIt'),
|
||||||
id: 'ok',
|
id: 'ok',
|
||||||
type: 'submit'
|
type: 'submit'
|
||||||
});
|
}
|
||||||
|
];
|
||||||
|
|
||||||
options.buttons = items;
|
|
||||||
return dialog.show(options);
|
return dialog.show(options);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -16,11 +16,14 @@ function getBaseProfileOptions(item) {
|
||||||
if (browser.edge) {
|
if (browser.edge) {
|
||||||
disableHlsVideoAudioCodecs.push('mp3');
|
disableHlsVideoAudioCodecs.push('mp3');
|
||||||
}
|
}
|
||||||
|
if (!browser.edgeChromium) {
|
||||||
disableHlsVideoAudioCodecs.push('ac3');
|
disableHlsVideoAudioCodecs.push('ac3');
|
||||||
disableHlsVideoAudioCodecs.push('eac3');
|
disableHlsVideoAudioCodecs.push('eac3');
|
||||||
|
}
|
||||||
|
if (!(browser.chrome || browser.edgeChromium || browser.firefox)) {
|
||||||
disableHlsVideoAudioCodecs.push('opus');
|
disableHlsVideoAudioCodecs.push('opus');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enableMkvProgressive: false,
|
enableMkvProgressive: false,
|
||||||
|
|
|
@ -43,8 +43,8 @@ export function enableHlsJsPlayer(runTimeTicks, mediaType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canPlayNativeHls()) {
|
if (canPlayNativeHls()) {
|
||||||
// Having trouble with chrome's native support and transcoded music
|
// Android Webview's native HLS has performance and compatiblity issues
|
||||||
if (browser.android && mediaType === 'Audio') {
|
if (browser.android && (mediaType === 'Audio' || mediaType === 'Video')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,12 @@
|
||||||
<span>${AllowHevcEncoding}</span>
|
<span>${AllowHevcEncoding}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="checkboxList">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" is="emby-checkbox" id="chkAllowAv1Encoding" />
|
||||||
|
<span>${AllowAv1Encoding}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="vppTonemappingOptions hide">
|
<div class="vppTonemappingOptions hide">
|
||||||
|
|
|
@ -18,6 +18,7 @@ function loadPage(page, config, systemInfo) {
|
||||||
page.querySelector('#chkIntelLpHevcHwEncoder').checked = config.EnableIntelLowPowerHevcHwEncoder;
|
page.querySelector('#chkIntelLpHevcHwEncoder').checked = config.EnableIntelLowPowerHevcHwEncoder;
|
||||||
page.querySelector('#chkHardwareEncoding').checked = config.EnableHardwareEncoding;
|
page.querySelector('#chkHardwareEncoding').checked = config.EnableHardwareEncoding;
|
||||||
page.querySelector('#chkAllowHevcEncoding').checked = config.AllowHevcEncoding;
|
page.querySelector('#chkAllowHevcEncoding').checked = config.AllowHevcEncoding;
|
||||||
|
page.querySelector('#chkAllowAv1Encoding').checked = config.AllowAv1Encoding;
|
||||||
$('#selectVideoDecoder', page).val(config.HardwareAccelerationType);
|
$('#selectVideoDecoder', page).val(config.HardwareAccelerationType);
|
||||||
$('#selectThreadCount', page).val(config.EncodingThreadCount);
|
$('#selectThreadCount', page).val(config.EncodingThreadCount);
|
||||||
page.querySelector('#chkEnableAudioVbr').checked = config.EnableAudioVbr;
|
page.querySelector('#chkEnableAudioVbr').checked = config.EnableAudioVbr;
|
||||||
|
@ -123,6 +124,7 @@ function onSubmit() {
|
||||||
config.EnableIntelLowPowerHevcHwEncoder = form.querySelector('#chkIntelLpHevcHwEncoder').checked;
|
config.EnableIntelLowPowerHevcHwEncoder = form.querySelector('#chkIntelLpHevcHwEncoder').checked;
|
||||||
config.EnableHardwareEncoding = form.querySelector('#chkHardwareEncoding').checked;
|
config.EnableHardwareEncoding = form.querySelector('#chkHardwareEncoding').checked;
|
||||||
config.AllowHevcEncoding = form.querySelector('#chkAllowHevcEncoding').checked;
|
config.AllowHevcEncoding = form.querySelector('#chkAllowHevcEncoding').checked;
|
||||||
|
config.AllowAv1Encoding = form.querySelector('#chkAllowAv1Encoding').checked;
|
||||||
ApiClient.updateNamedConfiguration('encoding', config).then(function () {
|
ApiClient.updateNamedConfiguration('encoding', config).then(function () {
|
||||||
updateEncoder(form);
|
updateEncoder(form);
|
||||||
}, function () {
|
}, function () {
|
||||||
|
|
|
@ -2,12 +2,6 @@
|
||||||
<button is="paper-icon-button-light" id="btnBookplayerToc" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1">
|
<button is="paper-icon-button-light" id="btnBookplayerToc" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1">
|
||||||
<span class="material-icons bookplayerButtonIcon toc" aria-hidden="true"></span>
|
<span class="material-icons bookplayerButtonIcon toc" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<!-- <button is="paper-icon-button-light" id="btnBookplayerPrev" class="autoSize bookplayerButton pageButton hide-mouse-idle-tv" tabindex="-1">
|
|
||||||
<span class="material-icons bookplayerButtonIcon navigate_before" aria-hidden="true"></span> ${Previous}
|
|
||||||
</button>
|
|
||||||
<button is="paper-icon-button-light" id="btnBookplayerNext" class="autoSize bookplayerButton pageButton hide-mouse-idle-tv" tabindex="-1">
|
|
||||||
${Next} <span class="material-icons bookplayerButtonIcon navigate_next" aria-hidden="true"></span>
|
|
||||||
</button> -->
|
|
||||||
<button is="paper-icon-button-light" id="btnBookplayerExit" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1">
|
<button is="paper-icon-button-light" id="btnBookplayerExit" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1">
|
||||||
<span class="material-icons bookplayerButtonIcon close" aria-hidden="true"></span>
|
<span class="material-icons bookplayerButtonIcon close" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -26,7 +26,7 @@ export function isEnabled() {
|
||||||
const playerId = localStorage.getItem('autocastPlayerId');
|
const playerId = localStorage.getItem('autocastPlayerId');
|
||||||
const currentPlayerInfo = playbackManager.getPlayerInfo();
|
const currentPlayerInfo = playbackManager.getPlayerInfo();
|
||||||
|
|
||||||
return (currentPlayerInfo && playerId && currentPlayerInfo.id === playerId);
|
return playerId && currentPlayerInfo?.id === playerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onOpen() {
|
function onOpen() {
|
||||||
|
|
|
@ -148,15 +148,12 @@ let _supportsCssAnimation;
|
||||||
let _supportsCssAnimationWithPrefix;
|
let _supportsCssAnimationWithPrefix;
|
||||||
function supportsCssAnimation(allowPrefix) {
|
function supportsCssAnimation(allowPrefix) {
|
||||||
// TODO: Assess if this is still needed, as all of our targets should natively support CSS animations.
|
// TODO: Assess if this is still needed, as all of our targets should natively support CSS animations.
|
||||||
if (allowPrefix) {
|
if (allowPrefix && (_supportsCssAnimationWithPrefix === true || _supportsCssAnimationWithPrefix === false)) {
|
||||||
if (_supportsCssAnimationWithPrefix === true || _supportsCssAnimationWithPrefix === false) {
|
|
||||||
return _supportsCssAnimationWithPrefix;
|
return _supportsCssAnimationWithPrefix;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (_supportsCssAnimation === true || _supportsCssAnimation === false) {
|
if (_supportsCssAnimation === true || _supportsCssAnimation === false) {
|
||||||
return _supportsCssAnimation;
|
return _supportsCssAnimation;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let animation = false;
|
let animation = false;
|
||||||
const domPrefixes = ['Webkit', 'O', 'Moz'];
|
const domPrefixes = ['Webkit', 'O', 'Moz'];
|
||||||
|
@ -187,13 +184,13 @@ function supportsCssAnimation(allowPrefix) {
|
||||||
const uaMatch = function (ua) {
|
const uaMatch = function (ua) {
|
||||||
ua = ua.toLowerCase();
|
ua = ua.toLowerCase();
|
||||||
|
|
||||||
const match = /(edg)[ /]([\w.]+)/.exec(ua)
|
const match = /(chrome)[ /]([\w.]+)/.exec(ua)
|
||||||
|
|| /(edg)[ /]([\w.]+)/.exec(ua)
|
||||||
|| /(edga)[ /]([\w.]+)/.exec(ua)
|
|| /(edga)[ /]([\w.]+)/.exec(ua)
|
||||||
|| /(edgios)[ /]([\w.]+)/.exec(ua)
|
|| /(edgios)[ /]([\w.]+)/.exec(ua)
|
||||||
|| /(edge)[ /]([\w.]+)/.exec(ua)
|
|| /(edge)[ /]([\w.]+)/.exec(ua)
|
||||||
|| /(opera)[ /]([\w.]+)/.exec(ua)
|
|| /(opera)[ /]([\w.]+)/.exec(ua)
|
||||||
|| /(opr)[ /]([\w.]+)/.exec(ua)
|
|| /(opr)[ /]([\w.]+)/.exec(ua)
|
||||||
|| /(chrome)[ /]([\w.]+)/.exec(ua)
|
|
||||||
|| /(safari)[ /]([\w.]+)/.exec(ua)
|
|| /(safari)[ /]([\w.]+)/.exec(ua)
|
||||||
|| /(firefox)[ /]([\w.]+)/.exec(ua)
|
|| /(firefox)[ /]([\w.]+)/.exec(ua)
|
||||||
|| ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua)
|
|| ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua)
|
||||||
|
|
|
@ -23,6 +23,17 @@ function canPlayHevc(videoTestElement, options) {
|
||||||
|| videoTestElement.canPlayType('video/mp4; codecs="hev1.1.0.L120"').replace(/no/, ''));
|
|| videoTestElement.canPlayType('video/mp4; codecs="hev1.1.0.L120"').replace(/no/, ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canPlayAv1(videoTestElement) {
|
||||||
|
if (browser.tizenVersion >= 5.5 || browser.web0sVersion >= 5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// av1 main level 5.3
|
||||||
|
return !!videoTestElement.canPlayType
|
||||||
|
&& (videoTestElement.canPlayType('video/mp4; codecs="av01.0.15M.08"').replace(/no/, '')
|
||||||
|
&& videoTestElement.canPlayType('video/mp4; codecs="av01.0.15M.10"').replace(/no/, ''));
|
||||||
|
}
|
||||||
|
|
||||||
let _supportsTextTracks;
|
let _supportsTextTracks;
|
||||||
function supportsTextTracks() {
|
function supportsTextTracks() {
|
||||||
if (browser.tizen) {
|
if (browser.tizen) {
|
||||||
|
@ -56,6 +67,14 @@ function canPlayNativeHls() {
|
||||||
|| media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, ''));
|
|| media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canPlayNativeHlsInFmp4() {
|
||||||
|
if (browser.tizenVersion >= 3 || browser.web0sVersion >= 3.5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (browser.iOS && browser.iOSVersion >= 11) || browser.osx;
|
||||||
|
}
|
||||||
|
|
||||||
function canPlayHlsWithMSE() {
|
function canPlayHlsWithMSE() {
|
||||||
// text tracks don’t work with this in firefox
|
// text tracks don’t work with this in firefox
|
||||||
return window.MediaSource != null; /* eslint-disable-line compat/compat */
|
return window.MediaSource != null; /* eslint-disable-line compat/compat */
|
||||||
|
@ -157,14 +176,6 @@ function testCanPlayMkv(videoTestElement) {
|
||||||
return !!browser.edgeUwp;
|
return !!browser.edgeUwp;
|
||||||
}
|
}
|
||||||
|
|
||||||
function testCanPlayAv1(videoTestElement) {
|
|
||||||
if (browser.tizenVersion >= 5.5 || browser.web0sVersion >= 5) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return videoTestElement.canPlayType('video/webm; codecs="av01.0.15M.10"').replace(/no/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function testCanPlayTs() {
|
function testCanPlayTs() {
|
||||||
return browser.tizen || browser.web0s || browser.edgeUwp;
|
return browser.tizen || browser.web0s || browser.edgeUwp;
|
||||||
}
|
}
|
||||||
|
@ -437,8 +448,15 @@ export default function (options) {
|
||||||
// Do not use AC3 for audio transcoding unless AAC and MP3 are not supported.
|
// Do not use AC3 for audio transcoding unless AAC and MP3 are not supported.
|
||||||
if (canPlayAc3VideoAudio) {
|
if (canPlayAc3VideoAudio) {
|
||||||
videoAudioCodecs.push('ac3');
|
videoAudioCodecs.push('ac3');
|
||||||
|
if (browser.edgeChromium) {
|
||||||
|
hlsInFmp4VideoAudioCodecs.push('ac3');
|
||||||
|
}
|
||||||
|
|
||||||
if (canPlayEac3VideoAudio) {
|
if (canPlayEac3VideoAudio) {
|
||||||
videoAudioCodecs.push('eac3');
|
videoAudioCodecs.push('eac3');
|
||||||
|
if (browser.edgeChromium) {
|
||||||
|
hlsInFmp4VideoAudioCodecs.push('eac3');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canPlayAc3VideoAudioInHls) {
|
if (canPlayAc3VideoAudioInHls) {
|
||||||
|
@ -492,6 +510,9 @@ export default function (options) {
|
||||||
if (browser.tizen) {
|
if (browser.tizen) {
|
||||||
hlsInTsVideoAudioCodecs.push('opus');
|
hlsInTsVideoAudioCodecs.push('opus');
|
||||||
}
|
}
|
||||||
|
if (!browser.safari) {
|
||||||
|
hlsInFmp4VideoAudioCodecs.push('opus');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canPlayAudioFormat('flac')) {
|
if (canPlayAudioFormat('flac')) {
|
||||||
|
@ -521,18 +542,23 @@ export default function (options) {
|
||||||
const hlsInTsVideoCodecs = [];
|
const hlsInTsVideoCodecs = [];
|
||||||
const hlsInFmp4VideoCodecs = [];
|
const hlsInFmp4VideoCodecs = [];
|
||||||
|
|
||||||
if ((browser.safari || browser.tizen || browser.web0s) && canPlayHevc(videoTestElement, options)) {
|
if (canPlayAv1(videoTestElement)
|
||||||
|
&& !browser.mobile && (browser.edgeChromium || browser.firefox || browser.chrome)) {
|
||||||
|
// disable av1 on mobile since it can be very slow software decoding
|
||||||
|
hlsInFmp4VideoCodecs.push('av1');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canPlayHevc(videoTestElement, options)
|
||||||
|
&& (browser.edgeChromium || browser.safari || browser.tizen || browser.web0s || (browser.chrome && (!browser.android || browser.chrome.versionMajor >= 105)))) {
|
||||||
|
// Chromium used to support HEVC on Android but not via MSE
|
||||||
hlsInFmp4VideoCodecs.push('hevc');
|
hlsInFmp4VideoCodecs.push('hevc');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canPlayH264(videoTestElement)) {
|
if (canPlayH264(videoTestElement)) {
|
||||||
mp4VideoCodecs.push('h264');
|
mp4VideoCodecs.push('h264');
|
||||||
hlsInTsVideoCodecs.push('h264');
|
hlsInTsVideoCodecs.push('h264');
|
||||||
|
|
||||||
if (browser.safari || browser.tizen || browser.web0s) {
|
|
||||||
hlsInFmp4VideoCodecs.push('h264');
|
hlsInFmp4VideoCodecs.push('h264');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (canPlayHevc(videoTestElement, options)) {
|
if (canPlayHevc(videoTestElement, options)) {
|
||||||
// safari is lying on HDR and 60fps videos, use fMP4 instead
|
// safari is lying on HDR and 60fps videos, use fMP4 instead
|
||||||
|
@ -566,7 +592,7 @@ export default function (options) {
|
||||||
webmVideoCodecs.push('vp9');
|
webmVideoCodecs.push('vp9');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testCanPlayAv1(videoTestElement)) {
|
if (canPlayAv1(videoTestElement)) {
|
||||||
mp4VideoCodecs.push('av1');
|
mp4VideoCodecs.push('av1');
|
||||||
webmVideoCodecs.push('av1');
|
webmVideoCodecs.push('av1');
|
||||||
}
|
}
|
||||||
|
@ -687,7 +713,11 @@ export default function (options) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (canPlayHls() && options.enableHls !== false) {
|
if (canPlayHls() && options.enableHls !== false) {
|
||||||
if (hlsInFmp4VideoCodecs.length && hlsInFmp4VideoAudioCodecs.length && userSettings.preferFmp4HlsContainer() && (browser.safari || browser.tizen || browser.web0s)) {
|
let enableFmp4Hls = userSettings.preferFmp4HlsContainer();
|
||||||
|
if ((browser.safari || browser.tizen || browser.web0s) && !canPlayNativeHlsInFmp4()) {
|
||||||
|
enableFmp4Hls = false;
|
||||||
|
}
|
||||||
|
if (hlsInFmp4VideoCodecs.length && hlsInFmp4VideoAudioCodecs.length && enableFmp4Hls) {
|
||||||
profile.TranscodingProfiles.push({
|
profile.TranscodingProfiles.push({
|
||||||
Container: 'mp4',
|
Container: 'mp4',
|
||||||
Type: 'Video',
|
Type: 'Video',
|
||||||
|
@ -817,6 +847,33 @@ export default function (options) {
|
||||||
hevcProfiles = 'main|main 10';
|
hevcProfiles = 'main|main 10';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let maxAv1Level = 15; // level 5.3
|
||||||
|
const av1Profiles = 'main'; // av1 main covers 4:2:0 8 & 10 bits
|
||||||
|
|
||||||
|
// av1 main level 6.0
|
||||||
|
if (videoTestElement.canPlayType('video/mp4; codecs="av01.0.16M.08"').replace(/no/, '')
|
||||||
|
&& videoTestElement.canPlayType('video/mp4; codecs="av01.0.16M.10"').replace(/no/, '')) {
|
||||||
|
maxAv1Level = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// av1 main level 6.1
|
||||||
|
if (videoTestElement.canPlayType('video/mp4; codecs="av01.0.17M.08"').replace(/no/, '')
|
||||||
|
&& videoTestElement.canPlayType('video/mp4; codecs="av01.0.17M.10"').replace(/no/, '')) {
|
||||||
|
maxAv1Level = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
// av1 main level 6.2
|
||||||
|
if (videoTestElement.canPlayType('video/mp4; codecs="av01.0.18M.08"').replace(/no/, '')
|
||||||
|
&& videoTestElement.canPlayType('video/mp4; codecs="av01.0.18M.10"').replace(/no/, '')) {
|
||||||
|
maxAv1Level = 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
// av1 main level 6.3
|
||||||
|
if (videoTestElement.canPlayType('video/mp4; codecs="av01.0.19M.08"').replace(/no/, '')
|
||||||
|
&& videoTestElement.canPlayType('video/mp4; codecs="av01.0.19M.10"').replace(/no/, '')) {
|
||||||
|
maxAv1Level = 19;
|
||||||
|
}
|
||||||
|
|
||||||
const h264VideoRangeTypes = 'SDR';
|
const h264VideoRangeTypes = 'SDR';
|
||||||
let hevcVideoRangeTypes = 'SDR';
|
let hevcVideoRangeTypes = 'SDR';
|
||||||
let vp9VideoRangeTypes = 'SDR';
|
let vp9VideoRangeTypes = 'SDR';
|
||||||
|
@ -830,12 +887,15 @@ export default function (options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (browser.tizen || browser.web0s) {
|
if (browser.tizen || browser.web0s) {
|
||||||
hevcVideoRangeTypes += '|HDR10|HLG|DOVI';
|
hevcVideoRangeTypes += '|HDR10|HLG';
|
||||||
vp9VideoRangeTypes += '|HDR10|HLG';
|
vp9VideoRangeTypes += '|HDR10|HLG';
|
||||||
av1VideoRangeTypes += '|HDR10|HLG';
|
av1VideoRangeTypes += '|HDR10|HLG';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (browser.edgeChromium || browser.chrome || browser.firefox) {
|
// Chrome mobile and Firefox have no client side tone-mapping
|
||||||
|
// Edge Chromium on Nvidia is known to have color issues on 10-bit video
|
||||||
|
if (browser.chrome && !browser.mobile) {
|
||||||
|
hevcVideoRangeTypes += '|HDR10|HLG';
|
||||||
vp9VideoRangeTypes += '|HDR10|HLG';
|
vp9VideoRangeTypes += '|HDR10|HLG';
|
||||||
av1VideoRangeTypes += '|HDR10|HLG';
|
av1VideoRangeTypes += '|HDR10|HLG';
|
||||||
}
|
}
|
||||||
|
@ -904,11 +964,29 @@ export default function (options) {
|
||||||
];
|
];
|
||||||
|
|
||||||
const av1CodecProfileConditions = [
|
const av1CodecProfileConditions = [
|
||||||
|
{
|
||||||
|
Condition: 'NotEquals',
|
||||||
|
Property: 'IsAnamorphic',
|
||||||
|
Value: 'true',
|
||||||
|
IsRequired: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Condition: 'EqualsAny',
|
||||||
|
Property: 'VideoProfile',
|
||||||
|
Value: av1Profiles,
|
||||||
|
IsRequired: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Condition: 'EqualsAny',
|
Condition: 'EqualsAny',
|
||||||
Property: 'VideoRangeType',
|
Property: 'VideoRangeType',
|
||||||
Value: av1VideoRangeTypes,
|
Value: av1VideoRangeTypes,
|
||||||
IsRequired: false
|
IsRequired: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Condition: 'LessThanEqual',
|
||||||
|
Property: 'VideoLevel',
|
||||||
|
Value: maxAv1Level.toString(),
|
||||||
|
IsRequired: false
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -942,6 +1020,13 @@ export default function (options) {
|
||||||
Value: maxVideoWidth.toString(),
|
Value: maxVideoWidth.toString(),
|
||||||
IsRequired: false
|
IsRequired: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
av1CodecProfileConditions.push({
|
||||||
|
Condition: 'LessThanEqual',
|
||||||
|
Property: 'Width',
|
||||||
|
Value: maxVideoWidth.toString(),
|
||||||
|
IsRequired: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString();
|
const globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString();
|
||||||
|
@ -950,6 +1035,8 @@ export default function (options) {
|
||||||
|
|
||||||
const hevcMaxVideoBitrate = globalMaxVideoBitrate;
|
const hevcMaxVideoBitrate = globalMaxVideoBitrate;
|
||||||
|
|
||||||
|
const av1MaxVideoBitrate = globalMaxVideoBitrate;
|
||||||
|
|
||||||
if (h264MaxVideoBitrate) {
|
if (h264MaxVideoBitrate) {
|
||||||
h264CodecProfileConditions.push({
|
h264CodecProfileConditions.push({
|
||||||
Condition: 'LessThanEqual',
|
Condition: 'LessThanEqual',
|
||||||
|
@ -968,6 +1055,15 @@ export default function (options) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (av1MaxVideoBitrate) {
|
||||||
|
av1CodecProfileConditions.push({
|
||||||
|
Condition: 'LessThanEqual',
|
||||||
|
Property: 'VideoBitrate',
|
||||||
|
Value: av1MaxVideoBitrate,
|
||||||
|
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 = {
|
||||||
|
|
|
@ -230,7 +230,6 @@ export function getDisplayTime(date) {
|
||||||
const timeLower = time.toLowerCase();
|
const timeLower = time.toLowerCase();
|
||||||
|
|
||||||
if (timeLower.indexOf('am') !== -1 || timeLower.indexOf('pm') !== -1) {
|
if (timeLower.indexOf('am') !== -1 || timeLower.indexOf('pm') !== -1) {
|
||||||
time = timeLower;
|
|
||||||
let hour = date.getHours() % 12;
|
let hour = date.getHours() % 12;
|
||||||
const suffix = date.getHours() > 11 ? 'pm' : 'am';
|
const suffix = date.getHours() > 11 ? 'pm' : 'am';
|
||||||
if (!hour) {
|
if (!hour) {
|
||||||
|
|
|
@ -1763,5 +1763,6 @@
|
||||||
"LabelSegmentKeepSeconds": "Doba ponechání částí",
|
"LabelSegmentKeepSeconds": "Doba ponechání částí",
|
||||||
"LabelSegmentKeepSecondsHelp": "Čas v sekundách, po který budou části překódovaného souboru uloženy. Musí být delší než čas určený v \"Omezit po\". Funguje pouze při zapnuté funkci Odstranění částí.",
|
"LabelSegmentKeepSecondsHelp": "Čas v sekundách, po který budou části překódovaného souboru uloženy. Musí být delší než čas určený v \"Omezit po\". Funguje pouze při zapnuté funkci Odstranění částí.",
|
||||||
"LabelBackdropScreensaverInterval": "Interval šetřiče \"Pozadí\"",
|
"LabelBackdropScreensaverInterval": "Interval šetřiče \"Pozadí\"",
|
||||||
"LabelBackdropScreensaverIntervalHelp": "Čas v sekundách mezi změnou pozadí při použití šetřiče \"Pozadí\"."
|
"LabelBackdropScreensaverIntervalHelp": "Čas v sekundách mezi změnou pozadí při použití šetřiče \"Pozadí\".",
|
||||||
|
"AllowAv1Encoding": "Povolit kódování do formátu AV1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1700,5 +1700,7 @@
|
||||||
"EnableCardLayout": "Vis visuel CardBox",
|
"EnableCardLayout": "Vis visuel CardBox",
|
||||||
"ResolutionMatchSource": "Match kilde",
|
"ResolutionMatchSource": "Match kilde",
|
||||||
"SubtitleCyan": "Cyan",
|
"SubtitleCyan": "Cyan",
|
||||||
"SubtitleMagenta": "Magenta"
|
"SubtitleMagenta": "Magenta",
|
||||||
|
"AllowCollectionManagement": "Tillad denne bruger at administrere samlinger",
|
||||||
|
"AllowSegmentDeletion": "Slet segmenter"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1715,5 +1715,6 @@
|
||||||
"Unreleased": "Not yet released",
|
"Unreleased": "Not yet released",
|
||||||
"LabelTonemappingMode": "Tone mapping mode",
|
"LabelTonemappingMode": "Tone mapping mode",
|
||||||
"TonemappingModeHelp": "Select the tone mapping mode. If you experience blown out highlights try switching to the RGB mode.",
|
"TonemappingModeHelp": "Select the tone mapping mode. If you experience blown out highlights try switching to the RGB mode.",
|
||||||
"Unknown": "Unknown"
|
"Unknown": "Unknown",
|
||||||
|
"AllowAv1Encoding": "Allow encoding in AV1 format"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1232,7 +1232,7 @@
|
||||||
"EnableTonemapping": "Käytä sävykartoitusta",
|
"EnableTonemapping": "Käytä sävykartoitusta",
|
||||||
"EnableBlurHashHelp": "Kuvat, joita ladataan vielä, näytetään yksilöllisellä paikkamerkillä.",
|
"EnableBlurHashHelp": "Kuvat, joita ladataan vielä, näytetään yksilöllisellä paikkamerkillä.",
|
||||||
"EnableBlurHash": "Ota sumennetut paikkamerkit käyttöön kuville",
|
"EnableBlurHash": "Ota sumennetut paikkamerkit käyttöön kuville",
|
||||||
"AllowTonemappingHelp": "Sävykartoitus voi muuttaa videon dynaamisen alueen HDR:stä SDR:ksi säilyttäen samalla kuvan yksityiskohdat ja värit, jotka ovat kohtauksen alkuperäisen ilmeen kannalta erittäin tärkeitä. Toimii tällä hetkellä vain 10bit HDR10, HLG ja Dovi -videoiden kanssa ja edellyttää soveltuvaa OpenCL- tai CUDA-suoritusalustaa.",
|
"AllowTonemappingHelp": "Sävykartoitus voi muuttaa videon dynaamisen alueen HDR:stä SDR:ksi säilyttäen samalla kuvan yksityiskohdat ja värit, jotka ovat kohtauksen alkuperäisen ilmeen kannalta erittäin tärkeitä. Toimii tällä hetkellä vain 10-bit HDR10-, HLG- ja DoVi-videoiden kanssa ja edellyttää soveltuvaa OpenCL- tai CUDA-suoritusalustaa.",
|
||||||
"LabelffmpegPathHelp": "FFmpeg-sovellustiedoston tai -kansion tiedostosijainti.",
|
"LabelffmpegPathHelp": "FFmpeg-sovellustiedoston tai -kansion tiedostosijainti.",
|
||||||
"LabelKodiMetadataEnablePathSubstitutionHelp": "Mahdollistaa kuvien tiedostosijaintien korvauksen palvelimen korvausasetuksien perusteella.",
|
"LabelKodiMetadataEnablePathSubstitutionHelp": "Mahdollistaa kuvien tiedostosijaintien korvauksen palvelimen korvausasetuksien perusteella.",
|
||||||
"ThumbCard": "Pienoiskortti",
|
"ThumbCard": "Pienoiskortti",
|
||||||
|
@ -1761,5 +1761,6 @@
|
||||||
"LabelSegmentKeepSeconds": "Osioiden säilytysaika",
|
"LabelSegmentKeepSeconds": "Osioiden säilytysaika",
|
||||||
"LabelSegmentKeepSecondsHelp": "Aika sekunteina, jonka osiot säilytetään ennen päällekirjoitusta. Oltava \"Rahoita kun on kulunut\" -aikaa suurempi. Toimii vain osioiden poiston ollessa käytössä.",
|
"LabelSegmentKeepSecondsHelp": "Aika sekunteina, jonka osiot säilytetään ennen päällekirjoitusta. Oltava \"Rahoita kun on kulunut\" -aikaa suurempi. Toimii vain osioiden poiston ollessa käytössä.",
|
||||||
"LabelBackdropScreensaverInterval": "Taustanäytönsäästäjän ajoitus",
|
"LabelBackdropScreensaverInterval": "Taustanäytönsäästäjän ajoitus",
|
||||||
"LabelBackdropScreensaverIntervalHelp": "Aika sekuntteina, jonka kuluttua kuva vaihtuu taustanäytönsäästäjää käytettäessä."
|
"LabelBackdropScreensaverIntervalHelp": "Aika sekuntteina, jonka kuluttua kuva vaihtuu taustanäytönsäästäjää käytettäessä.",
|
||||||
|
"AllowAv1Encoding": "Salli enkoodaus AV1-muodossa"
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,7 +169,7 @@
|
||||||
"EnableThemeSongsHelp": "Speel titelmuziek af tijdens het bladeren door de bibliotheek.",
|
"EnableThemeSongsHelp": "Speel titelmuziek af tijdens het bladeren door de bibliotheek.",
|
||||||
"EnableThemeVideosHelp": "Speel titelfilms af op de achtergrond tijdens het bladeren door de bibliotheek.",
|
"EnableThemeVideosHelp": "Speel titelfilms af op de achtergrond tijdens het bladeren door de bibliotheek.",
|
||||||
"Ended": "Gestopt",
|
"Ended": "Gestopt",
|
||||||
"EndsAtValue": "Eindigt om {0}",
|
"EndsAtValue": "Afgelopen om {0}",
|
||||||
"Episodes": "Afleveringen",
|
"Episodes": "Afleveringen",
|
||||||
"ErrorAddingListingsToSchedulesDirect": "Er ging iets mis bij het toevoegen van de lineup aan uw Schedules Direct account. Schedules Direct staat maar een beperkt aantal lineups per account toe. Het kan nodig zijn dat u zich aan moet melden op de Schedules Direct-website en andere lineups moet verwijderen voordat u verder kunt.",
|
"ErrorAddingListingsToSchedulesDirect": "Er ging iets mis bij het toevoegen van de lineup aan uw Schedules Direct account. Schedules Direct staat maar een beperkt aantal lineups per account toe. Het kan nodig zijn dat u zich aan moet melden op de Schedules Direct-website en andere lineups moet verwijderen voordat u verder kunt.",
|
||||||
"ErrorAddingMediaPathToVirtualFolder": "Er ging iets mis bij het toevoegen van het mediapad. Controleer of het pad klopt en of Jellyfin toegang heeft tot de locatie.",
|
"ErrorAddingMediaPathToVirtualFolder": "Er ging iets mis bij het toevoegen van het mediapad. Controleer of het pad klopt en of Jellyfin toegang heeft tot de locatie.",
|
||||||
|
@ -1762,5 +1762,6 @@
|
||||||
"LabelThrottleDelaySecondsHelp": "Tijd in seconden waarna de transcoder wordt afgeknepen. Deze tijd moet voldoende lang zijn zodat de cliënt een gezonde buffer in stand kan houden. Werkt alleen als afknijpen is ingeschakeld.",
|
"LabelThrottleDelaySecondsHelp": "Tijd in seconden waarna de transcoder wordt afgeknepen. Deze tijd moet voldoende lang zijn zodat de cliënt een gezonde buffer in stand kan houden. Werkt alleen als afknijpen is ingeschakeld.",
|
||||||
"LabelSegmentKeepSeconds": "Bewaartijd segmenten",
|
"LabelSegmentKeepSeconds": "Bewaartijd segmenten",
|
||||||
"LabelBackdropScreensaverIntervalHelp": "Het aantal seconden dat een achtergrondafbeelding wordt getoond als onderdeel van de schermbeveiliging.",
|
"LabelBackdropScreensaverIntervalHelp": "Het aantal seconden dat een achtergrondafbeelding wordt getoond als onderdeel van de schermbeveiliging.",
|
||||||
"LabelBackdropScreensaverInterval": "Interval schermbeveiliging"
|
"LabelBackdropScreensaverInterval": "Interval schermbeveiliging",
|
||||||
|
"AllowAv1Encoding": "Coderen in AV1-formaat toestaan"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1763,5 +1763,6 @@
|
||||||
"LabelThrottleDelaySeconds": "Ograniczaj po",
|
"LabelThrottleDelaySeconds": "Ograniczaj po",
|
||||||
"LabelThrottleDelaySecondsHelp": "Czas w sekundach, po którym transkoder zostanie ograniczony. Musi być wystarczająco duży, aby klient mógł utrzymać właściwy bufor. Działa tylko wtedy, gdy włączone jest ograniczanie.",
|
"LabelThrottleDelaySecondsHelp": "Czas w sekundach, po którym transkoder zostanie ograniczony. Musi być wystarczająco duży, aby klient mógł utrzymać właściwy bufor. Działa tylko wtedy, gdy włączone jest ograniczanie.",
|
||||||
"LabelBackdropScreensaverIntervalHelp": "Czas w sekundach między różnymi fototapetami podczas korzystania z wygaszacza ekranu z fototapetami.",
|
"LabelBackdropScreensaverIntervalHelp": "Czas w sekundach między różnymi fototapetami podczas korzystania z wygaszacza ekranu z fototapetami.",
|
||||||
"LabelBackdropScreensaverInterval": "Interwał fototapet wygaszacza ekranu"
|
"LabelBackdropScreensaverInterval": "Interwał fototapet wygaszacza ekranu",
|
||||||
|
"AllowAv1Encoding": "Zezwalaj na kodowanie w formacie AV1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1760,5 +1760,6 @@
|
||||||
"LabelSegmentKeepSecondsHelp": "Час у секундах, протягом якого сегменти мають зберігатися перед перезаписом. Має бути більшим за \"Обмежити після\". Працює тільки якщо увімкнено видалення сегментів.",
|
"LabelSegmentKeepSecondsHelp": "Час у секундах, протягом якого сегменти мають зберігатися перед перезаписом. Має бути більшим за \"Обмежити після\". Працює тільки якщо увімкнено видалення сегментів.",
|
||||||
"LabelSegmentKeepSeconds": "Час збереження сегментів",
|
"LabelSegmentKeepSeconds": "Час збереження сегментів",
|
||||||
"LabelBackdropScreensaverIntervalHelp": "Час у секундах між різними фонами при використанні фонової заставки.",
|
"LabelBackdropScreensaverIntervalHelp": "Час у секундах між різними фонами при використанні фонової заставки.",
|
||||||
"LabelBackdropScreensaverInterval": "Інтервал між фоновими заставками"
|
"LabelBackdropScreensaverInterval": "Інтервал між фоновими заставками",
|
||||||
|
"AllowAv1Encoding": "Дозволити кодування у форматі AV1"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue