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

Merge remote-tracking branch 'upstream/master' into lint-improvements

This commit is contained in:
MrTimscampi 2020-05-30 13:44:01 +02:00
commit 4d9fdb1963
20 changed files with 1207 additions and 571 deletions

View file

@ -58,6 +58,7 @@
"core-js": "^3.6.5",
"date-fns": "^2.14.0",
"document-register-element": "^1.14.3",
"epubjs": "^0.3.85",
"fast-text-encoding": "^1.0.1",
"flv.js": "^1.5.0",
"headroom.js": "^0.11.0",
@ -96,6 +97,8 @@
"src/components/playback/mediasession.js",
"src/components/sanatizefilename.js",
"src/components/scrollManager.js",
"src/components/bookPlayer/plugin.js",
"src/components/bookPlayer/tableOfContent.js",
"src/components/syncplay/playbackPermissionManager.js",
"src/components/syncplay/groupSelectionMenu.js",
"src/components/syncplay/timeSyncManager.js",

View file

@ -102,6 +102,11 @@ _define('jellyfin-noto', function () {
return noto;
});
var epubjs = require('epubjs');
_define('epubjs', function () {
return epubjs;
});
// page.js
var page = require('page');
_define('page', function() {

View file

@ -0,0 +1,288 @@
import connectionManager from 'connectionManager';
import loading from 'loading';
import keyboardnavigation from 'keyboardnavigation';
import dialogHelper from 'dialogHelper';
import events from 'events';
import 'css!./style';
import 'material-icons';
import 'paper-icon-button-light';
import TableOfContent from './tableOfContent';
export class BookPlayer {
constructor() {
this.name = 'Book Player';
this.type = 'mediaplayer';
this.id = 'bookplayer';
this.priority = 1;
this.onDialogClosed = this.onDialogClosed.bind(this);
this.openTableOfContents = this.openTableOfContents.bind(this);
this.onWindowKeyUp = this.onWindowKeyUp.bind(this);
}
play(options) {
this._progress = 0;
this._loaded = false;
loading.show();
let elem = this.createMediaElement();
return this.setCurrentSrc(elem, options);
}
stop() {
this.unbindEvents();
let elem = this._mediaElement;
let tocElement = this._tocElement;
let rendition = this._rendition;
if (elem) {
dialogHelper.close(elem);
this._mediaElement = null;
}
if (tocElement) {
tocElement.destroy();
this._tocElement = null;
}
if (rendition) {
rendition.destroy();
}
// Hide loader in case player was not fully loaded yet
loading.hide();
this._cancellationToken.shouldCancel = true;
}
currentItem() {
return this._currentItem;
}
currentTime() {
return this._progress * 1000;
}
duration() {
return 1000;
}
getBufferedRanges() {
return [{
start: 0,
end: 10000000
}];
}
volume() {
return 100;
}
isMuted() {
return false;
}
paused() {
return false;
}
seekable() {
return true;
}
onWindowKeyUp(e) {
let key = keyboardnavigation.getKeyName(e);
let rendition = this._rendition;
let book = rendition.book;
switch (key) {
case 'l':
case 'ArrowRight':
case 'Right':
if (this._loaded) {
book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next();
}
break;
case 'j':
case 'ArrowLeft':
case 'Left':
if (this._loaded) {
book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev();
}
break;
case 'Escape':
if (this._tocElement) {
// Close table of contents on ESC if it is open
this._tocElement.destroy();
} else {
// Otherwise stop the entire book player
this.stop();
}
break;
}
}
onDialogClosed() {
this.stop();
}
bindMediaElementEvents() {
let elem = this._mediaElement;
elem.addEventListener('close', this.onDialogClosed, {once: true});
elem.querySelector('.btnBookplayerExit').addEventListener('click', this.onDialogClosed, {once: true});
elem.querySelector('.btnBookplayerToc').addEventListener('click', this.openTableOfContents);
}
bindEvents() {
this.bindMediaElementEvents();
document.addEventListener('keyup', this.onWindowKeyUp);
// FIXME: I don't really get why document keyup event is not triggered when epub is in focus
this._rendition.on('keyup', this.onWindowKeyUp);
}
unbindMediaElementEvents() {
let elem = this._mediaElement;
elem.removeEventListener('close', this.onDialogClosed);
elem.querySelector('.btnBookplayerExit').removeEventListener('click', this.onDialogClosed);
elem.querySelector('.btnBookplayerToc').removeEventListener('click', this.openTableOfContents);
}
unbindEvents() {
if (this._mediaElement) {
this.unbindMediaElementEvents();
}
document.removeEventListener('keyup', this.onWindowKeyUp);
if (this._rendition) {
this._rendition.off('keyup', this.onWindowKeyUp);
}
}
openTableOfContents() {
if (this._loaded) {
this._tocElement = new TableOfContent(this);
}
}
createMediaElement() {
let elem = this._mediaElement;
if (elem) {
return elem;
}
elem = document.getElementById('bookPlayer');
if (!elem) {
elem = dialogHelper.createDialog({
exitAnimationDuration: 400,
size: 'fullscreen',
autoFocus: false,
scrollY: false,
exitAnimation: 'fadeout',
removeOnClose: true
});
elem.id = 'bookPlayer';
let html = '';
html += '<div class="topRightActionButtons">';
html += '<button is="paper-icon-button-light" class="autoSize bookplayerButton btnBookplayerExit hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon close"></i></button>';
html += '</div>';
html += '<div class="topLeftActionButtons">';
html += '<button is="paper-icon-button-light" class="autoSize bookplayerButton btnBookplayerToc hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon toc"></i></button>';
html += '</div>';
elem.innerHTML = html;
dialogHelper.open(elem);
}
this._mediaElement = elem;
return elem;
}
setCurrentSrc(elem, options) {
let item = options.items[0];
this._currentItem = item;
this.streamInfo = {
started: true,
ended: false,
mediaSource: {
Id: item.Id
}
};
if (!item.Path.endsWith('.epub')) {
return new Promise((resolve, reject) => {
let errorDialog = dialogHelper.createDialog({
size: 'small',
autoFocus: false,
removeOnClose: true
});
errorDialog.innerHTML = '<h1 class="bookplayerErrorMsg">This book type is not supported yet</h1>';
this.stop();
dialogHelper.open(errorDialog);
loading.hide();
return resolve();
});
}
let serverId = item.ServerId;
let apiClient = connectionManager.getApiClient(serverId);
return new Promise((resolve, reject) => {
require(['epubjs'], (epubjs) => {
let downloadHref = apiClient.getItemDownloadUrl(item.Id);
let book = epubjs.default(downloadHref, {openAs: 'epub'});
let rendition = book.renderTo(elem, {width: '100%', height: '97%'});
this._currentSrc = downloadHref;
this._rendition = rendition;
let cancellationToken = {
shouldCancel: false
};
this._cancellationToken = cancellationToken;
return rendition.display().then(() => {
let epubElem = document.querySelector('.epub-container');
epubElem.style.display = 'none';
this.bindEvents();
return this._rendition.book.locations.generate(1024).then(() => {
if (cancellationToken.shouldCancel) {
return reject();
}
this._loaded = true;
epubElem.style.display = 'block';
rendition.on('relocated', (locations) => {
this._progress = book.locations.percentageFromCfi(locations.start.cfi);
events.trigger(this, 'timeupdate');
});
loading.hide();
return resolve();
});
}, () => {
console.error('Failed to display epub');
return reject();
});
});
});
}
canPlayMediaType(mediaType) {
return (mediaType || '').toLowerCase() === 'book';
}
}
export default BookPlayer;

View file

@ -0,0 +1,39 @@
#bookPlayer {
position: relative;
height: 100%;
width: 100%;
overflow: auto;
z-index: 100;
background: #fff;
}
.topRightActionButtons {
right: 0.5vh;
top: 0.5vh;
z-index: 1002;
position: absolute;
}
.topLeftActionButtons {
left: 0.5vh;
top: 0.5vh;
z-index: 1002;
position: absolute;
}
.bookplayerButtonIcon {
color: black;
opacity: 0.7;
}
#dialogToc {
background-color: white;
}
.toc li {
margin-bottom: 5px;
}
.bookplayerErrorMsg {
text-align: center;
}

View file

@ -0,0 +1,90 @@
import dialogHelper from 'dialogHelper';
export default class TableOfContent {
constructor(bookPlayer) {
this._bookPlayer = bookPlayer;
this._rendition = bookPlayer._rendition;
this.onDialogClosed = this.onDialogClosed.bind(this);
this.createMediaElement();
}
destroy() {
let elem = this._elem;
if (elem) {
this.unbindEvents();
dialogHelper.close(elem);
}
this._bookPlayer._tocElement = null;
}
bindEvents() {
let elem = this._elem;
elem.addEventListener('close', this.onDialogClosed, {once: true});
elem.querySelector('.btnBookplayerTocClose').addEventListener('click', this.onDialogClosed, {once: true});
}
unbindEvents() {
let elem = this._elem;
elem.removeEventListener('close', this.onDialogClosed);
elem.querySelector('.btnBookplayerTocClose').removeEventListener('click', this.onDialogClosed);
}
onDialogClosed() {
this.destroy();
}
replaceLinks(contents, f) {
let links = contents.querySelectorAll('a[href]');
links.forEach((link) => {
let href = link.getAttribute('href');
link.onclick = () => {
f(href);
return false;
};
});
}
createMediaElement() {
let rendition = this._rendition;
let elem = dialogHelper.createDialog({
size: 'small',
autoFocus: false,
removeOnClose: true
});
elem.id = 'dialogToc';
let tocHtml = '<div class="topRightActionButtons">';
tocHtml += '<button is="paper-icon-button-light" class="autoSize bookplayerButton btnBookplayerTocClose hide-mouse-idle-tv" tabindex="-1"><span class="material-icons bookplayerButtonIcon close"></span></button>';
tocHtml += '</div>';
tocHtml += '<ul class="toc">';
rendition.book.navigation.forEach((chapter) => {
tocHtml += '<li>';
// Remove '../' from href
let link = chapter.href.startsWith('../') ? chapter.href.substr(3) : chapter.href;
tocHtml += `<a href="${rendition.book.path.directory + link}">${chapter.label}</a>`;
tocHtml += '</li>';
});
tocHtml += '</ul>';
elem.innerHTML = tocHtml;
this.replaceLinks(elem, (href) => {
let relative = rendition.book.path.relative(href);
rendition.display(relative);
this.destroy();
});
this._elem = elem;
this.bindEvents();
dialogHelper.open(elem);
}
}

View file

@ -46,7 +46,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir
function showNonPersistentNotification(title, options, timeoutMs) {
try {
var notif = new Notification(title, options);
var notif = new Notification(title, options); /* eslint-disable-line compat/compat */
if (notif.show) {
notif.show();

View file

@ -119,6 +119,7 @@ import connectionManager from 'connectionManager';
const canSeek = playState.CanSeek || false;
if ('mediaSession' in navigator) {
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.metadata = new MediaMetadata({
title: title,
artist: artist,
@ -179,6 +180,7 @@ import connectionManager from 'connectionManager';
function hideMediaControls() {
if ('mediaSession' in navigator) {
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.metadata = null;
} else {
window.NativeShell.hideMediaSession();
@ -210,26 +212,32 @@ import connectionManager from 'connectionManager';
}
if ('mediaSession' in navigator) {
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('previoustrack', function () {
execute('previousTrack');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('nexttrack', function () {
execute('nextTrack');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('play', function () {
execute('unpause');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('pause', function () {
execute('pause');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('seekbackward', function () {
execute('rewind');
});
/* eslint-disable-next-line compat/compat */
navigator.mediaSession.setActionHandler('seekforward', function () {
execute('fastForward');
});

View file

@ -2187,7 +2187,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
// Only used internally
self.getCurrentTicks = getCurrentTicks;
function playPhotos(items, options, user) {
function playOther(items, options, user) {
var playStartIndex = options.startIndex || 0;
var player = getPlayer(items[playStartIndex], options);
@ -2216,9 +2216,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
return Promise.reject();
}
if (firstItem.MediaType === 'Photo') {
if (firstItem.MediaType === 'Photo' || firstItem.MediaType === 'Book') {
return playPhotos(items, options, user);
return playOther(items, options, user);
}
var apiClient = connectionManager.getApiClient(firstItem.ServerId);

View file

@ -58,7 +58,7 @@ define(['events', 'globalize'], function (events, globalize) {
return new Promise(function (resolve, reject) {
require([pluginSpec], (pluginFactory) => {
var plugin = new pluginFactory();
var plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory();
// See if it's already installed
var existing = instance.pluginsList.filter(function (p) {

View file

@ -491,6 +491,7 @@ var AppInfo = {};
'components/htmlAudioPlayer/plugin',
'components/htmlVideoPlayer/plugin',
'components/photoPlayer/plugin',
'components/bookPlayer/plugin',
'components/youtubeplayer/plugin',
'components/backdropScreensaver/plugin',
'components/logoScreensaver/plugin'
@ -561,6 +562,7 @@ var AppInfo = {};
require(['components/playback/volumeosd']);
}
/* eslint-disable-next-line compat/compat */
if (navigator.mediaSession || window.NativeShell) {
require(['mediaSession']);
}
@ -677,6 +679,7 @@ var AppInfo = {};
'fetch',
'flvjs',
'jstree',
'epubjs',
'jQuery',
'hlsjs',
'howler',

View file

@ -1478,8 +1478,8 @@
"MessageConfirmAppExit": "Wirklich verlassen?",
"LabelVideoResolution": "Videoauflösung:",
"LabelStreamType": "Streamtyp:",
"EnableFastImageFadeInHelp": "Aktiviere schnellere Einblendeanimation für geladene Bilder",
"EnableFastImageFadeIn": "Schnelle Bildeinblendung",
"EnableFastImageFadeInHelp": "Zeige Poster und andere Bilder mit einer schnelleren Einblendeanimation, wenn diese fertig geladen sind.",
"EnableFastImageFadeIn": "Schnelle Bildeinblendungsanimationen",
"LabelPlayerDimensions": "Playerabmessungen:",
"LabelDroppedFrames": "Verlorene Frames:",
"LabelCorruptedFrames": "Fehlerhafte Frames:",
@ -1540,7 +1540,7 @@
"HeaderServerAddressSettings": "Server-Adresseinstellungen",
"HeaderRemoteAccessSettings": "Fernzugriffs-Einstellungen",
"HeaderHttpsSettings": "HTTPS-Einstellungen",
"SyncPlayAccessHelp": "Wähle die Berechtigungsstufe, die dieser Benutzer auf das SyncPlay-Feature hat. SyncPlay ermöglicht die Synchronisierung der Wiedergabe mit anderen Benutzern.",
"SyncPlayAccessHelp": "Wähle die Berechtigungsstufe, die dieser Benutzer auf das SyncPlay-Feature hat. SyncPlay ermöglicht die Synchronisierung der Wiedergabe mit anderen Geräten.",
"MessageSyncPlayErrorMedia": "SyncPlay konnte nicht aktiviert werden! Medienfehler.",
"MessageSyncPlayErrorMissingSession": "SyncPlay konnte nicht aktiviert werden! Fehlende Sitzung.",
"MessageSyncPlayErrorNoActivePlayer": "Keine aktive Wiedergabe gefunden. SyncPlay wurde deaktiviert.",
@ -1569,5 +1569,7 @@
"MillisecondsUnit": "ms",
"LabelSyncPlayTimeOffset": "Zeitversatz mit dem Server:",
"HeaderSyncPlayEnabled": "SyncPlay aktiviert",
"HeaderSyncPlaySelectGroup": "Tritt einer Gruppe bei"
"HeaderSyncPlaySelectGroup": "Tritt einer Gruppe bei",
"EnableDetailsBannerHelp": "Zeigt ein Bannerbild im oberen Bereich der Seite Item-Details.",
"EnableDetailsBanner": "Detailbanner"
}

View file

@ -1403,7 +1403,7 @@
"Suggestions": "Suggestions",
"Sunday": "Sunday",
"Sync": "Sync",
"SyncPlayAccessHelp": "Select the level of access this user has to the SyncPlay feature. SyncPlay enables to sync playback with other users.",
"SyncPlayAccessHelp": "Select the level of access this user has to the SyncPlay feature. SyncPlay enables to sync playback with other devices.",
"SystemDlnaProfilesHelp": "System profiles are read-only. Changes to a system profile will be saved to a new custom profile.",
"TV": "TV",
"TabAccess": "Access",

View file

@ -32,7 +32,7 @@
"AnamorphicVideoNotSupported": "Video anamorfico no soportado",
"AnyLanguage": "Cualquier idioma",
"Anytime": "En cualquier momento",
"AroundTime": "Alrededor de {0}",
"AroundTime": "Alrededor de",
"Art": "Arte",
"Artists": "Artistas",
"AsManyAsPossible": "Tantos como sea posible",
@ -103,7 +103,7 @@
"ButtonRemove": "Remover",
"ButtonRename": "Renombrar",
"ButtonRepeat": "Repetir",
"ButtonResetEasyPassword": "Reiniciar el código pin sencillo",
"ButtonResetEasyPassword": "Restablecer código PIN sencillo",
"ButtonResetPassword": "Restablecer contraseña",
"ButtonRestart": "Reiniciar",
"ButtonResume": "Continuar",
@ -203,7 +203,7 @@
"EnableBackdrops": "Imágenes de fondo",
"EnableBackdropsHelp": "Muestra imágenes de fondo en el fondo de algunas páginas mientras se navega por la biblioteca.",
"EnableCinemaMode": "Modo cine",
"EnableColorCodedBackgrounds": "Fondos de color codificados",
"EnableColorCodedBackgrounds": "Fondos de colores codificados",
"EnableDisplayMirroring": "Duplicado de pantalla",
"EnableExternalVideoPlayers": "Reproductores de video externos",
"EnableExternalVideoPlayersHelp": "Un menú de reproductor externo se mostrara cuando inicie la reproducción de un video.",
@ -410,8 +410,8 @@
"HeaderRecordingOptions": "Opciones de grabación",
"HeaderRecordingPostProcessing": "Post procesado de las grabaciones",
"HeaderRemoteControl": "Control remoto",
"HeaderRemoveMediaFolder": "Eliminar carpeta de medios",
"HeaderRemoveMediaLocation": "Eliminar ubicación de medios",
"HeaderRemoveMediaFolder": "Remover carpeta de medios",
"HeaderRemoveMediaLocation": "Remover ubicación de medios",
"HeaderResponseProfile": "Perfil de respuesta",
"HeaderResponseProfileHelp": "Los perfiles de respuesta proporcionan un medio para personalizar la información enviada al dispositivo cuando se reproducen ciertos tipos de medios.",
"HeaderRestart": "Reiniciar",
@ -697,7 +697,7 @@
"LabelNumberOfGuideDays": "Número de días de datos de la programación a descargar:",
"LabelNumberOfGuideDaysHelp": "Descargar más días de datos de programación permite programar con mayor anticipación y ver más listados, pero tomará más tiempo en descargar. Auto hará la selección basada en el número de canales.",
"LabelOptionalNetworkPath": "(Opcional) Carpeta de red compartida:",
"LabelOptionalNetworkPathHelp": "Si esta carpeta es compartida en su red, proveer la ruta del recurso compartido de red puede permitir a las aplicaciones Jellyfin en otros dispositivos acceder a los archivos de medios directamente.",
"LabelOptionalNetworkPathHelp": "Si esta carpeta es compartida en su red, proveer la ruta del recurso compartido de red puede permitir a las aplicaciones Jellyfin en otros dispositivos acceder a los archivos de medios directamente. Por ejemplo, {0} o {1}.",
"LabelOriginalAspectRatio": "Relación de aspecto original:",
"LabelOriginalTitle": "Título original:",
"LabelOverview": "Resumen:",
@ -878,7 +878,7 @@
"MessageConfirmDeleteTunerDevice": "¿Estás seguro de querer eliminar este dispositivo?",
"MessageConfirmProfileDeletion": "¿Estás seguro de querer eliminar este perfil?",
"MessageConfirmRecordingCancellation": "¿Cancelar grabación?",
"MessageConfirmRemoveMediaLocation": "¿Estás seguro de querer eliminar esta ubicación?",
"MessageConfirmRemoveMediaLocation": "¿Estás seguro de querer remover esta ubicación?",
"MessageConfirmRestart": "¿Estás seguro de que deseas reiniciar el servidor Jellyfin?",
"MessageConfirmRevokeApiKey": "¿Estás seguro de querer revocar esta clave API? La conexión de la aplicación con el servidor Jellyfin será terminada abruptamente.",
"MessageConfirmShutdown": "¿Estás seguro de que deseas apagar el servidor?",
@ -912,7 +912,7 @@
"MessagePluginInstallDisclaimer": "Los complementos desarrollados por miembros de la comunidad Jellyfin son una gran forma de mejorar tu experiencia con Jellyfin con características y beneficios adicionales. Antes de instalar, por favor, conoce el impacto que pueden ocasionar en tu servidor Jellyfin, tales como escaneo más largo de bibliotecas, procesamiento en segundo plano adicional y reducción de la estabilidad del sistema.",
"MessageReenableUser": "Ver abajo para volver a habilitar",
"MessageSettingsSaved": "Configuraciones guardadas.",
"MessageTheFollowingLocationWillBeRemovedFromLibrary": "Las siguientes ubicaciones de medios se eliminarán de tu biblioteca:",
"MessageTheFollowingLocationWillBeRemovedFromLibrary": "Las siguientes ubicaciones de medios se removerán de tu biblioteca:",
"MessageUnableToConnectToServer": "No podemos conectarnos al servidor seleccionado en este momento. Por favor, asegúrate de que está funcionando e inténtalo de nuevo.",
"MessageUnsetContentHelp": "El contenido será mostrado como carpetas simples. Para mejores resultados utiliza el administrador de metadatos para establecer los tipos de contenido para las subcarpetas.",
"MessageYouHaveVersionInstalled": "Actualmente cuentas con la versión {0} instalada.",
@ -1453,7 +1453,7 @@
"LabelTranscodingFramerate": "Velocidad de cuadros de la transcodificación:",
"LabelSize": "Tamaño:",
"SelectAdminUsername": "Por favor, selecciona un nombre de usuario para la cuenta de administrador.",
"EnableFastImageFadeInHelp": "Habilita una animación más rápida de desvanecimiento para las imágenes cargadas",
"EnableFastImageFadeInHelp": "Habilita una animación más rápida de desvanecimiento para las imágenes cargadas.",
"LabelDroppedFrames": "Cuadros saltados:",
"CopyStreamURLError": "Hubo un error al copiar la URL.",
"ButtonSplit": "Dividir",
@ -1484,7 +1484,7 @@
"MessageConfirmAppExit": "¿Deseas salir?",
"LabelVideoResolution": "Resolución de video:",
"LabelStreamType": "Tipo de transmisión:",
"EnableFastImageFadeIn": "Desvanecimiento rápido de las imágenes",
"EnableFastImageFadeIn": "Desvanecimiento rápido de animaciones",
"LabelPlayerDimensions": "Dimensiones del reproductor:",
"LabelCorruptedFrames": "Cuadros corruptos:",
"HeaderNavigation": "Navegación",
@ -1524,5 +1524,37 @@
"HeaderRemoteAccessSettings": "Opciones de acceso remoto",
"HeaderHttpsSettings": "Opciones HTTPS",
"HeaderDVR": "DVR",
"ApiKeysCaption": "Lista de claves API actualmente habilitadas"
"ApiKeysCaption": "Lista de claves API actualmente habilitadas",
"SyncPlayAccessHelp": "Selecciona el nivel de acceso que este usuario tiene a la función SyncPlay. SyncPlay permite sincronizar la reproducción con otros dispositivos.",
"MessageSyncPlayErrorMedia": "¡Fallo al activar SyncPlay! Error en el archivo de medios.",
"MessageSyncPlayErrorMissingSession": "¡Fallo al activar SyncPlay! Falta la sesión.",
"MessageSyncPlayErrorNoActivePlayer": "No se ha encontrado ningún reproductor activo. SyncPlay ha sido desactivado.",
"MessageSyncPlayErrorAccessingGroups": "Se produjo un error al acceder a la lista de grupos.",
"MessageSyncPlayLibraryAccessDenied": "El acceso a este contenido está restringido.",
"MessageSyncPlayJoinGroupDenied": "Permiso requerido para usar SyncPlay.",
"MessageSyncPlayCreateGroupDenied": "Permiso requerido para crear un grupo.",
"MessageSyncPlayGroupDoesNotExist": "Fallo al unirse al grupo porque éste no existe.",
"MessageSyncPlayPlaybackPermissionRequired": "Permiso de reproducción requerido.",
"MessageSyncPlayNoGroupsAvailable": "No hay grupos disponibles. Empieza a reproducir algo primero.",
"MessageSyncPlayGroupWait": "<b>{0}</b> está cargando...",
"MessageSyncPlayUserLeft": "<b>{0}</b> abandonó el grupo.",
"MessageSyncPlayUserJoined": "<b>{0}</b> se ha unido al grupo.",
"MessageSyncPlayDisabled": "SyncPlay deshabilitado.",
"MessageSyncPlayEnabled": "SyncPlay habilitado.",
"LabelSyncPlayAccess": "Acceso a SyncPlay",
"LabelSyncPlayAccessNone": "Deshabilitado para este usuario",
"LabelSyncPlayAccessJoinGroups": "Permitir al usuario unirse a grupos",
"LabelSyncPlayAccessCreateAndJoinGroups": "Permitir al usuario crear y unirse a grupos",
"LabelSyncPlayLeaveGroupDescription": "Deshabilitar SyncPlay",
"LabelSyncPlayLeaveGroup": "Abandonar grupo",
"LabelSyncPlayNewGroupDescription": "Crear un nuevo grupo",
"LabelSyncPlayNewGroup": "Nuevo grupo",
"LabelSyncPlaySyncMethod": "Método de sincronización:",
"LabelSyncPlayPlaybackDiff": "Diferencia de tiempo de reproducción:",
"MillisecondsUnit": "ms",
"LabelSyncPlayTimeOffset": "Tiempo compensado respecto al servidor:",
"HeaderSyncPlayEnabled": "SyncPlay habilitado",
"HeaderSyncPlaySelectGroup": "Unirse a un grupo",
"EnableDetailsBannerHelp": "Mostrar una imagen banner en la parte superior de la página de detalles del elemento.",
"EnableDetailsBanner": "Banner de detalles"
}

View file

@ -1525,7 +1525,7 @@
"TabDVR": "DVR",
"SaveChanges": "Guardar cambios",
"HeaderDVR": "DVR",
"SyncPlayAccessHelp": "Selecciona el nivel de acceso que posee este usuario al SyncPlay. SyncPlay permite sincronizar reproductores con otros usuarios.",
"SyncPlayAccessHelp": "Selecciona los permisos de este usuario para utilizar SyncPlay. SyncPlay te permite sincroniza la reproducción entre varios dispositivos.",
"MessageSyncPlayErrorMedia": "¡No se pudo activar SyncPlay! Error de medio.",
"MessageSyncPlayErrorMissingSession": "¡No se pudo activar SyncPlay! Sesión desconectada.",
"MessageSyncPlayErrorNoActivePlayer": "No hay reproductor activo. SyncPlay ha sido desactivado.",

42
src/strings/es_419.json Normal file
View file

@ -0,0 +1,42 @@
{
"ValueSpecialEpisodeName": "Especial - {0}",
"Sync": "Sincronizar",
"Songs": "Canciones",
"Shows": "Programas",
"Playlists": "Listas de reproducción",
"Photos": "Fotos",
"Movies": "Películas",
"HeaderNextUp": "A continuación",
"HeaderLiveTV": "TV en vivo",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderFavoriteShows": "Programas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderContinueWatching": "Continuar viendo",
"HeaderAlbumArtists": "Artistas del álbum",
"Genres": "Géneros",
"Folders": "Carpetas",
"Favorites": "Favoritos",
"Collections": "Colecciones",
"Channels": "Canales",
"Books": "Libros",
"Artists": "Artistas",
"Albums": "Álbumes",
"TabLatest": "Recientes",
"HeaderUser": "Usuario",
"AlbumArtist": "Artista del álbum",
"Album": "Álbum",
"Aired": "Transmitido",
"AirDate": "Fecha de emisión",
"AdditionalNotificationServices": "Explora el catálogo de complementos para instalar servicios de notificaciones adicionales.",
"AddedOnValue": "Agregado {0}",
"AddToPlaylist": "Agregar a lista de reproducción",
"AddToPlayQueue": "Agregar a la cola de reproducción",
"AddToCollection": "Agregar a colección",
"AddItemToCollectionHelp": "Agrega elementos a las colecciones buscándolos y utilizando sus menúes al hacer clic derecho o al tocarlos para agregarlos a una colección.",
"Add": "Agregar",
"Actor": "Actor",
"AccessRestrictedTryAgainLater": "El acceso está restringido actualmente. Por favor, inténtalo más tarde.",
"Absolute": "Absoluto"
}

View file

@ -1393,7 +1393,7 @@
"ButtonSplit": "Szétvág",
"Absolute": "Abszolút",
"LabelSkipIfAudioTrackPresentHelp": "Vedd ki a pipát, ha minden videóhoz szeretnél feliratot az audio nyelvétől függetlenül.",
"EnableFastImageFadeInHelp": "Gyorsabb előtűnés animáció a betöltött képekhez",
"EnableFastImageFadeInHelp": "Poszterek és más képek megjelenítése gyorsabb animációkkal.",
"EnableFastImageFadeIn": "Gyors kép-előtűnés",
"SubtitleOffset": "Felirat eltolása",
"SeriesDisplayOrderHelp": "Rakd sorba az epizódokat az adásba kerülésük dátuma, a DVD sorszám, vagy az abszolút számozás szerint.",
@ -1539,5 +1539,22 @@
"LabelSyncPlaySyncMethod": "Szinkronizálási mód:",
"MillisecondsUnit": "ms",
"HeaderSyncPlayEnabled": "SyncPlay engedélyezve",
"HeaderSyncPlaySelectGroup": "Csatlakozás csoporthoz"
"HeaderSyncPlaySelectGroup": "Csatlakozás csoporthoz",
"SyncPlayAccessHelp": "Válaszd ki, hogy ez a felhasználó milyen szinten férhet hozzá a SyncPlay funkcióhoz. A SyncPlay lehetőséget biztosít a lejátszások közötti szinkronizációra.",
"MessageSyncPlayErrorMedia": "Nem sikerült a SyncPlay engedélyezése! Média hiba.",
"MessageSyncPlayErrorMissingSession": "A SyncPlay lejátszása sikertelen! Hiányzó munkamenet.",
"MessageSyncPlayErrorNoActivePlayer": "Nem található aktív lejátszó. A SyncPlay letiltásra került.",
"MessageSyncPlayErrorAccessingGroups": "Hiba történt a csoportok listájának betöltésekor.",
"MessageSyncPlayLibraryAccessDenied": "A tartalomhoz való hozzáférés korlátozva van.",
"MessageSyncPlayJoinGroupDenied": "A SyncPlay használatához jogosultság szükséges.",
"MessageSyncPlayCreateGroupDenied": "Jogosultság szükséges a csoportok létrehozásához.",
"MessageSyncPlayGroupDoesNotExist": "Nem sikerült csatlakozni a csoporthoz, mivel az nem létezik.",
"MessageSyncPlayPlaybackPermissionRequired": "Lejátszási jogosultság szükséges.",
"MessageSyncPlayNoGroupsAvailable": "Nincsenek elérhető csoportok. Először kezdj el lejátszani valamit.",
"LabelSyncPlayAccessNone": "Letiltva ennél a felhasználónál",
"LabelSyncPlayAccessJoinGroups": "A felhasználó csoportokhoz való csatlakozásának engedélyezése",
"LabelSyncPlayPlaybackDiff": "Lejátszási időkülönbség:",
"LabelSyncPlayTimeOffset": "Időeltolás a szerverhez képest:",
"EnableDetailsBannerHelp": "Megjelenít egy banner képet a részletes információoldal tetején.",
"EnableDetailsBanner": "Banner a részletes oldalon"
}

View file

@ -1238,7 +1238,7 @@
"Repeat": "Repetă",
"RemoveFromPlaylist": "Scoateți din lista de redare",
"RemoveFromCollection": "Scoateți din colecție",
"RememberMe": "Ține-mă minte",
"RememberMe": "Ține-mă Minte",
"ReleaseDate": "Data lansării",
"RefreshQueued": "Actualizare adăugată în coadă.",
"RefreshMetadata": "Actualizați metadatele",
@ -1454,8 +1454,8 @@
"HeaderNavigation": "Navigare",
"MessageConfirmAppExit": "Vrei să ieși?",
"CopyStreamURLError": "A apărut o eroare la copierea adresei URL.",
"EnableFastImageFadeInHelp": "Activați animația mai rapidă de tranziție pentru imaginile încărcate",
"EnableFastImageFadeIn": "Tranziție a imaginii rapidă",
"EnableFastImageFadeInHelp": "Arătați postere și alte imagini cu o animație de tranziție rapidă când sunt deja încărcate.",
"EnableFastImageFadeIn": "Animație de Tranziție a Imaginii Rapidă",
"LabelVideoResolution": "Rezoluția video:",
"LabelStreamType": "Tipul streamului:",
"LabelPlayerDimensions": "Dimensiunile soft redare:",
@ -1519,5 +1519,37 @@
"HeaderHttpsSettings": "Setări https",
"TabDVR": "DVR",
"SaveChanges": "Salvează modificările",
"HeaderDVR": "DVR"
"HeaderDVR": "DVR",
"SyncPlayAccessHelp": "Selectați nivelul de acces pe care îl are acest utilizator la funcția SyncPlay. SyncPlay permite sincronizarea redării cu alte dispozitive.",
"MessageSyncPlayErrorMedia": "Eroare la activarea SyncPlay! Eroare media.",
"MessageSyncPlayErrorMissingSession": "Eroare la activarea SyncPlay! Sesiune lipsă.",
"MessageSyncPlayErrorNoActivePlayer": "Nu a fost găsit niciun soft de redare activ. SyncPlay a fost dezactivat.",
"MessageSyncPlayErrorAccessingGroups": "A apărut o eroare la accesarea listei de grupuri.",
"MessageSyncPlayLibraryAccessDenied": "Accesul la acest conținut este restricționat.",
"MessageSyncPlayJoinGroupDenied": "Permisiune necesară pentru a utiliza SyncPlay.",
"MessageSyncPlayCreateGroupDenied": "Permisiune necesară pentru crearea unui grup.",
"MessageSyncPlayGroupDoesNotExist": "Nu a reușit să se alăture grupului, deoarece nu există.",
"MessageSyncPlayPlaybackPermissionRequired": "Este necesară permisiunea de redare.",
"MessageSyncPlayNoGroupsAvailable": "Nu există grupuri disponibile. Începe să redai ceva mai întâi.",
"MessageSyncPlayGroupWait": "<b>{0}</b> se încarcă...",
"MessageSyncPlayUserLeft": "<b>{0}</b> a părăsit grupul.",
"MessageSyncPlayUserJoined": "<b>{0}</b> s-a alăturat grupului.",
"MessageSyncPlayDisabled": "SyncPlay dezactivat.",
"MessageSyncPlayEnabled": "SyncPlay activat.",
"LabelSyncPlayAccess": "Acces SyncPlay",
"LabelSyncPlayAccessNone": "Dezactivat pentru acest utilizator",
"LabelSyncPlayAccessJoinGroups": "Permiteți utilizatorului să se alăture grupurilor",
"LabelSyncPlayAccessCreateAndJoinGroups": "Permiteți utilizatorului să creeze și să se alăture grupurilor",
"LabelSyncPlayLeaveGroupDescription": "Dezactivează SyncPlay",
"LabelSyncPlayLeaveGroup": "Parăsește grup",
"LabelSyncPlayNewGroupDescription": "Crează un grup nou",
"LabelSyncPlayNewGroup": "Grup nou",
"LabelSyncPlaySyncMethod": "Metoda de sincronizare:",
"LabelSyncPlayPlaybackDiff": "Diferența de timp de redare:",
"MillisecondsUnit": "ms",
"LabelSyncPlayTimeOffset": "Decalare de timp cu serverul:",
"HeaderSyncPlayEnabled": "SyncPlay activat",
"HeaderSyncPlaySelectGroup": "Alăturați-vă unui grup",
"EnableDetailsBannerHelp": "Afișați o imagine de bandou în partea de sus a paginii cu detalii ale articolului.",
"EnableDetailsBanner": "Detalii Bandou"
}

View file

@ -1460,8 +1460,8 @@
"HeaderNavigation": "Навигация",
"LabelVideoResolution": "Разрешение видео:",
"LabelStreamType": "Тип потока:",
"EnableFastImageFadeInHelp": "Включить быстрое появление анимации для загруженных изображений",
"EnableFastImageFadeIn": "Быстрое появление изображения",
"EnableFastImageFadeInHelp": "Показывать постеры и другие рисунки анимацией побыстрее , когда они закончат загружаться.",
"EnableFastImageFadeIn": "Быстрое анимация рисунка",
"LabelPlayerDimensions": "Размеры проигрывателя:",
"LabelDroppedFrames": "Пропущенные кадры:",
"LabelCorruptedFrames": "Испорченные кадры:",
@ -1520,5 +1520,37 @@
"HeaderServerAddressSettings": "Параметры адреса сервера",
"HeaderRemoteAccessSettings": "Параметры удалённого доступа",
"HeaderHttpsSettings": "Параметры HTTPS",
"HeaderDVR": "DVR"
"HeaderDVR": "DVR",
"MessageSyncPlayJoinGroupDenied": "Требуется разрешение для использования SyncPlay.",
"MessageSyncPlayDisabled": "SyncPlay отключен.",
"MessageSyncPlayEnabled": "SyncPlay включён.",
"LabelSyncPlayAccess": "Доступ к SyncPlay",
"LabelSyncPlayLeaveGroupDescription": "Отключить SyncPlay",
"HeaderSyncPlayEnabled": "SyncPlay включён",
"HeaderSyncPlaySelectGroup": "Присоединить группу",
"EnableDetailsBanner": "Баннер подробностей",
"EnableDetailsBannerHelp": "Отображает рисунок баннера вверху страницы подробностей элемента.",
"MessageSyncPlayErrorAccessingGroups": "Произошла ошибка при попытке доступа к списку групп.",
"MessageSyncPlayLibraryAccessDenied": "Доступ к данному содержанию ограничен.",
"MessageSyncPlayCreateGroupDenied": "Требуется разрешение для создания группы.",
"MessageSyncPlayGroupDoesNotExist": "Не удалось присоединиться к группе, поскольку она не существует.",
"MessageSyncPlayPlaybackPermissionRequired": "Требуется разрешение на воспроизведение.",
"MessageSyncPlayNoGroupsAvailable": "Никакие группы не доступны. Сначала начните воспроизводить что-нибудь.",
"MessageSyncPlayGroupWait": "<b>{0}</b> буферизуется...",
"MessageSyncPlayUserLeft": "<b>{0}</b> покинул группу.",
"MessageSyncPlayUserJoined": "<b>{0}</b> присоединил группу.",
"LabelSyncPlayAccessNone": "Отключено для данного пользователя",
"LabelSyncPlayAccessJoinGroups": "Разрешить пользователю присоединяться к группам",
"LabelSyncPlayAccessCreateAndJoinGroups": "Разрешить пользователю создавать и присоединять группы",
"LabelSyncPlayLeaveGroup": "Покинуть группу",
"LabelSyncPlayNewGroupDescription": "Создание новой группы",
"LabelSyncPlayNewGroup": "Новая группа",
"LabelSyncPlaySyncMethod": "Метод синхронизации:",
"LabelSyncPlayPlaybackDiff": "Разница времени воспроизведения:",
"MillisecondsUnit": "мс",
"LabelSyncPlayTimeOffset": "Сдвиг времени относительно сервера:",
"SyncPlayAccessHelp": "Выберите уровень доступа данного пользователя к функциональности SyncPlay. SyncPlay позволяет синхронизировать воспроизведение с другими устройствами.",
"MessageSyncPlayErrorMedia": "Не удалось включить SyncPlay! Ошибка медиаданных.",
"MessageSyncPlayErrorMissingSession": "Не удалось включить SyncPlay! Отсутствует сеанс.",
"MessageSyncPlayErrorNoActivePlayer": "Активный проигрыватель не найден. SyncPlay был отключен."
}

View file

@ -721,7 +721,7 @@
"Refresh": "Obnoviť",
"RefreshMetadata": "Obnoviť metadáta",
"ReleaseDate": "Dátum vydania",
"RememberMe": "Zapamätať si ma",
"RememberMe": "Zapamätaj si ma",
"RemoveFromCollection": "Odobrať z kolekcie",
"Repeat": "Opakovať",
"RepeatAll": "Opakovať všetko",
@ -1457,8 +1457,8 @@
"MessageConfirmAppExit": "Chceli by ste odísiť?",
"LabelVideoResolution": "Rozlíšenie videa:",
"LabelStreamType": "Typ streamu:",
"EnableFastImageFadeInHelp": "Povoliť animáciu rýchleho rozjasnenia pre nahrané obrázky",
"EnableFastImageFadeIn": "Rýchle rozjasnenie obrázku",
"EnableFastImageFadeInHelp": "Zobrazí plagát a ostatné obrázky s rýchlejšou animáciou prechodu po dokončení načítania.",
"EnableFastImageFadeIn": "Rýchla animácia prechodu obrázku",
"LabelPlayerDimensions": "Rozmery prehrávača:",
"LabelDroppedFrames": "Vynechané snímky:",
"LabelCorruptedFrames": "Poškodené snímky:",
@ -1550,5 +1550,8 @@
"MillisecondsUnit": "ms",
"LabelSyncPlayTimeOffset": "Časový rozdiel so serverom:",
"HeaderSyncPlayEnabled": "Synchronizácia prehrávania je povolená",
"HeaderSyncPlaySelectGroup": "Pripojiť sa k skupine"
"HeaderSyncPlaySelectGroup": "Pripojiť sa k skupine",
"SyncPlayAccessHelp": "Vyberte úroveň prístupu pre tohto používateľa k funkcií synchronizácie prehrávania. Synchronizácia prehrávania umožňuje zosynchronizovať prehrávanie s ostatnými zariadeniami.",
"EnableDetailsBannerHelp": "Zobrazí banner na vrchnej časti detailu položky.",
"EnableDetailsBanner": "Detail banneru"
}

1112
yarn.lock

File diff suppressed because it is too large Load diff