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:
commit
bb0bf16c33
20 changed files with 297 additions and 76 deletions
9
.github/renovate.json
vendored
9
.github/renovate.json
vendored
|
@ -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" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
52
.github/workflows/update-sdk.yml
vendored
52
.github/workflows/update-sdk.yml
vendored
|
@ -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
14
package-lock.json
generated
|
@ -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": {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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'
|
||||
}
|
23
src/apps/stable/features/playback/constants/playerEvent.ts
Normal file
23
src/apps/stable/features/playback/constants/playerEvent.ts
Normal 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'
|
||||
}
|
33
src/apps/stable/features/playback/types/callbacks.ts
Normal file
33
src/apps/stable/features/playback/types/callbacks.ts
Normal 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[]
|
||||
}
|
34
src/apps/stable/features/playback/types/streamInfo.ts
Normal file
34
src/apps/stable/features/playback/types/streamInfo.ts
Normal 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
|
||||
}
|
101
src/apps/stable/features/playback/utils/playbackSubscriber.ts
Normal file
101
src/apps/stable/features/playback/utils/playbackSubscriber.ts
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -687,7 +687,7 @@ function sortPlayerTargets(a, b) {
|
|||
return aVal.localeCompare(bVal);
|
||||
}
|
||||
|
||||
class PlaybackManager {
|
||||
export class PlaybackManager {
|
||||
constructor() {
|
||||
const self = this;
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1960,5 +1960,9 @@
|
|||
"PreferNonstandardArtistsTag": "Надавати перевагу тегу ARTISTS, якщо він доступний",
|
||||
"PreferNonstandardArtistsTagHelp": "Використовувати нестандартний тег ARTISTS замість тега ARTIST, якщо він доступний.",
|
||||
"UseCustomTagDelimiters": "Використовувати власний роздільник тегів",
|
||||
"UseCustomTagDelimitersHelp": "Розділіть теги виконавця/жанру за допомогою спеціальних символів."
|
||||
"UseCustomTagDelimitersHelp": "Розділіть теги виконавця/жанру за допомогою спеціальних символів.",
|
||||
"DateModified": "Дату змінено",
|
||||
"MessageCancelSeriesTimerError": "Виникла помилка під час скасування таймера серіалу",
|
||||
"MessageCancelTimerError": "Виникла помилка під час скасування таймера",
|
||||
"MessageSplitVersionsError": "Виникла помилка під час розділення версій"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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": "所有录制的节目",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue