fix(videoplayer): few refactors and lint fixes

This commit is contained in:
Fernando Fernández
2023-04-03 12:52:57 +02:00
parent ce39c14fed
commit ae9d06bf2d
7 changed files with 72 additions and 99 deletions
@@ -48,12 +48,12 @@ const props = withDefaults(
iconOnly?: boolean;
fab?: boolean;
shuffle?: boolean;
videoTrackIndex?: number;
audioTrackIndex?: number;
subtitleTrackIndex?: number;
videoTrackIndex: number;
audioTrackIndex: number;
subtitleTrackIndex: number;
disabled?: boolean;
}>(),
{ disabled: false }
{ iconOnly: false, fab: false, shuffle: false, disabled: false }
);
const playbackManager = playbackManagerStore();
@@ -14,7 +14,6 @@
:model-value="isHovering"
v-bind="props"
contained
eager
scrim
height="100%"
width="100%">
@@ -26,7 +26,6 @@
<script setup lang="ts">
import { computed, watch, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import { isNil } from 'lodash-es';
import { useI18n } from 'vue-i18n';
import Hls, { ErrorData, ErrorTypes, Events } from 'hls.js';
@@ -40,9 +39,8 @@ import { mediaElementRef } from '@/store/playbackManager';
const playbackManager = playbackManagerStore();
const playerElement = playerElementStore();
const { t } = useI18n();
const router = useRouter();
/**
* Safari iOS doesn't support hls.js, so we need to handle the cases where we don't need hls.js
*/
@@ -156,27 +154,6 @@ watch(
}
);
watch(
() => ({ currentItem: playbackManager.currentItem }),
(newValue, oldValue) => {
if (
(!newValue.currentItem &&
router.currentRoute.value.fullPath === '/playback/video') ||
(newValue.currentItem &&
!oldValue?.currentItem &&
playbackManager.currentlyPlayingMediaType === 'Video')
) {
/**
* If no item is present, we either manually loaded this or playback is stopped
* OR
* If there was no item and there's now a video, default to going FS
*/
playerElement.toggleFullscreenVideoPlayer();
}
},
{ immediate: true }
);
watch(mediaElementRef, async () => {
await nextTick();
detachHls();
+1 -1
View File
@@ -25,7 +25,7 @@ const playerElement = playerElementStore();
const osd = ref(true);
let fullscreen = useFullscreen(document.body);
const fullscreen = useFullscreen(document.body);
/**
* Toggles the fullscreen view, based on browsers supporting it or not (basically iOS or the others)
+28 -17
View File
@@ -1,3 +1,9 @@
/**
* This store handles the state of the playback
*
* It must be used in an agnostic way to cover both local and remote playback.
* If you want to handle the state of the local player element, use playerElement store instead.
*/
import { reactive, ref, watch } from 'vue';
import { shuffle, isNil, cloneDeep } from 'lodash-es';
import {
@@ -615,18 +621,25 @@ class PlaybackManagerStore {
/**
* Report playback stopped to the server. Used by the "Now playing" statistics in other clients.
*/
private _reportPlaybackStopped = async (itemId: string): Promise<void> => {
private _reportPlaybackStopped = async (
itemId: string,
sessionId = state.playSessionId,
currentTime = this.currentTime,
updateState = true
): Promise<void> => {
const remote = useRemote();
await remote.sdk.newUserApi(getPlaystateApi).reportPlaybackStopped({
playbackStopInfo: {
ItemId: itemId,
PlaySessionId: state.playSessionId,
PositionTicks: msToTicks((this.currentTime || 0) * 1000)
PlaySessionId: sessionId,
PositionTicks: msToTicks((currentTime || 0) * 1000)
}
});
state.lastProgressUpdate = Date.now();
if (updateState) {
state.lastProgressUpdate = Date.now();
}
};
/**
@@ -831,24 +844,22 @@ class PlaybackManagerStore {
};
public stop = (): void => {
const sessionId = String(state.playSessionId || '');
const time = Number(this.currentTime);
const itemId = String(this.currentItem?.Id || '');
const volume = Number(this.currentVolume);
Object.assign(state, defaultState);
this.currentVolume = volume;
window.setTimeout(async () => {
const remote = useRemote();
try {
if (
!isNil(this.currentItem) &&
!isNil(this.currentItem.Id) &&
!isNil(remote.auth.currentUser)
) {
this._reportPlaybackStopped(this.currentItem.Id);
if (sessionId && itemId && time && remote.auth.currentUser) {
await this._reportPlaybackStopped(itemId, sessionId, time, false);
}
} catch {
} finally {
const volume = this.currentVolume;
Object.assign(state, defaultState);
this.currentVolume = volume;
}
} catch {}
});
};
+37 -5
View File
@@ -1,5 +1,11 @@
/**
* This store handles the state of the local player.
*
* In the other part, playbackManager is suited to handle the playback state in
* an agnostic way, regardless of where the media is being played (remotely or locally)
*/
import { cloneDeep } from 'lodash-es';
import { nextTick, reactive } from 'vue';
import { nextTick, reactive, watch } from 'vue';
// @ts-expect-error - No types on libass-wasm
import SubtitlesOctopus from '@jellyfin/libass-wasm';
import subtitlesOctopusWorkerUrl from '@jellyfin/libass-wasm/dist/js/subtitles-octopus-worker.js?url';
@@ -8,6 +14,7 @@ import { useRouter } from '@/composables';
const playbackManager = playbackManagerStore();
let subtitlesOctopus: SubtitlesOctopus | undefined;
const fullscreenRoute = '/playback/video';
/**
* == INTERFACES ==
@@ -56,12 +63,12 @@ class PlayerElementStore {
return state.isStretched;
}
public set isStretched(newisStretched: boolean) {
state.isStretched = newisStretched;
public set isStretched(newIsStretched: boolean) {
state.isStretched = newIsStretched;
}
public get isFullscreenVideoPlayer(): boolean {
return useRouter().currentRoute.value.fullPath === '/playback/video';
return useRouter().currentRoute.value.fullPath === fullscreenRoute;
}
/**
@@ -76,7 +83,7 @@ class PlayerElementStore {
const router = useRouter();
if (!this.isFullscreenVideoPlayer) {
router.push('/playback/video');
router.push(fullscreenRoute);
} else {
router.replace(
typeof router.options.history.state.back === 'string'
@@ -162,6 +169,31 @@ class PlayerElementStore {
}
}
};
public constructor() {
watch(
() => playbackManager.currentItem,
(newValue, oldValue) => {
const router = useRouter();
if (
(!newValue &&
router.currentRoute.value.fullPath === fullscreenRoute) ||
(newValue &&
!oldValue &&
playbackManager.currentlyPlayingMediaType === 'Video')
) {
/**
* If no item is present, we either manually loaded this or playback is stopped
* OR
* If there was no item and there's now a video, default to going FS
*/
playerElement.toggleFullscreenVideoPlayer();
}
},
{ immediate: true }
);
}
}
const playerElement = new PlayerElementStore();
+1 -47
View File
@@ -1,5 +1,4 @@
import {
isTv,
isApple,
isChrome,
isEdge,
@@ -7,68 +6,23 @@ import {
} from '@/utils/browser-detection';
export interface SupportedFeatures {
pictureInPicture: boolean;
airPlay: boolean;
googleCast: boolean;
playbackRate: boolean;
fullScreen: boolean;
}
const supportedFeatures: SupportedFeatures = {
pictureInPicture: false,
airPlay: false,
googleCast: false,
playbackRate: false,
fullScreen: false
playbackRate: false
};
/**
* Detects if the current platform supports showing fullscreen videos
*
* @returns - Whether fullscreen is supported or not
*/
function supportsFullscreen(): boolean {
// TVs don't support fullscreen.
if (isTv()) {
return false;
}
const element = document.documentElement;
const video = document.createElement('video');
return !!(
element.requestFullscreen ||
// check properties that are not recorded on the element type
('mozRequestFullScreen' in element && element.mozRequestFullScreen) ||
('webkitRequestFullscreen' in element && element.webkitRequestFullscreen) ||
('msRequestFullscreen' in element && element.msRequestFullscreen) ||
('webkitEnterFullscreen' in video && video.webkitEnterFullscreen)
);
}
const video = document.createElement('video');
if (
// Check non-standard Safari PiP support
('webkitSupportsPresentationMode' in video &&
typeof video.webkitSupportsPresentationMode === 'function' &&
video.webkitSupportsPresentationMode('picture-in-picture') &&
'webkitSetPresentationMode' in video &&
typeof video.webkitSetPresentationMode === 'function') ||
// Check standard PiP support
document.pictureInPictureEnabled
) {
supportedFeatures.pictureInPicture = true;
}
if (typeof video.playbackRate === 'number') {
supportedFeatures.playbackRate = true;
}
if (supportsFullscreen()) {
supportedFeatures.fullScreen = true;
}
if (isApple()) {
supportedFeatures.airPlay = true;
}