Fix iOS playback

This commit is contained in:
oxixes
2023-08-16 02:31:18 +02:00
parent 52eef6cc41
commit 2db658bfec
3 changed files with 40 additions and 24 deletions

View File

@@ -8,7 +8,7 @@
export function getQualities(resolution : number) { export function getQualities(resolution : number) {
// We add one to the minimum resolution since some movies // We add one to the minimum resolution since some movies
// have a resolution of 1080p, but the format isn't 16:9, // have a resolution of 1080p, but the format isn't 16:9,
// so the high is less than 1080, so we detect as 1080p // so the height is less than 1080, so we detect as 1080p
// anything higher than 720p, and so on for the other. // anything higher than 720p, and so on for the other.
let data = [ let data = [
{ {

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
export let min = 0; export let min = 0;
export let max = 100; export let max = 100;
export let step = 1; export let step = 0.01;
export let primaryValue = 0; export let primaryValue = 0;
export let secondaryValue = 0; export let secondaryValue = 0;

View File

@@ -84,6 +84,7 @@
let displayedTime : number = 0; let displayedTime : number = 0;
let bufferedTime : number = 0; let bufferedTime : number = 0;
let videoLoaded : boolean = false;
let seeking : boolean = false; let seeking : boolean = false;
let playerStateBeforeSeek : boolean; let playerStateBeforeSeek : boolean;
@@ -118,15 +119,23 @@
? `${JELLYFIN_BASE_URL}/Items/${item?.Id}/Images/Backdrop?quality=100&tag=${item?.BackdropImageTags?.[0]}` ? `${JELLYFIN_BASE_URL}/Items/${item?.Id}/Images/Backdrop?quality=100&tag=${item?.BackdropImageTags?.[0]}`
: ''; : '';
videoLoaded = false;
if (!directPlay) { if (!directPlay) {
if (!Hls.isSupported()) { if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(JELLYFIN_BASE_URL + playbackUri);
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.
*/
video.src = JELLYFIN_BASE_URL + playbackUri;
} else {
throw new Error('HLS is not supported'); throw new Error('HLS is not supported');
} }
const hls = new Hls();
hls.loadSource(JELLYFIN_BASE_URL + playbackUri);
hls.attachMedia(video);
} else { } else {
video.src = JELLYFIN_BASE_URL + playbackUri; video.src = JELLYFIN_BASE_URL + playbackUri;
} }
@@ -135,7 +144,7 @@
currentBitrate = maxBitrate || getQualities(resolution)[0].maxBitrate; currentBitrate = maxBitrate || getQualities(resolution)[0].maxBitrate;
if (item?.UserData?.PlaybackPositionTicks) { if (item?.UserData?.PlaybackPositionTicks) {
video.currentTime = item?.UserData?.PlaybackPositionTicks / 10_000_000; displayedTime = item?.UserData?.PlaybackPositionTicks / 10_000_000;
} }
// We should not requestFullscreen automatically, as it's not what // We should not requestFullscreen automatically, as it's not what
@@ -215,8 +224,8 @@
if (touch) shouldCloseUi = !shouldCloseUi; if (touch) shouldCloseUi = !shouldCloseUi;
else shouldCloseUi = false; else shouldCloseUi = false;
if (uiVisible) { if (!shouldCloseUi) {
clearTimeout(mouseMovementTimeout); if (mouseMovementTimeout) clearTimeout(mouseMovementTimeout);
mouseMovementTimeout = setTimeout(() => { mouseMovementTimeout = setTimeout(() => {
shouldCloseUi = true; shouldCloseUi = true;
}, 3000); }, 3000);
@@ -225,7 +234,7 @@
} }
} }
function handleQualityTogglVisibility() { function handleQualityToggleVisibility() {
if ($contextMenu === qualityContextMenuId) contextMenu.hide(); if ($contextMenu === qualityContextMenuId) contextMenu.hide();
else contextMenu.show(qualityContextMenuId); else contextMenu.show(qualityContextMenuId);
} }
@@ -241,7 +250,7 @@
await reportProgress?.(); await reportProgress?.();
await deleteEncoding?.(); await deleteEncoding?.();
await fetchPlaybackInfo?.($playerState.jellyfinId, bitrate, false); await fetchPlaybackInfo?.($playerState.jellyfinId, bitrate, false);
video.currentTime = timeBeforeLoad; displayedTime = timeBeforeLoad;
paused = stateBeforeLoad; paused = stateBeforeLoad;
} }
@@ -268,6 +277,10 @@
// Workaround because the paused state does not sync // Workaround because the paused state does not sync
// with the video element until a change is made // with the video element until a change is made
paused = false; paused = false;
if (video && $playerState.jellyfinId) {
if (video.src === '') fetchPlaybackInfo($playerState.jellyfinId);
}
}); });
onDestroy(() => { onDestroy(() => {
@@ -275,12 +288,6 @@
if (fullscreen) exitFullscreen?.(); if (fullscreen) exitFullscreen?.();
}); });
$: {
if (video && $playerState.jellyfinId) {
if (video.src === '') fetchPlaybackInfo($playerState.jellyfinId);
}
}
$: { $: {
if (fullscreen && !getFullscreenElement?.()) { if (fullscreen && !getFullscreenElement?.()) {
if (reqFullscreenFunc) reqFullscreenFunc(videoWrapper); if (reqFullscreenFunc) reqFullscreenFunc(videoWrapper);
@@ -307,11 +314,15 @@
<div class="bg-black w-screen h-screen flex items-center justify-center" bind:this={videoWrapper} <div class="bg-black w-screen h-screen flex items-center justify-center" bind:this={videoWrapper}
on:mousemove={() => handleUserInteraction(false)} on:touchend|preventDefault={() => handleUserInteraction(true)}> on:mousemove={() => handleUserInteraction(false)} on:touchend|preventDefault={() => handleUserInteraction(true)}>
<!-- svelte-ignore a11y-media-has-caption --> <!-- svelte-ignore a11y-media-has-caption -->
<video bind:this={video} bind:paused bind:duration on:timeupdate={() => displayedTime = (!seeking) ? video.currentTime : displayedTime} <video bind:this={video} bind:paused bind:duration on:timeupdate={() => displayedTime = (!seeking && videoLoaded) ? video.currentTime : displayedTime}
on:loadeddata={() => handleBuffer()} on:progress={() => handleBuffer()} on:play={() => { on:progress={() => handleBuffer()} on:play={() => {
if (seeking) video?.pause(); if (seeking) video?.pause();
}} }}
bind:volume bind:muted={mute} class="sm:w-full sm:h-full" /> on:loadeddata={() => {
video.currentTime = displayedTime;
videoLoaded = true;
}}
bind:volume bind:muted={mute} class="sm:w-full sm:h-full" playsinline={true} />
{#if uiVisible} {#if uiVisible}
<!-- Video controls --> <!-- Video controls -->
@@ -353,7 +364,7 @@
</SelectableMenuItem> </SelectableMenuItem>
{/each} {/each}
</svelte:fragment> </svelte:fragment>
<IconButton on:click={handleQualityTogglVisibility}> <IconButton on:click={handleQualityToggleVisibility}>
<Gear size={25} /> <Gear size={25} />
</IconButton> </IconButton>
</ContextMenu> </ContextMenu>
@@ -372,7 +383,7 @@
</IconButton> </IconButton>
<div class="w-32"> <div class="w-32">
<Slider bind:primaryValue={volume} secondaryValue={0} max={1} step={0.01} /> <Slider bind:primaryValue={volume} secondaryValue={0} max={1} />
</div> </div>
{#if reqFullscreenFunc} {#if reqFullscreenFunc}
@@ -383,6 +394,11 @@
<EnterFullScreen size={25} /> <EnterFullScreen size={25} />
{/if} {/if}
</IconButton> </IconButton>
<!-- Edge case to allow fullscreen on iPhone -->
{:else if video?.webkitEnterFullScreen}
<IconButton on:click={() => video.webkitEnterFullScreen()}>
<EnterFullScreen size={25} />
</IconButton>
{/if} {/if}
</div> </div>
</div> </div>