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

Merge branch 'master' into create-subtitles-keyboard-shortcut

This commit is contained in:
Baker Ousley 2024-10-02 11:36:33 -05:00 committed by GitHub
commit bb0bf16c33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 297 additions and 76 deletions

View file

@ -2,7 +2,14 @@
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>jellyfin/.github//renovate-presets/nodejs",
":semanticCommitsDisabled",
":dependencyDashboard"
],
"packageRules": [
{
"matchPackageNames": [ "@jellyfin/sdk" ],
"followTag": "unstable",
"minimumReleaseAge": null,
"schedule": [ "after 7:00 am" ]
}
]
}

View file

@ -1,52 +0,0 @@
name: Update the Jellyfin SDK
on:
schedule:
- cron: '0 7 * * *'
workflow_dispatch:
concurrency:
group: unstable-sdk-pr
cancel-in-progress: true
jobs:
update:
runs-on: ubuntu-latest
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
steps:
- name: Check out Git repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
ref: master
token: ${{ secrets.JF_BOT_TOKEN }}
- name: Set up Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 20
check-latest: true
cache: npm
- name: Install latest unstable SDK
run: |
npm i --save @jellyfin/sdk@unstable
VERSION=$(jq -r '.dependencies["@jellyfin/sdk"]' package.json)
echo "JF_SDK_VERSION=${VERSION}" >> $GITHUB_ENV
- name: Open a pull request
uses: peter-evans/create-pull-request@8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20 # v7.0.1
with:
token: ${{ secrets.JF_BOT_TOKEN }}
commit-message: Update @jellyfin/sdk to ${{env.JF_SDK_VERSION}}
committer: jellyfin-bot <team@jellyfin.org>
author: jellyfin-bot <team@jellyfin.org>
branch: update-jf-sdk
delete-branch: true
title: Update @jellyfin/sdk to ${{env.JF_SDK_VERSION}}
body: |
**Changes**
Updates to the latest unstable @jellyfin/sdk build
labels: |
dependencies
npm

14
package-lock.json generated
View file

@ -18,7 +18,7 @@
"@fontsource/noto-sans-sc": "5.1.0",
"@fontsource/noto-sans-tc": "5.1.0",
"@jellyfin/libass-wasm": "4.2.3",
"@jellyfin/sdk": "0.0.0-unstable.202409260501",
"@jellyfin/sdk": "0.0.0-unstable.202410020501",
"@mui/icons-material": "5.16.7",
"@mui/material": "5.16.7",
"@mui/x-date-pickers": "7.18.0",
@ -5068,9 +5068,9 @@
"license": "LGPL-2.1-or-later AND (FTL OR GPL-2.0-or-later) AND MIT AND MIT-Modern-Variant AND ISC AND NTP AND Zlib AND BSL-1.0"
},
"node_modules/@jellyfin/sdk": {
"version": "0.0.0-unstable.202409260501",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202409260501.tgz",
"integrity": "sha512-3W/J15A/jELTMJPrPiGvfkO8xsUaxKh4P/f+kVAxOSCYkPp+vvZxmRCGV7M1+1947O19iJ/l7ypD08L3YVf39g==",
"version": "0.0.0-unstable.202410020501",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202410020501.tgz",
"integrity": "sha512-Tl+bE4GoGDf5qmzYiauNLFclAYF4cPFVX3HldKl/KuwEarzRdQfudGNFCu0KmDbJGiZ6HtDlec6LoUaYeXmvng==",
"license": "MPL-2.0",
"peerDependencies": {
"axios": "^1.3.4"
@ -29306,9 +29306,9 @@
"integrity": "sha512-C0OlBxIr9NdeFESMTA/OVDqNSWtog6Mi7wwzwH12xbZpxsMD0RgCupUcIP7zZgcpTNecW3fZq5d6xVo7Q8HEJw=="
},
"@jellyfin/sdk": {
"version": "0.0.0-unstable.202409260501",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202409260501.tgz",
"integrity": "sha512-3W/J15A/jELTMJPrPiGvfkO8xsUaxKh4P/f+kVAxOSCYkPp+vvZxmRCGV7M1+1947O19iJ/l7ypD08L3YVf39g==",
"version": "0.0.0-unstable.202410020501",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202410020501.tgz",
"integrity": "sha512-Tl+bE4GoGDf5qmzYiauNLFclAYF4cPFVX3HldKl/KuwEarzRdQfudGNFCu0KmDbJGiZ6HtDlec6LoUaYeXmvng==",
"requires": {}
},
"@jridgewell/gen-mapping": {

View file

@ -79,7 +79,7 @@
"@fontsource/noto-sans-sc": "5.1.0",
"@fontsource/noto-sans-tc": "5.1.0",
"@jellyfin/libass-wasm": "4.2.3",
"@jellyfin/sdk": "0.0.0-unstable.202409260501",
"@jellyfin/sdk": "0.0.0-unstable.202410020501",
"@mui/icons-material": "5.16.7",
"@mui/material": "5.16.7",
"@mui/x-date-pickers": "7.18.0",

View file

@ -0,0 +1,14 @@
/**
* Events triggered by PlaybackManager.
*/
export enum PlaybackManagerEvent {
Pairing = 'pairing',
Paired = 'paired',
PairError = 'pairerror',
PlaybackCancelled = 'playbackcancelled',
PlaybackError = 'playbackerror',
PlaybackStart = 'playbackstart',
PlaybackStop = 'playbackstop',
PlayerChange = 'playerchange',
ReportPlayback = 'reportplayback'
}

View file

@ -0,0 +1,23 @@
/**
* Events triggered by media player plugins.
* TODO: This list is incomplete
*/
export enum PlayerEvent {
Error = 'error',
FullscreenChange = 'fullscreenchange',
ItemStarted = 'itemstarted',
ItemStopped = 'itemstopped',
MediaStreamsChange = 'mediastreamschange',
Pause = 'pause',
PlaybackStart = 'playbackstart',
PlaybackStop = 'playbackstop',
PlaylistItemAdd = 'playlistitemadd',
PlaylistItemMove = 'playlistitemmove',
PlaylistItemRemove = 'playlistitemremove',
RepeatModeChange = 'repeatmodechange',
ShuffleModeChange = 'shufflequeuemodechange',
Stopped = 'stopped',
TimeUpdate = 'timeupdate',
Unpause = 'unpause',
VolumeChange = 'volumechange'
}

View file

@ -0,0 +1,33 @@
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto';
import type { MediaSourceInfo } from '@jellyfin/sdk/lib/generated-client/models/media-source-info';
import type { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import type { StreamInfo } from './streamInfo';
export interface ManagedPlayerStopInfo {
item: BaseItemDto
mediaSource: MediaSourceInfo
nextItem?: BaseItemDto | null
nextMediaType?: MediaType | null
positionMs?: number
}
export interface MovedItem {
newIndex: number
playlistItemId: string
}
export type PlayerErrorCode = string;
export interface PlayerStopInfo {
src?: URL | BaseItemDto
}
export interface PlayerError {
streamInfo?: StreamInfo
type: MediaError | string
}
export interface RemovedItems {
playlistItemIds: string[]
}

View file

@ -0,0 +1,34 @@
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto';
import type { MediaSourceInfo } from '@jellyfin/sdk/lib/generated-client/models/media-source-info';
import type { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import type { PlayMethod } from '@jellyfin/sdk/lib/generated-client/models/play-method';
export interface StreamInfo {
ended?: boolean
fullscreen?: boolean
item?: BaseItemDto
lastMediaInfoQuery?: number
liveStreamId?: string
mediaSource?: MediaSourceInfo
mediaType?: MediaType
mimeType?: string
playMethod?: PlayMethod
playSessionId?: string
playbackStartTimeTicks?: number
playerStartPositionTicks?: number
resetSubtitleOffset?: boolean
started?: boolean
textTracks?: TrackInfo[]
title?: string
tracks?: TrackInfo[]
transcodingOffsetTicks?: number
url?: string
}
interface TrackInfo {
url: string
language: string
isDefault: boolean
index: number
format: string
}

View file

@ -0,0 +1,101 @@
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto';
import type { MediaSourceInfo } from '@jellyfin/sdk/lib/generated-client/models/media-source-info';
import type { PlaybackManager } from 'components/playback/playbackmanager';
import type { MediaError } from 'types/mediaError';
import type { PlayTarget } from 'types/playTarget';
import type { PlaybackStopInfo, PlayerState } from 'types/playbackStopInfo';
import type { Plugin } from 'types/plugin';
import Events, { type Event } from 'utils/events';
import { PlaybackManagerEvent } from '../constants/playbackManagerEvent';
import { PlayerEvent } from '../constants/playerEvent';
import type { ManagedPlayerStopInfo, MovedItem, PlayerError, PlayerErrorCode, PlayerStopInfo, RemovedItems } from '../types/callbacks';
export interface PlaybackSubscriber {
onPlaybackCancelled?(e: Event): void
onPlaybackError?(e: Event, errorType: MediaError): void
onPlaybackStart?(e: Event, player: Plugin, state: PlayerState): void
onPlaybackStop?(e: Event, info: PlaybackStopInfo): void
onPlayerChange?(e: Event, player: Plugin, target: PlayTarget, previousPlayer: Plugin): void
onPlayerError?(e: Event, error: PlayerError): void
onPlayerFullscreenChange?(e: Event): void
onPlayerItemStarted?(e: Event, item?: BaseItemDto, mediaSource?: MediaSourceInfo): void
onPlayerItemStopped?(e: Event, info: ManagedPlayerStopInfo): void
onPlayerMediaStreamsChange?(e: Event): void
onPlayerPause?(e: Event): void
onPlayerPlaybackStart?(e: Event, state: PlayerState): void
onPlayerPlaybackStop?(e: Event, state: PlayerState): void
onPlayerPlaylistItemAdd?(e: Event): void
onPlayerPlaylistItemMove?(e: Event, item: MovedItem): void
onPlayerPlaylistItemRemove?(e: Event, items?: RemovedItems): void
onPlayerRepeatModeChange?(e: Event): void
onPlayerShuffleModeChange?(e: Event): void
onPlayerStopped?(e: Event, info?: PlayerStopInfo | PlayerErrorCode): void
onPlayerTimeUpdate?(e: Event): void
onPlayerUnpause?(e: Event): void
onPlayerVolumeChange?(e: Event): void
onReportPlayback?(e: Event, isServerItem: boolean): void
}
export abstract class PlaybackSubscriber {
private player: Plugin | undefined;
private readonly playbackManagerEvents = {
[PlaybackManagerEvent.PlaybackCancelled]: this.onPlaybackCancelled,
[PlaybackManagerEvent.PlaybackError]: this.onPlaybackError,
[PlaybackManagerEvent.PlaybackStart]: this.onPlaybackStart,
[PlaybackManagerEvent.PlaybackStop]: this.onPlaybackStop,
[PlaybackManagerEvent.PlayerChange]: this.onPlayerChange,
[PlaybackManagerEvent.ReportPlayback]: this.onReportPlayback
};
private readonly playerEvents = {
[PlayerEvent.Error]: this.onPlayerError,
[PlayerEvent.FullscreenChange]: this.onPlayerFullscreenChange,
[PlayerEvent.ItemStarted]: this.onPlayerItemStarted,
[PlayerEvent.ItemStopped]: this.onPlayerItemStopped,
[PlayerEvent.MediaStreamsChange]: this.onPlayerMediaStreamsChange,
[PlayerEvent.Pause]: this.onPlayerPause,
[PlayerEvent.PlaybackStart]: this.onPlayerPlaybackStart,
[PlayerEvent.PlaybackStop]: this.onPlayerPlaybackStop,
[PlayerEvent.PlaylistItemAdd]: this.onPlayerPlaylistItemAdd,
[PlayerEvent.PlaylistItemMove]: this.onPlayerPlaylistItemMove,
[PlayerEvent.PlaylistItemRemove]: this.onPlayerPlaylistItemRemove,
[PlayerEvent.RepeatModeChange]: this.onPlayerRepeatModeChange,
[PlayerEvent.ShuffleModeChange]: this.onPlayerShuffleModeChange,
[PlayerEvent.Stopped]: this.onPlayerStopped,
[PlayerEvent.TimeUpdate]: this.onPlayerTimeUpdate,
[PlayerEvent.Unpause]: this.onPlayerUnpause,
[PlayerEvent.VolumeChange]: this.onPlayerVolumeChange
};
constructor(
protected readonly playbackManager: PlaybackManager
) {
Object.entries(this.playbackManagerEvents).forEach(([event, handler]) => {
if (handler) Events.on(playbackManager, event, handler);
});
this.bindPlayerEvents();
Events.on(playbackManager, PlaybackManagerEvent.PlayerChange, this.bindPlayerEvents.bind(this));
}
private bindPlayerEvents() {
const newPlayer = this.playbackManager.getCurrentPlayer();
if (this.player === newPlayer) return;
if (this.player) {
Object.entries(this.playerEvents).forEach(([event, handler]) => {
if (handler) Events.off(this.player, event, handler);
});
}
this.player = newPlayer;
if (!this.player) return;
Object.entries(this.playerEvents).forEach(([event, handler]) => {
if (handler) Events.on(this.player, event, handler);
});
}
}

View file

@ -687,7 +687,7 @@ function sortPlayerTargets(a, b) {
return aVal.localeCompare(bVal);
}
class PlaybackManager {
export class PlaybackManager {
constructor() {
const self = this;

View file

@ -121,6 +121,14 @@ class HtmlAudioPlayer {
normalizationGain =
options.mediaSource.albumNormalizationGain
?? options.item.NormalizationGain;
} else {
console.debug('normalization disabled');
return;
}
if (!self.gainNode) {
addGainElement(elem);
if (!self.gainNode) return;
}
if (normalizationGain) {
@ -276,8 +284,6 @@ class HtmlAudioPlayer {
self._mediaElement = elem;
addGainElement(elem);
return elem;
}
@ -317,7 +323,7 @@ class HtmlAudioPlayer {
function onVolumeChange() {
if (!self._isFadingOut) {
htmlMediaHelper.saveVolume(this.volume);
if (browser.safari) {
if (browser.safari && self.gainNode) {
self.gainNode.gain.value = this.volume * self.normalizationGain;
}
Events.trigger(self, 'volumechange');

View file

@ -1963,5 +1963,9 @@
"PreferNonstandardArtistsTag": "Prefer ARTISTS tag if available",
"PreferNonstandardArtistsTagHelp": "Use the non-standard ARTISTS tag instead of ARTIST tag when available.",
"UseCustomTagDelimiters": "Use custom tag delimiter",
"UseCustomTagDelimitersHelp": "Split artist/genre tags with custom characters."
"UseCustomTagDelimitersHelp": "Split artist/genre tags with custom characters.",
"DateModified": "Date modified",
"MessageCancelSeriesTimerError": "An error occurred while cancelling the series timer",
"MessageCancelTimerError": "An error occurred while cancelling the timer",
"MessageSplitVersionsError": "An error occurred while splitting versions"
}

View file

@ -156,7 +156,7 @@
"ChannelNumber": "Channel number",
"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.",
"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.",
"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. Switching between \"Off\" and other options requires restarting the current playback.",
"ClearQueue": "Clear queue",
"ClientSettings": "Client Settings",
"Collections": "Collections",

View file

@ -1267,7 +1267,7 @@
"OnWakeFromSleep": "À la sortie de veille",
"WeeklyAt": "{0} à {1}",
"DailyAt": "Tous les jours à {0}",
"LastSeen": "Vu {0}",
"LastSeen": "Dernière connexion {0}",
"PersonRole": "est {0}",
"ListPaging": "{0}-{1} de {2}",
"WriteAccessRequired": "Jellyfin a besoin d'un accès en écriture à ce dossier. Merci de vérifier les permissions de ce-dernier puis de réessayer.",
@ -1930,5 +1930,41 @@
"EnableHi10p": "Active le profil H.264 High 10",
"EnableHi10pHelp": "Activer pour éviter l'encodage des vidéos H.264 10-bits. Désactiver cette option si la vidéo affiche des images vides.",
"AlwaysRemuxFlacAudioFilesHelp": "Si votre navigateur refuse de lire des fichiers ou s'il calcule incorrectement l'horodatage, activer ceci en guise d'alternative.",
"AlwaysRemuxMp3AudioFilesHelp": "Si votre navigateur calcule incorrectement l'horodatage de certains fichiers, activer ceci en guise d'alternative."
"AlwaysRemuxMp3AudioFilesHelp": "Si votre navigateur calcule incorrectement l'horodatage de certains fichiers, activer ceci en guise d'alternative.",
"DateModified": "Date modifiée",
"FallbackMaxStreamingBitrateHelp": "Le débit maximum de streaming est utilisé par défaut quand ffprobe ne peut pas déterminer le débit du flux source. Cela aide à éviter que les clients demandent un débit de transcodage excessivement élevé, ce qui pourrait mener le lecteur à échouer et surcharger l'encodeur.",
"LabelAlwaysRemuxFlacAudioFiles": "Toujours remultiplexer les fichiers audio FLAC",
"LabelAlwaysRemuxMp3AudioFiles": "Toujours remultiplexer les fichiers audio MP3",
"LabelAllowStreamSharing": "Autoriser le partage de flux",
"MessageCancelSeriesTimerError": "Une erreur est survenue lors de l'annulation du minuteur de série",
"AllowStreamSharingHelp": "Autoriser Jellyfin à dupliquer le flux mpegts provenant du tuner et à partager ce flux dupliqué avec ses clients. Cela est utile lorsque le tuner a une limite de nombre total de flux, mais peut également causer des problèmes de lecture.",
"Reset": "Réinitialiser",
"UseCustomTagDelimitersHelp": "Diviser les balises d'artiste/genre avec des caractères personnalisés.",
"HeaderAudioAdvanced": "Audio Avancé",
"LabelAudioTagSettings": "Paramètres des balises audio",
"LabelCustomTagDelimiters": "Délimiteur de balise personnalisé",
"LabelCustomTagDelimitersHelp": "Caractères à traiter comme délimiteurs pour séparer les balises.",
"LabelLyricDownloaders": "Téléchargeurs de Lyrics",
"LabelDelimiterWhitelist": "Liste blanche des délimiteurs",
"LabelDelimiterWhitelistHelp": "Éléments à exclure de la division des balises. Un élément par ligne.",
"LabelDisableVbrAudioEncoding": "Désactiver l'encodage audio VBR",
"LabelSaveTrickplayLocally": "Enregistrer les images trickplay au même emplacement que les médias",
"LabelSaveTrickplayLocallyHelp": "Enregistrer les images trickplay dans les dossiers de médias les placera à côté de vos médias pour une migration et un accès faciles.",
"MediaInfoRotation": "Rotation",
"RenderPgsSubtitle": "Rendu expérimental des sous-titres PGS",
"ReplaceTrickplayImages": "Remplacer les images trickplay existantes",
"LibraryInvalidItemIdError": "La bibliothèque est dans un état invalide et ne peut pas être modifiée. Vous rencontrez probablement un bug : le chemin dans la base de données n'est pas le bon chemin sur le système de fichiers.",
"MessageCancelTimerError": "Une erreur est survenue lors de l'annulation du minuteur",
"MessageSplitVersionsError": "Une erreur s'est produite lors de la division des versions",
"PreferNonstandardArtistsTag": "Préférer la balise ARTISTS si disponible",
"PreferNonstandardArtistsTagHelp": "Utiliser la balise non standard ARTISTS au lieu de la balise ARTIST lorsqu'elle est disponible.",
"RenderPgsSubtitleHelp": "Détermine si le client doit rendre les sous-titres PGS au lieu d'utiliser des sous-titres incrustés. Cela peut éviter le transcodage côté serveur au profit de la performance de rendu côté client.",
"UseCustomTagDelimiters": "Utiliser un délimiteur de balise personnalisé",
"VideoCodecTagNotSupported": "La balise de codec vidéo n'est pas prise en charge",
"LabelFallbackMaxStreamingBitrate": "Bitrate de flux maximum de repli (Mbps)",
"LyricDownloadersHelp": "Activez et classez vos téléchargeurs de sous-titres préférés par ordre de priorité.",
"LabelAllowFmp4TranscodingContainer": "Autoriser le conteneur de transcodage fMP4",
"AllowTonemappingSoftwareHelp": "Le tone-mapping peut transformer la plage dynamique d'une vidéo de HDR à SDR tout en conservant les détails et les couleurs de l'image, qui sont des informations très importantes pour représenter la scène originale. Fonctionne uniquement avec les vidéos 10 bits HDR10, HLG et DoVi.",
"LabelTrickplayKeyFrameOnlyExtraction": "Générer uniquement des images à partir des images clés",
"LabelTrickplayKeyFrameOnlyExtractionHelp": "Extraire uniquement les images clés pour un traitement beaucoup plus rapide avec un minutage moins précis. Si le décodeur matériel configuré ne prend pas en charge ce mode, il utilisera le décodeur logiciel à la place."
}

View file

@ -1958,5 +1958,9 @@
"PreferNonstandardArtistsTag": "Foretrekk ARTISTER-tag om tilgjengelig",
"PreferNonstandardArtistsTagHelp": "Bruk ikke-standard ARTISTS-taggen istedenfor ARTIST-taggen når den er tilgjengelig.",
"UseCustomTagDelimiters": "Bruk egendefinert skilletegn for tagger",
"UseCustomTagDelimitersHelp": "Del artist- og sjanger-tagger med egendefinert tegn."
"UseCustomTagDelimitersHelp": "Del artist- og sjanger-tagger med egendefinert tegn.",
"DateModified": "Data modifisert",
"MessageCancelSeriesTimerError": "Det oppstod en feil under avbryting av serietidtakeren",
"MessageCancelTimerError": "Det oppstod en feil under avbryting av tidtakeren",
"MessageSplitVersionsError": "Det oppsto en feil under oppdeling av versjoner"
}

View file

@ -1776,7 +1776,7 @@
"BackdropScreensaver": "Schermbeveiliging met achtergronden",
"LogoScreensaver": "Schermbeveiliging met logo",
"LabelIsHearingImpaired": "Voor slechthorenden (ODS)",
"SelectAudioNormalizationHelp": "Nummerversterking regelt het volume van elk individueel nummer zodat alle nummers even luid afspelen. Albumversterking regelt het volume van alle nummers op een album, zodat het dynamische bereik van het album behouden blijft.",
"SelectAudioNormalizationHelp": "Nummerversterking regelt het volume van elk individueel nummer zodat alle nummers even luid afspelen. Albumversterking regelt het volume van alle nummers op een album, waarbij het dynamische bereik van het album behouden blijft. Na het wisselen tussen deze opties moet het afspelen herstart worden.",
"LabelAlbumGain": "Albumversterking",
"LabelSelectAudioNormalization": "Geluidsnormalisatie",
"LabelTrackGain": "Nummerversterking",

View file

@ -1960,5 +1960,9 @@
"PreferNonstandardArtistsTag": "Надавати перевагу тегу ARTISTS, якщо він доступний",
"PreferNonstandardArtistsTagHelp": "Використовувати нестандартний тег ARTISTS замість тега ARTIST, якщо він доступний.",
"UseCustomTagDelimiters": "Використовувати власний роздільник тегів",
"UseCustomTagDelimitersHelp": "Розділіть теги виконавця/жанру за допомогою спеціальних символів."
"UseCustomTagDelimitersHelp": "Розділіть теги виконавця/жанру за допомогою спеціальних символів.",
"DateModified": "Дату змінено",
"MessageCancelSeriesTimerError": "Виникла помилка під час скасування таймера серіалу",
"MessageCancelTimerError": "Виникла помилка під час скасування таймера",
"MessageSplitVersionsError": "Виникла помилка під час розділення версій"
}

View file

@ -1960,5 +1960,9 @@
"ReplaceTrickplayImages": "Thay thế ảnh tua nhanh hiện có",
"LabelAlwaysRemuxMp3AudioFiles": "Luôn làm lại các tập tin âm thanh MP3",
"LabelSaveTrickplayLocally": "Lưu ảnh tua nhanh bên cạnh phương tiện",
"LabelSaveTrickplayLocallyHelp": "Việc lưu ảnh tua anh vào thư mục phương tiện sẽ đặt chúng kế bên phương tiện của bạn để dễ di chuyển và truy cập."
"LabelSaveTrickplayLocallyHelp": "Việc lưu ảnh tua anh vào thư mục phương tiện sẽ đặt chúng kế bên phương tiện của bạn để dễ di chuyển và truy cập.",
"DateModified": "Ngày sửa đổi",
"MessageCancelSeriesTimerError": "Đã xảy ra lỗi khi hủy bộ hẹn giờ chuỗi",
"MessageCancelTimerError": "Đã xảy ra lỗi khi hủy bộ hẹn giờ",
"MessageSplitVersionsError": "Đã xảy ra lỗi khi chia nhỏ các phiên bản"
}

View file

@ -1019,7 +1019,7 @@
"SortName": "排序名称",
"Sports": "体育",
"StopRecording": "停止录制",
"Studios": "制片发行商",
"Studios": "工作室",
"SubtitleAppearanceSettingsAlsoPassedToCastDevices": "这些设置也会被应用于任何通过此设备发起的 Google Cast 播放。",
"SubtitleAppearanceSettingsDisclaimer": "以下设置不适用于上述图形字幕或嵌入其自身样式的 ASS/SSA 字幕。",
"SubtitleDownloadersHelp": "按优先顺序启用并排列您的首选字幕下载程序。",
@ -1728,7 +1728,7 @@
"MenuOpen": "打开菜单",
"MenuClose": "关闭菜单",
"UserMenu": "用户菜单",
"Studio": "制片发行商",
"Studio": "工作室",
"AllowCollectionManagement": "允许该用户管理收藏夹",
"EnableAudioNormalizationHelp": "音频标准化将添加一个恒定的增益,以保持平均音量在所需的级别(-18dB。",
"EnableAudioNormalization": "音频标准化",
@ -1779,7 +1779,7 @@
"LabelIsHearingImpaired": "用于听障/聋哑人士",
"SearchResultsEmpty": "抱歉!未找到与\"{0}\"相关的结果",
"LabelTrackGain": "音轨增益",
"SelectAudioNormalizationHelp": "音轨增益 - 调整每个音轨的音量,使它们播放时具有相同的响度。专辑增益 - 只调整专辑中所有音轨的音量,保持专辑的动态范围。",
"SelectAudioNormalizationHelp": "音轨增益 - 调整每个音轨的音量,使它们播放时具有相同的响度。专辑增益 - 只调整专辑中所有音轨的音量,保持专辑的动态范围。在“关闭”和其他选项之间切换后需要重新启动当前播放。",
"LabelAlbumGain": "专辑增益",
"LabelSelectAudioNormalization": "音频标准化",
"HeaderAllRecordings": "所有录制的节目",

View file

@ -1,3 +1,4 @@
import type { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import type { UserDto } from '@jellyfin/sdk/lib/generated-client/models/user-dto';
export interface PlayTarget {
@ -7,5 +8,7 @@ export interface PlayTarget {
playerName?: string
deviceType?: string
isLocalPlayer?: boolean
playableMediaTypes: MediaType[]
supportedCommands?: string[]
user?: UserDto
}