Merge branch 'master' into Search
This commit is contained in:
commit
5700219762
49 changed files with 856 additions and 478 deletions
|
@ -64,6 +64,7 @@ module.exports = {
|
||||||
'no-throw-literal': ['error'],
|
'no-throw-literal': ['error'],
|
||||||
'no-trailing-spaces': ['error'],
|
'no-trailing-spaces': ['error'],
|
||||||
'no-undef-init': ['error'],
|
'no-undef-init': ['error'],
|
||||||
|
'no-unneeded-ternary': ['error'],
|
||||||
'no-unused-expressions': ['off'],
|
'no-unused-expressions': ['off'],
|
||||||
'@typescript-eslint/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
|
'@typescript-eslint/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
|
||||||
'no-unused-private-class-members': ['error'],
|
'no-unused-private-class-members': ['error'],
|
||||||
|
|
|
@ -66,11 +66,12 @@
|
||||||
- [Fishbigger](https://github.com/fishbigger)
|
- [Fishbigger](https://github.com/fishbigger)
|
||||||
- [sleepycatcoding](https://github.com/sleepycatcoding)
|
- [sleepycatcoding](https://github.com/sleepycatcoding)
|
||||||
- [TheMelmacian](https://github.com/TheMelmacian)
|
- [TheMelmacian](https://github.com/TheMelmacian)
|
||||||
|
- [v0idMrK](https://github.com/v0idMrK)
|
||||||
- [tehciolo](https://github.com/tehciolo)
|
- [tehciolo](https://github.com/tehciolo)
|
||||||
- [scampower3](https://github.com/scampower3)
|
- [scampower3](https://github.com/scampower3)
|
||||||
|
- [LittleBigOwI] (https://github.com/LittleBigOwI/)
|
||||||
- [Nate G](https://github.com/GGProGaming)
|
- [Nate G](https://github.com/GGProGaming)
|
||||||
|
|
||||||
|
|
||||||
# Emby Contributors
|
# Emby Contributors
|
||||||
|
|
||||||
- [LukePulverenti](https://github.com/LukePulverenti)
|
- [LukePulverenti](https://github.com/LukePulverenti)
|
||||||
|
|
|
@ -71,8 +71,8 @@ const GenresSectionContainer: FC<GenresSectionContainerProps> = ({
|
||||||
centerText: true,
|
centerText: true,
|
||||||
cardLayout: false,
|
cardLayout: false,
|
||||||
shape: itemType === BaseItemKind.MusicAlbum ? 'overflowSquare' : 'overflowPortrait',
|
shape: itemType === BaseItemKind.MusicAlbum ? 'overflowSquare' : 'overflowPortrait',
|
||||||
showParentTitle: itemType === BaseItemKind.MusicAlbum ? true : false,
|
showParentTitle: itemType === BaseItemKind.MusicAlbum,
|
||||||
showYear: itemType === BaseItemKind.MusicAlbum ? false : true
|
showYear: itemType !== BaseItemKind.MusicAlbum
|
||||||
}}
|
}}
|
||||||
/>;
|
/>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,8 +11,8 @@ import browser from 'scripts/browser';
|
||||||
import datetime from 'scripts/datetime';
|
import datetime from 'scripts/datetime';
|
||||||
import dom from 'scripts/dom';
|
import dom from 'scripts/dom';
|
||||||
import globalize from 'scripts/globalize';
|
import globalize from 'scripts/globalize';
|
||||||
import imageHelper from 'scripts/imagehelper';
|
|
||||||
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
||||||
|
import imageHelper from 'utils/image';
|
||||||
import { randomInt } from 'utils/number';
|
import { randomInt } from 'utils/number';
|
||||||
|
|
||||||
import focusManager from '../focusManager';
|
import focusManager from '../focusManager';
|
||||||
|
|
|
@ -102,7 +102,15 @@ function onInputCommand(e) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function saveValues(context, settings, settingsKey, setfilters) {
|
function saveValues(context, settings, settingsKey) {
|
||||||
|
context.querySelectorAll('.simpleFilter').forEach(elem => {
|
||||||
|
if (elem.tagName === 'INPUT') {
|
||||||
|
setBasicFilter(context, settingsKey + '-filter-' + elem.getAttribute('data-settingname'), elem);
|
||||||
|
} else {
|
||||||
|
setBasicFilter(context, settingsKey + '-filter-' + elem.getAttribute('data-settingname'), elem.querySelector('input'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Video type
|
// Video type
|
||||||
const videoTypes = [];
|
const videoTypes = [];
|
||||||
context.querySelectorAll('.chkVideoTypeFilter').forEach(elem => {
|
context.querySelectorAll('.chkVideoTypeFilter').forEach(elem => {
|
||||||
|
@ -111,6 +119,8 @@ function saveValues(context, settings, settingsKey, setfilters) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
userSettings.setFilter(settingsKey + '-filter-VideoTypes', videoTypes.join(','));
|
||||||
|
|
||||||
// Series status
|
// Series status
|
||||||
const seriesStatuses = [];
|
const seriesStatuses = [];
|
||||||
context.querySelectorAll('.chkSeriesStatus').forEach(elem => {
|
context.querySelectorAll('.chkSeriesStatus').forEach(elem => {
|
||||||
|
@ -119,6 +129,8 @@ function saveValues(context, settings, settingsKey, setfilters) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
userSettings.setFilter(`${settingsKey}-filter-SeriesStatus`, seriesStatuses.join(','));
|
||||||
|
|
||||||
// Genres
|
// Genres
|
||||||
const genres = [];
|
const genres = [];
|
||||||
context.querySelectorAll('.chkGenreFilter').forEach(elem => {
|
context.querySelectorAll('.chkGenreFilter').forEach(elem => {
|
||||||
|
@ -127,39 +139,8 @@ function saveValues(context, settings, settingsKey, setfilters) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (setfilters) {
|
|
||||||
setfilters((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
StartIndex: 0,
|
|
||||||
IsPlayed: context.querySelector('.chkPlayed').checked,
|
|
||||||
IsUnplayed: context.querySelector('.chkUnplayed').checked,
|
|
||||||
IsFavorite: context.querySelector('.chkFavorite').checked,
|
|
||||||
IsResumable: context.querySelector('.chkResumable').checked,
|
|
||||||
Is4K: context.querySelector('.chk4KFilter').checked,
|
|
||||||
IsHD: context.querySelector('.chkHDFilter').checked,
|
|
||||||
IsSD: context.querySelector('.chkSDFilter').checked,
|
|
||||||
Is3D: context.querySelector('.chk3DFilter').checked,
|
|
||||||
VideoTypes: videoTypes.join(','),
|
|
||||||
SeriesStatus: seriesStatuses.join(','),
|
|
||||||
HasSubtitles: context.querySelector('.chkSubtitle').checked,
|
|
||||||
HasTrailer: context.querySelector('.chkTrailer').checked,
|
|
||||||
HasSpecialFeature: context.querySelector('.chkSpecialFeature').checked,
|
|
||||||
HasThemeSong: context.querySelector('.chkThemeSong').checked,
|
|
||||||
HasThemeVideo: context.querySelector('.chkThemeVideo').checked,
|
|
||||||
GenreIds: genres.join(',')
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
context.querySelectorAll('.simpleFilter').forEach(elem => {
|
|
||||||
if (elem.tagName === 'INPUT') {
|
|
||||||
setBasicFilter(context, settingsKey + '-filter-' + elem.getAttribute('data-settingname'), elem);
|
|
||||||
} else {
|
|
||||||
setBasicFilter(context, settingsKey + '-filter-' + elem.getAttribute('data-settingname'), elem.querySelector('input'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
userSettings.setFilter(settingsKey + '-filter-GenreIds', genres.join(','));
|
userSettings.setFilter(settingsKey + '-filter-GenreIds', genres.join(','));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
function bindCheckboxInput(context, on) {
|
function bindCheckboxInput(context, on) {
|
||||||
const elems = context.querySelectorAll('.checkboxList-verticalwrap');
|
const elems = context.querySelectorAll('.checkboxList-verticalwrap');
|
||||||
for (let i = 0, length = elems.length; i < length; i++) {
|
for (let i = 0, length = elems.length; i < length; i++) {
|
||||||
|
@ -289,7 +270,7 @@ class FilterMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submitted) {
|
if (submitted) {
|
||||||
saveValues(dlg, options.settings, options.settingsKey, options.setfilters);
|
saveValues(dlg, options.settings, options.settingsKey);
|
||||||
return resolve();
|
return resolve();
|
||||||
}
|
}
|
||||||
return resolve();
|
return resolve();
|
||||||
|
|
|
@ -291,7 +291,7 @@ function Guide(options) {
|
||||||
showPremiereIndicator: allowIndicators && userSettings.get('guide-indicator-premiere') !== 'false',
|
showPremiereIndicator: allowIndicators && userSettings.get('guide-indicator-premiere') !== 'false',
|
||||||
showNewIndicator: allowIndicators && userSettings.get('guide-indicator-new') !== 'false',
|
showNewIndicator: allowIndicators && userSettings.get('guide-indicator-new') !== 'false',
|
||||||
showRepeatIndicator: allowIndicators && userSettings.get('guide-indicator-repeat') === 'true',
|
showRepeatIndicator: allowIndicators && userSettings.get('guide-indicator-repeat') === 'true',
|
||||||
showEpisodeTitle: layoutManager.tv ? false : true
|
showEpisodeTitle: !layoutManager.tv
|
||||||
};
|
};
|
||||||
|
|
||||||
apiClient.getLiveTvChannels(channelQuery).then(function (channelsResult) {
|
apiClient.getLiveTvChannels(channelQuery).then(function (channelsResult) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import escapeHtml from 'escape-html';
|
||||||
import imageLoader from 'components/images/imageLoader';
|
import imageLoader from 'components/images/imageLoader';
|
||||||
import { appRouter } from 'components/router/appRouter';
|
import { appRouter } from 'components/router/appRouter';
|
||||||
import globalize from 'scripts/globalize';
|
import globalize from 'scripts/globalize';
|
||||||
import imageHelper from 'scripts/imagehelper';
|
import imageHelper from 'utils/image';
|
||||||
|
|
||||||
function getLibraryButtonsHtml(items: BaseItemDto[]) {
|
function getLibraryButtonsHtml(items: BaseItemDto[]) {
|
||||||
let html = '';
|
let html = '';
|
||||||
|
|
|
@ -9,6 +9,7 @@ import itemHelper from './itemHelper';
|
||||||
import { playbackManager } from './playback/playbackmanager';
|
import { playbackManager } from './playback/playbackmanager';
|
||||||
import ServerConnections from './ServerConnections';
|
import ServerConnections from './ServerConnections';
|
||||||
import toast from './toast/toast';
|
import toast from './toast/toast';
|
||||||
|
import * as userSettings from '../scripts/settings/userSettings';
|
||||||
|
|
||||||
export function getCommands(options) {
|
export function getCommands(options) {
|
||||||
const item = options.item;
|
const item = options.item;
|
||||||
|
@ -589,9 +590,16 @@ function play(item, resume, queue, queueNext) {
|
||||||
serverId: item.ServerId
|
serverId: item.ServerId
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
const sortParentId = 'items-' + (item.IsFolder ? item.Id : item.ParentId) + '-Folder';
|
||||||
|
const sortValues = userSettings.getSortValuesLegacy(sortParentId);
|
||||||
|
|
||||||
playbackManager[method]({
|
playbackManager[method]({
|
||||||
items: [item],
|
items: [item],
|
||||||
startPositionTicks: startPosition
|
startPositionTicks: startPosition,
|
||||||
|
queryOptions: {
|
||||||
|
SortBy: sortValues.sortBy,
|
||||||
|
SortOrder: sortValues.sortOrder
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,7 +177,7 @@ export function getListViewHtml(options) {
|
||||||
const isLargeStyle = options.imageSize === 'large';
|
const isLargeStyle = options.imageSize === 'large';
|
||||||
const enableOverview = options.enableOverview;
|
const enableOverview = options.enableOverview;
|
||||||
|
|
||||||
const clickEntireItem = layoutManager.tv ? true : false;
|
const clickEntireItem = layoutManager.tv;
|
||||||
const outerTagName = clickEntireItem ? 'button' : 'div';
|
const outerTagName = clickEntireItem ? 'button' : 'div';
|
||||||
const enableSideMediaInfo = options.enableSideMediaInfo != null ? options.enableSideMediaInfo : true;
|
const enableSideMediaInfo = options.enableSideMediaInfo != null ? options.enableSideMediaInfo : true;
|
||||||
|
|
||||||
|
|
|
@ -183,8 +183,8 @@ export function getMediaInfoHtml(item, options = {}) {
|
||||||
if (item.EndDate) {
|
if (item.EndDate) {
|
||||||
try {
|
try {
|
||||||
const endYear = datetime.toLocaleString(datetime.parseISO8601Date(item.EndDate).getFullYear(), { useGrouping: false });
|
const endYear = datetime.toLocaleString(datetime.parseISO8601Date(item.EndDate).getFullYear(), { useGrouping: false });
|
||||||
|
/* At this point, text will contain only the start year */
|
||||||
if (endYear !== item.ProductionYear) {
|
if (endYear !== text) {
|
||||||
text += ` - ${endYear}`;
|
text += ` - ${endYear}`;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { PluginType } from '../../types/plugin.ts';
|
||||||
import { includesAny } from '../../utils/container.ts';
|
import { includesAny } from '../../utils/container.ts';
|
||||||
import { getItems } from '../../utils/jellyfin-apiclient/getItems.ts';
|
import { getItems } from '../../utils/jellyfin-apiclient/getItems.ts';
|
||||||
import { getItemBackdropImageUrl } from '../../utils/jellyfin-apiclient/backdropImage';
|
import { getItemBackdropImageUrl } from '../../utils/jellyfin-apiclient/backdropImage';
|
||||||
|
import merge from 'lodash-es/merge';
|
||||||
|
|
||||||
const UNLIMITED_ITEMS = -1;
|
const UNLIMITED_ITEMS = -1;
|
||||||
|
|
||||||
|
@ -145,7 +146,7 @@ function createStreamInfoFromUrlItem(item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergePlaybackQueries(obj1, obj2) {
|
function mergePlaybackQueries(obj1, obj2) {
|
||||||
const query = Object.assign(obj1, obj2);
|
const query = merge({}, obj1, obj2);
|
||||||
|
|
||||||
const filters = query.Filters ? query.Filters.split(',') : [];
|
const filters = query.Filters ? query.Filters.split(',') : [];
|
||||||
if (filters.indexOf('IsNotFolder') === -1) {
|
if (filters.indexOf('IsNotFolder') === -1) {
|
||||||
|
@ -1798,15 +1799,15 @@ class PlaybackManager {
|
||||||
SortBy: options.shuffle ? 'Random' : null
|
SortBy: options.shuffle ? 'Random' : null
|
||||||
});
|
});
|
||||||
} else if (firstItem.Type === 'MusicArtist') {
|
} else if (firstItem.Type === 'MusicArtist') {
|
||||||
promise = getItemsForPlayback(serverId, {
|
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
|
||||||
ArtistIds: firstItem.Id,
|
ArtistIds: firstItem.Id,
|
||||||
Filters: 'IsNotFolder',
|
Filters: 'IsNotFolder',
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
SortBy: options.shuffle ? 'Random' : 'SortName',
|
SortBy: options.shuffle ? 'Random' : 'SortName',
|
||||||
MediaTypes: 'Audio'
|
MediaTypes: 'Audio'
|
||||||
});
|
}, queryOptions));
|
||||||
} else if (firstItem.MediaType === 'Photo') {
|
} else if (firstItem.MediaType === 'Photo') {
|
||||||
promise = getItemsForPlayback(serverId, {
|
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
|
||||||
ParentId: firstItem.ParentId,
|
ParentId: firstItem.ParentId,
|
||||||
Filters: 'IsNotFolder',
|
Filters: 'IsNotFolder',
|
||||||
// Setting this to true may cause some incorrect sorting
|
// Setting this to true may cause some incorrect sorting
|
||||||
|
@ -1814,7 +1815,7 @@ class PlaybackManager {
|
||||||
SortBy: options.shuffle ? 'Random' : 'SortName',
|
SortBy: options.shuffle ? 'Random' : 'SortName',
|
||||||
MediaTypes: 'Photo,Video',
|
MediaTypes: 'Photo,Video',
|
||||||
Limit: UNLIMITED_ITEMS
|
Limit: UNLIMITED_ITEMS
|
||||||
}).then(function (result) {
|
}, queryOptions)).then(function (result) {
|
||||||
const playbackItems = result.Items;
|
const playbackItems = result.Items;
|
||||||
|
|
||||||
let index = playbackItems.map(function (i) {
|
let index = playbackItems.map(function (i) {
|
||||||
|
@ -1830,7 +1831,7 @@ class PlaybackManager {
|
||||||
return Promise.resolve(result);
|
return Promise.resolve(result);
|
||||||
});
|
});
|
||||||
} else if (firstItem.Type === 'PhotoAlbum') {
|
} else if (firstItem.Type === 'PhotoAlbum') {
|
||||||
promise = getItemsForPlayback(serverId, {
|
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
|
||||||
ParentId: firstItem.Id,
|
ParentId: firstItem.Id,
|
||||||
Filters: 'IsNotFolder',
|
Filters: 'IsNotFolder',
|
||||||
// Setting this to true may cause some incorrect sorting
|
// Setting this to true may cause some incorrect sorting
|
||||||
|
@ -1839,15 +1840,15 @@ class PlaybackManager {
|
||||||
// Only include Photos because we do not handle mixed queues currently
|
// Only include Photos because we do not handle mixed queues currently
|
||||||
MediaTypes: 'Photo',
|
MediaTypes: 'Photo',
|
||||||
Limit: UNLIMITED_ITEMS
|
Limit: UNLIMITED_ITEMS
|
||||||
});
|
}, queryOptions));
|
||||||
} else if (firstItem.Type === 'MusicGenre') {
|
} else if (firstItem.Type === 'MusicGenre') {
|
||||||
promise = getItemsForPlayback(serverId, {
|
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
|
||||||
GenreIds: firstItem.Id,
|
GenreIds: firstItem.Id,
|
||||||
Filters: 'IsNotFolder',
|
Filters: 'IsNotFolder',
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
SortBy: options.shuffle ? 'Random' : 'SortName',
|
SortBy: options.shuffle ? 'Random' : 'SortName',
|
||||||
MediaTypes: 'Audio'
|
MediaTypes: 'Audio'
|
||||||
});
|
}, queryOptions));
|
||||||
} else if (firstItem.Type === 'Series' || firstItem.Type === 'Season') {
|
} else if (firstItem.Type === 'Series' || firstItem.Type === 'Season') {
|
||||||
const apiClient = ServerConnections.getApiClient(firstItem.ServerId);
|
const apiClient = ServerConnections.getApiClient(firstItem.ServerId);
|
||||||
|
|
||||||
|
@ -2773,6 +2774,12 @@ class PlaybackManager {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
if (item.AlbumId != null) {
|
||||||
|
return apiClient.getItem(apiClient.getCurrentUserId(), item.AlbumId).then(function(result) {
|
||||||
|
mediaSource.albumLUFS = result.LUFS;
|
||||||
|
return mediaSource;
|
||||||
|
});
|
||||||
|
}
|
||||||
return mediaSource;
|
return mediaSource;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -46,7 +46,7 @@ function showQualityMenu(player, btn) {
|
||||||
const bitrate = parseInt(id, 10);
|
const bitrate = parseInt(id, 10);
|
||||||
if (bitrate !== selectedBitrate) {
|
if (bitrate !== selectedBitrate) {
|
||||||
playbackManager.setMaxStreamingBitrate({
|
playbackManager.setMaxStreamingBitrate({
|
||||||
enableAutomaticBitrateDetection: bitrate ? false : true,
|
enableAutomaticBitrateDetection: !bitrate,
|
||||||
maxBitrate: bitrate
|
maxBitrate: bitrate
|
||||||
}, player);
|
}, player);
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ function loadForm(context, user, userSettings, apiClient) {
|
||||||
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
|
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
|
||||||
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
|
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
|
||||||
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
|
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
|
||||||
context.querySelector('.chkEnableAudioNormalization').checked = userSettings.enableAudioNormalization();
|
context.querySelector('#selectAudioNormalization').value = userSettings.selectAudioNormalization();
|
||||||
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
|
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
|
||||||
context.querySelector('.chkRememberAudioSelections').checked = user.Configuration.RememberAudioSelections || false;
|
context.querySelector('.chkRememberAudioSelections').checked = user.Configuration.RememberAudioSelections || false;
|
||||||
context.querySelector('.chkRememberSubtitleSelections').checked = user.Configuration.RememberSubtitleSelections || false;
|
context.querySelector('.chkRememberSubtitleSelections').checked = user.Configuration.RememberSubtitleSelections || false;
|
||||||
|
@ -218,7 +218,7 @@ function saveUser(context, user, userSettingsInstance, apiClient) {
|
||||||
user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked;
|
user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked;
|
||||||
userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked);
|
userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked);
|
||||||
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
|
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
|
||||||
userSettingsInstance.enableAudioNormalization(context.querySelector('.chkEnableAudioNormalization').checked);
|
userSettingsInstance.selectAudioNormalization(context.querySelector('#selectAudioNormalization').value);
|
||||||
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
|
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
|
||||||
user.Configuration.RememberAudioSelections = context.querySelector('.chkRememberAudioSelections').checked;
|
user.Configuration.RememberAudioSelections = context.querySelector('.chkRememberAudioSelections').checked;
|
||||||
user.Configuration.RememberSubtitleSelections = context.querySelector('.chkRememberSubtitleSelections').checked;
|
user.Configuration.RememberSubtitleSelections = context.querySelector('.chkRememberSubtitleSelections').checked;
|
||||||
|
|
|
@ -72,12 +72,13 @@
|
||||||
${TabAdvanced}
|
${TabAdvanced}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="selectContainer">
|
||||||
<label>
|
<select is="emby-select" id="selectAudioNormalization" label="${LabelSelectAudioNormalization}">
|
||||||
<input type="checkbox" is="emby-checkbox" class="chkEnableAudioNormalization" />
|
<option value="Off">${Off}</option>
|
||||||
<span>${EnableAudioNormalization}</span>
|
<option value="TrackGain">${LabelTrackGain}</option>
|
||||||
</label>
|
<option value="AlbumGain">${LabelAlbumGain}</option>
|
||||||
<div class="fieldDescription checkboxFieldDescription">${EnableAudioNormalizationHelp}</div>
|
</select>
|
||||||
|
<div class="fieldDescription">${SelectAudioNormalizationHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
|
|
|
@ -11,6 +11,7 @@ import dom from '../scripts/dom';
|
||||||
import recordingHelper from './recordingcreator/recordinghelper';
|
import recordingHelper from './recordingcreator/recordinghelper';
|
||||||
import ServerConnections from './ServerConnections';
|
import ServerConnections from './ServerConnections';
|
||||||
import toast from './toast/toast';
|
import toast from './toast/toast';
|
||||||
|
import * as userSettings from '../scripts/settings/userSettings';
|
||||||
|
|
||||||
function playAllFromHere(card, serverId, queue) {
|
function playAllFromHere(card, serverId, queue) {
|
||||||
const parent = card.parentNode;
|
const parent = card.parentNode;
|
||||||
|
@ -177,6 +178,10 @@ function executeAction(card, target, action) {
|
||||||
|
|
||||||
const item = getItemInfoFromCard(card);
|
const item = getItemInfoFromCard(card);
|
||||||
|
|
||||||
|
const itemsContainer = dom.parentWithClass(card, 'itemsContainer');
|
||||||
|
|
||||||
|
const sortParentId = 'items-' + (item.IsFolder ? item.Id : itemsContainer?.getAttribute('data-parentid')) + '-Folder';
|
||||||
|
|
||||||
const serverId = item.ServerId;
|
const serverId = item.ServerId;
|
||||||
const type = item.Type;
|
const type = item.Type;
|
||||||
|
|
||||||
|
@ -200,12 +205,17 @@ function executeAction(card, target, action) {
|
||||||
});
|
});
|
||||||
} else if (action === 'play' || action === 'resume') {
|
} else if (action === 'play' || action === 'resume') {
|
||||||
const startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0', 10);
|
const startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0', 10);
|
||||||
|
const sortValues = userSettings.getSortValuesLegacy(sortParentId, 'SortName');
|
||||||
|
|
||||||
if (playbackManager.canPlay(item)) {
|
if (playbackManager.canPlay(item)) {
|
||||||
playbackManager.play({
|
playbackManager.play({
|
||||||
ids: [playableItemId],
|
ids: [playableItemId],
|
||||||
startPositionTicks: startPositionTicks,
|
startPositionTicks: startPositionTicks,
|
||||||
serverId: serverId
|
serverId: serverId,
|
||||||
|
queryOptions: {
|
||||||
|
SortBy: sortValues.sortBy,
|
||||||
|
SortOrder: sortValues.sortOrder
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.warn('Unable to play item', item);
|
console.warn('Unable to play item', item);
|
||||||
|
|
|
@ -18,8 +18,8 @@ function onSubmit(e) {
|
||||||
function initEditor(context, settings) {
|
function initEditor(context, settings) {
|
||||||
context.querySelector('form').addEventListener('submit', onSubmit);
|
context.querySelector('form').addEventListener('submit', onSubmit);
|
||||||
|
|
||||||
context.querySelector('.selectSortOrder').value = settings.SortOrder;
|
context.querySelector('.selectSortOrder').value = settings.sortOrder;
|
||||||
context.querySelector('.selectSortBy').value = settings.SortBy;
|
context.querySelector('.selectSortBy').value = settings.sortBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
function centerFocus(elem, horiz, on) {
|
function centerFocus(elem, horiz, on) {
|
||||||
|
@ -37,19 +37,10 @@ function fillSortBy(context, options) {
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveValues(context, settingsKey, setSortValues) {
|
function saveValues(context, settingsKey) {
|
||||||
if (setSortValues) {
|
|
||||||
setSortValues((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
StartIndex: 0,
|
|
||||||
SortBy: context.querySelector('.selectSortBy').value,
|
|
||||||
SortOrder: context.querySelector('.selectSortOrder').value
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
userSettings.setFilter(settingsKey + '-sortorder', context.querySelector('.selectSortOrder').value);
|
userSettings.setFilter(settingsKey + '-sortorder', context.querySelector('.selectSortOrder').value);
|
||||||
userSettings.setFilter(settingsKey + '-sortby', context.querySelector('.selectSortBy').value);
|
userSettings.setFilter(settingsKey + '-sortby', context.querySelector('.selectSortBy').value);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class SortMenu {
|
class SortMenu {
|
||||||
show(options) {
|
show(options) {
|
||||||
|
@ -104,7 +95,7 @@ class SortMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submitted) {
|
if (submitted) {
|
||||||
saveValues(dlg, options.settingsKey, options.setSortValues);
|
saveValues(dlg, options.settingsKey);
|
||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import Toolbar from '@mui/material/Toolbar';
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import React, { FC, ReactNode } from 'react';
|
import React, { FC, ReactNode } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import appIcon from 'assets/img/icon-transparent.png';
|
import appIcon from 'assets/img/icon-transparent.png';
|
||||||
import { appRouter } from 'components/router/appRouter';
|
import { appRouter } from 'components/router/appRouter';
|
||||||
|
@ -38,9 +38,13 @@ const AppToolbar: FC<AppToolbarProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const { user } = useApi();
|
const { user } = useApi();
|
||||||
const isUserLoggedIn = Boolean(user);
|
const isUserLoggedIn = Boolean(user);
|
||||||
|
const currentLocation = useLocation();
|
||||||
|
|
||||||
const isBackButtonAvailable = appRouter.canGoBack();
|
const isBackButtonAvailable = appRouter.canGoBack();
|
||||||
|
|
||||||
|
// Handles a specific case to hide the user menu on the select server page while authenticated
|
||||||
|
const isUserMenuAvailable = currentLocation.pathname !== '/selectserver.html';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Toolbar
|
<Toolbar
|
||||||
variant='dense'
|
variant='dense'
|
||||||
|
@ -111,7 +115,7 @@ const AppToolbar: FC<AppToolbarProps> = ({
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
{isUserLoggedIn && (
|
{isUserLoggedIn && isUserMenuAvailable && (
|
||||||
<>
|
<>
|
||||||
<Box sx={{ display: 'flex', flexGrow: 1, justifyContent: 'flex-end' }}>
|
<Box sx={{ display: 'flex', flexGrow: 1, justifyContent: 'flex-end' }}>
|
||||||
{buttons}
|
{buttons}
|
||||||
|
|
|
@ -29,17 +29,7 @@ function initEditor(context, settings) {
|
||||||
context.querySelector('.selectImageType').value = settings.imageType || 'primary';
|
context.querySelector('.selectImageType').value = settings.imageType || 'primary';
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveValues(context, settings, settingsKey, setviewsettings) {
|
function saveValues(context, settings, settingsKey) {
|
||||||
if (setviewsettings) {
|
|
||||||
setviewsettings((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
StartIndex: 0,
|
|
||||||
imageType: context.querySelector('.selectImageType').value,
|
|
||||||
showTitle: context.querySelector('.chkShowTitle').checked || false,
|
|
||||||
showYear: context.querySelector('.chkShowYear').checked || false,
|
|
||||||
cardLayout: context.querySelector('.chkEnableCardLayout').checked || false
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
const elems = context.querySelectorAll('.viewSetting-checkboxContainer');
|
const elems = context.querySelectorAll('.viewSetting-checkboxContainer');
|
||||||
for (const elem of elems) {
|
for (const elem of elems) {
|
||||||
userSettings.set(settingsKey + '-' + elem.getAttribute('data-settingname'), elem.querySelector('input').checked);
|
userSettings.set(settingsKey + '-' + elem.getAttribute('data-settingname'), elem.querySelector('input').checked);
|
||||||
|
@ -47,7 +37,6 @@ function saveValues(context, settings, settingsKey, setviewsettings) {
|
||||||
|
|
||||||
userSettings.set(settingsKey + '-imageType', context.querySelector('.selectImageType').value);
|
userSettings.set(settingsKey + '-imageType', context.querySelector('.selectImageType').value);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function centerFocus(elem, horiz, on) {
|
function centerFocus(elem, horiz, on) {
|
||||||
import('../../scripts/scrollHelper').then((scrollHelper) => {
|
import('../../scripts/scrollHelper').then((scrollHelper) => {
|
||||||
|
@ -112,7 +101,6 @@ class ViewSettings {
|
||||||
dlg.querySelector('.selectImageType').addEventListener('change', function () {
|
dlg.querySelector('.selectImageType').addEventListener('change', function () {
|
||||||
showIfAllowed(dlg, '.chkTitleContainer', this.value !== 'list' && this.value !== 'banner');
|
showIfAllowed(dlg, '.chkTitleContainer', this.value !== 'list' && this.value !== 'banner');
|
||||||
showIfAllowed(dlg, '.chkYearContainer', this.value !== 'list' && this.value !== 'banner');
|
showIfAllowed(dlg, '.chkYearContainer', this.value !== 'list' && this.value !== 'banner');
|
||||||
showIfAllowed(dlg, '.chkCardLayoutContainer', this.value !== 'list' && this.value !== 'banner');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dlg.querySelector('.btnCancel').addEventListener('click', function () {
|
dlg.querySelector('.btnCancel').addEventListener('click', function () {
|
||||||
|
@ -137,7 +125,7 @@ class ViewSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submitted) {
|
if (submitted) {
|
||||||
saveValues(dlg, options.settings, options.settingsKey, options.setviewsettings);
|
saveValues(dlg, options.settings, options.settingsKey);
|
||||||
return resolve();
|
return resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,13 +35,6 @@
|
||||||
<span>${GroupBySeries}</span>
|
<span>${GroupBySeries}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer viewSetting viewSetting-checkboxContainer hide chkCardLayoutContainer" data-settingname="cardLayout">
|
|
||||||
<label>
|
|
||||||
<input is="emby-checkbox" type="checkbox" class="chkEnableCardLayout" />
|
|
||||||
<span>${EnableCardLayout}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import playMethodHelper from '../../components/playback/playmethodhelper';
|
||||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||||
import imageLoader from '../../components/images/imageLoader';
|
import imageLoader from '../../components/images/imageLoader';
|
||||||
import ActivityLog from '../../components/activitylog';
|
import ActivityLog from '../../components/activitylog';
|
||||||
import imageHelper from '../../scripts/imagehelper';
|
import imageHelper from '../../utils/image';
|
||||||
import indicators from '../../components/indicators/indicators';
|
import indicators from '../../components/indicators/indicators';
|
||||||
import '../../components/listview/listview.scss';
|
import '../../components/listview/listview.scss';
|
||||||
import '../../elements/emby-button/emby-button';
|
import '../../elements/emby-button/emby-button';
|
||||||
|
|
|
@ -3,7 +3,7 @@ import cardBuilder from '../../../components/cardbuilder/cardBuilder';
|
||||||
import loading from '../../../components/loading/loading';
|
import loading from '../../../components/loading/loading';
|
||||||
import dom from '../../../scripts/dom';
|
import dom from '../../../scripts/dom';
|
||||||
import globalize from '../../../scripts/globalize';
|
import globalize from '../../../scripts/globalize';
|
||||||
import imageHelper from '../../../scripts/imagehelper';
|
import imageHelper from '../../../utils/image';
|
||||||
import { formatDistanceToNow } from 'date-fns';
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
import { getLocaleWithSuffix } from '../../../utils/dateFnsLocale.ts';
|
import { getLocaleWithSuffix } from '../../../utils/dateFnsLocale.ts';
|
||||||
import '../../../elements/emby-button/emby-button';
|
import '../../../elements/emby-button/emby-button';
|
||||||
|
|
|
@ -254,7 +254,9 @@
|
||||||
</div>
|
</div>
|
||||||
<button type="button" is="paper-icon-button-light" id="btnSelectFallbackFontPath" class="emby-input-iconbutton"><span class="material-icons search" aria-hidden="true"></span></button>
|
<button type="button" is="paper-icon-button-light" id="btnSelectFallbackFontPath" class="emby-input-iconbutton"><span class="material-icons search" aria-hidden="true"></span></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="fieldDescription">${LabelFallbackFontPathHelp}</div>
|
<div class="fieldDescription">
|
||||||
|
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="https://jellyfin.org/docs/general/administration/configuration#fonts" target="_blank">${LabelFallbackFontPathHelp}</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
<label>
|
<label>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import loading from '../../components/loading/loading';
|
||||||
import libraryMenu from '../../scripts/libraryMenu';
|
import libraryMenu from '../../scripts/libraryMenu';
|
||||||
import globalize from '../../scripts/globalize';
|
import globalize from '../../scripts/globalize';
|
||||||
import dom from '../../scripts/dom';
|
import dom from '../../scripts/dom';
|
||||||
import imageHelper from '../../scripts/imagehelper';
|
import imageHelper from '../../utils/image';
|
||||||
import '../../components/cardbuilder/card.scss';
|
import '../../components/cardbuilder/card.scss';
|
||||||
import '../../elements/emby-itemrefreshindicator/emby-itemrefreshindicator';
|
import '../../elements/emby-itemrefreshindicator/emby-itemrefreshindicator';
|
||||||
import Dashboard, { pageClassOn, pageIdOn } from '../../utils/dashboard';
|
import Dashboard, { pageClassOn, pageIdOn } from '../../utils/dashboard';
|
||||||
|
|
|
@ -1374,8 +1374,8 @@ function renderChildren(page, item) {
|
||||||
items: result.Items,
|
items: result.Items,
|
||||||
showIndexNumber: false,
|
showIndexNumber: false,
|
||||||
enableOverview: true,
|
enableOverview: true,
|
||||||
enablePlayedButton: layoutManager.mobile ? false : true,
|
enablePlayedButton: !layoutManager.mobile,
|
||||||
infoButton: layoutManager.mobile ? false : true,
|
infoButton: !layoutManager.mobile,
|
||||||
imageSize: 'large',
|
imageSize: 'large',
|
||||||
enableSideMediaInfo: false,
|
enableSideMediaInfo: false,
|
||||||
highlight: false,
|
highlight: false,
|
||||||
|
|
|
@ -724,8 +724,13 @@ class ItemsView {
|
||||||
const currentItem = self.currentItem;
|
const currentItem = self.currentItem;
|
||||||
|
|
||||||
if (currentItem && !self.hasFilters) {
|
if (currentItem && !self.hasFilters) {
|
||||||
|
const values = self.getSortValues();
|
||||||
playbackManager.play({
|
playbackManager.play({
|
||||||
items: [currentItem],
|
items: [currentItem],
|
||||||
|
queryOptions: {
|
||||||
|
SortBy: values.sortBy,
|
||||||
|
SortOrder: values.sortOrder
|
||||||
|
},
|
||||||
autoplay: true
|
autoplay: true
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -960,10 +965,7 @@ class ItemsView {
|
||||||
|
|
||||||
getSortValues() {
|
getSortValues() {
|
||||||
const basekey = this.getSettingsKey();
|
const basekey = this.getSettingsKey();
|
||||||
return {
|
return userSettings.getSortValuesLegacy(basekey, this.getDefaultSortBy());
|
||||||
sortBy: userSettings.getFilter(basekey + '-sortby') || this.getDefaultSortBy(),
|
|
||||||
sortOrder: userSettings.getFilter(basekey + '-sortorder') === 'Descending' ? 'Descending' : 'Ascending'
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultSortBy() {
|
getDefaultSortBy() {
|
||||||
|
|
|
@ -1,19 +1,48 @@
|
||||||
import RemoteControl from '../../../components/remotecontrol/remotecontrol';
|
import RemoteControl from '../../../components/remotecontrol/remotecontrol';
|
||||||
|
import { playbackManager } from '../../../components/playback/playbackmanager';
|
||||||
import libraryMenu from '../../../scripts/libraryMenu';
|
import libraryMenu from '../../../scripts/libraryMenu';
|
||||||
import '../../../elements/emby-button/emby-button';
|
import '../../../elements/emby-button/emby-button';
|
||||||
|
|
||||||
export default function (view) {
|
export default function (view) {
|
||||||
const remoteControl = new RemoteControl();
|
const remoteControl = new RemoteControl();
|
||||||
remoteControl.init(view, view.querySelector('.remoteControlContent'));
|
remoteControl.init(view, view.querySelector('.remoteControlContent'));
|
||||||
|
|
||||||
|
let currentPlayer;
|
||||||
|
|
||||||
|
function onKeyDown(e) {
|
||||||
|
if (e.keyCode === 32 && e.target.tagName !== 'BUTTON') {
|
||||||
|
playbackManager.playPause(currentPlayer);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function releaseCurrentPlayer() {
|
||||||
|
const player = currentPlayer;
|
||||||
|
if (player) currentPlayer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindToPlayer(player) {
|
||||||
|
if (player !== currentPlayer) {
|
||||||
|
releaseCurrentPlayer();
|
||||||
|
currentPlayer = player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
view.addEventListener('viewshow', function () {
|
view.addEventListener('viewshow', function () {
|
||||||
libraryMenu.setTransparentMenu(true);
|
libraryMenu.setTransparentMenu(true);
|
||||||
|
bindToPlayer(playbackManager.getCurrentPlayer());
|
||||||
|
document.addEventListener('keydown', onKeyDown);
|
||||||
|
|
||||||
if (remoteControl) {
|
if (remoteControl) {
|
||||||
remoteControl.onShow();
|
remoteControl.onShow();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
view.addEventListener('viewbeforehide', function () {
|
view.addEventListener('viewbeforehide', function () {
|
||||||
libraryMenu.setTransparentMenu(false);
|
libraryMenu.setTransparentMenu(false);
|
||||||
|
document.removeEventListener('keydown', onKeyDown);
|
||||||
|
releaseCurrentPlayer();
|
||||||
|
|
||||||
if (remoteControl) {
|
if (remoteControl) {
|
||||||
remoteControl.destroy();
|
remoteControl.destroy();
|
||||||
|
|
|
@ -21,7 +21,7 @@ function onKeyDown(e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const enableRefreshHack = browser.tizen || browser.orsay || browser.operaTv || browser.web0s ? true : false;
|
const enableRefreshHack = browser.tizen || browser.orsay || browser.operaTv || browser.web0s;
|
||||||
|
|
||||||
function forceRefresh(loading) {
|
function forceRefresh(loading) {
|
||||||
const elem = this.parentNode;
|
const elem = this.parentNode;
|
||||||
|
|
|
@ -3,11 +3,7 @@ import scrollerFactory from '../../libraries/scroller';
|
||||||
import globalize from '../../scripts/globalize';
|
import globalize from '../../scripts/globalize';
|
||||||
import IconButton from '../emby-button/IconButton';
|
import IconButton from '../emby-button/IconButton';
|
||||||
import './emby-scrollbuttons.scss';
|
import './emby-scrollbuttons.scss';
|
||||||
|
import { ScrollDirection, scrollerItemSlideIntoView } from './utils';
|
||||||
enum Direction {
|
|
||||||
RIGHT,
|
|
||||||
LEFT,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ScrollButtonsProps {
|
interface ScrollButtonsProps {
|
||||||
scrollerFactoryRef: React.MutableRefObject<scrollerFactory | null>;
|
scrollerFactoryRef: React.MutableRefObject<scrollerFactory | null>;
|
||||||
|
@ -22,31 +18,16 @@ const ScrollButtons: FC<ScrollButtonsProps> = ({ scrollerFactoryRef, scrollState
|
||||||
const [localeScrollPos, setLocaleScrollPos] = useState<number>(0);
|
const [localeScrollPos, setLocaleScrollPos] = useState<number>(0);
|
||||||
const scrollButtonsRef = useRef<HTMLDivElement>(null);
|
const scrollButtonsRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const scrollToPosition = useCallback((pos: number, immediate: boolean) => {
|
const onScrollButtonClick = useCallback((direction: ScrollDirection) => {
|
||||||
if (scrollerFactoryRef.current) {
|
scrollerItemSlideIntoView({
|
||||||
scrollerFactoryRef.current.slideTo(pos, immediate, undefined );
|
direction,
|
||||||
}
|
scroller: scrollerFactoryRef.current,
|
||||||
}, [scrollerFactoryRef]);
|
scrollState
|
||||||
|
});
|
||||||
|
}, [scrollState, scrollerFactoryRef]);
|
||||||
|
|
||||||
const onScrollButtonClick = useCallback((direction: Direction) => {
|
const triggerScrollLeft = useCallback(() => onScrollButtonClick(ScrollDirection.LEFT), [ onScrollButtonClick ]);
|
||||||
let newPos;
|
const triggerScrollRight = useCallback(() => onScrollButtonClick(ScrollDirection.RIGHT), [ onScrollButtonClick ]);
|
||||||
if (direction === Direction.LEFT) {
|
|
||||||
newPos = Math.max(0, scrollState.scrollPos - scrollState.scrollSize);
|
|
||||||
} else {
|
|
||||||
newPos = scrollState.scrollPos + scrollState.scrollSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (globalize.getIsRTL() && direction === Direction.LEFT) {
|
|
||||||
newPos = scrollState.scrollPos + scrollState.scrollSize;
|
|
||||||
} else if (globalize.getIsRTL()) {
|
|
||||||
newPos = Math.min(0, scrollState.scrollPos - scrollState.scrollSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToPosition(newPos, false);
|
|
||||||
}, [ scrollState.scrollPos, scrollState.scrollSize, scrollToPosition ]);
|
|
||||||
|
|
||||||
const triggerScrollLeft = useCallback(() => onScrollButtonClick(Direction.LEFT), [ onScrollButtonClick ]);
|
|
||||||
const triggerScrollRight = useCallback(() => onScrollButtonClick(Direction.RIGHT), [ onScrollButtonClick ]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const parent = scrollButtonsRef.current?.parentNode as HTMLDivElement;
|
const parent = scrollButtonsRef.current?.parentNode as HTMLDivElement;
|
||||||
|
@ -67,7 +48,7 @@ const ScrollButtons: FC<ScrollButtonsProps> = ({ scrollerFactoryRef, scrollState
|
||||||
className='emby-scrollbuttons-button btnPrev'
|
className='emby-scrollbuttons-button btnPrev'
|
||||||
onClick={triggerScrollLeft}
|
onClick={triggerScrollLeft}
|
||||||
icon='chevron_left'
|
icon='chevron_left'
|
||||||
disabled={localeScrollPos > 0 ? false : true}
|
disabled={localeScrollPos <= 0}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -75,7 +56,7 @@ const ScrollButtons: FC<ScrollButtonsProps> = ({ scrollerFactoryRef, scrollState
|
||||||
className='emby-scrollbuttons-button btnNext'
|
className='emby-scrollbuttons-button btnNext'
|
||||||
onClick={triggerScrollRight}
|
onClick={triggerScrollRight}
|
||||||
icon='chevron_right'
|
icon='chevron_right'
|
||||||
disabled={scrollState.scrollWidth > 0 && localeScrollPos + scrollState.scrollSize >= scrollState.scrollWidth ? true : false}
|
disabled={scrollState.scrollWidth > 0 && localeScrollPos + scrollState.scrollSize >= scrollState.scrollWidth}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import './emby-scrollbuttons.scss';
|
||||||
import 'webcomponents.js/webcomponents-lite';
|
import 'webcomponents.js/webcomponents-lite';
|
||||||
import '../emby-button/paper-icon-button-light';
|
import '../emby-button/paper-icon-button-light';
|
||||||
import globalize from '../../scripts/globalize';
|
import globalize from '../../scripts/globalize';
|
||||||
|
import { scrollerItemSlideIntoView } from './utils';
|
||||||
|
|
||||||
const EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype);
|
const EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype);
|
||||||
|
|
||||||
|
@ -128,26 +129,16 @@ function getScrollSize(elem) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onScrollButtonClick() {
|
function onScrollButtonClick() {
|
||||||
const scroller = this.parentNode.nextSibling;
|
|
||||||
|
|
||||||
const direction = this.getAttribute('data-direction');
|
const direction = this.getAttribute('data-direction');
|
||||||
const scrollSize = getScrollSize(scroller);
|
const scroller = this.parentNode.nextSibling;
|
||||||
const scrollPos = getScrollPosition(scroller);
|
const scrollPosition = getScrollPosition(scroller);
|
||||||
|
scrollerItemSlideIntoView({
|
||||||
let newPos;
|
direction,
|
||||||
if (direction === 'left') {
|
scroller,
|
||||||
newPos = Math.max(0, scrollPos - scrollSize);
|
scrollState: {
|
||||||
} else {
|
scrollPos: scrollPosition
|
||||||
newPos = scrollPos + scrollSize;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
if (globalize.getIsRTL() && direction === 'left') {
|
|
||||||
newPos = scrollPos + scrollSize;
|
|
||||||
} else if (globalize.getIsRTL()) {
|
|
||||||
newPos = Math.min(0, scrollPos - scrollSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
scroller.scrollToPosition(newPos, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EmbyScrollButtonsPrototype.attachedCallback = function () {
|
EmbyScrollButtonsPrototype.attachedCallback = function () {
|
||||||
|
|
103
src/elements/emby-scrollbuttons/utils.ts
Normal file
103
src/elements/emby-scrollbuttons/utils.ts
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
import ScrollerFactory from 'libraries/scroller';
|
||||||
|
import globalize from 'scripts/globalize';
|
||||||
|
|
||||||
|
export enum ScrollDirection {
|
||||||
|
RIGHT = 'right',
|
||||||
|
LEFT = 'left',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ScrollState {
|
||||||
|
scrollPos: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ScrollerItemSlideIntoViewProps {
|
||||||
|
direction: ScrollDirection;
|
||||||
|
scroller: ScrollerFactory | null;
|
||||||
|
scrollState: ScrollState;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ScrollToWindowProps {
|
||||||
|
scroller: ScrollerFactory;
|
||||||
|
items: HTMLElement[];
|
||||||
|
scrollState: ScrollState;
|
||||||
|
direction: ScrollDirection
|
||||||
|
}
|
||||||
|
|
||||||
|
export function scrollerItemSlideIntoView({ direction, scroller, scrollState }: ScrollerItemSlideIntoViewProps) {
|
||||||
|
if (!scroller) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slider: HTMLElement = scroller.getScrollSlider();
|
||||||
|
const items = [...slider.children] as HTMLElement[];
|
||||||
|
|
||||||
|
scrollToWindow({
|
||||||
|
scroller,
|
||||||
|
items,
|
||||||
|
scrollState,
|
||||||
|
direction
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFirstAndLastVisible(scrollFrame: HTMLElement, items: HTMLElement[], { scrollPos: scrollPosition }: ScrollState) {
|
||||||
|
const isRTL = globalize.getIsRTL();
|
||||||
|
const localeModifier = isRTL ? -1 : 1;
|
||||||
|
|
||||||
|
const currentScrollPos = scrollPosition * localeModifier;
|
||||||
|
const scrollerWidth = scrollFrame.offsetWidth;
|
||||||
|
const itemWidth = items[0].offsetWidth;
|
||||||
|
|
||||||
|
// Rounding down here will give us the first item index which is fully visible. We want the first partially visible
|
||||||
|
// index so we'll subtract one.
|
||||||
|
const firstVisibleIndex = Math.max(Math.floor(currentScrollPos / itemWidth) - 1, 0);
|
||||||
|
// Rounding up will give us the last index which is at least partially visible (overflows at container end).
|
||||||
|
const lastVisibleIndex = Math.floor((currentScrollPos + scrollerWidth) / itemWidth);
|
||||||
|
|
||||||
|
return [firstVisibleIndex, lastVisibleIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollToWindow({
|
||||||
|
scroller,
|
||||||
|
items,
|
||||||
|
scrollState,
|
||||||
|
direction = ScrollDirection.RIGHT
|
||||||
|
}: ScrollToWindowProps) {
|
||||||
|
// When we're rendering RTL, scrolling toward the end of the container is toward the left so all of our scroll
|
||||||
|
// positions need to be negative.
|
||||||
|
const isRTL = globalize.getIsRTL();
|
||||||
|
const localeModifier = isRTL ? -1 : 1;
|
||||||
|
|
||||||
|
// NOTE: The legacy scroller is passing in an Element which is the frame element and has some of the scroller
|
||||||
|
// factory functions on it, but is not a true scroller factory. For legacy, we need to pass `scroller` directly
|
||||||
|
// instead of getting the frame from the factory instance.
|
||||||
|
const frame = scroller.getScrollFrame?.() ?? scroller;
|
||||||
|
const [firstVisibleIndex, lastVisibleIndex] = getFirstAndLastVisible(frame, items, scrollState);
|
||||||
|
|
||||||
|
let scrollToPosition: number;
|
||||||
|
|
||||||
|
if (direction === ScrollDirection.RIGHT) {
|
||||||
|
const nextItem = items[lastVisibleIndex];
|
||||||
|
|
||||||
|
// This will be the position to anchor the item at `lastVisibleIndex` to the start of the view window.
|
||||||
|
const nextItemScrollOffset = lastVisibleIndex * nextItem.offsetWidth;
|
||||||
|
scrollToPosition = nextItemScrollOffset * localeModifier;
|
||||||
|
} else {
|
||||||
|
const previousItem = items[firstVisibleIndex];
|
||||||
|
const previousItemScrollOffset = firstVisibleIndex * previousItem.offsetWidth;
|
||||||
|
|
||||||
|
// Find the total number of items that can fit in a view window and subtract one to account for item at
|
||||||
|
// `firstVisibleIndex`. The total width of these items is the amount that we need to adjust the scroll position by
|
||||||
|
// to anchor item at `firstVisibleIndex` to the end of the view window.
|
||||||
|
const offsetAdjustment = (Math.floor(frame.offsetWidth / previousItem.offsetWidth) - 1) * previousItem.offsetWidth;
|
||||||
|
|
||||||
|
// This will be the position to anchor the item at `firstVisibleIndex` to the end of the view window.
|
||||||
|
scrollToPosition = (previousItemScrollOffset - offsetAdjustment) * localeModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scroller.slideTo) {
|
||||||
|
scroller.slideTo(scrollToPosition, false, undefined);
|
||||||
|
} else {
|
||||||
|
// @ts-expect-error Legacy support passes in a `scroller` that isn't a ScrollFactory
|
||||||
|
scroller.scrollToPosition(scrollToPosition);
|
||||||
|
}
|
||||||
|
}
|
|
@ -112,9 +112,14 @@ class HtmlAudioPlayer {
|
||||||
let val = options.url;
|
let val = options.url;
|
||||||
console.debug('playing url: ' + val);
|
console.debug('playing url: ' + val);
|
||||||
import('../../scripts/settings/userSettings').then((userSettings) => {
|
import('../../scripts/settings/userSettings').then((userSettings) => {
|
||||||
if (userSettings.enableAudioNormalization() && options.item.LUFS != null) {
|
if (userSettings.selectAudioNormalization() == 'TrackGain' && options.item.LUFS != null) {
|
||||||
const dbGain = -18 - options.item.LUFS;
|
const dbGain = -18 - options.item.LUFS;
|
||||||
self.gainNode.gain.value = Math.pow(10, (dbGain / 20));
|
self.gainNode.gain.value = Math.pow(10, (dbGain / 20));
|
||||||
|
console.debug('[HtmlAudioPlayer] Using track gain');
|
||||||
|
} else if (userSettings.selectAudioNormalization() == 'AlbumGain' && options.mediaSource.albumLUFS != null) {
|
||||||
|
const dbGain = -18 - options.mediaSource.albumLUFS;
|
||||||
|
self.gainNode.gain.value = Math.pow(10, (dbGain / 20));
|
||||||
|
console.debug('[HtmlAudioPlayer] Using album gain');
|
||||||
} else {
|
} else {
|
||||||
self.gainNode.gain.value = 1;
|
self.gainNode.gain.value = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -692,7 +692,7 @@ export default function (options) {
|
||||||
|
|
||||||
profile.TranscodingProfiles = [];
|
profile.TranscodingProfiles = [];
|
||||||
|
|
||||||
const hlsBreakOnNonKeyFrames = browser.iOS || browser.osx || browser.edge || !canPlayNativeHls() ? true : false;
|
const hlsBreakOnNonKeyFrames = browser.iOS || browser.osx || browser.edge || !canPlayNativeHls();
|
||||||
|
|
||||||
if (canPlayHls() && browser.enableHlsAudio !== false) {
|
if (canPlayHls() && browser.enableHlsAudio !== false) {
|
||||||
profile.TranscodingProfiles.push({
|
profile.TranscodingProfiles.push({
|
||||||
|
|
|
@ -125,7 +125,8 @@ function renderSection(item, element, type) {
|
||||||
ArtistIds: '',
|
ArtistIds: '',
|
||||||
AlbumArtistIds: '',
|
AlbumArtistIds: '',
|
||||||
Limit: 10,
|
Limit: 10,
|
||||||
SortBy: 'SortName'
|
SortOrder: 'Descending,Desending,Ascending',
|
||||||
|
SortBy: 'PremiereDate,ProductionYear,SortName'
|
||||||
}, {
|
}, {
|
||||||
shape: 'overflowPortrait',
|
shape: 'overflowPortrait',
|
||||||
showTitle: true,
|
showTitle: true,
|
||||||
|
@ -194,7 +195,7 @@ function renderSection(item, element, type) {
|
||||||
PersonTypes: '',
|
PersonTypes: '',
|
||||||
ArtistIds: '',
|
ArtistIds: '',
|
||||||
AlbumArtistIds: '',
|
AlbumArtistIds: '',
|
||||||
SortOrder: 'Descending',
|
SortOrder: 'Descending,Desending,Ascending',
|
||||||
SortBy: 'PremiereDate,ProductionYear,Sortname'
|
SortBy: 'PremiereDate,ProductionYear,Sortname'
|
||||||
}, {
|
}, {
|
||||||
shape: 'overflowSquare',
|
shape: 'overflowSquare',
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { pluginManager } from '../components/pluginManager';
|
||||||
import groupSelectionMenu from '../plugins/syncPlay/ui/groupSelectionMenu';
|
import groupSelectionMenu from '../plugins/syncPlay/ui/groupSelectionMenu';
|
||||||
import browser from './browser';
|
import browser from './browser';
|
||||||
import globalize from './globalize';
|
import globalize from './globalize';
|
||||||
import imageHelper from './imagehelper';
|
import imageHelper from '../utils/image';
|
||||||
import { getMenuLinks } from '../scripts/settings/webSettings';
|
import { getMenuLinks } from '../scripts/settings/webSettings';
|
||||||
import Dashboard, { pageClassOn } from '../utils/dashboard';
|
import Dashboard, { pageClassOn } from '../utils/dashboard';
|
||||||
import ServerConnections from '../components/ServerConnections';
|
import ServerConnections from '../components/ServerConnections';
|
||||||
|
|
|
@ -158,15 +158,15 @@ export class UserSettings {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get or set 'Enable Audio Normalization' state.
|
* Get or set 'Enable Audio Normalization' state.
|
||||||
* @param {boolean|undefined} val - Flag to enable 'Enable Audio Normalization' or undefined.
|
* @param {string|undefined} val - Flag to enable 'Enable Audio Normalization' or undefined.
|
||||||
* @return {boolean} 'Enable Audio Normalization' state.
|
* @return {string} 'Enable Audio Normalization' state.
|
||||||
*/
|
*/
|
||||||
enableAudioNormalization(val) {
|
selectAudioNormalization(val) {
|
||||||
if (val !== undefined) {
|
if (val !== undefined) {
|
||||||
return this.set('enableAudioNormalization', val.toString(), false);
|
return this.set('selectAudioNormalization', val, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return toBoolean(this.get('enableAudioNormalization', false), true);
|
return this.get('selectAudioNormalization', false) || 'TrackGain';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -622,6 +622,21 @@ export class UserSettings {
|
||||||
getFilter(key) {
|
getFilter(key) {
|
||||||
return this.get(key, true);
|
return this.get(key, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current sort values (Legacy - Non-JSON)
|
||||||
|
* (old views such as list.js [Photos] will
|
||||||
|
* use this one)
|
||||||
|
* @param {string} key - Filter key.
|
||||||
|
* @param {string} defaultSortBy - Default SortBy value.
|
||||||
|
* @return {Object} sortOptions object
|
||||||
|
*/
|
||||||
|
getSortValuesLegacy(key, defaultSortBy) {
|
||||||
|
return {
|
||||||
|
sortBy: this.getFilter(key + '-sortby') || defaultSortBy,
|
||||||
|
sortOrder: this.getFilter(key + '-sortorder') === 'Descending' ? 'Descending' : 'Ascending'
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const currentSettings = new UserSettings;
|
export const currentSettings = new UserSettings;
|
||||||
|
@ -636,7 +651,7 @@ export const serverConfig = currentSettings.serverConfig.bind(currentSettings);
|
||||||
export const allowedAudioChannels = currentSettings.allowedAudioChannels.bind(currentSettings);
|
export const allowedAudioChannels = currentSettings.allowedAudioChannels.bind(currentSettings);
|
||||||
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
|
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
|
||||||
export const enableCinemaMode = currentSettings.enableCinemaMode.bind(currentSettings);
|
export const enableCinemaMode = currentSettings.enableCinemaMode.bind(currentSettings);
|
||||||
export const enableAudioNormalization = currentSettings.enableAudioNormalization.bind(currentSettings);
|
export const selectAudioNormalization = currentSettings.selectAudioNormalization.bind(currentSettings);
|
||||||
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
|
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
|
||||||
export const enableVideoRemainingTime = currentSettings.enableVideoRemainingTime.bind(currentSettings);
|
export const enableVideoRemainingTime = currentSettings.enableVideoRemainingTime.bind(currentSettings);
|
||||||
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
||||||
|
@ -672,3 +687,4 @@ export const customCss = currentSettings.customCss.bind(currentSettings);
|
||||||
export const disableCustomCss = currentSettings.disableCustomCss.bind(currentSettings);
|
export const disableCustomCss = currentSettings.disableCustomCss.bind(currentSettings);
|
||||||
export const getSavedView = currentSettings.getSavedView.bind(currentSettings);
|
export const getSavedView = currentSettings.getSavedView.bind(currentSettings);
|
||||||
export const saveViewSetting = currentSettings.saveViewSetting.bind(currentSettings);
|
export const saveViewSetting = currentSettings.saveViewSetting.bind(currentSettings);
|
||||||
|
export const getSortValuesLegacy = currentSettings.getSortValuesLegacy.bind(currentSettings);
|
||||||
|
|
|
@ -1429,7 +1429,7 @@
|
||||||
"DeleteAll": "Odstranit vše",
|
"DeleteAll": "Odstranit vše",
|
||||||
"EnableFallbackFontHelp": "Povolit vlastní alternativní písma. Může vyřešit problémy s nesprávným vykreslením titulků.",
|
"EnableFallbackFontHelp": "Povolit vlastní alternativní písma. Může vyřešit problémy s nesprávným vykreslením titulků.",
|
||||||
"EnableFallbackFont": "Povolit záložní písma",
|
"EnableFallbackFont": "Povolit záložní písma",
|
||||||
"LabelFallbackFontPathHelp": "Zadejte cestu k náhradním písmům pro vykreslení titulků ve formátu ASS/SSA. Maximální celková velikost písma je 20 MB. Doporučujeme používat malé webové písma jako například formát woff2.",
|
"LabelFallbackFontPathHelp": "Tato písma používají někteří klienti k vykreslení titulků. Více informací naleznete v dokumentaci.",
|
||||||
"LabelFallbackFontPath": "Cesta k náhradním písmům",
|
"LabelFallbackFontPath": "Cesta k náhradním písmům",
|
||||||
"HeaderSelectFallbackFontPathHelp": "Vyberte či zadejte cestu k záložním písmům pro vykreslení titulků ve formátu ASS/SSA.",
|
"HeaderSelectFallbackFontPathHelp": "Vyberte či zadejte cestu k záložním písmům pro vykreslení titulků ve formátu ASS/SSA.",
|
||||||
"HeaderSelectFallbackFontPath": "Vybrat cestu k záložním fontům",
|
"HeaderSelectFallbackFontPath": "Vybrat cestu k záložním fontům",
|
||||||
|
@ -1776,5 +1776,9 @@
|
||||||
"UnknownError": "Došlo k neznámé chybě.",
|
"UnknownError": "Došlo k neznámé chybě.",
|
||||||
"BackdropScreensaver": "Pozadí",
|
"BackdropScreensaver": "Pozadí",
|
||||||
"LogoScreensaver": "Logo",
|
"LogoScreensaver": "Logo",
|
||||||
"LabelIsHearingImpaired": "Titulky pro neslyšící"
|
"LabelIsHearingImpaired": "Titulky pro neslyšící",
|
||||||
|
"LabelSelectAudioNormalization": "Normalizace hlasitosti",
|
||||||
|
"LabelAlbumGain": "Na úrovni alba",
|
||||||
|
"LabelTrackGain": "Na úrovni skladby",
|
||||||
|
"SelectAudioNormalizationHelp": "Normalizace na úrovni skladby upraví hlasitost všech skladeb tak, aby byla všude stejná. Normalizace na úrovni alba upraví hlasitost všech skladeb tak, aby byla hlasitost stejná v rámci jednotlivých alb."
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,7 @@
|
||||||
"ChannelNumber": "Channel number",
|
"ChannelNumber": "Channel number",
|
||||||
"Channels": "Channels",
|
"Channels": "Channels",
|
||||||
"CinemaModeConfigurationHelp": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.",
|
"CinemaModeConfigurationHelp": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.",
|
||||||
"EnableAudioNormalizationHelp": "Audio normalization will add a constant gain to keep the average at a desired level (-18dB).",
|
"SelectAudioNormalizationHelp": "Track gain - adjusts the volume of each track so they playback with the same loudness. Album gain - adjusts the volume of all the tracks in an album only, keeping the album's dynamic range.",
|
||||||
"ClearQueue": "Clear queue",
|
"ClearQueue": "Clear queue",
|
||||||
"ClientSettings": "Client Settings",
|
"ClientSettings": "Client Settings",
|
||||||
"Collections": "Collections",
|
"Collections": "Collections",
|
||||||
|
@ -226,7 +226,6 @@
|
||||||
"EnableBlurHash": "Enable blurred placeholders for images",
|
"EnableBlurHash": "Enable blurred placeholders for images",
|
||||||
"EnableBlurHashHelp": "Images that are still being loaded will be displayed with a unique placeholder.",
|
"EnableBlurHashHelp": "Images that are still being loaded will be displayed with a unique placeholder.",
|
||||||
"EnableCinemaMode": "Cinema mode",
|
"EnableCinemaMode": "Cinema mode",
|
||||||
"EnableAudioNormalization": "Audio Normalization",
|
|
||||||
"EnableColorCodedBackgrounds": "Color coded backgrounds",
|
"EnableColorCodedBackgrounds": "Color coded backgrounds",
|
||||||
"EnableDecodingColorDepth10Hevc": "Enable 10-bit hardware decoding for HEVC",
|
"EnableDecodingColorDepth10Hevc": "Enable 10-bit hardware decoding for HEVC",
|
||||||
"EnableDecodingColorDepth10Vp9": "Enable 10-bit hardware decoding for VP9",
|
"EnableDecodingColorDepth10Vp9": "Enable 10-bit hardware decoding for VP9",
|
||||||
|
@ -549,6 +548,7 @@
|
||||||
"LabelAlbumArtMaxResHelp": "Maximum resolution of album art exposed via the 'upnp:albumArtURI' property.",
|
"LabelAlbumArtMaxResHelp": "Maximum resolution of album art exposed via the 'upnp:albumArtURI' property.",
|
||||||
"LabelAlbumArtMaxWidth": "Album art max width",
|
"LabelAlbumArtMaxWidth": "Album art max width",
|
||||||
"LabelAlbumArtPN": "Album art PN",
|
"LabelAlbumArtPN": "Album art PN",
|
||||||
|
"LabelAlbumGain": "Album Gain",
|
||||||
"LabelAllowedRemoteAddresses": "Remote IP address filter",
|
"LabelAllowedRemoteAddresses": "Remote IP address filter",
|
||||||
"LabelAllowedRemoteAddressesMode": "Remote IP address filter mode",
|
"LabelAllowedRemoteAddressesMode": "Remote IP address filter mode",
|
||||||
"LabelAllowHWTranscoding": "Allow hardware transcoding",
|
"LabelAllowHWTranscoding": "Allow hardware transcoding",
|
||||||
|
@ -561,6 +561,7 @@
|
||||||
"LabelAudioChannels": "Audio channels",
|
"LabelAudioChannels": "Audio channels",
|
||||||
"LabelAudioCodec": "Audio codec",
|
"LabelAudioCodec": "Audio codec",
|
||||||
"LabelAudioLanguagePreference": "Preferred audio language",
|
"LabelAudioLanguagePreference": "Preferred audio language",
|
||||||
|
"LabelSelectAudioNormalization": "Audio Normalization",
|
||||||
"LabelAudioSampleRate": "Audio sample rate",
|
"LabelAudioSampleRate": "Audio sample rate",
|
||||||
"LabelAuthProvider": "Authentication Provider",
|
"LabelAuthProvider": "Authentication Provider",
|
||||||
"LabelAutomaticallyAddToCollection": "Automatically add to collection",
|
"LabelAutomaticallyAddToCollection": "Automatically add to collection",
|
||||||
|
@ -1625,7 +1626,7 @@
|
||||||
"HeaderSelectFallbackFontPath": "Select Fallback Font Folder Path",
|
"HeaderSelectFallbackFontPath": "Select Fallback Font Folder Path",
|
||||||
"HeaderSelectFallbackFontPathHelp": "Browse or enter the path of the fallback font folder to use for rendering ASS/SSA subtitles.",
|
"HeaderSelectFallbackFontPathHelp": "Browse or enter the path of the fallback font folder to use for rendering ASS/SSA subtitles.",
|
||||||
"LabelFallbackFontPath": "Fallback font folder path",
|
"LabelFallbackFontPath": "Fallback font folder path",
|
||||||
"LabelFallbackFontPathHelp": "Specify a path containing fallback fonts for rendering ASS/SSA subtitles. The maximum allowed total font size is 20 MB. Lightweight and web-friendly font formats such as woff2 are recommended.",
|
"LabelFallbackFontPathHelp": "These fonts are used by some clients to render subtitles. Please refer to the documentation for more information.",
|
||||||
"EnableFallbackFont": "Enable fallback fonts",
|
"EnableFallbackFont": "Enable fallback fonts",
|
||||||
"EnableFallbackFontHelp": "Enable custom alternate fonts. This can avoid the problem of incorrect subtitle rendering.",
|
"EnableFallbackFontHelp": "Enable custom alternate fonts. This can avoid the problem of incorrect subtitle rendering.",
|
||||||
"AspectRatioCover": "Cover",
|
"AspectRatioCover": "Cover",
|
||||||
|
@ -1636,6 +1637,7 @@
|
||||||
"LabelPlaybackInfo": "Playback Info",
|
"LabelPlaybackInfo": "Playback Info",
|
||||||
"LabelAudioInfo": "Audio Info",
|
"LabelAudioInfo": "Audio Info",
|
||||||
"LabelVideoInfo": "Video Info",
|
"LabelVideoInfo": "Video Info",
|
||||||
|
"LabelTrackGain": "Track Gain",
|
||||||
"LabelTranscodingInfo": "Transcoding Info",
|
"LabelTranscodingInfo": "Transcoding Info",
|
||||||
"LabelDirectStreamingInfo": "Direct Streaming Info",
|
"LabelDirectStreamingInfo": "Direct Streaming Info",
|
||||||
"LabelRemuxingInfo": "Remuxing Info",
|
"LabelRemuxingInfo": "Remuxing Info",
|
||||||
|
|
|
@ -1450,7 +1450,7 @@
|
||||||
"LabelAutomaticallyAddToCollectionHelp": "Kun ainakin kahdelle elokuvalle on ilmoitettu sama kokoelma, lisätään ne kokoelmaan automaattisesti.",
|
"LabelAutomaticallyAddToCollectionHelp": "Kun ainakin kahdelle elokuvalle on ilmoitettu sama kokoelma, lisätään ne kokoelmaan automaattisesti.",
|
||||||
"OptionSaveMetadataAsHiddenHelp": "Tämän muutos vaikuttaa vain jatkossa tehtäviin metatietojen tallennuksiin. Olemassa olevat metatietotiedostot päivitetään, kun palvelin tallentaa ne seuraavan kerran.",
|
"OptionSaveMetadataAsHiddenHelp": "Tämän muutos vaikuttaa vain jatkossa tehtäviin metatietojen tallennuksiin. Olemassa olevat metatietotiedostot päivitetään, kun palvelin tallentaa ne seuraavan kerran.",
|
||||||
"SpecialFeatures": "Lisämateriaalit",
|
"SpecialFeatures": "Lisämateriaalit",
|
||||||
"LabelFallbackFontPathHelp": "Määritä ASS/SSA-tekstitysten varmistusfonttien tiedostosijainti. Suurin sallittu fontin kokonaiskoko on 20 Mt. Kevyet verkkoystävälliset fontit, kuten woff2, ovat suositeltuja.",
|
"LabelFallbackFontPathHelp": "Jotkin päätelaitteet käyttävät tekstityksille näitä fontteja. Katso lisätietoja käyttöoppaasta.",
|
||||||
"HeaderSelectFallbackFontPathHelp": "Selaa tai syötä ASS/SSA-tekstitysten renderöintiin käytettävien varmistusfonttien tiedostosijainti.",
|
"HeaderSelectFallbackFontPathHelp": "Selaa tai syötä ASS/SSA-tekstitysten renderöintiin käytettävien varmistusfonttien tiedostosijainti.",
|
||||||
"PathNotFound": "Tiedostosijaintia ei löydy. Varmista, että se on oikein ja yritä uudelleen.",
|
"PathNotFound": "Tiedostosijaintia ei löydy. Varmista, että se on oikein ja yritä uudelleen.",
|
||||||
"XmlTvPathHelp": "XMLTV-tiedoston sijainti. Jellyfin lukee tiedoston ajoittain muutosten varalta. Olet itse vastuussa tiedoston luonnista ja päivityksestä.",
|
"XmlTvPathHelp": "XMLTV-tiedoston sijainti. Jellyfin lukee tiedoston ajoittain muutosten varalta. Olet itse vastuussa tiedoston luonnista ja päivityksestä.",
|
||||||
|
@ -1774,5 +1774,9 @@
|
||||||
"UnknownError": "Tapahtui tuntematon virhe.",
|
"UnknownError": "Tapahtui tuntematon virhe.",
|
||||||
"BackdropScreensaver": "Backdrop-näytönsäästäjä",
|
"BackdropScreensaver": "Backdrop-näytönsäästäjä",
|
||||||
"LogoScreensaver": "Logo-näytönsäästäjä",
|
"LogoScreensaver": "Logo-näytönsäästäjä",
|
||||||
"LabelIsHearingImpaired": "Kuulorajoitteisille (SDH)"
|
"LabelIsHearingImpaired": "Kuulorajoitteisille (SDH)",
|
||||||
|
"SelectAudioNormalizationHelp": "Kappalekohtainen vahvistus – jokaisen kappaleen äänenvoimakkuus pyritään säätämään samalle tasolle. Albumikohtainen vahvistus – kappaleiden äänenvoimakkuus pyritään tasaamaan albumikohtaisesti albumikohtaisen dynamiikan säilyttämiseksi.",
|
||||||
|
"LabelAlbumGain": "Albumikohtainen vahvistus",
|
||||||
|
"LabelSelectAudioNormalization": "Äänenvoimakkuuden normalisointi",
|
||||||
|
"LabelTrackGain": "Kappelkohtainen vahvistus"
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
"AllowedRemoteAddressesHelp": "Liste séparée par des virgules des adresses IP autorisées à se connecter à distance. Une liste laissée vide signifie que toutes les adresses sont autorisées.",
|
"AllowedRemoteAddressesHelp": "Liste séparée par des virgules des adresses IP autorisées à se connecter à distance. Une liste laissée vide signifie que toutes les adresses sont autorisées.",
|
||||||
"AlwaysPlaySubtitles": "Toujours afficher",
|
"AlwaysPlaySubtitles": "Toujours afficher",
|
||||||
"AlwaysPlaySubtitlesHelp": "Les sous-titres correspondant à la langue préférée seront chargés indépendamment de la langue de l'audio.",
|
"AlwaysPlaySubtitlesHelp": "Les sous-titres correspondant à la langue préférée seront chargés indépendamment de la langue de l'audio.",
|
||||||
"AnyLanguage": "N'importe quel langage",
|
"AnyLanguage": "N'importe quelle langue",
|
||||||
"Anytime": "N'importe quand",
|
"Anytime": "N'importe quand",
|
||||||
"AroundTime": "Aux environs de {0}",
|
"AroundTime": "Aux environs de {0}",
|
||||||
"Artists": "Artistes",
|
"Artists": "Artistes",
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,11 +23,11 @@
|
||||||
"AddToFavorites": "Додади во омилени",
|
"AddToFavorites": "Додади во омилени",
|
||||||
"All": "Сите",
|
"All": "Сите",
|
||||||
"Add": "Додади",
|
"Add": "Додади",
|
||||||
"Actor": "Актер",
|
"Actor": "Глумец",
|
||||||
"Absolute": "Абсолутно",
|
"Absolute": "Апсолутно",
|
||||||
"ButtonSubmit": "Испрати",
|
"ButtonSubmit": "Испрати",
|
||||||
"ButtonStop": "Стопирај",
|
"ButtonStop": "Стопирај",
|
||||||
"ButtonStart": "Почеток",
|
"ButtonStart": "Почни",
|
||||||
"ButtonSplit": "Раздели",
|
"ButtonSplit": "Раздели",
|
||||||
"ButtonExitApp": "Излези од апликацијата",
|
"ButtonExitApp": "Излези од апликацијата",
|
||||||
"ButtonSignOut": "Одјави се",
|
"ButtonSignOut": "Одјави се",
|
||||||
|
@ -44,50 +44,50 @@
|
||||||
"ButtonRemove": "Отстрани",
|
"ButtonRemove": "Отстрани",
|
||||||
"ButtonRefreshGuideData": "Освежи податоци на водич",
|
"ButtonRefreshGuideData": "Освежи податоци на водич",
|
||||||
"ButtonQuickStartGuide": "Водич за брз почеток",
|
"ButtonQuickStartGuide": "Водич за брз почеток",
|
||||||
"ButtonPreviousTrack": "Претходна трака",
|
"ButtonPreviousTrack": "Претходна нумера",
|
||||||
"ButtonPause": "Пауза",
|
"ButtonPause": "Паузирај",
|
||||||
"ButtonParentalControl": "Родителска контрола",
|
"ButtonParentalControl": "Родителска контрола",
|
||||||
"ButtonOpen": "Отвори",
|
"ButtonOpen": "Отвори",
|
||||||
"ButtonOk": "Ок",
|
"ButtonOk": "Во ред",
|
||||||
"ButtonNextTrack": "Следна трака",
|
"ButtonNextTrack": "Следна нумера",
|
||||||
"ButtonMore": "Повеќе",
|
"ButtonMore": "Повеќе",
|
||||||
"ButtonManualLogin": "Мануелна најава",
|
"ButtonManualLogin": "Рачна најава",
|
||||||
"ButtonLibraryAccess": "Пристап до библиотека",
|
"ButtonLibraryAccess": "Пристап до библиотека",
|
||||||
"ButtonInfo": "Инфо",
|
"ButtonInfo": "Информации",
|
||||||
"ButtonGotIt": "Потврдувам",
|
"ButtonGotIt": "Потврдувам",
|
||||||
"ButtonFullscreen": "Цел екран",
|
"ButtonFullscreen": "Цел екран",
|
||||||
"ButtonForgotPassword": "Заборавена лозинка",
|
"ButtonForgotPassword": "Заборавена лозинка",
|
||||||
"ButtonEditOtherUserPreferences": "Ажурирај го профилот на овој корисник, слики и лични преференци.",
|
"ButtonEditOtherUserPreferences": "Ажурирај го профилот на овој корисник, слики и лични поставки.",
|
||||||
"ButtonClose": "Затвори",
|
"ButtonClose": "Затвори",
|
||||||
"ButtonChangeServer": "Смени сервер",
|
"ButtonChangeServer": "Смени сервер",
|
||||||
"ButtonCast": "Проектирај на уред",
|
"ButtonCast": "Проектирај на уред",
|
||||||
"ButtonCancel": "Откажи",
|
"ButtonCancel": "Откажи",
|
||||||
"ButtonBack": "Назад",
|
"ButtonBack": "Назад",
|
||||||
"ButtonAudioTracks": "Аудио траки",
|
"ButtonAudioTracks": "Јазик на звук",
|
||||||
"ButtonArrowRight": "Десно",
|
"ButtonArrowRight": "Десно",
|
||||||
"ButtonArrowLeft": "Лево",
|
"ButtonArrowLeft": "Лево",
|
||||||
"ButtonAddUser": "Додај корисник",
|
"ButtonAddUser": "Додај корисник",
|
||||||
"ButtonAddServer": "Додај сервер",
|
"ButtonAddServer": "Додај сервер",
|
||||||
"ButtonAddScheduledTaskTrigger": "Додај тригер",
|
"ButtonAddScheduledTaskTrigger": "Додај прекинувач",
|
||||||
"ButtonAddMediaLibrary": "Додај медија библиотека",
|
"ButtonAddMediaLibrary": "Додај медиумска библиотека",
|
||||||
"ButtonAddImage": "Додај слика",
|
"ButtonAddImage": "Додај слика",
|
||||||
"ButtonActivate": "Активирај",
|
"ButtonActivate": "Активирај",
|
||||||
"Browse": "Прелистувај",
|
"Browse": "Пребарувај",
|
||||||
"BoxSet": "Сет на кутии",
|
"BoxSet": "Сет на кутии",
|
||||||
"BoxRear": "Кутија (позади)",
|
"BoxRear": "Омот (позади)",
|
||||||
"Box": "Кутија",
|
"Box": "Омот",
|
||||||
"BookLibraryHelp": "Аудио и текстуални книги се поддржани. Проверете го {0} водич за именување на книги {1}.",
|
"BookLibraryHelp": "Аудио и текстуални книги се поддржани. Проверете го {0} водич за именување на книги {1}.",
|
||||||
"Blacklist": "Црна листа",
|
"Blacklist": "Црна листа",
|
||||||
"BirthPlaceValue": "Место на раѓање: {0}",
|
"BirthPlaceValue": "Место на раѓање: {0}",
|
||||||
"BirthLocation": "Место на раѓање",
|
"BirthLocation": "Место на раѓање",
|
||||||
"BirthDateValue": "Роден: {0}",
|
"BirthDateValue": "Роден: {0}",
|
||||||
"Banner": "Банер",
|
"Banner": "Банер",
|
||||||
"Auto": "Ауто",
|
"Auto": "Автоматски",
|
||||||
"AuthProviderHelp": "Одбери автентикациски провајдер кој што ќе се користи за лозинката на овој корисник.",
|
"AuthProviderHelp": "Одбери провајдер за автентикација кој што ќе се користи за лозинката на овој корисник.",
|
||||||
"Authorize": "Авторизирај",
|
"Authorize": "Одобри",
|
||||||
"Audio": "Аудио",
|
"Audio": "Звук",
|
||||||
"AspectRatio": "Соодно",
|
"AspectRatio": "Сооднос",
|
||||||
"AsManyAsPossible": "Колку што е можно",
|
"AsManyAsPossible": "Колку што е можно повеќе",
|
||||||
"AskAdminToCreateLibrary": "Побарај од администратор да создаде библиотека.",
|
"AskAdminToCreateLibrary": "Побарај од администратор да создаде библиотека.",
|
||||||
"Ascending": "Растечки",
|
"Ascending": "Растечки",
|
||||||
"Artist": "Изведувач",
|
"Artist": "Изведувач",
|
||||||
|
@ -96,59 +96,59 @@
|
||||||
"ApiKeysCaption": "Листа на моментално вклучени API клучеви",
|
"ApiKeysCaption": "Листа на моментално вклучени API клучеви",
|
||||||
"Anytime": "Било кога",
|
"Anytime": "Било кога",
|
||||||
"AnyLanguage": "Било кој јазик",
|
"AnyLanguage": "Било кој јазик",
|
||||||
"AlwaysPlaySubtitlesHelp": "Преводи кои се совпаѓаат со јазичните преференци ќе бидат вчитани без разлика на јазикот на аудиото.",
|
"AlwaysPlaySubtitlesHelp": "Преводи кои се совпаѓаат со претпочитаниот јазик ќе бидат вчитани без разлика на јазикот на звукот.",
|
||||||
"AlwaysPlaySubtitles": "Пуштај секогаш",
|
"AlwaysPlaySubtitles": "Пуштај секогаш",
|
||||||
"AllowTonemappingHelp": "Тонско-мапирање може да го трансформира динамичкиот опсег на видео од HDR во SDR зачувајќи ги бојата и деталите на сликата, кои што се важни информации за репрезентирање на оригиналната сцена. Моментално функционира само со HDR10 или HLG видеа. Задолжителни се соодветните OpenCL или CUDA рантајм.",
|
"AllowTonemappingHelp": "Тонско-мапирање може да го трансформира динамичкиот опсег на видео од HDR во SDR зачувувајќи ги бојата и деталите на сликата, кои што се важни информации за претставување на оригиналната сцена. Моментално функционира само со HDR10 или HLG видеа. Задолжителни се соодветните OpenCL или CUDA рантајм.",
|
||||||
"AllowRemoteAccessHelp": "Ако не е штиклирано, сите далечински конекции ќе бидат блокирано.",
|
"AllowRemoteAccessHelp": "Ако не е штиклирано, сите далечински врски ќе бидат блокирани.",
|
||||||
"AllowRemoteAccess": "Дозволи далечински конекции до овој сервер",
|
"AllowRemoteAccess": "Дозволи далечински врски до овој сервер",
|
||||||
"AllowOnTheFlySubtitleExtractionHelp": "Вградени преводи може да бидат екстрахирани од видеа и испорачани на клиентите во обичен текст, со цел да се превентира видео транскодирање. На некои системи ова може да трае долго и да причини заглавување на видеото при екстракцијата. Оневозможете го ова за да ги имате вградените преводи во видео транскодирањето дури и кога не се нативно поддржани на клиент уредот.",
|
"AllowOnTheFlySubtitleExtractionHelp": "Вградени преводи може да бидат одвоени од видеа и испорачани на клиентите во обичен текст, со цел да се спречи видео транскодирање. На некои системи ова може да трае долго и да причини заглавување на видеото при екстракцијата. Оневозможете го ова за да ги имате вградените преводи во видео транскодирањето дури и кога не се нативно поддржани на клиент уредот.",
|
||||||
"AllowOnTheFlySubtitleExtraction": "Дозволи екстракција на преводи во живо",
|
"AllowOnTheFlySubtitleExtraction": "Дозволи екстракција на преводи во реално време",
|
||||||
"AllowMediaConversionHelp": "Додај или одземи пристап за конвертирање на медија.",
|
"AllowMediaConversionHelp": "Дозволи или забрани пристап за конвертирање.",
|
||||||
"AllowMediaConversion": "Дозволи конверзија на медија",
|
"AllowMediaConversion": "Дозволи конверзија на медиумска содржина",
|
||||||
"AllowHWTranscodingHelp": "Дозволи му на тјунерот да транскодира стримови во живо. Со ова може да се намали транскодирањето потребно од серверот.",
|
"AllowHWTranscodingHelp": "Дозволи му на тјунерот да транскодира стримови во живо. Со ова може да се намали транскодирањето потребно од серверот.",
|
||||||
"AllowFfmpegThrottlingHelp": "Кога транскодирање или ремукс е доволно пред моментална позиција на плејбек, паузирај го процесот за да конзумира помалку ресурси. Ова е најкорисно кога се гледа без често премотување. Изгасете го ова доколку имате проблеми со гледање и премотување.",
|
"AllowFfmpegThrottlingHelp": "Кога транскодирање или ремукс е доволно пред моментална позиција на плејбек, паузирај го процесот за да конзумира помалку ресурси. Ова е најкорисно кога се гледа без често премотување. Изгасете го ова доколку имате проблеми со гледање и премотување.",
|
||||||
"AllowFfmpegThrottling": "Пригуши транскодирања",
|
"AllowFfmpegThrottling": "Пригуши транскодирања",
|
||||||
"AllowedRemoteAddressesHelp": "Листа на IP адреси или IP/нетмаск парови кои ќе имаат дозвола да се конектираат далечински. Ако е оставено празно, сите адреси ќе ја имаат оваа дозвола.",
|
"AllowedRemoteAddressesHelp": "Листа на IP адреси или IP/нетмаск парови кои ќе имаат дозвола да се поврзуваат далечински. Ако е оставено празно, сите адреси ќе ја имаат оваа дозвола.",
|
||||||
"AllLibraries": "Сите библиотеки",
|
"AllLibraries": "Сите библиотеки",
|
||||||
"AllLanguages": "Сите јазици",
|
"AllLanguages": "Сите јазици",
|
||||||
"AllEpisodes": "Сите епизоди",
|
"AllEpisodes": "Сите епизоди",
|
||||||
"AllComplexFormats": "Сите комплексни формати (ASS, SSA, VobSub, PGS, SUB, IDX, …)",
|
"AllComplexFormats": "Сите сложени формати (ASS, SSA, VobSub, PGS, SUB, IDX, …)",
|
||||||
"AllChannels": "Сите канали",
|
"AllChannels": "Сите канали",
|
||||||
"Alerts": "Алармирања",
|
"Alerts": "Предупредувања",
|
||||||
"AlbumArtist": "Изведувач",
|
"AlbumArtist": "Изведувач",
|
||||||
"Album": "Албум",
|
"Album": "Албум",
|
||||||
"Aired": "Емитувано",
|
"Aired": "Емитувано",
|
||||||
"AirDate": "Датум на емитување",
|
"AirDate": "Датум на емитување",
|
||||||
"AgeValue": "({0} години)",
|
"AgeValue": "({0} години)",
|
||||||
"AddToPlayQueue": "Додај во редица за гледање",
|
"AddToPlayQueue": "Додади во ред за пуштање",
|
||||||
"AdditionalNotificationServices": "Пребарувајте низ каталогот за плугини за да инсталирате додатни сервиси за известувања.",
|
"AdditionalNotificationServices": "Пребарувајте низ каталогот за плугини за да инсталирате додатни сервиси за известувања.",
|
||||||
"AddedOnValue": "Додадено {0}",
|
"AddedOnValue": "Додадено {0}",
|
||||||
"AccessRestrictedTryAgainLater": "Пристапот е моментално органичен. Ве молиме пробајте подоцна.",
|
"AccessRestrictedTryAgainLater": "Пристапот е моментално органичен. Ве молиме пробајте подоцна.",
|
||||||
"DirectStreaming": "Директно стримање",
|
"DirectStreaming": "Директен стрим",
|
||||||
"DirectStreamHelp2": "Моќта конзумирана со директно стримање најчесто е зависна од аудио профилот. Само видео стримот е без губење на квалитет.",
|
"DirectStreamHelp2": "Енергијата која се троши од директното стримување најчесто зависи од аудио профилот. Само видео стримот е без губење на квалитет.",
|
||||||
"DirectStreamHelp1": "Видео стримот е компатибилен со овој уред, но има некомпатибилен аудио формат (DTS, Dolby TrueHD, etc.) или број на аудио канали. Видео стримот ќе биде препакуван без губење на квалитет во живо пред да биде пратен на уредот. Само аудио стримот ќе биде транскодиран.",
|
"DirectStreamHelp1": "Видео стримот е компатибилен со овој уред, но има некомпатибилен формат на звук (DTS, Dolby TrueHD, etc.) или број на аудио канали. Видео стримот ќе биде препакуван без губење на квалитет во живо пред да биде пратен на уредот. Само аудио стримот ќе биде транскодиран.",
|
||||||
"DirectPlayHelp": "Изворната датотека е комплетно компатибилна со овој клиент, и сесијата ја прима оваа датотека без модификации.",
|
"DirectPlayHelp": "Изворната датотека е комплетно компатибилна со овој клиент, и сесијата ја прима оваа датотека без модификации.",
|
||||||
"DirectPlaying": "Директно гледање",
|
"DirectPlaying": "Директно гледање",
|
||||||
"Directors": "Режисери",
|
"Directors": "Режисери",
|
||||||
"Director": "Режисер",
|
"Director": "Режисер",
|
||||||
"Digital": "Дигитален",
|
"Digital": "Дигитално",
|
||||||
"DeviceAccessHelp": "Ова важи само за уреди кои што можат да бидат уникатно идентифицирани и нема да превентираат пристап на прелистувач. Филтерирање на кориснички пристап на уред ќе ги превентира од користење на нови уреди додека не се одобрени тука.",
|
"DeviceAccessHelp": "Ова важи само за уреди кои што можат да бидат уникатно препознаени и нема да спречат пристап до прелистувачот. Филтрирање на кориснички пристап на уред ќе спречи користење на нови уреди додека не се одобрат тука.",
|
||||||
"DetectingDevices": "Детектирање на уреди",
|
"DetectingDevices": "Откривање на уреди",
|
||||||
"Desktop": "Десктоп",
|
"Desktop": "Работна површина",
|
||||||
"Descending": "Опаѓачки",
|
"Descending": "Опаѓачки",
|
||||||
"DeleteUserConfirmation": "Дали сте сигурни дека сакате да го избришете овој корисник?",
|
"DeleteUserConfirmation": "Дали сте сигурни дека сакате да го избришете овој корисник?",
|
||||||
"DeleteUser": "Избриши корисник",
|
"DeleteUser": "Избриши корисник",
|
||||||
"DeleteMedia": "Избриши медија",
|
"DeleteMedia": "Избриши медиумска содржина",
|
||||||
"DeleteImageConfirmation": "Дали сте сигурни дека сакате да ја избришете оваа слика?",
|
"DeleteImageConfirmation": "Дали сте сигурни дека сакате да ја избришете оваа слика?",
|
||||||
"DeleteImage": "Избриши слика",
|
"DeleteImage": "Избриши слика",
|
||||||
"DeleteDevicesConfirmation": "Дали сте сигурни дека сакате да ги избришете сите уреди? Сите други сесии ќе бидат одјавени. Уредите ќе се појават повторно кога корисникот ќе се најави.",
|
"DeleteDevicesConfirmation": "Дали сте сигурни дека сакате да ги избришете сите уреди? Сите други сесии ќе бидат одјавени. Уредите ќе се појават повторно кога корисникот ќе се најави.",
|
||||||
"DeleteDeviceConfirmation": "Дали сте сигурен дека сакате да го избришете овој уред? Ќе се појави следниот пат кога корисникот ќе се најави со него.",
|
"DeleteDeviceConfirmation": "Дали сте сигурни дека сакате да го избришете овој уред? Ќе се појави повторно следниот пат кога корисникот ќе се најави со него.",
|
||||||
"DeleteAll": "Избриши ги сите",
|
"DeleteAll": "Избриши сѐ",
|
||||||
"Delete": "Избриши",
|
"Delete": "Избриши",
|
||||||
"DeinterlaceMethodHelp": "Одбери го методот за одпреплетување кога софтверски се транскодира преплетена содржина. Кога хардверска акцелерација која што поддржува хардверско одпреплетување е вклучено хардверскиот одпреплетувач ќе биде користен наместо ова подесување.",
|
"DeinterlaceMethodHelp": "Одбери го методот за одпреплетување кога софтверски се транскодира преплетена содржина. Кога хардверско забрзување кое што поддржува хардверско одпреплетување е вклучено, хардверскиот одпреплетувач ќе биде користен наместо оваа поставка.",
|
||||||
"DefaultSubtitlesHelp": "Преводите се вчитани базирано на стандардните и форсирани знаменца во вградената метадата. Преференците за јазик се земени во предвид кога има повеќе можни опции.",
|
"DefaultSubtitlesHelp": "Преводите се вчитани базирано на стандардните и форсирани знаменца во вградената метадата. Поставките за јазик се земени во предвид кога има повеќе можни опции.",
|
||||||
"DefaultMetadataLangaugeDescription": "Ова се вашите стандардни опции и можат да бидат прилагодени посебно за секоја библиотека.",
|
"DefaultMetadataLangaugeDescription": "Ова се вашите зададени вредности и можат да бидат прилагодени посебно за секоја библиотека.",
|
||||||
"DeathDateValue": "Починал: {0}",
|
"DeathDateValue": "Починал/а: {0}",
|
||||||
"DatePlayed": "Датум на гледање",
|
"DatePlayed": "Датум на гледање",
|
||||||
"DateAdded": "Датум на додавање",
|
"DateAdded": "Датум на додавање",
|
||||||
"Data": "Податоци",
|
"Data": "Податоци",
|
||||||
|
@ -157,32 +157,32 @@
|
||||||
"DashboardOperatingSystem": "Оперативен систем: {0}",
|
"DashboardOperatingSystem": "Оперативен систем: {0}",
|
||||||
"DashboardArchitecture": "Архитектура: {0}",
|
"DashboardArchitecture": "Архитектура: {0}",
|
||||||
"DailyAt": "Секој ден во {0}",
|
"DailyAt": "Секој ден во {0}",
|
||||||
"CustomDlnaProfilesHelp": "Создади прилагоден профил за таргетирање на нов уред или за прескокање на системски профил.",
|
"CustomDlnaProfilesHelp": "Создадете прилагоден профил за да насочите нов уред или да го отфрлите системскиот профил.",
|
||||||
"Cursive": "Закосено",
|
"Cursive": "Ракописно",
|
||||||
"CriticRating": "Оценка на критичари",
|
"CriticRating": "Оценка на критичари",
|
||||||
"CopyStreamURLSuccess": "Успешно копирана адреса.",
|
"CopyStreamURLSuccess": "Успешно копирана адреса.",
|
||||||
"CopyStreamURL": "Копирај адреса на стрим",
|
"CopyStreamURL": "Копирај адреса на стрим",
|
||||||
"CopyFailed": "Неуспешно копирање",
|
"CopyFailed": "Неуспешно копирање",
|
||||||
"Copy": "Копирај",
|
"Copy": "Копирај",
|
||||||
"Copied": "Копирано",
|
"Copied": "Копирано",
|
||||||
"Continuing": "Продолжување",
|
"Continuing": "Продолжува",
|
||||||
"ContinueWatching": "Продолжи со гледање",
|
"ContinueWatching": "Продолжи со гледање",
|
||||||
"Console": "Конзола",
|
"Console": "Конзола",
|
||||||
"Connect": "Поврзување",
|
"Connect": "Поврзи",
|
||||||
"ConfirmEndPlayerSession": "Дали сакате да го изгасите Jellyfin на {0}?",
|
"ConfirmEndPlayerSession": "Дали сакате да го исклучите Jellyfin на {0}?",
|
||||||
"ConfirmDeletion": "Потврди бришење",
|
"ConfirmDeletion": "Потврди бришење",
|
||||||
"ConfirmDeleteItems": "Бришење на овие ставки ќе ги избрише и од податочниот систем и од библиотеката. Дали сте сигурни дека сакате да продолжите?",
|
"ConfirmDeleteItems": "Бришењето на овие ставки ќе ги избрише и од податочниот систем и од библиотеката. Дали сте сигурни дека сакате да продолжите?",
|
||||||
"ConfirmDeleteItem": "Бришење на оваа ставка ќе го избрише и од податочниот систем и од библиотеката. Дали сте сигурни дека сакате да продолжите?",
|
"ConfirmDeleteItem": "Бришењето на оваа ставка ќе го избрише и од податочниот систем и од библиотеката. Дали сте сигурни дека сакате да продолжите?",
|
||||||
"ConfirmDeleteImage": "Избриши слика?",
|
"ConfirmDeleteImage": "Избриши слика?",
|
||||||
"ConfigureDateAdded": "Намести како метадатата за „датум на додавање“ е одредена во Контролна табла > Библиотеки > NFO подесувања",
|
"ConfigureDateAdded": "Поставете како се одредуваат метаподатоците за „Датум на додадено“ во Контролна табла > Библиотеки > Поставки за NFO",
|
||||||
"Conductor": "Кондуктор",
|
"Conductor": "Диригент",
|
||||||
"Composer": "Композитор",
|
"Composer": "Композитор",
|
||||||
"CommunityRating": "Оценка на заедницата",
|
"CommunityRating": "Оценка на заедницата",
|
||||||
"ColorTransfer": "Трансфер на бои",
|
"ColorTransfer": "Пренос на бои",
|
||||||
"ColorSpace": "Простор на бои",
|
"ColorSpace": "Простор на бои",
|
||||||
"ColorPrimaries": "Примарни бои",
|
"ColorPrimaries": "Основни бои",
|
||||||
"ClientSettings": "Кориснички поставувања",
|
"ClientSettings": "Кориснички поставувања",
|
||||||
"ClearQueue": "Исчисти редица",
|
"ClearQueue": "Исчисти ред",
|
||||||
"CinemaModeConfigurationHelp": "Кино режин го носи кино искуството директно во вашата дневна соба со можноста на пуштање на трејлери и интра пред главните сцени.",
|
"CinemaModeConfigurationHelp": "Кино режин го носи кино искуството директно во вашата дневна соба со можноста на пуштање на трејлери и интра пред главните сцени.",
|
||||||
"ChannelNumber": "Број на канал",
|
"ChannelNumber": "Број на канал",
|
||||||
"ChannelNameOnly": "Само канал {0}",
|
"ChannelNameOnly": "Само канал {0}",
|
||||||
|
@ -197,5 +197,108 @@
|
||||||
"ButtonUseQuickConnect": "Користи „брзо поврзување“",
|
"ButtonUseQuickConnect": "Користи „брзо поврзување“",
|
||||||
"ButtonUninstall": "Деинсталирај",
|
"ButtonUninstall": "Деинсталирај",
|
||||||
"ButtonTrailer": "Трејлер",
|
"ButtonTrailer": "Трејлер",
|
||||||
"ButtonTogglePlaylist": "Плејлиста"
|
"ButtonTogglePlaylist": "Плејлиста",
|
||||||
|
"Backdrop": "Позадина",
|
||||||
|
"LabelThrottleDelaySecondsHelp": "Време во секунди по кое транскодерот ќе биде пригушен. Мора да биде доволно долго за клиентот да одржува здрав бафер. Работи само ако е овозможено пригушување.",
|
||||||
|
"Art": "Графика",
|
||||||
|
"ButtonSyncPlay": "SyncPlay",
|
||||||
|
"ButtonBackspace": "бек-спејс",
|
||||||
|
"ButtonPlayer": "Плеер",
|
||||||
|
"ButtonSpace": "Празно место",
|
||||||
|
"BackdropScreensaver": "Заштитник на екран во позадина",
|
||||||
|
"AllowCollectionManagement": "Дозволете овој корисник да управува со колекциите",
|
||||||
|
"AllowSegmentDeletion": "Избриши сегменти",
|
||||||
|
"AllowSegmentDeletionHelp": "Избришете ги старите сегменти откако ќе бидат испратени до клиентот. Ова го спречува складирањето на целата транскодирана датотека на дискот. Ќе работи само со овозможено пригушување. Исклучете го ова ако имате проблеми со репродукцијата.",
|
||||||
|
"LabelThrottleDelaySeconds": "Пригуши после",
|
||||||
|
"LabelSegmentKeepSeconds": "Време за чување на сегментите",
|
||||||
|
"LabelSegmentKeepSecondsHelp": "Време во секунди за кое сегментите треба да се чуваат пред да бидат препишани. Мора да биде подолго од „Пригуши после“. Работи само ако е овозможено бришењето сегменти.",
|
||||||
|
"Backdrops": "Позадини",
|
||||||
|
"BurnSubtitlesHelp": "Определете дали серверот треба да вградува преводи додека транскодирате видеа. Избегнувањето на ова во голема мера ќе ги подобри перформансите. Изберете Автоматски за снимање на формати базирани на слики (VobSub, PGS, SUB, IDX, итн.) и одредени ASS или SSA преводи.",
|
||||||
|
"DisableCustomCss": "Оневозможи приспособен CSS код обезбеден од серверот",
|
||||||
|
"Download": "Преземи",
|
||||||
|
"EditImages": "Уреди слики",
|
||||||
|
"EditSubtitles": "Уреди превод",
|
||||||
|
"EnableBackdropsHelp": "Прикажувајте ги заднините во заднината на некои страници додека ја прелистувате библиотеката.",
|
||||||
|
"EnableBlurHashHelp": "Сликите што сè уште се вчитуваат ќе се прикажат со единствено место.",
|
||||||
|
"EnableDisplayMirroring": "Пресликување на екранот",
|
||||||
|
"EnableExternalVideoPlayers": "Надворешни видео плеери",
|
||||||
|
"EnableExternalVideoPlayersHelp": "Ќе се прикаже мени за надворешен плеер кога ќе започнете со репродукција на видео.",
|
||||||
|
"Episodes": "Епизоди",
|
||||||
|
"ErrorAddingListingsToSchedulesDirect": "Настана грешка при додавањето на поставата на вашата сметка на Распореди Директно. Распореди Директно дозволува само ограничен број на постави по сметка. Можеби ќе треба да се најавите на веб-локацијата Распоред Директно и да ги отстраните другите списоци од вашата сметка пред да продолжите.",
|
||||||
|
"ErrorGettingTvLineups": "Настана грешка при преземањето ТВ-постави. Проверете дали вашите информации се точни и обидете се повторно.",
|
||||||
|
"EveryXHours": "Секои {0} часови",
|
||||||
|
"ExtractChapterImagesHelp": "Извлекувањето слики од поглавја ќе им овозможи на клиентите да прикажуваат графички менија за избор на сцена. Процесот може да биде бавен, интензивен за ресурси и може да бара неколку гигабајти простор. Работи кога ќе се откријат видеата, а исто така и како ноќна закажана задача. Распоредот може да се конфигурира во областа за закажани задачи. Не се препорачува да се извршува оваа задача за време на максимални часови на употреба.",
|
||||||
|
"FFmpegSavePathNotFound": "Не можеме да го најдеме FFmpeg користејќи ја патеката што ја внесовте. Потребна е и FFprobe и мора да постои во истата папка. Овие компоненти обично се заедно во истото преземање. Проверете ја патеката и обидете се повторно.",
|
||||||
|
"FileNotFound": "Датотеката не е пронајдена.",
|
||||||
|
"Disc": "Диск",
|
||||||
|
"EnableDetailsBanner": "Детали за банер",
|
||||||
|
"Extras": "Додатоци",
|
||||||
|
"Features": "Можности",
|
||||||
|
"File": "Датотека",
|
||||||
|
"EnableFasterAnimationsHelp": "Користи побрзи анимации и транзиции.",
|
||||||
|
"EnableFasterAnimations": "Побрзи анимации",
|
||||||
|
"EnableHardwareEncoding": "Овозможи хардверско кодирање",
|
||||||
|
"ErrorPlayerNotFound": "Не е пронајден плеер за бараните медиумски содржини.",
|
||||||
|
"EveryHour": "Секој час",
|
||||||
|
"EveryNDays": "Секои {0} дена",
|
||||||
|
"EnablePlugin": "Овозможи",
|
||||||
|
"DoNotRecord": "Не снимај",
|
||||||
|
"DownloadsValue": "{0} преземања",
|
||||||
|
"EnableBlurHash": "Овозможете заматени места за слики",
|
||||||
|
"ErrorStartHourGreaterThanEnd": "Времето на завршување мора да биде поголемо од времето на започнување.",
|
||||||
|
"ExitFullscreen": "Излези од приказ на цел екран",
|
||||||
|
"ExtraLarge": "Екстра големо",
|
||||||
|
"FastForward": "Премотај-напред",
|
||||||
|
"Favorite": "Омилено",
|
||||||
|
"DisplayInOtherHomeScreenSections": "Прикажување во деловите на почетниот екран како што се „Неодамна додадени медиуми“ и „Продолжи со гледање“",
|
||||||
|
"EnableThemeSongsHelp": "Пуштете ги тематските песни во позадина додека ја прелистувате библиотеката.",
|
||||||
|
"EnableRewatchingNextUp": "Овозможи повторно гледање во Следно",
|
||||||
|
"EnableRewatchingNextUpHelp": "Овозможи прикажување на веќе гледаните епизоди во деловите „Следно“.",
|
||||||
|
"Depressed": "Застарено",
|
||||||
|
"DisablePlugin": "Оневозможи",
|
||||||
|
"Disconnect": "Исклучи",
|
||||||
|
"Display": "Прикажи",
|
||||||
|
"DisplayInMyMedia": "Прикажи на почетен екран",
|
||||||
|
"EnableStreamLooping": "Автоматско-повторување на преноси во живо",
|
||||||
|
"EnableQuickConnect": "Овозможи Брзо Поврзување на овој сервер",
|
||||||
|
"EnableNextVideoInfoOverlayHelp": "На крајот на видеото, прикажи информации за следното видео во моменталната плејлиста.",
|
||||||
|
"Episode": "Епизода",
|
||||||
|
"DisplayMissingEpisodesWithinSeasonsHelp": "Ова мора да биде овозможено и за ТВ библиотеките во конфигурацијата на серверот.",
|
||||||
|
"Down": "Надолу",
|
||||||
|
"Edit": "Уреди",
|
||||||
|
"EditMetadata": "Уреди метаподатоци",
|
||||||
|
"EnableAutoCast": "Постави за зададено",
|
||||||
|
"EnableDecodingColorDepth10Vp9": "Овозможи 10-битно хардверско декодирање за VP9",
|
||||||
|
"EnableCinemaMode": "Кино режим",
|
||||||
|
"EnableDetailsBannerHelp": "Прикажете слика на банер на врвот на страницата со детали за ставката.",
|
||||||
|
"EnableAudioNormalization": "Нормализација на звук",
|
||||||
|
"EnableDecodingColorDepth10Hevc": "Овозможи 10-битно хардверско декодирање за HEVC",
|
||||||
|
"EnableNextVideoInfoOverlay": "Прикажи ги информациите за следното видео за време на гледањето",
|
||||||
|
"EncoderPresetHelp": "Изберете побрза вредност за подобрување на перформансите или побавна вредност за подобрување на квалитетот.",
|
||||||
|
"Ended": "Завршено",
|
||||||
|
"EndsAtValue": "Завршува во {0}",
|
||||||
|
"Engineer": "Звучен инжинер",
|
||||||
|
"ErrorAddingMediaPathToVirtualFolder": "Настана грешка при додавањето на патеката за медиумски содржини. Ве молиме проверете дали патеката е валидна и Jellyfin има пристап до таа локација.",
|
||||||
|
"ErrorAddingTunerDevice": "Настана грешка при додавањето на тјунерот. Проверете дали е достапен и обидете се повторно.",
|
||||||
|
"ErrorAddingXmlTvFile": "Настана грешка при пристапот до датотеката XMLTV. Проверете дали постои датотеката и обидете се повторно.",
|
||||||
|
"ErrorDefault": "Настана грешка при обработката на барањето. Обидете се повторно подоцна.",
|
||||||
|
"EveryXMinutes": "Секои {0} минути",
|
||||||
|
"EnableAudioNormalizationHelp": "Нормализацијата на звукот ќе додаде постојано засилување за да го задржи просекот на посакуваното ниво (-18dB).",
|
||||||
|
"DisplayModeHelp": "Изберете го стилот на распоред што го сакате за интерфејсот.",
|
||||||
|
"Experimental": "Експериментално",
|
||||||
|
"DropShadow": "Прикажи сенка",
|
||||||
|
"EnablePhotos": "Прикажи фотографии",
|
||||||
|
"EnablePhotosHelp": "Сликите ќе бидат откриени и прикажани заедно со други медиумски датотеки.",
|
||||||
|
"ErrorDeletingItem": "Настана грешка при бришењето на ставката од серверот. Ве молиме проверете дали Jellyfin има пристап за пишување до медиумската папка и обидете се повторно.",
|
||||||
|
"ErrorPleaseSelectLineup": "Изберете постава и обидете се повторно. Ако не се достапни постави, тогаш ве молиме проверете дали вашето корисничко име, лозинка и поштенски код се точни.",
|
||||||
|
"ErrorSavingTvProvider": "Настана грешка при зачувувањето на ТВ-провајдерот. Проверете дали е достапен и обидете се повторно.",
|
||||||
|
"EnableCardLayout": "Прикажи визуелен CardBox",
|
||||||
|
"DownloadAll": "Преземи сѐ",
|
||||||
|
"EnableColorCodedBackgrounds": "Позадини со кодирани бои",
|
||||||
|
"DisplayMissingEpisodesWithinSeasons": "Прикажи ги епизодите што недостасуваат во сезоните",
|
||||||
|
"EnableTonemapping": "Овозможи мапирање на тонови",
|
||||||
|
"FetchingData": "Прибавување на додатни податоци",
|
||||||
|
"EnableStreamLoopingHelp": "Овозможете го ова ако преносите во живо содржат само неколку секунди податоци и треба постојано да се бараат. Овозможувањето на ова кога не е потребно може да предизвика проблеми.",
|
||||||
|
"EnableThemeVideosHelp": "Пуштете ги тематските видеа во заднина додека ја прелистувате библиотеката.",
|
||||||
|
"DrmChannelsNotImported": "Канали со DRM заштита нема да бидат внесени."
|
||||||
}
|
}
|
||||||
|
|
|
@ -1454,7 +1454,7 @@
|
||||||
"AspectRatioCover": "Okładka",
|
"AspectRatioCover": "Okładka",
|
||||||
"EnableFallbackFontHelp": "Włącz niestandardowe czcionki. Może pozwolić uniknąć problemów przy renderowaniu napisów.",
|
"EnableFallbackFontHelp": "Włącz niestandardowe czcionki. Może pozwolić uniknąć problemów przy renderowaniu napisów.",
|
||||||
"EnableFallbackFont": "Włącz czcionki zastępcze",
|
"EnableFallbackFont": "Włącz czcionki zastępcze",
|
||||||
"LabelFallbackFontPathHelp": "Sprecyzuj ścieżkę zawierającą zastępcze czcionki dla renderowania napisów ASS/SSA. Maksymalny dozwolony łączny rozmiar czcionki to 20 MB. Lekkie i przyjazne dla sieci (web-friendly) formaty czcionek takie jak woff2 są zalecane.",
|
"LabelFallbackFontPathHelp": "Czcionki te są używane przez niektóre klienty do renderowania napisów. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją.",
|
||||||
"LabelFallbackFontPath": "Ścieżka folderu czcionki zastępczej",
|
"LabelFallbackFontPath": "Ścieżka folderu czcionki zastępczej",
|
||||||
"HeaderSelectFallbackFontPathHelp": "Wyszukaj lub podaj ścieżkę folderu czcionki zastępczej do użycia przy renderowaniu napisów ASS/SAA.",
|
"HeaderSelectFallbackFontPathHelp": "Wyszukaj lub podaj ścieżkę folderu czcionki zastępczej do użycia przy renderowaniu napisów ASS/SAA.",
|
||||||
"HeaderSelectFallbackFontPath": "Wybierz ścieżkę folderu czcionki zastępczej",
|
"HeaderSelectFallbackFontPath": "Wybierz ścieżkę folderu czcionki zastępczej",
|
||||||
|
@ -1776,5 +1776,9 @@
|
||||||
"UnknownError": "Wystąpił nieznany błąd.",
|
"UnknownError": "Wystąpił nieznany błąd.",
|
||||||
"BackdropScreensaver": "Wygaszacz ekranu z fototapetami",
|
"BackdropScreensaver": "Wygaszacz ekranu z fototapetami",
|
||||||
"LogoScreensaver": "Wygaszacz ekranu z logo",
|
"LogoScreensaver": "Wygaszacz ekranu z logo",
|
||||||
"LabelIsHearingImpaired": "Dla osób niedosłyszących"
|
"LabelIsHearingImpaired": "Dla osób niedosłyszących",
|
||||||
|
"LabelAlbumGain": "Wzmocnienie albumu",
|
||||||
|
"LabelSelectAudioNormalization": "Normalizacja dźwięku",
|
||||||
|
"LabelTrackGain": "Wzmocnienie utworu",
|
||||||
|
"SelectAudioNormalizationHelp": "Wzmocnienie utworu – reguluje głośność każdego utworu tak, aby odtwarzał się z tą samą głośnością. Wzmocnienie albumu – reguluje głośność tylko wszystkich utworów w albumie, zachowując zakres dynamiki albumu."
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,5 +23,7 @@
|
||||||
"AllChannels": "සියලුම නාලිකා",
|
"AllChannels": "සියලුම නාලිකා",
|
||||||
"AllComplexFormats": "සියලුම සංකීර්ණ ආකෘති (ASS, SSA, VobSub, PGS, SUB, IDX, …)",
|
"AllComplexFormats": "සියලුම සංකීර්ණ ආකෘති (ASS, SSA, VobSub, PGS, SUB, IDX, …)",
|
||||||
"AllEpisodes": "සියලුම කථාංග",
|
"AllEpisodes": "සියලුම කථාංග",
|
||||||
"AllowedRemoteAddressesHelp": "දුරස්ථව සම්බන්ධ වීමට ඉඩ දෙන ජාල සඳහා කොමාවෙන් වෙන් කළ IP ලිපින ලැයිස්තුව හෝ IP/netmask ඇතුළත් කිරීම්. හිස්ව තැබුවහොත්, සියලු දුරස්ථ ලිපිනවලට ඉඩ දෙනු ලැබේ."
|
"AllowedRemoteAddressesHelp": "දුරස්ථව සම්බන්ධ වීමට ඉඩ දෙන ජාල සඳහා කොමාවෙන් වෙන් කළ IP ලිපින ලැයිස්තුව හෝ IP/netmask ඇතුළත් කිරීම්. හිස්ව තැබුවහොත්, සියලු දුරස්ථ ලිපිනවලට ඉඩ දෙනු ලැබේ.",
|
||||||
|
"AllowSegmentDeletion": "මේ කොටස මකන්න",
|
||||||
|
"AllowSegmentDeletionHelp": "client වෙත යැවීමෙන් පසු පැරණි කොටස් මකන්න. මෙය සම්පූර්ණ ට්රාන්ස්කෝඩ් ගොනුව තැටියේ ගබඩා කිරීම වළක්වයි. ක්රියා කරන්නේ තෙරපුම සක්රීය කර පමණි. ඔබ පසුධාවන ගැටළු අත්විඳින්නේ නම් මෙය අක්රිය කරන්න."
|
||||||
}
|
}
|
||||||
|
|
|
@ -1454,7 +1454,7 @@
|
||||||
"AspectRatioFill": "Vyplniť",
|
"AspectRatioFill": "Vyplniť",
|
||||||
"EnableFallbackFontHelp": "Povoliť vlastné alternatívne fonty. Toto môže vyriešiť problémy s nesprávnym vykreslením titulkov.",
|
"EnableFallbackFontHelp": "Povoliť vlastné alternatívne fonty. Toto môže vyriešiť problémy s nesprávnym vykreslením titulkov.",
|
||||||
"EnableFallbackFont": "Povoliť záložné fonty",
|
"EnableFallbackFont": "Povoliť záložné fonty",
|
||||||
"LabelFallbackFontPathHelp": "Špecifikuje cestu obsahujúcu záložné fonty pre vykreslenie ASS/SSA tituliek. Maximálna povolená veľkosť fontu je 20 MB. Doporučujeme používať malé a webovo-priateľské formy fontov ako napr. woff2.",
|
"LabelFallbackFontPathHelp": "Tieto písma používajú niektorí klienti na vykresľovanie titulkov. Viac informácií nájdete v dokumentácii.",
|
||||||
"LabelFallbackFontPath": "Cesta k priečinku záložných fontov",
|
"LabelFallbackFontPath": "Cesta k priečinku záložných fontov",
|
||||||
"HeaderSelectFallbackFontPathHelp": "Vyberte alebo zadajte cestu k priečinku záložných fontov pre vykreslenie ASS/SSA titulkov.",
|
"HeaderSelectFallbackFontPathHelp": "Vyberte alebo zadajte cestu k priečinku záložných fontov pre vykreslenie ASS/SSA titulkov.",
|
||||||
"HeaderSelectFallbackFontPath": "Vybrať cestu k priečinku záložných fontov",
|
"HeaderSelectFallbackFontPath": "Vybrať cestu k priečinku záložných fontov",
|
||||||
|
@ -1776,5 +1776,9 @@
|
||||||
"LabelSegmentKeepSeconds": "Doba ponechania segmentov",
|
"LabelSegmentKeepSeconds": "Doba ponechania segmentov",
|
||||||
"LabelThrottleDelaySecondsHelp": "Čas v sekundách, po ktorom bude prekódovanie obmedzené. Musí byť dostatočne veľký, aby mal klient v rezerve dostatočné množstvo prehrávaného súboru. Funguje len vtedy, ak je povolená funkcia Obmedziť prekódovanie.",
|
"LabelThrottleDelaySecondsHelp": "Čas v sekundách, po ktorom bude prekódovanie obmedzené. Musí byť dostatočne veľký, aby mal klient v rezerve dostatočné množstvo prehrávaného súboru. Funguje len vtedy, ak je povolená funkcia Obmedziť prekódovanie.",
|
||||||
"AllowSegmentDeletionHelp": "Odstránenie starých segmentov po ich odoslaní klientovi. Tým sa zabráni tomu, aby sa celý prekódovaný súbor musel ukladať na disk. Funguje len so zapnutou funkciou Obmedziť prekódovanie. Ak sa vyskytnú problémy s prehrávaním, vypnite túto funkciu.",
|
"AllowSegmentDeletionHelp": "Odstránenie starých segmentov po ich odoslaní klientovi. Tým sa zabráni tomu, aby sa celý prekódovaný súbor musel ukladať na disk. Funguje len so zapnutou funkciou Obmedziť prekódovanie. Ak sa vyskytnú problémy s prehrávaním, vypnite túto funkciu.",
|
||||||
"LabelSegmentKeepSecondsHelp": "Čas v sekundách, počas ktorého budú segmenty uložené. Musí byť dlhší ako je čas určený v \"Obmedziť po\". Funguje len vtedy, ak je povolená funkcia Zmazania segmentov."
|
"LabelSegmentKeepSecondsHelp": "Čas v sekundách, počas ktorého budú segmenty uložené. Musí byť dlhší ako je čas určený v \"Obmedziť po\". Funguje len vtedy, ak je povolená funkcia Zmazania segmentov.",
|
||||||
|
"SelectAudioNormalizationHelp": "Zosilnenie stopy - upravuje hlasitosť jednotlivých stôp tak, aby sa prehrávali s rovnakou hlasitosťou. Zosilnenie pre album - upravuje hlasitosť všetkých skladieb iba v albume, pričom zachováva dynamický rozsah albumu.",
|
||||||
|
"LabelAlbumGain": "Zosilnenie pre album",
|
||||||
|
"LabelSelectAudioNormalization": "Normalizácia hlasitosti",
|
||||||
|
"LabelTrackGain": "Zosilnenie stopy"
|
||||||
}
|
}
|
||||||
|
|
|
@ -981,7 +981,7 @@
|
||||||
"PreferEmbeddedTitlesOverFileNames": "Dosya adları yerine gömülü başlıkları tercih et",
|
"PreferEmbeddedTitlesOverFileNames": "Dosya adları yerine gömülü başlıkları tercih et",
|
||||||
"LabelSpecialSeasonsDisplayName": "Görünen özel sezon adı",
|
"LabelSpecialSeasonsDisplayName": "Görünen özel sezon adı",
|
||||||
"SpecialFeatures": "Özel Özellikler",
|
"SpecialFeatures": "Özel Özellikler",
|
||||||
"LabelFallbackFontPathHelp": "ASS/SSA altyazılarını oluşturmak için yedek yazı tiplerini içeren bir yol belirtin. İzin verilen maksimum toplam yazı tipi boyutu 20 MB'dir. Woff2 gibi hafif ve web dostu yazı tipi formatları önerilir.",
|
"LabelFallbackFontPathHelp": "Bu yazı tipleri bazı istemciler tarafından altyazıları oluşturmak için kullanılır. Daha fazla bilgi için lütfen belgelere bakın.",
|
||||||
"HeaderSelectFallbackFontPathHelp": "ASS/SSA altyazılarının işlenmesinde kullanılacak yedek yazı tipi klasörünün yoluna göz atın veya girin.",
|
"HeaderSelectFallbackFontPathHelp": "ASS/SSA altyazılarının işlenmesinde kullanılacak yedek yazı tipi klasörünün yoluna göz atın veya girin.",
|
||||||
"TheseSettingsAffectSubtitlesOnThisDevice": "Bu ayarlar, bu cihazdaki altyazıları etkiler",
|
"TheseSettingsAffectSubtitlesOnThisDevice": "Bu ayarlar, bu cihazdaki altyazıları etkiler",
|
||||||
"Subtitles": "Altyazılar",
|
"Subtitles": "Altyazılar",
|
||||||
|
@ -1768,5 +1768,9 @@
|
||||||
"AiTranslated": "AI Çevirisi",
|
"AiTranslated": "AI Çevirisi",
|
||||||
"MachineTranslated": "Makine Çevirisi",
|
"MachineTranslated": "Makine Çevirisi",
|
||||||
"ForeignPartsOnly": "Gömülü/Yalnız yabancı parçalar",
|
"ForeignPartsOnly": "Gömülü/Yalnız yabancı parçalar",
|
||||||
"HearingImpairedShort": "HI/SDH"
|
"HearingImpairedShort": "HI/SDH",
|
||||||
|
"SelectAudioNormalizationHelp": "Parça ses kazancı - her parçanın ses seviyesini, aynı ses yüksekliğinde çalınacak şekilde ayarlar. Albüm ses kazancı - albümün dinamik aralığını koruyarak yalnızca bir albümdeki tüm parçaların ses düzeyini ayarlar.",
|
||||||
|
"LabelSelectAudioNormalization": "Ses Normalleştirme",
|
||||||
|
"LabelAlbumGain": "Albüm Ses Kazancı",
|
||||||
|
"LabelTrackGain": "Parça Ses Kazancı"
|
||||||
}
|
}
|
||||||
|
|
|
@ -640,7 +640,7 @@
|
||||||
"AllowHevcEncoding": "Дозволити кодування у форматі HEVC",
|
"AllowHevcEncoding": "Дозволити кодування у форматі HEVC",
|
||||||
"PreferFmp4HlsContainerHelp": "Віддавайте перевагу використанню fMP4 як контейнера за умовчанням для HLS, що дає змогу направляти потоковий вміст HEVC на підтримувані пристрої.",
|
"PreferFmp4HlsContainerHelp": "Віддавайте перевагу використанню fMP4 як контейнера за умовчанням для HLS, що дає змогу направляти потоковий вміст HEVC на підтримувані пристрої.",
|
||||||
"RemuxHelp1": "Носій даних знаходиться у несумісному контейнері файлів (MKV, AVI, WMV тощо), але і відеопотік, і аудіопотік сумісні з пристроєм. Носії будуть перепаковані без втрат на льоту перед надсиланням на пристрій.",
|
"RemuxHelp1": "Носій даних знаходиться у несумісному контейнері файлів (MKV, AVI, WMV тощо), але і відеопотік, і аудіопотік сумісні з пристроєм. Носії будуть перепаковані без втрат на льоту перед надсиланням на пристрій.",
|
||||||
"LabelFallbackFontPathHelp": "Вкажіть шлях, що містить резервні шрифти для відображення субтитрів ASS/SSA. Максимально допустимий загальний розмір шрифту - 20 МБ. Рекомендуються легкі та зручні для Інтернету формати шрифтів, такі як woff2.",
|
"LabelFallbackFontPathHelp": "Ці шрифти використовуються деякими клієнтами для відтворення субтитрів. Будь ласка, зверніться до документації для отримання додаткової інформації.",
|
||||||
"XmlTvPathHelp": "Шлях до файлу XMLTV. Jellyfin прочитає цей файл і періодично перевірятиме його на наявність оновлень. Ви несете відповідальність за створення та оновлення файлу.",
|
"XmlTvPathHelp": "Шлях до файлу XMLTV. Jellyfin прочитає цей файл і періодично перевірятиме його на наявність оновлень. Ви несете відповідальність за створення та оновлення файлу.",
|
||||||
"WriteAccessRequired": "Jellyfin вимагає доступу до запису в цю папку. Будь ласка, забезпечте доступ до запису та повторіть спробу.",
|
"WriteAccessRequired": "Jellyfin вимагає доступу до запису в цю папку. Будь ласка, забезпечте доступ до запису та повторіть спробу.",
|
||||||
"Watched": "Переглянуто",
|
"Watched": "Переглянуто",
|
||||||
|
@ -1652,7 +1652,7 @@
|
||||||
"HomeVideosPhotos": "Домашні відео та фото",
|
"HomeVideosPhotos": "Домашні відео та фото",
|
||||||
"EnableSplashScreen": "Увімкнути заставку",
|
"EnableSplashScreen": "Увімкнути заставку",
|
||||||
"ScreenResolution": "Роздільна здатність екрану",
|
"ScreenResolution": "Роздільна здатність екрану",
|
||||||
"RememberSubtitleSelectionsHelp": "Спробувати обрати дорожку субтитрів, яка найбільше відповідає попередньому відео.",
|
"RememberSubtitleSelectionsHelp": "Спробувати обрати доріжку субтитрів, яка найбільше відповідає попередньому відео.",
|
||||||
"RememberSubtitleSelections": "Обрати дорожку субтитрів засновуючись на попередньому елементі",
|
"RememberSubtitleSelections": "Обрати дорожку субтитрів засновуючись на попередньому елементі",
|
||||||
"RememberAudioSelectionsHelp": "Спробувати обрати звукову доріжку, яка найбільше відповідає попередньому відео.",
|
"RememberAudioSelectionsHelp": "Спробувати обрати звукову доріжку, яка найбільше відповідає попередньому відео.",
|
||||||
"RememberAudioSelections": "Обрати звукову доріжку засновуючись на попередньому елементі",
|
"RememberAudioSelections": "Обрати звукову доріжку засновуючись на попередньому елементі",
|
||||||
|
@ -1773,5 +1773,9 @@
|
||||||
"GoHome": "На головну",
|
"GoHome": "На головну",
|
||||||
"BackdropScreensaver": "Фонова заставка",
|
"BackdropScreensaver": "Фонова заставка",
|
||||||
"LogoScreensaver": "Заставка з логотипом",
|
"LogoScreensaver": "Заставка з логотипом",
|
||||||
"LabelIsHearingImpaired": "Для людей з вадами слуху (SDH)"
|
"LabelIsHearingImpaired": "Для людей з вадами слуху (SDH)",
|
||||||
|
"SelectAudioNormalizationHelp": "Посилення треку - регулює гучність кожного треку так, щоб вони відтворювалися з однаковою гучністю. Посилення альбому - регулює гучність лише всіх треків в альбомі, зберігаючи динамічний діапазон альбому.",
|
||||||
|
"LabelAlbumGain": "Посилення альбому",
|
||||||
|
"LabelSelectAudioNormalization": "Нормалізація звуку",
|
||||||
|
"LabelTrackGain": "Посилення треку"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1429,7 +1429,7 @@
|
||||||
"DeleteAll": "Xóa Hết",
|
"DeleteAll": "Xóa Hết",
|
||||||
"EnableFallbackFontHelp": "Bật phông chữ thay thế tùy chỉnh. Điều này có thể tránh sự cố hiển thị phụ đề không chính xác.",
|
"EnableFallbackFontHelp": "Bật phông chữ thay thế tùy chỉnh. Điều này có thể tránh sự cố hiển thị phụ đề không chính xác.",
|
||||||
"EnableFallbackFont": "Bật phông chữ dự phòng",
|
"EnableFallbackFont": "Bật phông chữ dự phòng",
|
||||||
"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.",
|
"LabelFallbackFontPathHelp": "Những phông chữ này được một số máy khách sử dụng để hiển thị phụ đề. Vui lòng tham khảo tài liệu để biết thêm thông tin.",
|
||||||
"LabelFallbackFontPath": "Đường dẫn thư mục phông chữ dự phòng",
|
"LabelFallbackFontPath": "Đường dẫn thư mục phông chữ dự phòng",
|
||||||
"HeaderSelectFallbackFontPathHelp": "Duyệt hoặc nhập đường dẫn của thư mục phông chữ dự phòng dùng để hiển thị phụ đề ASS/SSA.",
|
"HeaderSelectFallbackFontPathHelp": "Duyệt hoặc nhập đường dẫn của thư mục phông chữ dự phòng dùng để 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",
|
||||||
|
@ -1757,5 +1757,6 @@
|
||||||
"HeaderGuestCast": "Ngôi Sao Khách Mời",
|
"HeaderGuestCast": "Ngôi Sao Khách Mời",
|
||||||
"GoHome": "Về Trang Chủ",
|
"GoHome": "Về Trang Chủ",
|
||||||
"AiTranslated": "Được AI Dịch",
|
"AiTranslated": "Được AI Dịch",
|
||||||
"MachineTranslated": "Được Máy Dịch"
|
"MachineTranslated": "Được Máy Dịch",
|
||||||
|
"LabelSelectAudioNormalization": "Chuẩn Hóa Âm Thanh"
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ progress {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: #ccc !important;
|
background: #ccc !important;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
progress[role]::after {
|
progress[role]::after {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import type { DeviceInfo } from '@jellyfin/sdk/lib/generated-client/models/device-info';
|
||||||
|
import type { SessionInfo } from '@jellyfin/sdk/lib/generated-client/models/session-info';
|
||||||
|
|
||||||
const BASE_DEVICE_IMAGE_URL = 'assets/img/devices/';
|
const BASE_DEVICE_IMAGE_URL = 'assets/img/devices/';
|
||||||
|
|
||||||
// audit note: this module is expected to return safe text for use in HTML
|
// audit note: this module is expected to return safe text for use in HTML
|
||||||
function getWebDeviceIcon(browser) {
|
function getWebDeviceIcon(browser: string | null | undefined) {
|
||||||
switch (browser) {
|
switch (browser) {
|
||||||
case 'Opera':
|
case 'Opera':
|
||||||
case 'Opera TV':
|
case 'Opera TV':
|
||||||
|
@ -31,8 +34,8 @@ function getWebDeviceIcon(browser) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDeviceIcon(device) {
|
export function getDeviceIcon(info: DeviceInfo | SessionInfo) {
|
||||||
switch (device.AppName || device.Client) {
|
switch ((info as DeviceInfo).AppName || (info as SessionInfo).Client) {
|
||||||
case 'Samsung Smart TV':
|
case 'Samsung Smart TV':
|
||||||
return BASE_DEVICE_IMAGE_URL + 'samsung.svg';
|
return BASE_DEVICE_IMAGE_URL + 'samsung.svg';
|
||||||
case 'Xbox One':
|
case 'Xbox One':
|
||||||
|
@ -58,13 +61,20 @@ export function getDeviceIcon(device) {
|
||||||
case 'Finamp':
|
case 'Finamp':
|
||||||
return BASE_DEVICE_IMAGE_URL + 'finamp.svg';
|
return BASE_DEVICE_IMAGE_URL + 'finamp.svg';
|
||||||
case 'Jellyfin Web':
|
case 'Jellyfin Web':
|
||||||
return getWebDeviceIcon(device.Name || device.DeviceName);
|
return getWebDeviceIcon((info as DeviceInfo).Name || (info as SessionInfo).DeviceName);
|
||||||
default:
|
default:
|
||||||
|
if (info.Capabilities?.IconUrl) {
|
||||||
|
try {
|
||||||
|
return new URL(info.Capabilities.IconUrl).toString();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[getDeviceIcon] device capabilities has invalid IconUrl', info, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
return BASE_DEVICE_IMAGE_URL + 'other.svg';
|
return BASE_DEVICE_IMAGE_URL + 'other.svg';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLibraryIcon(library) {
|
export function getLibraryIcon(library: string | null | undefined) {
|
||||||
switch (library) {
|
switch (library) {
|
||||||
case 'movies':
|
case 'movies':
|
||||||
return 'video_library';
|
return 'video_library';
|
||||||
|
@ -94,6 +104,6 @@ export function getLibraryIcon(library) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getDeviceIcon: getDeviceIcon,
|
getDeviceIcon,
|
||||||
getLibraryIcon: getLibraryIcon
|
getLibraryIcon
|
||||||
};
|
};
|
Loading…
Add table
Add a link
Reference in a new issue