mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-16 02:18:16 -07:00
Merge branch 'master' into unavailable-yt-video-trailer-bug-fix
This commit is contained in:
commit
3124a4af9b
@ -19,15 +19,15 @@ jobs:
|
||||
versionSpec: '12.x'
|
||||
|
||||
- task: Cache@2
|
||||
displayName: 'Check Cache'
|
||||
displayName: 'Cache node_modules'
|
||||
inputs:
|
||||
key: 'yarn | yarn.lock'
|
||||
path: 'node_modules'
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
|
||||
- script: 'yarn install --frozen-lockfile'
|
||||
displayName: 'Install Dependencies'
|
||||
condition: ne(variables.CACHE_RESTORED, 'true')
|
||||
env:
|
||||
SKIP_PREPARE: 'true'
|
||||
|
||||
- script: 'yarn build:development'
|
||||
displayName: 'Build Development'
|
||||
|
@ -12,15 +12,15 @@ jobs:
|
||||
versionSpec: '12.x'
|
||||
|
||||
- task: Cache@2
|
||||
displayName: 'Check Cache'
|
||||
displayName: 'Cache node_modules'
|
||||
inputs:
|
||||
key: 'yarn | yarn.lock'
|
||||
path: 'node_modules'
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
|
||||
- script: 'yarn install --frozen-lockfile'
|
||||
displayName: 'Install Dependencies'
|
||||
condition: ne(variables.CACHE_RESTORED, 'true')
|
||||
env:
|
||||
SKIP_PREPARE: 'true'
|
||||
|
||||
- script: 'yarn run lint --quiet'
|
||||
displayName: 'Run ESLint'
|
||||
|
@ -128,7 +128,7 @@
|
||||
"scripts": {
|
||||
"start": "yarn serve",
|
||||
"serve": "webpack serve --config webpack.dev.js",
|
||||
"prepare": "webpack --config webpack.prod.js",
|
||||
"prepare": "./scripts/prepare.sh",
|
||||
"build:development": "webpack --config webpack.dev.js",
|
||||
"build:production": "webpack --config webpack.prod.js",
|
||||
"lint": "eslint \".\"",
|
||||
|
5
scripts/prepare.sh
Executable file
5
scripts/prepare.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ -z "${SKIP_PREPARE}" ]; then
|
||||
webpack --config webpack.prod.js
|
||||
fi
|
@ -34,7 +34,6 @@ function getDeviceProfile(item, options = {}) {
|
||||
profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder);
|
||||
} else {
|
||||
const builderOpts = getBaseProfileOptions(item);
|
||||
builderOpts.enableSsaRender = (item && !options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats');
|
||||
profile = profileBuilder(builderOpts);
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,8 @@ import toast from '../toast/toast';
|
||||
|
||||
showHideQualityFields(context, user, apiClient);
|
||||
|
||||
context.querySelector('#selectAllowedAudioChannels').value = userSettings.allowedAudioChannels();
|
||||
|
||||
apiClient.getCultures().then(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('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
|
||||
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
|
||||
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
|
||||
context.querySelector('.chkExternalVideoPlayer').checked = appSettings.enableSystemExternalPlayers();
|
||||
@ -224,10 +227,11 @@ import toast from '../toast/toast';
|
||||
setMaxBitrateFromField(context.querySelector('.selectVideoInternetQuality'), false, 'Video');
|
||||
setMaxBitrateFromField(context.querySelector('.selectMusicInternetQuality'), false, 'Audio');
|
||||
|
||||
userSettingsInstance.allowedAudioChannels(context.querySelector('#selectAllowedAudioChannels').value);
|
||||
user.Configuration.AudioLanguagePreference = context.querySelector('#selectAudioLanguage').value;
|
||||
user.Configuration.PlayDefaultAudioTrack = context.querySelector('.chkPlayDefaultAudioTrack').checked;
|
||||
user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked;
|
||||
|
||||
userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked);
|
||||
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
|
||||
|
||||
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
|
||||
|
@ -4,6 +4,16 @@
|
||||
${HeaderAudioSettings}
|
||||
</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">
|
||||
<select is="emby-select" id="selectAudioLanguage" label="${LabelAudioLanguagePreference}"></select>
|
||||
</div>
|
||||
@ -49,6 +59,14 @@
|
||||
${TabAdvanced}
|
||||
</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">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkEnableCinemaMode" />
|
||||
|
@ -372,15 +372,26 @@ import ServerConnections from '../ServerConnections';
|
||||
const session = responses[1];
|
||||
|
||||
const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);
|
||||
let localizedDisplayMethod = displayPlayMethod;
|
||||
|
||||
if (displayPlayMethod === 'DirectPlay') {
|
||||
localizedDisplayMethod = globalize.translate('DirectPlaying');
|
||||
} else if (displayPlayMethod === 'Remux') {
|
||||
localizedDisplayMethod = globalize.translate('Remuxing');
|
||||
} else if (displayPlayMethod === 'DirectStream') {
|
||||
localizedDisplayMethod = globalize.translate('DirectStreaming');
|
||||
} else if (displayPlayMethod === 'Transcode') {
|
||||
localizedDisplayMethod = globalize.translate('Transcoding');
|
||||
}
|
||||
|
||||
const baseCategory = {
|
||||
stats: [],
|
||||
name: 'Playback Info'
|
||||
name: globalize.translate('LabelPlaybackInfo')
|
||||
};
|
||||
|
||||
baseCategory.stats.unshift({
|
||||
label: globalize.translate('LabelPlayMethod'),
|
||||
value: displayPlayMethod
|
||||
value: localizedDisplayMethod
|
||||
});
|
||||
|
||||
baseCategory.stats.unshift({
|
||||
@ -395,30 +406,37 @@ import ServerConnections from '../ServerConnections';
|
||||
for (let i = 0, length = playerStats.length; i < length; i++) {
|
||||
const category = playerStats[i];
|
||||
if (category.type === 'audio') {
|
||||
category.name = 'Audio Info';
|
||||
category.name = globalize.translate('LabelAudioInfo');
|
||||
} else if (category.type === 'video') {
|
||||
category.name = 'Video Info';
|
||||
category.name = globalize.translate('LabelVideoInfo');
|
||||
}
|
||||
categories.push(category);
|
||||
}
|
||||
|
||||
let localizedTranscodingInfo = globalize.translate('LabelTranscodingInfo');
|
||||
if (displayPlayMethod === 'Remux') {
|
||||
localizedTranscodingInfo = globalize.translate('LabelRemuxingInfo');
|
||||
} else if (displayPlayMethod === 'DirectStream') {
|
||||
localizedTranscodingInfo = globalize.translate('LabelDirectStreamingInfo');
|
||||
}
|
||||
|
||||
if (session.TranscodingInfo) {
|
||||
categories.push({
|
||||
stats: getTranscodingStats(session, player, displayPlayMethod),
|
||||
name: displayPlayMethod === 'Transcode' ? 'Transcoding Info' : 'Direct Stream Info'
|
||||
name: localizedTranscodingInfo
|
||||
});
|
||||
}
|
||||
|
||||
categories.push({
|
||||
stats: getMediaSourceStats(session, player),
|
||||
name: 'Original Media Info'
|
||||
name: globalize.translate('LabelOriginalMediaInfo')
|
||||
});
|
||||
|
||||
const apiClient = ServerConnections.getApiClient(playbackManager.currentItem(player).ServerId);
|
||||
if (syncPlayManager.isSyncPlayEnabled() && apiClient.isMinServerVersion('10.6.0')) {
|
||||
categories.push({
|
||||
stats: getSyncPlayStats(),
|
||||
name: 'SyncPlay Info'
|
||||
name: globalize.translate('LabelSyncPlayInfo')
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,12 @@ import confirm from '../../components/confirm/confirm';
|
||||
const text = [];
|
||||
const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);
|
||||
|
||||
if (displayPlayMethod === 'DirectStream') {
|
||||
if (displayPlayMethod === 'Remux') {
|
||||
title = globalize.translate('Remuxing');
|
||||
text.push(globalize.translate('RemuxHelp1'));
|
||||
text.push('<br/>');
|
||||
text.push(globalize.translate('RemuxHelp2'));
|
||||
} else if (displayPlayMethod === 'DirectStream') {
|
||||
title = globalize.translate('DirectStreaming');
|
||||
text.push(globalize.translate('DirectStreamHelp1'));
|
||||
text.push('<br/>');
|
||||
@ -397,7 +402,11 @@ import confirm from '../../components/confirm/confirm';
|
||||
let showTranscodingInfo = false;
|
||||
const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session);
|
||||
|
||||
if (displayPlayMethod === 'DirectStream') {
|
||||
if (displayPlayMethod === 'DirectPlay') {
|
||||
html += globalize.translate('DirectPlaying');
|
||||
} else if (displayPlayMethod === 'Remux') {
|
||||
html += globalize.translate('Remuxing');
|
||||
} else if (displayPlayMethod === 'DirectStream') {
|
||||
html += globalize.translate('DirectStreaming');
|
||||
} else if (displayPlayMethod === 'Transcode') {
|
||||
html += globalize.translate('Transcoding');
|
||||
@ -407,8 +416,6 @@ import confirm from '../../components/confirm/confirm';
|
||||
}
|
||||
|
||||
showTranscodingInfo = true;
|
||||
} else if (displayPlayMethod === 'DirectPlay') {
|
||||
html += globalize.translate('DirectPlaying');
|
||||
}
|
||||
|
||||
if (showTranscodingInfo) {
|
||||
@ -424,20 +431,20 @@ import confirm from '../../components/confirm/confirm';
|
||||
}
|
||||
|
||||
if (session.TranscodingInfo.Container) {
|
||||
line.push(session.TranscodingInfo.Container);
|
||||
line.push(session.TranscodingInfo.Container.toUpperCase());
|
||||
}
|
||||
|
||||
if (session.TranscodingInfo.VideoCodec) {
|
||||
line.push(session.TranscodingInfo.VideoCodec);
|
||||
line.push(session.TranscodingInfo.VideoCodec.toUpperCase());
|
||||
}
|
||||
|
||||
if (session.TranscodingInfo.AudioCodec && session.TranscodingInfo.AudioCodec != session.TranscodingInfo.Container) {
|
||||
line.push(session.TranscodingInfo.AudioCodec);
|
||||
line.push(session.TranscodingInfo.AudioCodec.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
if (line.length) {
|
||||
html += ' - ' + line.join(' ');
|
||||
html += '<br/><br/>' + line.join(' ');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,6 +94,15 @@
|
||||
</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="checkboxListContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
@ -218,11 +227,13 @@
|
||||
</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>
|
||||
|
@ -15,6 +15,7 @@ import alert from '../../components/alert';
|
||||
page.querySelector('#chkDecodingColorDepth10Hevc').checked = config.EnableDecodingColorDepth10Hevc;
|
||||
page.querySelector('#chkDecodingColorDepth10Vp9').checked = config.EnableDecodingColorDepth10Vp9;
|
||||
page.querySelector('#chkHardwareEncoding').checked = config.EnableHardwareEncoding;
|
||||
page.querySelector('#chkAllowHevcEncoding').checked = config.AllowHevcEncoding;
|
||||
$('#selectVideoDecoder', page).val(config.HardwareAccelerationType);
|
||||
$('#selectThreadCount', page).val(config.EncodingThreadCount);
|
||||
$('#txtDownMixAudioBoost', page).val(config.DownMixAudioBoost);
|
||||
@ -34,6 +35,7 @@ import alert from '../../components/alert';
|
||||
page.querySelector('#txtTonemappingParam').value = config.TonemappingParam || '';
|
||||
page.querySelector('#selectEncoderPreset').value = config.EncoderPreset || '';
|
||||
page.querySelector('#txtH264Crf').value = config.H264Crf || '';
|
||||
page.querySelector('#txtH265Crf').value = config.H265Crf || '';
|
||||
page.querySelector('#selectDeinterlaceMethod').value = config.DeinterlaceMethod || '';
|
||||
page.querySelector('#chkDoubleRateDeinterlacing').checked = config.DeinterlaceDoubleRate;
|
||||
page.querySelector('#chkEnableSubtitleExtraction').checked = config.EnableSubtitleExtraction || false;
|
||||
@ -87,6 +89,7 @@ import alert from '../../components/alert';
|
||||
config.TonemappingParam = form.querySelector('#txtTonemappingParam').value || '0';
|
||||
config.EncoderPreset = form.querySelector('#selectEncoderPreset').value;
|
||||
config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0');
|
||||
config.H265Crf = parseInt(form.querySelector('#txtH265Crf').value || '0');
|
||||
config.DeinterlaceMethod = form.querySelector('#selectDeinterlaceMethod').value;
|
||||
config.DeinterlaceDoubleRate = form.querySelector('#chkDoubleRateDeinterlacing').checked;
|
||||
config.EnableSubtitleExtraction = form.querySelector('#chkEnableSubtitleExtraction').checked;
|
||||
@ -99,6 +102,7 @@ import alert from '../../components/alert';
|
||||
config.EnableDecodingColorDepth10Hevc = form.querySelector('#chkDecodingColorDepth10Hevc').checked;
|
||||
config.EnableDecodingColorDepth10Vp9 = form.querySelector('#chkDecodingColorDepth10Vp9').checked;
|
||||
config.EnableHardwareEncoding = form.querySelector('#chkHardwareEncoding').checked;
|
||||
config.AllowHevcEncoding = form.querySelector('#chkAllowHevcEncoding').checked;
|
||||
ApiClient.updateNamedConfiguration('encoding', config).then(function () {
|
||||
updateEncoder(form);
|
||||
}, function () {
|
||||
|
@ -13,11 +13,11 @@
|
||||
<div class="osdTextContainer osdSecondaryMediaInfo"></div>
|
||||
|
||||
<div class="flex flex-direction-row align-items-center">
|
||||
<div class="osdTextContainer startTimeText" style="margin: 0 .25em 0 0;"></div>
|
||||
<div class="osdTextContainer startTimeText osdPositionText" style="margin: 0 .25em 0 0;"></div>
|
||||
<div class="sliderContainer flex-grow" style="margin: .5em 0 .25em;">
|
||||
<input type="range" step=".01" min="0" max="100" value="0" is="emby-slider" class="osdPositionSlider" data-slider-keep-progress="true" />
|
||||
</div>
|
||||
<div class="osdTextContainer endTimeText" style="margin: 0 0 0 .25em;"></div>
|
||||
<div class="osdTextContainer endTimeText osdDurationText" style="margin: 0 0 0 .25em;"></div>
|
||||
</div>
|
||||
|
||||
<div class="buttons focuscontainer-x">
|
||||
@ -46,8 +46,6 @@
|
||||
</button>
|
||||
|
||||
<div class="osdTimeText">
|
||||
<span class="osdPositionText"></span>
|
||||
<span class="osdDurationText"></span>
|
||||
<span class="endsAtText"></span>
|
||||
</div>
|
||||
|
||||
|
@ -764,7 +764,7 @@ import { appRouter } from '../../../components/appRouter';
|
||||
}
|
||||
|
||||
if (runtimeTicks && positionTicks != null && currentRuntimeTicks && !enableProgressByTimeOfDay && currentItem.RunTimeTicks && currentItem.Type !== 'Recording') {
|
||||
endsAtText.innerHTML = ' - ' + mediaInfo.getEndsAtFromPosition(runtimeTicks, positionTicks, true);
|
||||
endsAtText.innerHTML = ' ' + mediaInfo.getEndsAtFromPosition(runtimeTicks, positionTicks, true);
|
||||
} else {
|
||||
endsAtText.innerHTML = '';
|
||||
}
|
||||
@ -774,8 +774,20 @@ import { appRouter } from '../../../components/appRouter';
|
||||
nowPlayingPositionSlider.setBufferedRanges(bufferedRanges, runtimeTicks, positionTicks);
|
||||
}
|
||||
|
||||
updateTimeText(nowPlayingPositionText, positionTicks);
|
||||
updateTimeText(nowPlayingDurationText, runtimeTicks, true);
|
||||
if (positionTicks >= 0) {
|
||||
updateTimeText(nowPlayingPositionText, positionTicks);
|
||||
nowPlayingPositionText.classList.remove('hide');
|
||||
} else {
|
||||
nowPlayingPositionText.classList.add('hide');
|
||||
}
|
||||
|
||||
const leftTicks = runtimeTicks - positionTicks;
|
||||
if (leftTicks >= 0) {
|
||||
updateTimeText(nowPlayingDurationText, leftTicks);
|
||||
nowPlayingDurationText.classList.remove('hide');
|
||||
} else {
|
||||
nowPlayingPositionText.classList.add('hide');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@
|
||||
}
|
||||
|
||||
.mdl-slider-background-flex {
|
||||
background: #333;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
height: 0.2em;
|
||||
margin-top: -0.1em;
|
||||
width: 100%;
|
||||
|
@ -442,7 +442,7 @@ import '../emby-input/emby-input';
|
||||
position = (position / runtime) * 100;
|
||||
}
|
||||
|
||||
for (const range in ranges) {
|
||||
for (const range of ranges) {
|
||||
if (position != null) {
|
||||
if (position >= range.end) {
|
||||
continue;
|
||||
|
@ -1,230 +0,0 @@
|
||||
import { Events } from 'jellyfin-apiclient';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
|
||||
// LinkParser
|
||||
//
|
||||
// https://github.com/ravisorg/LinkParser
|
||||
//
|
||||
// Locate and extract almost any URL within a string. Handles protocol-less domains, IPv4 and
|
||||
// IPv6, unrecognised TLDs, and more.
|
||||
//
|
||||
// This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
// http://creativecommons.org/licenses/by-sa/4.0/
|
||||
(function () {
|
||||
// Original URL regex from the Android android.text.util.Linkify function, found here:
|
||||
// http://stackoverflow.com/a/19696443
|
||||
//
|
||||
// However there were problems with it, most probably related to the fact it was
|
||||
// written in 2007, and it's been highly modified.
|
||||
//
|
||||
// 1) I didn't like the fact that it was tied to specific TLDs, since new ones
|
||||
// are being added all the time it wouldn't be reasonable to expect developer to
|
||||
// be continually updating their regular expressions.
|
||||
//
|
||||
// 2) It didn't allow unicode characters in the domains which are now allowed in
|
||||
// many languages, (including some IDN TLDs). Again these are constantly being
|
||||
// added to and it doesn't seem reasonable to hard-code them. Note this ended up
|
||||
// not being possible in standard JS due to the way it handles multibyte strings.
|
||||
// It is possible using XRegExp, however a big performance hit results. Disabled
|
||||
// for now.
|
||||
//
|
||||
// 3) It didn't allow for IPv6 hostnames
|
||||
// IPv6 regex from http://stackoverflow.com/a/17871737
|
||||
//
|
||||
// 4) It was very poorly commented
|
||||
//
|
||||
// 5) It wasn't as smart as it could have been about what should be part of a
|
||||
// URL and what should be part of human language.
|
||||
|
||||
const protocols = '(?:(?:http|https|rtsp|ftp):\\/\\/)';
|
||||
const credentials = "(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}" // username (1-64 normal or url escaped characters)
|
||||
+ "(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?" // followed by optional password (: + 1-25 normal or url escaped characters)
|
||||
+ '\\@)';
|
||||
|
||||
// IPv6 Regex http://forums.intermapper.com/viewtopic.php?t=452
|
||||
// by Dartware, LLC is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License
|
||||
// http://intermapper.com/
|
||||
const ipv6 = '('
|
||||
+ '(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
|
||||
+ '|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
|
||||
+ ')(%.+)?';
|
||||
|
||||
const ipv4 = '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.'
|
||||
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
|
||||
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
|
||||
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])';
|
||||
|
||||
// This would have been a lot cleaner if JS RegExp supported conditionals...
|
||||
const linkRegExpString =
|
||||
|
||||
// begin match for protocol / username / password / host
|
||||
'(?:'
|
||||
|
||||
// ============================
|
||||
// If we have a recognized protocol at the beginning of the URL, we're
|
||||
// more relaxed about what we accept, because we assume the user wants
|
||||
// this to be a URL, and we're not accidentally matching human language
|
||||
+ protocols + '?'
|
||||
|
||||
// optional username:password@
|
||||
+ credentials + '?'
|
||||
|
||||
// IP address (both v4 and v6)
|
||||
+ '(?:'
|
||||
|
||||
// IPv6
|
||||
+ ipv6
|
||||
|
||||
// IPv4
|
||||
+ '|' + ipv4
|
||||
|
||||
+ ')'
|
||||
|
||||
// end match for protocol / username / password / host
|
||||
+ ')'
|
||||
|
||||
// optional port number
|
||||
+ '(?:\\:\\d{1,5})?'
|
||||
|
||||
// plus optional path and query params (no unicode allowed here?)
|
||||
+ '(?:'
|
||||
+ '\\/(?:'
|
||||
// some characters we'll accept because it's unlikely human language
|
||||
// would use them after a URL unless they were part of the url
|
||||
+ '(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])'
|
||||
+ '|(?:\\%[a-f0-9]{2})'
|
||||
// some characters are much more likely to be used AFTER a url and
|
||||
// were not intended to be included in the url itself. Mostly end
|
||||
// of sentence type things. It's also likely that the URL would
|
||||
// still work if any of these characters were missing from the end
|
||||
// because we parsed it incorrectly. For these characters to be accepted
|
||||
// they must be followed by another character that we're reasonably
|
||||
// sure is part of the url
|
||||
+ "|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})))"
|
||||
+ ')*'
|
||||
+ '|\\b|\$'
|
||||
+ ')';
|
||||
|
||||
// regex = XRegExp(regex,'gi');
|
||||
const linkRegExp = RegExp(linkRegExpString, 'gi');
|
||||
|
||||
const protocolRegExp = RegExp('^' + protocols, 'i');
|
||||
|
||||
// if url doesn't begin with a known protocol, add http by default
|
||||
function ensureProtocol(url) {
|
||||
if (!url.match(protocolRegExp)) {
|
||||
url = 'http://' + url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
// look for links in the text
|
||||
const LinkParser = {
|
||||
parse: function (text) {
|
||||
const links = [];
|
||||
let match;
|
||||
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while (match = linkRegExp.exec(text)) {
|
||||
console.debug(match);
|
||||
const txt = match[0];
|
||||
const pos = match.index;
|
||||
const len = txt.length;
|
||||
const url = ensureProtocol(text);
|
||||
links.push({ 'pos': pos, 'text': txt, 'len': len, 'url': url });
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
window.LinkParser = LinkParser;
|
||||
})();
|
||||
|
||||
let cache = {};
|
||||
|
||||
// TODO: Replace with isIP (https://www.npmjs.com/package/is-ip)
|
||||
function isValidIpAddress(address) {
|
||||
const links = LinkParser.parse(address);
|
||||
|
||||
return links.length == 1;
|
||||
}
|
||||
|
||||
// TODO: Add IPv6 support. Potentially replace with isLocalhost (https://www.npmjs.com/package/is-localhost-ip)
|
||||
function isLocalIpAddress(address) {
|
||||
address = address.toLowerCase();
|
||||
|
||||
if (address.includes('127.0.0.1')) {
|
||||
return true;
|
||||
}
|
||||
if (address.includes('localhost')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getServerAddress(apiClient) {
|
||||
const serverAddress = apiClient.serverAddress();
|
||||
|
||||
if (isValidIpAddress(serverAddress) && !isLocalIpAddress(serverAddress)) {
|
||||
return Promise.resolve(serverAddress);
|
||||
}
|
||||
|
||||
const cachedValue = getCachedValue(serverAddress);
|
||||
if (cachedValue) {
|
||||
return Promise.resolve(cachedValue);
|
||||
}
|
||||
|
||||
return apiClient.getEndpointInfo().then(function (endpoint) {
|
||||
if (endpoint.IsInNetwork) {
|
||||
return apiClient.getPublicSystemInfo().then(function (info) {
|
||||
let localAddress = info.LocalAddress;
|
||||
if (!localAddress) {
|
||||
console.debug('No valid local address returned, defaulting to external one');
|
||||
localAddress = serverAddress;
|
||||
}
|
||||
addToCache(serverAddress, localAddress);
|
||||
return localAddress;
|
||||
});
|
||||
} else {
|
||||
addToCache(serverAddress, serverAddress);
|
||||
return serverAddress;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function clearCache() {
|
||||
cache = {};
|
||||
}
|
||||
|
||||
function addToCache(key, value) {
|
||||
cache[key] = {
|
||||
value: value,
|
||||
time: new Date().getTime()
|
||||
};
|
||||
}
|
||||
|
||||
function getCachedValue(key) {
|
||||
const obj = cache[key];
|
||||
|
||||
if (obj && (new Date().getTime() - obj.time) < 180000) {
|
||||
return obj.value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Events.on(ServerConnections, 'localusersignedin', clearCache);
|
||||
Events.on(ServerConnections, 'localusersignedout', clearCache);
|
||||
|
||||
export default {
|
||||
getServerAddress: getServerAddress
|
||||
};
|
@ -353,14 +353,7 @@ class CastPlayer {
|
||||
message.subtitleBurnIn = appSettings.get('subtitleburnin') || '';
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
import('./chromecastHelper').then(({ default: chromecastHelper }) => {
|
||||
chromecastHelper.getServerAddress(apiClient).then(function (serverAddress) {
|
||||
message.serverAddress = serverAddress;
|
||||
player.sendMessageInternal(message).then(resolve, reject);
|
||||
}, reject);
|
||||
});
|
||||
});
|
||||
return player.sendMessageInternal(message);
|
||||
}
|
||||
|
||||
sendMessageInternal(message) {
|
||||
|
@ -1539,7 +1539,7 @@ function tryRemoveElement(elem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static isAirPlayEnabled() {
|
||||
isAirPlayEnabled() {
|
||||
if (document.AirPlayEnabled) {
|
||||
return !!document.AirplayElement;
|
||||
}
|
||||
@ -1741,13 +1741,13 @@ function tryRemoveElement(elem) {
|
||||
|
||||
getSupportedAspectRatios() {
|
||||
return [{
|
||||
name: 'Auto',
|
||||
name: globalize.translate('Auto'),
|
||||
id: 'auto'
|
||||
}, {
|
||||
name: 'Cover',
|
||||
name: globalize.translate('AspectRatioCover'),
|
||||
id: 'cover'
|
||||
}, {
|
||||
name: 'Fill',
|
||||
name: globalize.translate('AspectRatioFill'),
|
||||
id: 'fill'
|
||||
}];
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import { appRouter } from '../../components/appRouter';
|
||||
import './style.css';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import { Events } from 'jellyfin-apiclient';
|
||||
import { GlobalWorkerOptions, getDocument } from 'pdfjs-dist';
|
||||
|
||||
export class PdfPlayer {
|
||||
constructor() {
|
||||
@ -189,28 +190,26 @@ export class PdfPlayer {
|
||||
const apiClient = ServerConnections.getApiClient(serverId);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
import('pdfjs-dist').then(({default: pdfjs}) => {
|
||||
const downloadHref = apiClient.getItemDownloadUrl(item.Id);
|
||||
const downloadHref = apiClient.getItemDownloadUrl(item.Id);
|
||||
|
||||
this.bindEvents();
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = appRouter.baseUrl() + '/libraries/pdf.worker.js';
|
||||
this.bindEvents();
|
||||
GlobalWorkerOptions.workerSrc = appRouter.baseUrl() + '/libraries/pdf.worker.js';
|
||||
|
||||
const downloadTask = pdfjs.getDocument(downloadHref);
|
||||
downloadTask.promise.then(book => {
|
||||
if (this.cancellationToken) return;
|
||||
this.book = book;
|
||||
this.loaded = true;
|
||||
const downloadTask = getDocument(downloadHref);
|
||||
downloadTask.promise.then(book => {
|
||||
if (this.cancellationToken) return;
|
||||
this.book = book;
|
||||
this.loaded = true;
|
||||
|
||||
const percentageTicks = options.startPositionTicks / 10000;
|
||||
if (percentageTicks !== 0) {
|
||||
this.loadPage(percentageTicks);
|
||||
this.progress = percentageTicks;
|
||||
} else {
|
||||
this.loadPage(1);
|
||||
}
|
||||
const percentageTicks = options.startPositionTicks / 10000;
|
||||
if (percentageTicks !== 0) {
|
||||
this.loadPage(percentageTicks);
|
||||
this.progress = percentageTicks;
|
||||
} else {
|
||||
this.loadPage(1);
|
||||
}
|
||||
|
||||
return resolve();
|
||||
});
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import appSettings from './settings/appSettings';
|
||||
import * as userSettings from './settings/userSettings';
|
||||
import browser from './browser';
|
||||
/* 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/, ''));
|
||||
}
|
||||
|
||||
function canPlayH265(videoTestElement, options) {
|
||||
function canPlayHevc(videoTestElement, options) {
|
||||
if (browser.tizen || browser.xboxOne || browser.web0s || options.supportsHevc) {
|
||||
return true;
|
||||
}
|
||||
@ -14,6 +16,7 @@ import browser from './browser';
|
||||
return false;
|
||||
}
|
||||
|
||||
// hevc main level 4.0
|
||||
return !!videoTestElement.canPlayType &&
|
||||
(videoTestElement.canPlayType('video/mp4; codecs="hvc1.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
|
||||
if (browser.tizenVersion >= 4) {
|
||||
videoCodecs.push('h264');
|
||||
if (canPlayH265(videoTestElement, options)) {
|
||||
videoCodecs.push('h265');
|
||||
if (canPlayHevc(videoTestElement, options)) {
|
||||
videoCodecs.push('hevc');
|
||||
}
|
||||
}
|
||||
@ -246,8 +248,8 @@ import browser from './browser';
|
||||
case 'ts':
|
||||
supported = testCanPlayTs();
|
||||
videoCodecs.push('h264');
|
||||
if (canPlayH265(videoTestElement, options)) {
|
||||
videoCodecs.push('h265');
|
||||
// safari doesn't support hevc in TS-HLS
|
||||
if ((browser.tizen || browser.web0s) && canPlayHevc(videoTestElement, options)) {
|
||||
videoCodecs.push('hevc');
|
||||
}
|
||||
if (supportsVc1(videoTestElement)) {
|
||||
@ -295,7 +297,9 @@ import browser from './browser';
|
||||
export default function (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();
|
||||
|
||||
@ -311,12 +315,13 @@ import browser from './browser';
|
||||
|
||||
profile.MaxStreamingBitrate = bitrateSetting;
|
||||
profile.MaxStaticBitrate = 100000000;
|
||||
profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 192000);
|
||||
profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 384000);
|
||||
|
||||
profile.DirectPlayProfiles = [];
|
||||
|
||||
let videoAudioCodecs = [];
|
||||
let hlsVideoAudioCodecs = [];
|
||||
let hlsInTsVideoAudioCodecs = [];
|
||||
let hlsInFmp4VideoAudioCodecs = [];
|
||||
|
||||
const supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').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
|
||||
// Put ac3/eac3 first only when the audio channels > 2 and need transcoding
|
||||
if (canPlayAc3VideoAudioInHls && physicalAudioChannels > 2) {
|
||||
hlsVideoAudioCodecs.push('ac3');
|
||||
hlsInTsVideoAudioCodecs.push('ac3');
|
||||
hlsInFmp4VideoAudioCodecs.push('ac3');
|
||||
if (canPlayEac3VideoAudio) {
|
||||
hlsVideoAudioCodecs.push('eac3');
|
||||
hlsInTsVideoAudioCodecs.push('eac3');
|
||||
hlsInFmp4VideoAudioCodecs.push('eac3');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canPlayAacVideoAudio) {
|
||||
videoAudioCodecs.push('aac');
|
||||
hlsVideoAudioCodecs.push('aac');
|
||||
hlsInTsVideoAudioCodecs.push('aac');
|
||||
hlsInFmp4VideoAudioCodecs.push('aac');
|
||||
}
|
||||
|
||||
if (supportsMp3VideoAudio) {
|
||||
@ -368,16 +376,31 @@ import browser from './browser';
|
||||
|
||||
// PS4 fails to load HLS with mp3 audio
|
||||
if (!browser.ps4) {
|
||||
hlsVideoAudioCodecs.push('mp3');
|
||||
hlsInTsVideoAudioCodecs.push('mp3');
|
||||
}
|
||||
|
||||
hlsInFmp4VideoAudioCodecs.push('mp3');
|
||||
}
|
||||
|
||||
// For ac3/eac3 directstream
|
||||
if (canPlayAc3VideoAudio) {
|
||||
if (canPlayAc3VideoAudioInHls && hlsVideoAudioCodecs.indexOf('ac3') === -1) {
|
||||
hlsVideoAudioCodecs.push('ac3');
|
||||
if (canPlayEac3VideoAudio && hlsVideoAudioCodecs.indexOf('eac3') === -1) {
|
||||
hlsVideoAudioCodecs.push('eac3');
|
||||
if (canPlayAc3VideoAudioInHls) {
|
||||
if (hlsInTsVideoAudioCodecs.indexOf('ac3') === -1) {
|
||||
hlsInTsVideoAudioCodecs.push('ac3');
|
||||
}
|
||||
|
||||
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')) {
|
||||
videoAudioCodecs.push('opus');
|
||||
hlsVideoAudioCodecs.push('opus');
|
||||
hlsInTsVideoAudioCodecs.push('opus');
|
||||
webmAudioCodecs.push('opus');
|
||||
}
|
||||
|
||||
if (canPlayAudioFormat('flac')) {
|
||||
videoAudioCodecs.push('flac');
|
||||
hlsInFmp4VideoAudioCodecs.push('flac');
|
||||
}
|
||||
|
||||
if (canPlayAudioFormat('alac')) {
|
||||
videoAudioCodecs.push('alac');
|
||||
hlsInFmp4VideoAudioCodecs.push('alac');
|
||||
}
|
||||
|
||||
videoAudioCodecs = videoAudioCodecs.filter(function (c) {
|
||||
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;
|
||||
});
|
||||
|
||||
const mp4VideoCodecs = [];
|
||||
const webmVideoCodecs = [];
|
||||
const hlsVideoCodecs = [];
|
||||
const hlsInTsVideoCodecs = [];
|
||||
const hlsInFmp4VideoCodecs = [];
|
||||
|
||||
if ((browser.safari || browser.tizen || browser.web0s) && canPlayHevc(videoTestElement, options)) {
|
||||
hlsInFmp4VideoCodecs.push('hevc');
|
||||
}
|
||||
|
||||
if (canPlayH264(videoTestElement)) {
|
||||
mp4VideoCodecs.push('h264');
|
||||
hlsVideoCodecs.push('h264');
|
||||
hlsInTsVideoCodecs.push('h264');
|
||||
|
||||
if (browser.safari || browser.tizen || browser.web0s) {
|
||||
hlsInFmp4VideoCodecs.push('h264');
|
||||
}
|
||||
}
|
||||
|
||||
if (canPlayH265(videoTestElement, options)) {
|
||||
mp4VideoCodecs.push('h265');
|
||||
mp4VideoCodecs.push('hevc');
|
||||
|
||||
if (browser.tizen || browser.web0s) {
|
||||
hlsVideoCodecs.push('h265');
|
||||
hlsVideoCodecs.push('hevc');
|
||||
if (canPlayHevc(videoTestElement, options)) {
|
||||
// safari is lying on HDR and 60fps videos, use fMP4 instead
|
||||
if (!browser.safari) {
|
||||
mp4VideoCodecs.push('hevc');
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,18 +643,34 @@ import browser from './browser';
|
||||
});
|
||||
}
|
||||
|
||||
if (canPlayHls() && hlsVideoAudioCodecs.length && options.enableHls !== false) {
|
||||
profile.TranscodingProfiles.push({
|
||||
Container: 'ts',
|
||||
Type: 'Video',
|
||||
AudioCodec: hlsVideoAudioCodecs.join(','),
|
||||
VideoCodec: hlsVideoCodecs.join(','),
|
||||
Context: 'Streaming',
|
||||
Protocol: 'hls',
|
||||
MaxAudioChannels: physicalAudioChannels.toString(),
|
||||
MinSegments: browser.iOS || browser.osx ? '2' : '1',
|
||||
BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames
|
||||
});
|
||||
if (canPlayHls() && options.enableHls !== false) {
|
||||
if (hlsInFmp4VideoCodecs.length && hlsInFmp4VideoAudioCodecs.length && userSettings.preferFmp4HlsContainer() && (browser.safari || browser.tizen || browser.web0s)) {
|
||||
profile.TranscodingProfiles.push({
|
||||
Container: 'mp4',
|
||||
Type: 'Video',
|
||||
AudioCodec: hlsInFmp4VideoAudioCodecs.join(','),
|
||||
VideoCodec: hlsInFmp4VideoCodecs.join(','),
|
||||
Context: 'Streaming',
|
||||
Protocol: 'hls',
|
||||
MaxAudioChannels: physicalAudioChannels.toString(),
|
||||
MinSegments: browser.iOS || browser.osx ? '2' : '1',
|
||||
BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -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 = [
|
||||
{
|
||||
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) {
|
||||
h264CodecProfileConditions.push({
|
||||
Condition: 'NotEquals',
|
||||
@ -739,6 +845,13 @@ import browser from './browser';
|
||||
Value: 'true',
|
||||
IsRequired: false
|
||||
});
|
||||
|
||||
hevcCodecProfileConditions.push({
|
||||
Condition: 'NotEquals',
|
||||
Property: 'IsInterlaced',
|
||||
Value: 'true',
|
||||
IsRequired: false
|
||||
});
|
||||
}
|
||||
|
||||
if (maxVideoWidth) {
|
||||
@ -748,12 +861,21 @@ import browser from './browser';
|
||||
Value: maxVideoWidth.toString(),
|
||||
IsRequired: false
|
||||
});
|
||||
|
||||
hevcCodecProfileConditions.push({
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'Width',
|
||||
Value: maxVideoWidth.toString(),
|
||||
IsRequired: false
|
||||
});
|
||||
}
|
||||
|
||||
const globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString();
|
||||
|
||||
const h264MaxVideoBitrate = globalMaxVideoBitrate;
|
||||
|
||||
const hevcMaxVideoBitrate = globalMaxVideoBitrate;
|
||||
|
||||
if (h264MaxVideoBitrate) {
|
||||
h264CodecProfileConditions.push({
|
||||
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
|
||||
if (browser.iOS && browser.iOSVersion < 13) {
|
||||
const codecProfile = {
|
||||
@ -790,6 +921,12 @@ import browser from './browser';
|
||||
Conditions: h264CodecProfileConditions
|
||||
});
|
||||
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'Video',
|
||||
Codec: 'hevc',
|
||||
Conditions: hevcCodecProfileConditions
|
||||
});
|
||||
|
||||
const globalVideoConditions = [];
|
||||
|
||||
if (globalMaxVideoBitrate) {
|
||||
@ -825,7 +962,7 @@ import browser from './browser';
|
||||
Method: 'External'
|
||||
});
|
||||
}
|
||||
if (options.enableSsaRender) {
|
||||
if (options.enableSsaRender !== false && (!options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats')) {
|
||||
profile.SubtitleProfiles.push({
|
||||
Format: 'ass',
|
||||
Method: 'External'
|
||||
|
@ -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.
|
||||
* @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 get = currentSettings.get.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 enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
|
||||
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
||||
|
@ -162,8 +162,8 @@
|
||||
"EnableHardwareEncoding": "Ενεργοποίηση αποκωδικοποίησης υλικού",
|
||||
"EnableNextVideoInfoOverlay": "Εμφάνιση πληροφοριών επόμενου βίντεο κατά την αναπαραγωγή",
|
||||
"EnableNextVideoInfoOverlayHelp": "Στο τέλος ενός βίντεο, εμφανίστε πληροφορίες σχετικά με το επόμενο βίντεο που εμφανίζεται στην τρέχουσα λίστα αναπαραγωγής.",
|
||||
"EnableThemeSongsHelp": "Αν είναι ενεργοποιημένη, τα τραγούδια θεμάτων θα αναπαραχθούν στο παρασκήνιο κατά την περιήγηση στη βιβλιοθήκη.",
|
||||
"EnableThemeVideosHelp": "Αν είναι ενεργοποιημένη, τα βίντεο θεμάτων θα αναπαραχθούν στο παρασκήνιο κατά την περιήγηση στη βιβλιοθήκη.",
|
||||
"EnableThemeSongsHelp": "Αν είναι ενεργοποιημένη, τα τραγούδια θεμάτων θα αναπαράγονται στο παρασκήνιο κατά την περιήγηση στη βιβλιοθήκη.",
|
||||
"EnableThemeVideosHelp": "Αν είναι ενεργοποιημένη, τα βίντεο θεμάτων θα αναπαράγονται στο παρασκήνιο κατά την περιήγηση στη βιβλιοθήκη.",
|
||||
"Ended": "Τέλος",
|
||||
"EndsAtValue": "Τελειώνει σε {0}",
|
||||
"Episodes": "Επεισόδια",
|
||||
@ -1056,5 +1056,7 @@
|
||||
"EnableDecodingColorDepth10Vp9": "Ενεργοποίηση αποκωδικοποίησης υλικού 10-bit για το VP9",
|
||||
"EnableDecodingColorDepth10Hevc": "Ενεργοποίηση αποκωδικοποίησης υλικού 10-bit για HEVC",
|
||||
"EnableAutoCast": "Ορίσετε ως προεπιλογή",
|
||||
"ButtonUseQuickConnect": "Χρήση γρήγορης σύνδεσης"
|
||||
"ButtonUseQuickConnect": "Χρήση γρήγορης σύνδεσης",
|
||||
"EnableDetailsBanner": "Πανό Λεπτομερειών",
|
||||
"DeleteAll": "Διαγραφή Ολων"
|
||||
}
|
||||
|
@ -164,8 +164,8 @@
|
||||
"DetectingDevices": "Detecting devices",
|
||||
"DeviceAccessHelp": "This only applies to devices that can be uniquely identified and will not prevent browser access. Filtering user device access will prevent them from using new devices until they've been approved here.",
|
||||
"DirectPlaying": "Direct playing",
|
||||
"DirectStreamHelp1": "The media is compatible with the device regarding resolution and media type (H.264, AC3, etc), but in an incompatible file container (mkv, avi, wmv, etc). The video will be re-packaged on the fly before being sent to the device.",
|
||||
"DirectStreamHelp2": "Direct stream uses very little processing power with a minimal loss in video quality.",
|
||||
"DirectStreamHelp1": "The video stream is compatible with the device, but has an incompatible audio format (DTS, TRUEHD, etc) or number of audio channels. The video stream will be repackaged losslessly on the fly before being sent to the device. Only the audio stream will be transcoded.",
|
||||
"DirectStreamHelp2": "Power consumed by direct streaming usually depends on the audio profile. Only the video stream is lossless.",
|
||||
"DirectStreaming": "Direct streaming",
|
||||
"Director": "Director",
|
||||
"Directors": "Directors",
|
||||
@ -252,7 +252,7 @@
|
||||
"Guide": "Guide",
|
||||
"GuideProviderLogin": "Login",
|
||||
"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.",
|
||||
"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.",
|
||||
@ -589,7 +589,8 @@
|
||||
"LabelGroupMoviesIntoCollections": "Group movies into collections",
|
||||
"LabelGroupMoviesIntoCollectionsHelp": "When displaying movie lists, movies in a collection will be displayed as one grouped item.",
|
||||
"LabelH264Crf": "H264 encoding CRF:",
|
||||
"LabelEncoderPreset": "H264 and H265 encoding preset:",
|
||||
"LabelH265Crf": "H265 encoding CRF:",
|
||||
"LabelEncoderPreset": "Encoding preset:",
|
||||
"LabelHardwareAccelerationType": "Hardware acceleration:",
|
||||
"LabelHardwareAccelerationTypeHelp": "Hardware acceleration requires additional configuration.",
|
||||
"LabelHomeNetworkQuality": "Home network quality:",
|
||||
@ -1429,6 +1430,26 @@
|
||||
"Preview": "Preview",
|
||||
"LabelMaxMuxingQueueSize": "Max muxing queue size:",
|
||||
"LabelMaxMuxingQueueSizeHelp": "Maximum number of packets that can be buffered while waiting for all streams to initialize. Try to increase it if you still encounter \"Too many packets buffered for output stream\" error in ffmpeg logs. The recommended value is 2048.",
|
||||
"AspectRatioCover": "Cover",
|
||||
"AspectRatioFill": "Fill",
|
||||
"Remuxing": "Remuxing",
|
||||
"RemuxHelp1": "The media is in an incompatible file container (MKV, AVI, WMV, etc) but both the video stream and audio stream are compatible with the device. The media will be repackaged losslessly on the fly before being sent to the device.",
|
||||
"RemuxHelp2": "Remux uses very little processing power with a completely lossless media quality.",
|
||||
"LabelPlaybackInfo": "Playback Info",
|
||||
"LabelAudioInfo": "Audio Info",
|
||||
"LabelVideoInfo": "Video Info",
|
||||
"LabelTranscodingInfo": "Transcoding Info",
|
||||
"LabelDirectStreamingInfo": "Direct Streaming Info",
|
||||
"LabelRemuxingInfo": "Remuxing Info",
|
||||
"LabelOriginalMediaInfo": "Original Media 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",
|
||||
"YoutubeBadRequest": "Bad request.",
|
||||
"YoutubePlaybackError": "Requested video cannot be played.",
|
||||
"YoutubeNotFound": "Video not found.",
|
||||
|
@ -171,7 +171,7 @@
|
||||
"GuestStar": "Estrella invitada",
|
||||
"Guide": "Guía",
|
||||
"GuideProviderSelectListings": "Seleccionar listados",
|
||||
"H264CrfHelp": "El factor de velocidad constante (CRF) es el ajuste de calidad predeterminado para el codificador x264. Puede establecer los valores entre 0 y 51, donde valores más bajos resultarían en una mejor calidad (a expensas de tamaños de archivo más altos). Los valores sanos están entre 18 y 28. El valor predeterminado para x264 es 23, por lo que puede utilizar esto como punto de partida.",
|
||||
"H264CrfHelp": "El factor de velocidad constante (CRF) es el ajuste de calidad predeterminado para los codificadores x264 y x265. Puede establecer los valores entre 0 y 51, donde valores más bajos resultarían en una mejor calidad (a expensas de tamaños de archivo más altos). Los valores sanos están entre 18 y 28. El valor predeterminado para x264 es 23, y para x265 es 28, por lo que puede utilizar esto como punto de partida.",
|
||||
"EncoderPresetHelp": "Elija un valor más rápido para mejorar el rendimiento o un valor más lento para mejorar la calidad.",
|
||||
"HDPrograms": "Programas en HD",
|
||||
"HardwareAccelerationWarning": "Activar la aceleración por hardware puede producir inestabilidades en algunos entornos. Asegúrate de que tu sistema operativo y tus controladores de vídeo están actualizados. Si tienes dificultades para reproducir los vídeos después de activar esto, tendrás que volver a poner este ajuste en None.",
|
||||
@ -461,7 +461,7 @@
|
||||
"LabelGroupMoviesIntoCollections": "Agrupar películas en colecciones",
|
||||
"LabelGroupMoviesIntoCollectionsHelp": "Al mostrar las listas de películas, las películas pertenecientes a una colección se mostrarán como un elemento agrupado.",
|
||||
"LabelH264Crf": "H264 que codifica CRF:",
|
||||
"LabelEncoderPreset": "Configuración de codificación H264:",
|
||||
"LabelEncoderPreset": "Preajuste de codificación:",
|
||||
"LabelHardwareAccelerationType": "Aceleración por hardware:",
|
||||
"LabelHardwareAccelerationTypeHelp": "La aceleración por hardware requiere configuración adicional.",
|
||||
"LabelHomeScreenSectionValue": "Sección de la pantalla de inicio {0}:",
|
||||
@ -1072,8 +1072,8 @@
|
||||
"DateAdded": "Añadido el",
|
||||
"DatePlayed": "Reproducido el",
|
||||
"Descending": "Descendiente",
|
||||
"DirectStreamHelp1": "El tipo de archivo (H.264, AC3, etc.) y la resolución son compatibles con el dispositivo, pero no el contenedor (mkv, avi, wmv, etc.). El vídeo será re-empaquetado al vuelo antes de transmitirlo al dispositivo.",
|
||||
"DirectStreamHelp2": "La transmisión directa del archivo usa muy poco procesamiento sin mínima pérdida de calidad en el vídeo.",
|
||||
"DirectStreamHelp1": "La transmisión de video es compatible con el dispositivo, pero tiene un formato de audio incompatible (DTS, TRUEHD, etc.) o un número de canales de audio. La transmisión de video se volverá a empaquetar sin pérdidas sobre la marcha antes de enviarse al dispositivo. Solo se transcodificará la transmisión de audio.",
|
||||
"DirectStreamHelp2": "La energía consumida por la transmisión directa generalmente depende del perfil de audio. Solo la transmisión de video no tiene pérdidas.",
|
||||
"Director": "Director",
|
||||
"Directors": "Directores",
|
||||
"Display": "Visualización",
|
||||
@ -1431,5 +1431,24 @@
|
||||
"LabelFallbackFontPathHelp": "Especifica el camino conteniendo las fuentes alternativas para el renderizado de subtítulos ASS/SSA. El tamaño total máximo de la fuente es de 20 MB. Formatos de fuente ligeras y compatibles web como woff2 son recomendadas.",
|
||||
"LabelFallbackFontPath": "Dirección de la carpeta alternativa de fuentes:",
|
||||
"HeaderSelectFallbackFontPathHelp": "Busca o escribe la dirección de la carpeta alternativa de fuentes usada para la renderización de los subtitulos ASS/SSA.",
|
||||
"HeaderSelectFallbackFontPath": "Seleccionar carpeta de fuentes alternativa"
|
||||
"HeaderSelectFallbackFontPath": "Seleccionar carpeta de fuentes alternativa",
|
||||
"LabelSelectMono": "Mono",
|
||||
"LabelSelectAudioChannels": "Canales",
|
||||
"LabelAllowedAudioChannels": "Canales de audio máximos permitidos",
|
||||
"AllowHevcEncoding": "Permitir la codificación en formato HEVC",
|
||||
"PreferFmp4HlsContainerHelp": "Preferir usar fMP4 como contenedor predeterminado para HLS, haciendo posible transmitir contenido HEVC directo en dispositivos compatibles.",
|
||||
"PreferFmp4HlsContainer": "Preferir contenedor de medios fMP4-HLS",
|
||||
"LabelH265Crf": "CRF de codificación H265:",
|
||||
"LabelSelectStereo": "Estéreo",
|
||||
"LabelSyncPlayInfo": "Información de SyncPlay",
|
||||
"LabelOriginalMediaInfo": "Información de medios originales",
|
||||
"LabelRemuxingInfo": "Información de remuxing",
|
||||
"LabelDirectStreamingInfo": "Información de transmisión directa",
|
||||
"LabelTranscodingInfo": "Información de transcodificación",
|
||||
"LabelVideoInfo": "Información de video",
|
||||
"LabelAudioInfo": "Información de audio",
|
||||
"LabelPlaybackInfo": "Información de reproducción",
|
||||
"RemuxHelp2": "Remux utiliza muy poca potencia de procesamiento con una calidad multimedia sin pérdidas.",
|
||||
"RemuxHelp1": "Los medios están en un contenedor de archivos incompatible (MKV, AVI, WMV, etc.) pero tanto la transmisión de video como la transmisión de audio son compatibles con el dispositivo. Los medios se volverán a empaquetar sin pérdidas sobre la marcha antes de enviarse al dispositivo.",
|
||||
"Remuxing": "Remuxing"
|
||||
}
|
||||
|
@ -211,7 +211,7 @@
|
||||
"GroupVersions": "Grouper les versions",
|
||||
"GuideProviderLogin": "Connexion",
|
||||
"GuideProviderSelectListings": "Sélectionner les listings",
|
||||
"H264CrfHelp": "Le facteur de débit constant (CRF) est le paramètre de qualité par défaut pour l'encodeur x264. Vous pouvez régler les valeurs entre 0 et 51, où des valeurs plus faibles se traduiraient par une meilleure qualité (en augmentant le taille des fichiers). De bonne valeurs se situent entre 18 et 28. La valeur par défaut pour le x264 est 23, vous pouvez l'utiliser comme point de départ.",
|
||||
"H264CrfHelp": "Le facteur de débit constant (CRF) est le paramètre de qualité par défaut pour les encodeurs x264 et x265. Vous pouvez définir les valeurs entre 0 et 51, où des valeurs inférieures entraîneraient une meilleure qualité (au détriment de tailles de fichier plus élevées). Les valeurs saines sont comprises entre 18 et 28. La valeur par défaut pour x264 est 23 et pour x265 est 28, vous pouvez donc l'utiliser comme point de départ.",
|
||||
"EncoderPresetHelp": "Choisissez une valeur plus rapide pour améliorer la performance, ou plus lente pour améliorer la qualité.",
|
||||
"HDPrograms": "Programmes HD",
|
||||
"HardwareAccelerationWarning": "L'activation de l'accélération matérielle peut provoquer une instabilité dans certains environnements. Assurez-vous que votre système d'exploitation et vos pilotes vidéo sont complètement à jour. Si vous avez des difficultés pour lire des vidéos après l'activation, vous devrez remettre ce paramètre sur Aucun.",
|
||||
@ -516,7 +516,7 @@
|
||||
"LabelGroupMoviesIntoCollections": "Grouper les films en collections",
|
||||
"LabelGroupMoviesIntoCollectionsHelp": "Dans l'affichage des listes de films, les films d'une collection seront affichés comme un élément groupé.",
|
||||
"LabelH264Crf": "CRF d'encodage H264 :",
|
||||
"LabelEncoderPreset": "Profil d'encodage H264 :",
|
||||
"LabelEncoderPreset": "Profil d'encodage :",
|
||||
"LabelHardwareAccelerationType": "Accélération matérielle :",
|
||||
"LabelHardwareAccelerationTypeHelp": "L'accélération matérielle nécessite une configuration supplémentaire.",
|
||||
"LabelHomeNetworkQuality": "Qualité du réseau local :",
|
||||
@ -1431,5 +1431,6 @@
|
||||
"LabelFallbackFontPathHelp": "Spécifiez un chemin contenant les polices de remplacement pour le rendu des sous-titres ASS / SSA. La taille de police totale maximale autorisée est de 20 Mo. Les formats de police légers et conviviaux pour le Web tels que woff2 sont recommandés.",
|
||||
"LabelFallbackFontPath": "Chemin du dossier des polices de secours :",
|
||||
"HeaderSelectFallbackFontPathHelp": "Parcourez ou entrez le chemin du dossier de polices de secours à utiliser pour le rendu des sous-titres ASS/SSA.",
|
||||
"HeaderSelectFallbackFontPath": "Sélectionnez le dossier des polices de secours"
|
||||
"HeaderSelectFallbackFontPath": "Sélectionnez le dossier des polices de secours",
|
||||
"LabelH265Crf": "CRF d'encodage H265 :"
|
||||
}
|
||||
|
@ -416,7 +416,7 @@
|
||||
"Items": "Pozycje",
|
||||
"Kids": "Dla dzieci",
|
||||
"Label3DFormat": "Format 3D:",
|
||||
"LabelAbortedByServerShutdown": "(Przerwano w skuter wyłączenia serwera)",
|
||||
"LabelAbortedByServerShutdown": "(Przerwano w skutek wyłączenia serwera)",
|
||||
"LabelAccessDay": "Dzień tygodnia:",
|
||||
"LabelAccessEnd": "Czas zakończenia:",
|
||||
"LabelAccessStart": "Czas startu:",
|
||||
@ -1381,5 +1381,9 @@
|
||||
"Authorize": "Autoryzuj",
|
||||
"HeaderDeleteDevices": "Usuń wszystkie urządzenia",
|
||||
"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."
|
||||
}
|
||||
|
@ -276,7 +276,7 @@
|
||||
"HardwareAccelerationWarning": "வன்பொருள் முடுக்கம் இயக்குவது சில சூழல்களில் உறுதியற்ற தன்மையை ஏற்படுத்தக்கூடும். உங்கள் இயக்க முறைமை மற்றும் வீடியோ இயக்கிகள் முழுமையாக புதுப்பித்த நிலையில் இருப்பதை உறுதிசெய்க. இதை இயக்கிய பின் வீடியோவை இயக்குவதில் சிக்கல் இருந்தால், நீங்கள் அமைப்பை எதுவும் இல்லை என மாற்ற வேண்டும்.",
|
||||
"HDPrograms": "HD நிரல்கள்",
|
||||
"EncoderPresetHelp": "செயல்திறனை மேம்படுத்த வேகமான மதிப்பை அல்லது தரத்தை மேம்படுத்த மெதுவான மதிப்பைத் தேர்வுசெய்க.",
|
||||
"H264CrfHelp": "நிலையான விகித காரணி (CRF) என்பது x264 குறியாக்கிக்கான இயல்புநிலை தர அமைப்பாகும். நீங்கள் 0 மற்றும் 51 க்கு இடையில் மதிப்புகளை அமைக்கலாம், அங்கு குறைந்த மதிப்புகள் சிறந்த தரத்தை ஏற்படுத்தும் (அதிக கோப்பு அளவுகளின் இழப்பில்). சேன் மதிப்புகள் 18 முதல் 28 வரை உள்ளன. X264 இன் இயல்புநிலை 23 ஆகும், எனவே இதை நீங்கள் ஒரு தொடக்க புள்ளியாக பயன்படுத்தலாம்.",
|
||||
"H264CrfHelp": "நிலையான விகித காரணி (CRF) என்பது x264 மற்றும் x265 குறியாக்கிக்கான இயல்புநிலை தர அமைப்பாகும். நீங்கள் 0 மற்றும் 51 க்கு இடையில் மதிப்புகளை அமைக்கலாம், அங்கு குறைந்த மதிப்புகள் சிறந்த தரத்தை ஏற்படுத்தும் (அதிக கோப்பு அளவுகளின் இழப்பில்). சேன் மதிப்புகள் 18 முதல் 28 வரை உள்ளன. X264 இன் இயல்புநிலை 23, மற்றும் x265 க்கு 28 ஆகும், எனவே இதை நீங்கள் ஒரு தொடக்க புள்ளியாக பயன்படுத்தலாம்.",
|
||||
"GuideProviderSelectListings": "பட்டியல்களைத் தேர்ந்தெடுக்கவும்",
|
||||
"GuideProviderLogin": "உள்நுழைய",
|
||||
"Guide": "வழிகாட்டி",
|
||||
@ -668,7 +668,7 @@
|
||||
"LabelHomeNetworkQuality": "முகப்பு நெட்வொர்க் தரம்:",
|
||||
"LabelHardwareAccelerationTypeHelp": "வன்பொருள் முடுக்கம் கூடுதல் உள்ளமைவு தேவை.",
|
||||
"LabelHardwareAccelerationType": "வன்பொருள் முடுக்கம்:",
|
||||
"LabelEncoderPreset": "H264 மற்றும் H265 குறியாக்க முன்னமைவு:",
|
||||
"LabelEncoderPreset": "குறியீட்டு முன்னமைவு:",
|
||||
"LabelH264Crf": "H264 குறியாக்கம் CRF:",
|
||||
"LabelGroupMoviesIntoCollectionsHelp": "மூவி பட்டியல்களைக் காண்பிக்கும் போது, ஒரு தொகுப்பில் உள்ள திரைப்படங்கள் ஒரு குழுவாகக் காட்டப்படும்.",
|
||||
"LabelGroupMoviesIntoCollections": "திரைப்படங்களை தொகுப்பாக குழு செய்யவும்",
|
||||
@ -1446,5 +1446,13 @@
|
||||
"LabelFallbackFontPathHelp": "ASS / SSA வசன வரிகளை வழங்குவதற்கான குறைவடையும் எழுத்துருக்களைக் கொண்ட பாதையைக் குறிப்பிடவும். அனுமதிக்கப்பட்ட அதிகபட்ச எழுத்துரு அளவு 20 எம்பி. Woff2 போன்ற இலகுரக மற்றும் வலை நட்பு எழுத்துரு வடிவங்கள் பரிந்துரைக்கப்படுகின்றன.",
|
||||
"LabelFallbackFontPath": "குறைவடையும் எழுத்துரு கோப்புறை பாதை:",
|
||||
"HeaderSelectFallbackFontPathHelp": "ASS / SSA வசன வரிகளை வழங்குவதற்கு குறைவடையும் எழுத்துரு கோப்புறையின் பாதையை உலாவவும் அல்லது உள்ளிடவும்.",
|
||||
"HeaderSelectFallbackFontPath": "குறைவடையும் எழுத்துரு கோப்புறை பாதையைத் தேர்ந்தெடுக்கவும்"
|
||||
"HeaderSelectFallbackFontPath": "குறைவடையும் எழுத்துரு கோப்புறை பாதையைத் தேர்ந்தெடுக்கவும்",
|
||||
"LabelSelectStereo": "ஸ்டீரியோ",
|
||||
"LabelSelectMono": "மோனோ",
|
||||
"LabelSelectAudioChannels": "சேனல்கள்",
|
||||
"LabelAllowedAudioChannels": "அதிகபட்ச அனுமதிக்கப்பட்ட ஆடியோ சேனல்கள்",
|
||||
"AllowHevcEncoding": "HEVC வடிவத்தில் குறியாக்கத்தை அனுமதிக்கவும்",
|
||||
"PreferFmp4HlsContainerHelp": "HEVC இயல்புநிலை கொள்கலனாக fMP4ஐப் பயன்படுத்த விரும்பினால், ஆதரிக்கப்படும் சாதனங்களில் ஸ்ட்ரீம் ஹெச்.வி.சி உள்ளடக்கத்தை நேரடியாக இயக்க முடியும்.",
|
||||
"PreferFmp4HlsContainer": "FMP4-HLS மீடியா கொள்கலனை விரும்புங்கள்",
|
||||
"LabelH265Crf": "H265 குறியாக்கம் CRF:"
|
||||
}
|
||||
|
@ -222,8 +222,8 @@
|
||||
"Directors": "Đạo Diễn",
|
||||
"Director": "Đạo Diễn",
|
||||
"DirectStreaming": "Phát trực tuyến",
|
||||
"DirectStreamHelp2": "Phát trực tiếp sử dụng ít sức mạnh xử lý với chất lượng video bị giảm thiểu chút ít.",
|
||||
"DirectStreamHelp1": "Nội dung này tương thích với thiết bị về độ phân giải và loại mã hoá (H.264, AC3, v.v.), nhưng lại không tương thích định dạng (mkv, avi, wmv, v.v.). Video sẽ được đóng gói nhanh chóng trước khi được gửi đến phát trên thiết bị.",
|
||||
"DirectStreamHelp2": "Mức độ tổn hao khi phát trực tiếp thường phụ thuộc vào cấu hình âm thanh. Chỉ có luồng video là không mất dữ liệu.",
|
||||
"DirectStreamHelp1": "Luồng video tương thích với thiết bị, nhưng có định dạng âm thanh (DTS, TRUEHD, v.v.) hoặc số kênh âm thanh không tương thích. Luồng video sẽ được đóng gói lại không mất mát dữ liệu trước khi gửi đến thiết bị. Chỉ luồng âm thanh được chuyển mã.",
|
||||
"DirectPlaying": "Phát trực tiếp",
|
||||
"DeviceAccessHelp": "Thiết lập này chỉ áp dụng cho những thiết bị có thể định danh và sẽ không chặn được truy cập từ trình duyệt. Chọn lọc thiết bị người dùng sẽ chặn người dùng này truy cập từ những thiết bị mới cho đến khi được duyệt.",
|
||||
"DetectingDevices": "Đang tìm kiếm thiết bị",
|
||||
@ -294,7 +294,7 @@
|
||||
"HeaderAccessSchedule": "Thời Gian Truy Cập",
|
||||
"HDPrograms": "Chương trình chất lượng cao (HD)",
|
||||
"EncoderPresetHelp": "Chọn một giá trị nhanh hơn để cải thiện hiệu suất máy chủ, hoặc một giá trị chậm hơn để tăng chất lượng video.",
|
||||
"H264CrfHelp": "Hệ Số Tỉ Lệ Cố Định (Constant Rate Factor (CRF)) là thiết lập chất lượng mặc định dành cho bộ mã hoá x264. Bạn có thể điều chỉnh giá trị trong khoảng 0 đến 51, trong đó giá trị càng nhỏ thì chất lượng càng tốt (đồng nghĩa với việc dung lượng tập tin lớn hơn). Giá trị vừa phải nằm trong khoảng từ 18 đến 28. Giá trị mặc định dành cho x264 là 23, vì thế bạn có thể sử dụng nó để bắt đầu điều chỉnh cho phù hợp.",
|
||||
"H264CrfHelp": "Hệ Số Tỉ Lệ Cố Định (CRF) là cài đặt chất lượng mặc định cho mã hóa x264 và x265. Bạn có thể chỉnh giá trị từ 0 đến 51, giá trị càng nhỏ thì chất lượng càng tốt (dung lượng tệp sẽ lớn hơn). Giá trị vừa phải từ 18 đến 28. Giá trị mặc định cho x264 là 23 và x265 là 28, vì vậy bạn có thể bắt đầu với những giá trị tham khảo này.",
|
||||
"GuideProviderSelectListings": "Chọn Danh Sách",
|
||||
"GuideProviderLogin": "Đăng nhập",
|
||||
"Guide": "Lịch phát sóng",
|
||||
@ -341,7 +341,7 @@
|
||||
"EnablePhotosHelp": "Hình ảnh sẽ được nhận diện và hiển thị bên cạnh những nội dung media.",
|
||||
"EnablePhotos": "Hiển thị hình ảnh",
|
||||
"EnableNextVideoInfoOverlayHelp": "Lúc cuối video, hiển thị thông tin video sắp phát tiếp theo trong danh sách phát hiện tại.",
|
||||
"EnableNextVideoInfoOverlay": "Hiển thị thông tin video tiếp theo ở phần cuối video",
|
||||
"EnableNextVideoInfoOverlay": "Hiển thị thông tin video kế tiếp ở phần cuối video",
|
||||
"EnableHardwareEncoding": "Sử dụng phần cứng để hỗ trợ chuyển mã",
|
||||
"EnableExternalVideoPlayersHelp": "Phần mềm phát video từ thiết bị sẽ được hiển thị khi bắt đầu phát video.",
|
||||
"EnableExternalVideoPlayers": "Sử dụng phần mềm phát video từ thiết bị",
|
||||
@ -480,17 +480,17 @@
|
||||
"LabelBlockContentWithTags": "Chặn những mục có nhãn:",
|
||||
"LabelBlastMessageIntervalHelp": "Xác định thời gian tồn tại giữa các tin nhắn (tính bằng giây).",
|
||||
"LabelBlastMessageInterval": "Khoảng thời gian gửi tin nhắn tồn tại (giây)",
|
||||
"LabelBitrate": "Bitrate:",
|
||||
"LabelBitrate": "Tốc độ bit:",
|
||||
"LabelBirthYear": "Năm sinh:",
|
||||
"LabelBirthDate": "Ngày sinh:",
|
||||
"LabelBindToLocalNetworkAddressHelp": "Ghi đè địa chỉ IP cục bộ cho máy chủ HTTP. Nếu bỏ trống, máy chủ sẽ kết hợp tất cả địa chỉ có sẵn. Thay đổi giá trị này buộc phải khởi động lại.",
|
||||
"LabelBindToLocalNetworkAddress": "Liên kết với địa chỉ mạng cục bộ:",
|
||||
"LabelAutomaticallyRefreshInternetMetadataEvery": "Tự động cập nhật dữ liệu mô tả từ Internet:",
|
||||
"LabelAuthProvider": "Cách Xác Thực:",
|
||||
"LabelAudioSampleRate": "Sample rate âm thanh:",
|
||||
"LabelAudioSampleRate": "Tỉ lệ mẫu âm thanh:",
|
||||
"LabelAudioCodec": "Bộ giải mã âm thanh:",
|
||||
"LabelAudioChannels": "Các kênh âm thanh:",
|
||||
"LabelAudioBitrate": "Bitrate của âm thanh:",
|
||||
"LabelAudioBitrate": "Tốc độ bit âm thanh:",
|
||||
"LabelAudioBitDepth": "Chiều sâu của âm thanh:",
|
||||
"LabelArtistsHelp": "Tách nhiều nghệ sĩ bằng dấu chấm phẩy \";\".",
|
||||
"LabelArtists": "Nghệ sĩ:",
|
||||
@ -641,7 +641,7 @@
|
||||
"LabelHomeNetworkQuality": "Chất lượng mạng nhà:",
|
||||
"LabelHardwareAccelerationTypeHelp": "Hỗ trợ phần cần những thiết lập bổ sung.",
|
||||
"LabelHardwareAccelerationType": "Tăng tốc phần cứng:",
|
||||
"LabelEncoderPreset": "Thiết lập cài sẵn của mã H264 và H265:",
|
||||
"LabelEncoderPreset": "Mẫu cài đặt sẵn H264 và H265:",
|
||||
"LabelH264Crf": "CRF của mã H264:",
|
||||
"LabelGroupMoviesIntoCollectionsHelp": "Khi hiển thị danh sách phim, các bộ phim trong một bộ sưu tập sẽ hiển thị như một mục được nhóm lại.",
|
||||
"LabelGroupMoviesIntoCollections": "Gom nhóm các phim vào bộ sưu tập",
|
||||
@ -655,7 +655,7 @@
|
||||
"LabelFailed": "Thất bại",
|
||||
"LabelMaxResumePercentage": "Phần trăm tối đa có thể phát tiếp tục:",
|
||||
"LabelMaxParentalRating": "Xếp hạng tối đa cho phép của phụ huynh:",
|
||||
"LabelMaxChromecastBitrate": "Chất lượng truyền tải Chromecast:",
|
||||
"LabelMaxChromecastBitrate": "Chất lượng luồng phát Chromecast:",
|
||||
"LabelMaxBackdropsPerItem": "Số lượng phông nền tối đa mỗi mục:",
|
||||
"LabelMatchType": "Loại phù hợp:",
|
||||
"LabelManufacturerUrl": "URL của nhà sản xuất",
|
||||
@ -691,8 +691,8 @@
|
||||
"LabelMetadata": "Dữ Liệu Mô Tả:",
|
||||
"LabelMessageTitle": "Tiêu đề tin nhắn:",
|
||||
"LabelMessageText": "Nội dung tin nhắn:",
|
||||
"LabelMaxStreamingBitrateHelp": "Thiết lập bitrate tối đa khi truyền tải.",
|
||||
"LabelMaxStreamingBitrate": "Chất lượng phát tối đa:",
|
||||
"LabelMaxStreamingBitrateHelp": "Thiết lập tốc độ bit tối đa khi phát.",
|
||||
"LabelMaxStreamingBitrate": "Chất lượng luồng phát tối đa:",
|
||||
"LabelMaxScreenshotsPerItem": "Số lượng ảnh chụp tối đa mỗi mục:",
|
||||
"LabelMaxResumePercentageHelp": "Nội dung sẽ được cho là đã kết thúc nếu ngừng phát sau thời gian này.",
|
||||
"ButtonSyncPlay": "Đồng Bộ Phát",
|
||||
@ -817,8 +817,8 @@
|
||||
"LabelRuntimeMinutes": "Thời lượng:",
|
||||
"LabelRequireHttpsHelp": "Nếu được chọn, máy chủ sẽ tự động chuyển hướng tất cả các yêu cầu qua HTTP sang HTTPS. Điều này không ảnh hưởng nếu máy chủ không nghe trên HTTPS.",
|
||||
"LabelRequireHttps": "Yêu cầu HTTPS",
|
||||
"LabelRemoteClientBitrateLimitHelp": "Giới hạn tốc độ bit trên mỗi luồng cho tất cả các thiết bị ngoài mạng. Nó hữu ích để ngăn các thiết bị yêu cầu tốc độ bit cao hơn mức kết nối internet của bạn có thể xử lý. Điều này có thể dẫn đến việc tăng tải CPU trên máy chủ của bạn để chuyển mã video nhanh chóng xuống tốc độ bit thấp hơn.",
|
||||
"LabelRemoteClientBitrateLimit": "Giới hạn tốc độ bit phát trực tuyến Internet (Mbps):",
|
||||
"LabelRemoteClientBitrateLimitHelp": "Giới hạn tốc độ bit trên mỗi luồng cho tất cả các thiết bị ngoài mạng. Nó hữu ích để ngăn các thiết bị yêu cầu tốc độ bit cao hơn mức kết nối internet của bạn. Điều này có thể dẫn đến tăng tải CPU trên máy chủ của bạn để chuyển mã video nhanh chóng xuống tốc độ bit thấp hơn.",
|
||||
"LabelRemoteClientBitrateLimit": "Giới hạn tốc độ bit luồng phát qua Internet (Mbps):",
|
||||
"LabelReleaseDate": "Ngày phát hành:",
|
||||
"LabelRefreshMode": "Chế độ làm mới :",
|
||||
"LabelRecordingPathHelp": "Chỉ định vị trí mặc định để lưu bản ghi. Nếu để trống, thư mục dữ liệu chương trình của máy chủ sẽ được sử dụng.",
|
||||
@ -869,7 +869,7 @@
|
||||
"LabelNewName": "Tên mới:",
|
||||
"LabelUnstable": "Không ổn định",
|
||||
"LabelChromecastVersion": "Phiên bản Chromecast",
|
||||
"LabelMusicStreamingTranscodingBitrateHelp": "Chỉ định tốc độ bit tối đa khi phát nhạc trực tuyến.",
|
||||
"LabelMusicStreamingTranscodingBitrateHelp": "Chỉ định tốc độ bit tối đa khi phát nhạc.",
|
||||
"LabelMusicStreamingTranscodingBitrate": "Tốc độ bit chuyển mã âm nhạc:",
|
||||
"LabelMovieRecordingPath": "Đường dẫn quay phim:",
|
||||
"LabelCurrentStatus": "Tình trạng hiện tại:",
|
||||
@ -1431,5 +1431,26 @@
|
||||
"LabelFallbackFontPathHelp": "Chỉ định đường dẫn chứa phông chữ dự phòng để hiển thị phụ đề ASS / SSA. Tổng kích thước phông chữ tối đa được phép là 20 MB. Các định dạng phông chữ nhẹ và thân thiện với web như woff2 được khuyến khích.",
|
||||
"LabelFallbackFontPath": "Đường dẫn thư mục phông chữ dự phòng:",
|
||||
"HeaderSelectFallbackFontPathHelp": "Duyệt qua hoặc nhập đường dẫn của thư mục phông chữ dự phòng để sử dụng cho việc hiển thị phụ đề ASS / SSA.",
|
||||
"HeaderSelectFallbackFontPath": "Chọn Đường dẫn Thư mục Phông chữ Dự phòng"
|
||||
"HeaderSelectFallbackFontPath": "Chọn Đường dẫn Thư mục Phông chữ Dự phòng",
|
||||
"LabelSelectStereo": "Âm Thanh Nổi",
|
||||
"LabelSelectMono": "Âm Thanh Đơn Kênh",
|
||||
"LabelSelectAudioChannels": "Kênh",
|
||||
"LabelAllowedAudioChannels": "Kênh Âm Thanh Cho Phép Tối Đa",
|
||||
"AllowHevcEncoding": "Cho phép mã hóa ở định dạng HEVC",
|
||||
"PreferFmp4HlsContainerHelp": "Thích sử dụng fMP4 làm vùng chứa mặc định cho HLS, giúp bạn có thể truyền trực tiếp nội dung HEVC trên các thiết bị được hỗ trợ.",
|
||||
"PreferFmp4HlsContainer": "Thích vùng chứa phương tiện fMP4-HLS",
|
||||
"LabelH265Crf": "CRF mã hóa H265:",
|
||||
"LabelSyncPlayInfo": "Thông Tin Phát Đồng Bộ",
|
||||
"LabelOriginalMediaInfo": "Thông Tin Phương Tiện Gốc",
|
||||
"LabelRemuxingInfo": "Thông Tin Pha Trộn",
|
||||
"LabelDirectStreamingInfo": "Thông Tin Phát Trực Tiếp",
|
||||
"LabelTranscodingInfo": "Thông Tin Chuyển Mã",
|
||||
"LabelVideoInfo": "Thông Tin Video",
|
||||
"LabelAudioInfo": "Thông Tin Âm Thanh",
|
||||
"LabelPlaybackInfo": "Thông Tin Phát Lại",
|
||||
"RemuxHelp2": "Pha trộn sử dụng rất ít sức mạnh xử lý với chất lượng phương tiện hoàn toàn không mất dữ liệu.",
|
||||
"RemuxHelp1": "Phương tiện nằm trong vùng chứa tệp không tương thích (MKV, AVI, WMV, v.v.) nhưng cả luồng video và luồng âm thanh đều tương thích với thiết bị. Phương tiện sẽ được đóng gói lại giữ chất lượng tốt trước khi được gửi đến thiết bị.",
|
||||
"Remuxing": "Pha Trộn",
|
||||
"AspectRatioFill": "Lấp Đầy",
|
||||
"AspectRatioCover": "Bao Phủ"
|
||||
}
|
||||
|
@ -128,7 +128,7 @@
|
||||
"DetectingDevices": "正在侦测设备",
|
||||
"DeviceAccessHelp": "这仅适用于可以唯一标识的设备,而不会阻止浏览器访问。限制用户设备访问会阻止使用未在此被批准的新增设备。",
|
||||
"DirectPlaying": "直接播放",
|
||||
"DirectStreamHelp2": "直接串流只占用占用很少的CPU并且视频的品质只会有极小程度的损失。",
|
||||
"DirectStreamHelp2": "直接串流占用的 CPU 资源通常取决于音频规格。只有视频流是无损的。",
|
||||
"DirectStreaming": "直接串流",
|
||||
"Director": "导演",
|
||||
"Disc": "光盘",
|
||||
@ -197,7 +197,7 @@
|
||||
"GuestStar": "特邀明星",
|
||||
"GuideProviderLogin": "登入",
|
||||
"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 的默认值为 23,x265 的默认值为 28,因此可以将其用作起始点。",
|
||||
"EncoderPresetHelp": "选择一个更快的值以提升性能,或者选择一个更慢的值以提升质量。",
|
||||
"HDPrograms": "高清节目",
|
||||
"HardwareAccelerationWarning": "启动硬件加速可能在某些环境下导致系统不稳定。请确认你的操作系统和显卡驱动程序是最新的。如果你在开启此项后播放视频时遇到困难,那么你需要将此选项设置回“没有”。",
|
||||
@ -493,7 +493,8 @@
|
||||
"LabelGroupMoviesIntoCollections": "批量添加电影到集合",
|
||||
"LabelGroupMoviesIntoCollectionsHelp": "显示电影列表时,同一集合的电影将显示为一个分组。",
|
||||
"LabelH264Crf": "H264 CRF 编码质量等级:",
|
||||
"LabelEncoderPreset": "H264 和 H265 编码预设:",
|
||||
"LabelH265Crf": "H265 CRF 编码质量等级:",
|
||||
"LabelEncoderPreset": "编码预设:",
|
||||
"LabelHardwareAccelerationType": "硬件加速:",
|
||||
"LabelHardwareAccelerationTypeHelp": "硬件加速需要额外配置。",
|
||||
"LabelHomeNetworkQuality": "家庭网络质量:",
|
||||
@ -1142,7 +1143,7 @@
|
||||
"ErrorDeletingItem": "从服务器删除项目时出错。请检查Jellyfin是否拥有对媒体目录的写权限,然后重试。",
|
||||
"GroupBySeries": "按系列分组",
|
||||
"HeaderApp": "应用程序",
|
||||
"DirectStreamHelp1": "该媒体文件的分辨率和编码(H.264、AC3 等)与您的设备兼容,但文件格式(.mkv、.avi、.wmv 等)不受支持。因此,视频在串流至您的设备之前将会被即时封装为另一种格式。",
|
||||
"DirectStreamHelp1": "该媒体的载体(mkv、avi、wmv 等)不被设备支持,视频流在与您的设备兼容,但是音频流有着不被设备支持的媒体格式(DTS、TRUEHD 等)或声道数量(5.1、7.1 等)。因此视频流在串流至您的设备之前将被即时无损地再封装。只有音频流将被转码。",
|
||||
"HeaderAppearsOn": "同时出现于",
|
||||
"HeaderCancelSeries": "取消系列",
|
||||
"HeaderKeepRecording": "继续录制",
|
||||
@ -1431,5 +1432,25 @@
|
||||
"OptionAllowContentDownload": "允许媒体下载",
|
||||
"HeaderDeleteDevices": "删除所有设备",
|
||||
"DeleteDevicesConfirmation": "您确定要删除所有设备吗?所有其他会话将被注销。用户下次登录时,设备会重新出现。",
|
||||
"DeleteAll": "删除全部"
|
||||
"DeleteAll": "删除全部",
|
||||
"AspectRatioCover": "覆盖",
|
||||
"AspectRatioFill": "填充",
|
||||
"Remuxing": "转封装",
|
||||
"RemuxHelp1": "该媒体的载体(mkv、avi、wmv 等)不被设备支持,但视频流和音频流均与您的设备兼容,因此媒体在串流至您的设备之前将被即时无损地再封装。",
|
||||
"RemuxHelp2": "转封装只占用很少的 CPU 资源并且媒体将被完全无损地传输。",
|
||||
"LabelPlaybackInfo": "播放信息",
|
||||
"LabelAudioInfo": "音频信息",
|
||||
"LabelVideoInfo": "视频信息",
|
||||
"LabelTranscodingInfo": "转码信息",
|
||||
"LabelDirectStreamingInfo": "直接串流信息",
|
||||
"LabelRemuxingInfo": "转封装信息",
|
||||
"LabelOriginalMediaInfo": "媒体源信息",
|
||||
"LabelSyncPlayInfo": "同步播放信息",
|
||||
"PreferFmp4HlsContainer": "优先使用 fMP4-HLS 媒体容器",
|
||||
"PreferFmp4HlsContainerHelp": "优先使用 fMP4 作为 HLS 播放的默认容器,从而可以在支持的设备上直接串流 HEVC 格式的内容。",
|
||||
"AllowHevcEncoding": "允许以 HEVC 格式编码",
|
||||
"LabelAllowedAudioChannels": "允许的最大声道数量",
|
||||
"LabelSelectAudioChannels": "声道",
|
||||
"LabelSelectMono": "单声道",
|
||||
"LabelSelectStereo": "立体声"
|
||||
}
|
||||
|
@ -5,6 +5,24 @@ const packageConfig = require('./package.json');
|
||||
const WorkboxPlugin = require('workbox-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
const Assets = [
|
||||
'alameda/alameda.js',
|
||||
'native-promise-only/npo.js',
|
||||
'libarchive.js/dist/worker-bundle.js',
|
||||
'libass-wasm/dist/js/subtitles-octopus-worker.js',
|
||||
'libass-wasm/dist/js/subtitles-octopus-worker.data',
|
||||
'libass-wasm/dist/js/subtitles-octopus-worker.wasm',
|
||||
'libass-wasm/dist/js/subtitles-octopus-worker-legacy.js',
|
||||
'libass-wasm/dist/js/subtitles-octopus-worker-legacy.data',
|
||||
'libass-wasm/dist/js/subtitles-octopus-worker-legacy.js.mem',
|
||||
'pdfjs-dist/build/pdf.worker.js'
|
||||
];
|
||||
|
||||
const LibarchiveWasm = [
|
||||
'libarchive.js/dist/wasm-gen/libarchive.js',
|
||||
'libarchive.js/dist/wasm-gen/libarchive.wasm'
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
context: path.resolve(__dirname, 'src'),
|
||||
target: 'web',
|
||||
@ -39,6 +57,22 @@ module.exports = {
|
||||
}
|
||||
]
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: Assets.map(asset => {
|
||||
return {
|
||||
from: path.resolve(__dirname, `./node_modules/${asset}`),
|
||||
to: path.resolve(__dirname, './dist/libraries')
|
||||
};
|
||||
})
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: LibarchiveWasm.map(asset => {
|
||||
return {
|
||||
from: path.resolve(__dirname, `./node_modules/${asset}`),
|
||||
to: path.resolve(__dirname, './dist/libraries/wasm-gen')
|
||||
};
|
||||
})
|
||||
}),
|
||||
new WorkboxPlugin.InjectManifest({
|
||||
swSrc: path.resolve(__dirname, 'src/serviceworker.js'),
|
||||
swDest: 'serviceworker.js'
|
||||
|
Loading…
Reference in New Issue
Block a user