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 fix_long_getItems_request_URL

This commit is contained in:
dann-merlin 2023-04-13 11:05:09 +00:00 committed by GitHub
commit 6d6d03a9c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
220 changed files with 21471 additions and 16761 deletions

View file

@ -25,7 +25,7 @@ class BackdropScreensaver {
const apiClient = ServerConnections.currentApiClient();
apiClient.getItems(apiClient.getCurrentUserId(), query).then((result) => {
if (result.Items.length) {
import('../../components/slideshow/slideshow').then(({default: Slideshow}) => {
import('../../components/slideshow/slideshow').then(({ default: Slideshow }) => {
const newSlideShow = new Slideshow({
showTitle: true,
cover: true,

View file

@ -17,15 +17,31 @@ import '../../elements/emby-button/paper-icon-button-light';
import html from './template.html';
import './style.scss';
const THEMES = {
'dark': { 'body': { 'color': '#d8dadc', 'background': '#000', 'font-size': 'medium' } },
'sepia': { 'body': { 'color': '#d8a262', 'background': '#000', 'font-size': 'medium' } },
'light': { 'body': { 'color': '#000', 'background': '#fff', 'font-size': 'medium' } }
};
const THEME_ORDER = ['dark', 'sepia', 'light'];
const FONT_SIZES = ['x-small', 'small', 'medium', 'large', 'x-large'];
export class BookPlayer {
constructor() {
this.name = 'Book Player';
this.type = PluginType.MediaPlayer;
this.id = 'bookplayer';
this.priority = 1;
if (!userSettings.theme() || userSettings.theme() === 'dark') {
this.theme = 'dark';
} else {
this.theme = 'light';
}
this.fontSize = 'medium';
this.onDialogClosed = this.onDialogClosed.bind(this);
this.openTableOfContents = this.openTableOfContents.bind(this);
this.rotateTheme = this.rotateTheme.bind(this);
this.increaseFontSize = this.increaseFontSize.bind(this);
this.decreaseFontSize = this.decreaseFontSize.bind(this);
this.previous = this.previous.bind(this);
this.next = this.next.bind(this);
this.onWindowKeyUp = this.onWindowKeyUp.bind(this);
@ -164,6 +180,9 @@ export class BookPlayer {
elem.querySelector('#btnBookplayerExit').addEventListener('click', this.onDialogClosed, { once: true });
elem.querySelector('#btnBookplayerToc').addEventListener('click', this.openTableOfContents);
elem.querySelector('#btnBookplayerFullscreen').addEventListener('click', this.toggleFullscreen);
elem.querySelector('#btnBookplayerRotateTheme').addEventListener('click', this.rotateTheme);
elem.querySelector('#btnBookplayerIncreaseFontSize').addEventListener('click', this.increaseFontSize);
elem.querySelector('#btnBookplayerDecreaseFontSize').addEventListener('click', this.decreaseFontSize);
elem.querySelector('#btnBookplayerPrev')?.addEventListener('click', this.previous);
elem.querySelector('#btnBookplayerNext')?.addEventListener('click', this.next);
}
@ -184,6 +203,9 @@ export class BookPlayer {
elem.querySelector('#btnBookplayerExit').removeEventListener('click', this.onDialogClosed);
elem.querySelector('#btnBookplayerToc').removeEventListener('click', this.openTableOfContents);
elem.querySelector('#btnBookplayerFullscreen').removeEventListener('click', this.toggleFullscreen);
elem.querySelector('#btnBookplayerRotateTheme').removeEventListener('click', this.rotateTheme);
elem.querySelector('#btnBookplayerIncreaseFontSize').removeEventListener('click', this.increaseFontSize);
elem.querySelector('#btnBookplayerDecreaseFontSize').removeEventListener('click', this.decreaseFontSize);
elem.querySelector('#btnBookplayerPrev')?.removeEventListener('click', this.previous);
elem.querySelector('#btnBookplayerNext')?.removeEventListener('click', this.next);
}
@ -214,6 +236,31 @@ export class BookPlayer {
}
}
rotateTheme() {
if (this.loaded) {
const newTheme = THEME_ORDER[(THEME_ORDER.indexOf(this.theme) + 1) % THEME_ORDER.length];
this.rendition.themes.register('default', THEMES[newTheme]);
this.rendition.themes.update('default');
this.theme = newTheme;
}
}
increaseFontSize() {
if (this.loaded && this.fontSize !== FONT_SIZES[FONT_SIZES.length - 1]) {
const newFontSize = FONT_SIZES[(FONT_SIZES.indexOf(this.fontSize) + 1)];
this.rendition.themes.fontSize(newFontSize);
this.fontSize = newFontSize;
}
}
decreaseFontSize() {
if (this.loaded && this.fontSize !== FONT_SIZES[0]) {
const newFontSize = FONT_SIZES[(FONT_SIZES.indexOf(this.fontSize) - 1)];
this.rendition.themes.fontSize(newFontSize);
this.fontSize = newFontSize;
}
}
previous(e) {
e?.preventDefault();
if (this.rendition) {
@ -296,11 +343,8 @@ export class BookPlayer {
this.currentSrc = downloadHref;
this.rendition = rendition;
rendition.themes.register('dark', { 'body': { 'color': '#fff' } });
if (userSettings.theme(undefined) === 'dark' || userSettings.theme(undefined) === null) {
rendition.themes.select('dark');
}
rendition.themes.register('default', THEMES[this.theme]);
rendition.themes.select('default');
return rendition.display().then(() => {
const epubElem = document.querySelector('.epub-container');

View file

@ -23,8 +23,8 @@ export default class TableOfContents {
bindEvents() {
const elem = this.elem;
elem.addEventListener('close', this.onDialogClosed, {once: true});
elem.querySelector('.btnBookplayerTocClose').addEventListener('click', this.onDialogClosed, {once: true});
elem.addEventListener('close', this.onDialogClosed, { once: true });
elem.querySelector('.btnBookplayerTocClose').addEventListener('click', this.onDialogClosed, { once: true });
}
unbindEvents() {

View file

@ -11,6 +11,15 @@
<button is="paper-icon-button-light" id="btnBookplayerExit" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1">
<span class="material-icons bookplayerButtonIcon close" aria-hidden="true"></span>
</button>
<button is="paper-icon-button-light" id="btnBookplayerRotateTheme" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1">
<span class="material-icons bookplayerButtonIcon remove_red_eye" aria-hidden="true"></span>
</button>
<button is="paper-icon-button-light" id="btnBookplayerDecreaseFontSize" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1">
<span class="material-icons bookplayerButtonIcon text_decrease" aria-hidden="true"></span>
</button>
<button is="paper-icon-button-light" id="btnBookplayerIncreaseFontSize" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1">
<span class="material-icons bookplayerButtonIcon text_increase" aria-hidden="true"></span>
</button>
<button is="paper-icon-button-light" id="btnBookplayerFullscreen" class="autoSize bookplayerButton hide-mouse-idle-tv" tabindex="-1">
<span class="material-icons bookplayerButtonIcon fullscreen" aria-hidden="true"></span>
</button>

View file

@ -107,7 +107,7 @@ function tryRemoveElement(elem) {
}
function requireHlsPlayer(callback) {
import('hls.js').then(({default: hls}) => {
import('hls.js').then(({ default: hls }) => {
hls.DefaultConfig.lowLatencyMode = false;
hls.DefaultConfig.backBufferLength = Infinity;
hls.DefaultConfig.liveBackBufferLength = 90;
@ -396,7 +396,7 @@ function tryRemoveElement(elem) {
* @private
*/
setSrcWithFlvJs(elem, options, url) {
return import('flv.js').then(({default: flvjs}) => {
return import('flv.js').then(({ default: flvjs }) => {
const flvPlayer = flvjs.createPlayer({
type: 'flv',
url: url
@ -1296,7 +1296,7 @@ function tryRemoveElement(elem) {
resizeVariation: 0.2,
renderAhead: 90
};
import('@jellyfin/libass-wasm').then(({default: SubtitlesOctopus}) => {
import('@jellyfin/libass-wasm').then(({ default: SubtitlesOctopus }) => {
Promise.all([
apiClient.getNamedConfiguration('encoding'),
// Worker in Tizen 5 doesn't resolve relative path with async request

View file

@ -279,16 +279,22 @@ export class PdfPlayer {
renderPage(canvas, number) {
this.book.getPage(number).then(page => {
const original = page.getViewport({ scale: 1 });
const width = dom.getWindowSize().innerWidth;
const height = dom.getWindowSize().innerHeight;
const scale = Math.ceil(window.devicePixelRatio || 1);
const viewport = page.getViewport({ scale });
const context = canvas.getContext('2d');
const widthRatio = dom.getWindowSize().innerWidth / original.width;
const heightRatio = dom.getWindowSize().innerHeight / original.height;
const scale = Math.min(heightRatio, widthRatio);
const viewport = page.getViewport({ scale: scale });
canvas.width = viewport.width;
canvas.height = viewport.height;
if (width < height) {
canvas.style.width = '100%';
canvas.style.height = 'auto';
} else {
canvas.style.height = '100%';
canvas.style.width = 'auto';
}
const renderContext = {
canvasContext: context,
viewport: viewport

View file

@ -11,7 +11,7 @@ export default class PhotoPlayer {
play(options) {
return new Promise(function (resolve) {
import('../../components/slideshow/slideshow').then(({default: Slideshow}) => {
import('../../components/slideshow/slideshow').then(({ default: Slideshow }) => {
const index = options.startIndex || 0;
const apiClient = ServerConnections.currentApiClient();

View file

@ -133,9 +133,9 @@ class PlaybackCore {
*/
async sendBufferingRequest(isBuffering = true) {
const playerWrapper = this.manager.getPlayerWrapper();
const currentPosition = (playerWrapper.currentTimeAsync
? await playerWrapper.currentTimeAsync()
: playerWrapper.currentTime());
const currentPosition = (playerWrapper.currentTimeAsync ?
await playerWrapper.currentTimeAsync() :
playerWrapper.currentTime());
const currentPositionTicks = Math.round(currentPosition * Helper.TicksPerMillisecond);
const isPlaying = playerWrapper.isPlaying();
@ -172,11 +172,11 @@ class PlaybackCore {
*/
async applyCommand(command) {
// Check if duplicate.
if (this.lastCommand &&
this.lastCommand.When.getTime() === command.When.getTime() &&
this.lastCommand.PositionTicks === command.PositionTicks &&
this.lastCommand.Command === command.Command &&
this.lastCommand.PlaylistItemId === command.PlaylistItemId
if (this.lastCommand
&& this.lastCommand.When.getTime() === command.When.getTime()
&& this.lastCommand.PositionTicks === command.PositionTicks
&& this.lastCommand.Command === command.Command
&& this.lastCommand.PlaylistItemId === command.PlaylistItemId
) {
// Duplicate command found, check playback state and correct if needed.
console.debug('SyncPlay applyCommand: duplicate command received!', command);
@ -192,9 +192,9 @@ class PlaybackCore {
} else {
// Check if playback state matches requested command.
const playerWrapper = this.manager.getPlayerWrapper();
const currentPositionTicks = Math.round((playerWrapper.currentTimeAsync
? await playerWrapper.currentTimeAsync()
: playerWrapper.currentTime()) * Helper.TicksPerMillisecond);
const currentPositionTicks = Math.round((playerWrapper.currentTimeAsync ?
await playerWrapper.currentTimeAsync() :
playerWrapper.currentTime()) * Helper.TicksPerMillisecond);
const isPlaying = playerWrapper.isPlaying();
switch (command.Command) {
@ -279,9 +279,9 @@ class PlaybackCore {
const playAtTimeLocal = this.timeSyncCore.remoteDateToLocal(playAtTime);
const playerWrapper = this.manager.getPlayerWrapper();
const currentPositionTicks = (playerWrapper.currentTimeAsync
? await playerWrapper.currentTimeAsync()
: playerWrapper.currentTime()) * Helper.TicksPerMillisecond;
const currentPositionTicks = (playerWrapper.currentTimeAsync ?
await playerWrapper.currentTimeAsync() :
playerWrapper.currentTime()) * Helper.TicksPerMillisecond;
if (playAtTimeLocal > currentTime) {
const playTimeout = playAtTimeLocal - currentTime;

View file

@ -174,9 +174,9 @@ class QueueCore {
const currentTime = new Date();
const now = this.manager.timeSyncCore.localDateToRemote(currentTime);
const currentPosition = (playerWrapper.currentTimeAsync
? await playerWrapper.currentTimeAsync()
: playerWrapper.currentTime());
const currentPosition = (playerWrapper.currentTimeAsync ?
await playerWrapper.currentTimeAsync() :
playerWrapper.currentTime());
const currentPositionTicks = Math.round(currentPosition * Helper.TicksPerMillisecond);
const isPlaying = playerWrapper.isPlaying();

View file

@ -12,6 +12,7 @@ class SyncPlayPlugin implements Plugin {
id: string;
type: string;
priority: number;
instance: typeof SyncPlay;
constructor() {
this.name = 'SyncPlay Plugin';
@ -21,6 +22,8 @@ class SyncPlayPlugin implements Plugin {
this.type = PluginType.SyncPlay;
this.priority = 1;
this.instance = SyncPlay;
this.init();
}

View file

@ -1,11 +1,12 @@
import SyncPlay from '../core';
import SyncPlaySettingsEditor from './settings/SettingsEditor';
import loading from '../../../components/loading/loading';
import toast from '../../../components/toast/toast';
import actionsheet from '../../../components/actionSheet/actionSheet';
import globalize from '../../../scripts/globalize';
import playbackPermissionManager from './playbackPermissionManager';
import { pluginManager } from '../../../components/pluginManager';
import ServerConnections from '../../../components/ServerConnections';
import { PluginType } from '../../../types/plugin.ts';
import Events from '../../../utils/events.ts';
import './groupSelectionMenu.scss';
@ -17,8 +18,22 @@ class GroupSelectionMenu {
constructor() {
// Register to SyncPlay events.
this.syncPlayEnabled = false;
Events.on(SyncPlay.Manager, 'enabled', (e, enabled) => {
this.syncPlayEnabled = enabled;
this.SyncPlay = pluginManager.firstOfType(PluginType.SyncPlay)?.instance;
if (this.SyncPlay) {
Events.on(this.SyncPlay.Manager, 'enabled', (_event, enabled) => {
this.syncPlayEnabled = enabled;
});
}
Events.on(pluginManager, 'registered', (_event0, plugin) => {
if (plugin.type === PluginType.SyncPlay) {
this.SyncPlay = plugin.instance;
Events.on(plugin.instance.Manager, 'enabled', (_event1, enabled) => {
this.syncPlayEnabled = enabled;
});
}
});
}
@ -103,10 +118,11 @@ class GroupSelectionMenu {
* @param {Object} apiClient - ApiClient.
*/
showLeaveGroupSelection(button, user, apiClient) {
const groupInfo = SyncPlay.Manager.getGroupInfo();
const groupInfo = this.SyncPlay?.Manager.getGroupInfo();
const menuItems = [];
if (!SyncPlay.Manager.isPlaylistEmpty() && !SyncPlay.Manager.isPlaybackActive()) {
if (!this.SyncPlay?.Manager.isPlaylistEmpty()
&& !this.SyncPlay?.Manager.isPlaybackActive()) {
menuItems.push({
name: globalize.translate('LabelSyncPlayResumePlayback'),
icon: 'play_circle_filled',
@ -114,7 +130,7 @@ class GroupSelectionMenu {
selected: false,
secondaryText: globalize.translate('LabelSyncPlayResumePlaybackDescription')
});
} else if (SyncPlay.Manager.isPlaybackActive()) {
} else if (this.SyncPlay?.Manager.isPlaybackActive()) {
menuItems.push({
name: globalize.translate('LabelSyncPlayHaltPlayback'),
icon: 'pause_circle_filled',
@ -149,15 +165,15 @@ class GroupSelectionMenu {
border: true
};
actionsheet.show(menuOptions).then(function (id) {
actionsheet.show(menuOptions).then((id) => {
if (id == 'resume-playback') {
SyncPlay.Manager.resumeGroupPlayback(apiClient);
this.SyncPlay?.Manager.resumeGroupPlayback(apiClient);
} else if (id == 'halt-playback') {
SyncPlay.Manager.haltGroupPlayback(apiClient);
this.SyncPlay?.Manager.haltGroupPlayback(apiClient);
} else if (id == 'leave-group') {
apiClient.leaveSyncPlayGroup();
} else if (id == 'settings') {
new SyncPlaySettingsEditor(apiClient, SyncPlay.Manager.getTimeSyncCore(), { groupInfo: groupInfo })
new SyncPlaySettingsEditor(apiClient, this.SyncPlay?.Manager.getTimeSyncCore(), { groupInfo: groupInfo })
.embed()
.catch(error => {
if (error) {

View file

@ -3,13 +3,14 @@
* @module components/syncPlay/settings/SettingsEditor
*/
import SyncPlay from '../../core';
import { setSetting } from '../../core/Settings';
import dialogHelper from '../../../../components/dialogHelper/dialogHelper';
import layoutManager from '../../../../components/layoutManager';
import { pluginManager } from '../../../../components/pluginManager';
import loading from '../../../../components/loading/loading';
import toast from '../../../../components/toast/toast';
import globalize from '../../../../scripts/globalize';
import { PluginType } from '../../../../types/plugin.ts';
import Events from '../../../../utils/events.ts';
import 'material-design-icons-iconfont';
@ -36,6 +37,7 @@ class SettingsEditor {
this.apiClient = apiClient;
this.timeSyncCore = timeSyncCore;
this.options = options;
this.SyncPlay = pluginManager.firstOfType(PluginType.SyncPlay)?.instance;
}
async embed() {
@ -95,14 +97,14 @@ class SettingsEditor {
async initEditor() {
const { context } = this;
context.querySelector('#txtExtraTimeOffset').value = SyncPlay.Manager.timeSyncCore.extraTimeOffset;
context.querySelector('#chkSyncCorrection').checked = SyncPlay.Manager.playbackCore.enableSyncCorrection;
context.querySelector('#txtMinDelaySpeedToSync').value = SyncPlay.Manager.playbackCore.minDelaySpeedToSync;
context.querySelector('#txtMaxDelaySpeedToSync').value = SyncPlay.Manager.playbackCore.maxDelaySpeedToSync;
context.querySelector('#txtSpeedToSyncDuration').value = SyncPlay.Manager.playbackCore.speedToSyncDuration;
context.querySelector('#txtMinDelaySkipToSync').value = SyncPlay.Manager.playbackCore.minDelaySkipToSync;
context.querySelector('#chkSpeedToSync').checked = SyncPlay.Manager.playbackCore.useSpeedToSync;
context.querySelector('#chkSkipToSync').checked = SyncPlay.Manager.playbackCore.useSkipToSync;
context.querySelector('#txtExtraTimeOffset').value = this.SyncPlay?.Manager.timeSyncCore.extraTimeOffset;
context.querySelector('#chkSyncCorrection').checked = this.SyncPlay?.Manager.playbackCore.enableSyncCorrection;
context.querySelector('#txtMinDelaySpeedToSync').value = this.SyncPlay?.Manager.playbackCore.minDelaySpeedToSync;
context.querySelector('#txtMaxDelaySpeedToSync').value = this.SyncPlay?.Manager.playbackCore.maxDelaySpeedToSync;
context.querySelector('#txtSpeedToSyncDuration').value = this.SyncPlay?.Manager.playbackCore.speedToSyncDuration;
context.querySelector('#txtMinDelaySkipToSync').value = this.SyncPlay?.Manager.playbackCore.minDelaySkipToSync;
context.querySelector('#chkSpeedToSync').checked = this.SyncPlay?.Manager.playbackCore.useSpeedToSync;
context.querySelector('#chkSkipToSync').checked = this.SyncPlay?.Manager.playbackCore.useSkipToSync;
}
onSubmit() {
@ -139,7 +141,7 @@ class SettingsEditor {
setSetting('useSpeedToSync', useSpeedToSync);
setSetting('useSkipToSync', useSkipToSync);
Events.trigger(SyncPlay.Manager, 'settings-update');
Events.trigger(this.SyncPlay?.Manager, 'settings-update');
}
}