-
\ No newline at end of file
diff --git a/src/lib/components/VideoPlayer/Slider.svelte b/src/lib/components/VideoPlayer/Slider.svelte
index 28c61e9..9191763 100644
--- a/src/lib/components/VideoPlayer/Slider.svelte
+++ b/src/lib/components/VideoPlayer/Slider.svelte
@@ -1,39 +1,47 @@
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
-
-
+
+
-
-
\ No newline at end of file
+
+
diff --git a/src/lib/components/VideoPlayer/VideoPlayer.svelte b/src/lib/components/VideoPlayer/VideoPlayer.svelte
index 1684b86..55e03a3 100644
--- a/src/lib/components/VideoPlayer/VideoPlayer.svelte
+++ b/src/lib/components/VideoPlayer/VideoPlayer.svelte
@@ -6,26 +6,36 @@
reportJellyfinPlaybackProgress,
reportJellyfinPlaybackStarted,
reportJellyfinPlaybackStopped,
- delteActiveEncoding
+ delteActiveEncoding as deleteActiveEncoding
} from '$lib/apis/jellyfin/jellyfinApi';
import { getQualities } from '$lib/apis/jellyfin/qualities';
import getDeviceProfile from '$lib/apis/jellyfin/playback-profiles';
import classNames from 'classnames';
import Hls from 'hls.js';
- import { Cross2, Play, Pause, EnterFullScreen, ExitFullScreen,
- SpeakerLoud, SpeakerModerate, SpeakerQuiet, SpeakerOff, Gear } from 'radix-icons-svelte';
+ import {
+ Cross2,
+ Play,
+ Pause,
+ EnterFullScreen,
+ ExitFullScreen,
+ SpeakerLoud,
+ SpeakerModerate,
+ SpeakerQuiet,
+ SpeakerOff,
+ Gear
+ } from 'radix-icons-svelte';
import { onDestroy, onMount } from 'svelte';
import IconButton from '../IconButton.svelte';
import { playerState } from './VideoPlayer';
import { modalStack } from '../Modal/Modal';
import { JELLYFIN_BASE_URL } from '$lib/constants';
import { contextMenu } from '../ContextMenu/ContextMenu';
- import Slider from './Slider.svelte'
+ import Slider from './Slider.svelte';
import ContextMenu from '../ContextMenu/ContextMenu.svelte';
- import SelectableMenuItem from '../ContextMenu/SelectableMenuItem.svelte';
+ import SelectableContextMenuItem from '../ContextMenu/SelectableContextMenuItem.svelte';
export let modalId: symbol;
-
+
let qualityContextMenuId = Symbol();
let video: HTMLVideoElement;
@@ -37,7 +47,7 @@
let progressInterval: NodeJS.Timeout;
// These functions are different in every browser
- let reqFullscreenFunc: ((elem : HTMLElement) => void) | undefined = undefined;
+ let reqFullscreenFunc: ((elem: HTMLElement) => void) | undefined = undefined;
let exitFullscreen: (() => void) | undefined = undefined;
let fullscreenChangeEvent: string | undefined = undefined;
let getFullscreenElement: (() => HTMLElement) | undefined = undefined;
@@ -46,60 +56,72 @@
let elem = document.createElement('div');
// @ts-ignore
if (elem.requestFullscreen) {
- reqFullscreenFunc = (elem) => { elem.requestFullscreen(); };
+ reqFullscreenFunc = (elem) => {
+ elem.requestFullscreen();
+ };
fullscreenChangeEvent = 'fullscreenchange';
- getFullscreenElement = () => document.fullscreenElement;
+ getFullscreenElement = () => document.fullscreenElement;
if (document.exitFullscreen) exitFullscreen = () => document.exitFullscreen();
- // @ts-ignore
- } else if (elem.webkitRequestFullscreen) {
// @ts-ignore
- reqFullscreenFunc = (elem) => { elem.webkitRequestFullscreen(); };
+ } else if (elem.webkitRequestFullscreen) {
+ reqFullscreenFunc = (elem) => {
+ // @ts-ignore
+ elem.webkitRequestFullscreen();
+ };
fullscreenChangeEvent = 'webkitfullscreenchange';
// @ts-ignore
- getFullscreenElement = () => document.webkitFullscreenElement;
- // @ts-ignore
- if (document.webkitExitFullscreen) exitFullscreen = () => document.webkitExitFullscreen();
- // @ts-ignore
- } else if (elem.msRequestFullscreen) {
+ getFullscreenElement = () => document.webkitFullscreenElement;
// @ts-ignore
- reqFullscreenFunc = (elem) => { elem.msRequestFullscreen(); }
+ if (document.webkitExitFullscreen) exitFullscreen = () => document.webkitExitFullscreen();
+ // @ts-ignore
+ } else if (elem.msRequestFullscreen) {
+ reqFullscreenFunc = (elem) => {
+ // @ts-ignore
+ elem.msRequestFullscreen();
+ };
fullscreenChangeEvent = 'MSFullscreenChange';
// @ts-ignore
- getFullscreenElement = () => document.msFullscreenElement;
+ getFullscreenElement = () => document.msFullscreenElement;
// @ts-ignore
if (document.msExitFullscreen) exitFullscreen = () => document.msExitFullscreen();
- // @ts-ignore
- } else if (elem.mozRequestFullScreen) {
// @ts-ignore
- reqFullscreenFunc = (elem) => { elem.mozRequestFullScreen(); };
+ } else if (elem.mozRequestFullScreen) {
+ reqFullscreenFunc = (elem) => {
+ // @ts-ignore
+ elem.mozRequestFullScreen();
+ };
fullscreenChangeEvent = 'mozfullscreenchange';
// @ts-ignore
- getFullscreenElement = () => document.mozFullScreenElement;
+ getFullscreenElement = () => document.mozFullScreenElement;
// @ts-ignore
if (document.mozCancelFullScreen) exitFullscreen = () => document.mozCancelFullScreen();
}
- let paused : boolean;
- let duration : number = 0;
- let displayedTime : number = 0;
- let bufferedTime : number = 0;
+ let paused: boolean;
+ let duration: number = 0;
+ let displayedTime: number = 0;
+ let bufferedTime: number = 0;
- let videoLoaded : boolean = false;
- let seeking : boolean = false;
- let playerStateBeforeSeek : boolean;
+ let videoLoaded: boolean = false;
+ let seeking: boolean = false;
+ let playerStateBeforeSeek: boolean;
- let fullscreen : boolean = false;
- let volume : number = 1;
- let mute : boolean = false;
+ let fullscreen: boolean = false;
+ let volume: number = 1;
+ let mute: boolean = false;
- let resolution : number = 1080;
- let currentBitrate : number = 0;
+ let resolution: number = 1080;
+ let currentBitrate: number = 0;
let shouldCloseUi = false;
let uiVisible = true;
- $: uiVisible = !shouldCloseUi || seeking || paused || $contextMenu === qualityContextMenuId
+ $: uiVisible = !shouldCloseUi || seeking || paused || $contextMenu === qualityContextMenuId;
- const fetchPlaybackInfo = (itemId: string, maxBitrate: number | undefined = undefined, starting : boolean = true) =>
+ const fetchPlaybackInfo = (
+ itemId: string,
+ maxBitrate: number | undefined = undefined,
+ starting: boolean = true
+ ) =>
getJellyfinItem(itemId).then((item) =>
getJellyfinPlaybackInfo(
itemId,
@@ -125,13 +147,13 @@
const hls = new Hls();
hls.loadSource(JELLYFIN_BASE_URL + playbackUri);
- hls.attachMedia(video);
+ hls.attachMedia(video);
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
/*
- * HLS.js does NOT work on iOS on iPhone because Safari on iPhone does not support MSE.
- * This is not a problem, since HLS is natively supported on iOS. But any other browser
- * that does not support MSE will not be able to play the video.
- */
+ * HLS.js does NOT work on iOS on iPhone because Safari on iPhone does not support MSE.
+ * This is not a problem, since HLS is natively supported on iOS. But any other browser
+ * that does not support MSE will not be able to play the video.
+ */
video.src = JELLYFIN_BASE_URL + playbackUri;
} else {
throw new Error('HLS is not supported');
@@ -154,7 +176,8 @@
// A start report should only be sent when the video starts playing,
// not every time a playback info request is made
- if (mediaSourceId && starting) await reportJellyfinPlaybackStarted(itemId, sessionId, mediaSourceId);
+ if (mediaSourceId && starting)
+ await reportJellyfinPlaybackStarted(itemId, sessionId, mediaSourceId);
reportProgress = async () => {
await reportJellyfinPlaybackProgress(
@@ -163,7 +186,7 @@
video?.paused == true,
video?.currentTime * 10_000_000
);
- }
+ };
if (progressInterval) clearInterval(progressInterval);
progressInterval = setInterval(() => {
@@ -172,8 +195,8 @@
}, 5000);
deleteEncoding = () => {
- delteActiveEncoding(sessionId);
- }
+ deleteActiveEncoding(sessionId);
+ };
stopCallback = () => {
reportJellyfinPlaybackStopped(itemId, sessionId, video?.currentTime * 10_000_000);
@@ -184,7 +207,7 @@
function onSeekStart() {
if (seeking) return;
-
+
playerStateBeforeSeek = paused;
seeking = true;
paused = true;
@@ -201,7 +224,7 @@
function handleBuffer() {
let timeRanges = video.buffered;
- // Find the first one whose end time is after the current time
+ // Find the first one whose end time is after the current time
// (the time ranges given by the browser are normalized, which means
// that they are sorted and non-overlapping)
for (let i = 0; i < timeRanges.length; i++) {
@@ -220,7 +243,7 @@
modalStack.close(modalId);
}
- function handleUserInteraction(touch : boolean = false) {
+ function handleUserInteraction(touch: boolean = false) {
if (touch) shouldCloseUi = !shouldCloseUi;
else shouldCloseUi = false;
@@ -239,7 +262,7 @@
else contextMenu.show(qualityContextMenuId);
}
- async function handleSelectQuality(bitrate : number) {
+ async function handleSelectQuality(bitrate: number) {
if (!$playerState.jellyfinId || !video || seeking) return;
if (bitrate === currentBitrate) return;
@@ -254,13 +277,13 @@
paused = stateBeforeLoad;
}
- function secondsToTime(seconds : number, forceHours = false) {
+ function secondsToTime(seconds: number, forceHours = false) {
if (isNaN(seconds)) return '00:00';
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds - hours * 3600) / 60);
const secondsLeft = Math.floor(seconds - hours * 3600 - minutes * 60);
-
+
let str = '';
if (hours > 0 || forceHours) str += `${hours}:`;
@@ -307,78 +330,115 @@