From 7267b66b1bfaec95f1aa4c7763f09bfb73f64413 Mon Sep 17 00:00:00 2001 From: viown <48097677+viown@users.noreply.github.com> Date: Mon, 14 Oct 2024 17:21:40 +0300 Subject: [PATCH] Move to PlaybackSubscriber --- src/components/playback/playbackmanager.js | 13 +- src/components/playback/skipsegment.ts | 240 ++++++++++----------- src/index.jsx | 1 - 3 files changed, 121 insertions(+), 133 deletions(-) diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 37548ac325c4..a7a5ca4bb9d3 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -26,6 +26,8 @@ import { MediaError } from 'types/mediaError'; import { getMediaError } from 'utils/mediaError'; import { toApi } from 'utils/jellyfin-apiclient/compat'; import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind.js'; +import browser from 'scripts/browser.js'; +import { bindSkipSegment } from './skipsegment.ts'; const UNLIMITED_ITEMS = -1; @@ -933,11 +935,9 @@ export class PlaybackManager { return Promise.resolve(self._playQueueManager.getPlaylist()); }; - self.promptToSkip = function (mediaSegment, player) { - player = player || self._currentPlayer; - - if (player && mediaSegment) { - Events.trigger(player, 'promptskip', [mediaSegment]); + self.promptToSkip = function (mediaSegment) { + if (mediaSegment && this._skipSegment) { + this._skipSegment.onPromptSkip(mediaSegment); } }; @@ -3674,6 +3674,9 @@ export class PlaybackManager { } bindMediaSegmentManager(self); + if (!browser.tv && !browser.xboxOne && !browser.ps4) { + this._skipSegment = bindSkipSegment(self); + } } getCurrentPlayer() { diff --git a/src/components/playback/skipsegment.ts b/src/components/playback/skipsegment.ts index 3c9712025248..660a751a9083 100644 --- a/src/components/playback/skipsegment.ts +++ b/src/components/playback/skipsegment.ts @@ -1,176 +1,162 @@ import { EventType } from 'types/eventType'; -import { playbackManager } from './playbackmanager'; +import { PlaybackManager } from './playbackmanager'; import { TICKS_PER_MILLISECOND } from 'constants/time'; import Events, { type Event } from '../../utils/events'; import { isInSegment } from 'apps/stable/features/playback/utils/mediaSegments'; import './skipbutton.scss'; import dom from 'scripts/dom'; import globalize from 'lib/globalize'; -import { Plugin } from 'types/plugin'; import { MediaSegmentDto, MediaSegmentType } from '@jellyfin/sdk/lib/generated-client'; +import { PlaybackSubscriber } from 'apps/stable/features/playback/utils/playbackSubscriber'; interface ShowOptions { animate?: boolean; keep?: boolean; } -let currentPlayer: Plugin | null; -let skipElement: HTMLButtonElement | null; -let currentSegment: MediaSegmentDto | null; -let hideTimeout: ReturnType | null; - -function createSkipElement() { - if (!skipElement && currentSegment) { - const elem = document.createElement('button'); - elem.classList.add('skip-button'); - elem.classList.add('hide'); - elem.classList.add('skip-button-hidden'); - - elem.addEventListener('click', () => { - if (currentSegment) { - playbackManager.seek(currentSegment.EndTicks); - } - }); +class SkipSegment extends PlaybackSubscriber { + private skipElement: HTMLButtonElement | undefined; + private currentSegment: MediaSegmentDto | null | undefined; + private hideTimeout: ReturnType | null | undefined; - document.body.appendChild(elem); - skipElement = elem; - } -} + constructor(playbackManager: PlaybackManager) { + super(playbackManager); -function setButtonText() { - if (skipElement && currentSegment) { - if (currentPlayer && currentSegment.EndTicks - && currentSegment.Type === MediaSegmentType.Outro - && currentSegment.EndTicks >= playbackManager.currentItem(currentPlayer).RunTimeTicks - && playbackManager.getNextItem() - ) { - // Display "Next Episode" if it's an outro segment, exceeds or is equal to the runtime, and if there is a next track. - skipElement.innerHTML += globalize.translate('MediaSegmentNextEpisode'); - } else { - skipElement.innerHTML = globalize.translate('MediaSegmentSkipPrompt', globalize.translate(`MediaSegmentType.${currentSegment.Type}`)); - } - skipElement.innerHTML += ''; - } -} + this.onOsdChanged = this.onOsdChanged.bind(this); -function clearHideTimeout() { - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; + Events.on(document, EventType.SHOW_VIDEO_OSD, this.onOsdChanged); } -} -function onHideComplete() { - if (skipElement) { - skipElement.classList.add('hide'); + onOsdChanged(_e: Event, isOpen: boolean) { + if (this.currentSegment) { + if (isOpen) { + this.showSkipButton({ + animate: false, + keep: true + }); + } else if (!this.hideTimeout) { + this.hideSkipButton(); + } + } } -} -function showSkipButton(options: ShowOptions) { - const elem = skipElement; - if (elem) { - clearHideTimeout(); - dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: true - }); - elem.classList.remove('hide'); - if (!options.animate) { - elem.classList.add('no-transition'); - } else { - elem.classList.remove('no-transition'); + onHideComplete() { + if (this.skipElement) { + this.skipElement.classList.add('hide'); } + } - void elem.offsetWidth; + createSkipElement() { + if (!this.skipElement && this.currentSegment) { + const elem = document.createElement('button'); + elem.classList.add('skip-button'); + elem.classList.add('hide'); + elem.classList.add('skip-button-hidden'); - requestAnimationFrame(() => { - elem.classList.remove('skip-button-hidden'); + elem.addEventListener('click', () => { + if (this.currentSegment) { + this.playbackManager.seek(this.currentSegment.EndTicks); + } + }); - if (!options.keep) { - hideTimeout = setTimeout(hideSkipButton, 6000); - } - }); + document.body.appendChild(elem); + this.skipElement = elem; + } } -} -function hideSkipButton() { - const elem = skipElement; - if (elem) { - elem.classList.remove('no-transition'); - void elem.offsetWidth; - - requestAnimationFrame(function() { - elem.classList.add('skip-button-hidden'); + setButtonText() { + if (this.skipElement && this.currentSegment) { + if (this.player && this.currentSegment.EndTicks + && this.currentSegment.Type === MediaSegmentType.Outro + && this.currentSegment.EndTicks >= this.playbackManager.currentItem(this.player).RunTimeTicks + && this.playbackManager.getNextItem() + ) { + // Display "Next Episode" if it's an outro segment, exceeds or is equal to the runtime, and if there is a next track. + this.skipElement.innerHTML += globalize.translate('MediaSegmentNextEpisode'); + } else { + this.skipElement.innerHTML = globalize.translate('MediaSegmentSkipPrompt', globalize.translate(`MediaSegmentType.${this.currentSegment.Type}`)); + } + this.skipElement.innerHTML += ''; + } + } - dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + showSkipButton(options: ShowOptions) { + const elem = this.skipElement; + if (elem) { + this.clearHideTimeout(); + dom.removeEventListener(elem, dom.whichTransitionEvent(), this.onHideComplete, { once: true }); - }); - } -} - -function onPromptSkip(_e: Event, segment: MediaSegmentDto) { - if (!currentSegment) { - currentSegment = segment; + elem.classList.remove('hide'); + if (!options.animate) { + elem.classList.add('no-transition'); + } else { + elem.classList.remove('no-transition'); + } - createSkipElement(); + void elem.offsetWidth; - setButtonText(); + requestAnimationFrame(() => { + elem.classList.remove('skip-button-hidden'); - showSkipButton({ animate: true }); + if (!options.keep) { + this.hideTimeout = setTimeout(this.hideSkipButton.bind(this), 6000); + } + }); + } } -} -function onOsdChanged(_e: Event, isOpen: boolean) { - if (currentSegment) { - if (isOpen) { - showSkipButton({ - animate: false, - keep: true + hideSkipButton() { + const elem = this.skipElement; + if (elem) { + elem.classList.remove('no-transition'); + void elem.offsetWidth; + + requestAnimationFrame(() => { + elem.classList.add('skip-button-hidden'); + + dom.addEventListener(elem, dom.whichTransitionEvent(), this.onHideComplete, { + once: true + }); }); - } else if (!hideTimeout) { - hideSkipButton(); } } -} -function onTimeUpdate() { - if (currentSegment) { - const time = playbackManager.currentTime(currentPlayer) * TICKS_PER_MILLISECOND; - - if (!isInSegment(currentSegment, time)) { - currentSegment = null; - hideSkipButton(); + clearHideTimeout() { + if (this.hideTimeout) { + clearTimeout(this.hideTimeout); + this.hideTimeout = null; } } -} -function cleanupPlayer() { - Events.off(currentPlayer, 'promptskip', onPromptSkip); - Events.off(document, EventType.SHOW_VIDEO_OSD, onOsdChanged); - Events.off(currentPlayer, 'playbackstop', onPlaybackStopped); - Events.off(currentPlayer, 'timeupdate', onTimeUpdate); -} + onPromptSkip(segment: MediaSegmentDto) { + if (!this.currentSegment) { + this.currentSegment = segment; -function onPlaybackStopped() { - currentSegment = null; - hideSkipButton(); - cleanupPlayer(); -} + this.createSkipElement(); + + this.setButtonText(); -function bindToPlayer(player: Plugin) { - if (!player) { - return; + this.showSkipButton({ animate: true }); + } } - currentPlayer = player; + onPlayerTimeUpdate() { + if (this.currentSegment) { + const time = this.playbackManager.currentTime(this.player) * TICKS_PER_MILLISECOND; - Events.on(player, 'promptskip', onPromptSkip); - Events.on(document, EventType.SHOW_VIDEO_OSD, onOsdChanged); - Events.on(player, 'playbackstop', onPlaybackStopped); - Events.on(player, 'timeupdate', onTimeUpdate); + if (!isInSegment(this.currentSegment, time)) { + this.currentSegment = null; + this.hideSkipButton(); + } + } + } + + onPlaybackStop() { + this.currentSegment = null; + this.hideSkipButton(); + Events.off(document, EventType.SHOW_VIDEO_OSD, this.onOsdChanged); + } } -Events.on(playbackManager, 'playerchange', (_e, newPlayer) => { - bindToPlayer(newPlayer); -}); -bindToPlayer(playbackManager.getCurrentPlayer()); +export const bindSkipSegment = (playbackManager: PlaybackManager) => new SkipSegment(playbackManager); diff --git a/src/index.jsx b/src/index.jsx index fdee4a9c7881..cbbc633ce6c0 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -172,7 +172,6 @@ async function loadPlugins() { function loadPlatformFeatures() { if (!browser.tv && !browser.xboxOne && !browser.ps4) { - import('./components/playback/skipsegment'); import('./components/nowPlayingBar/nowPlayingBar'); }