Added video previews

This commit is contained in:
WardPearce
2024-04-13 18:54:17 +12:00
parent f4fd99ddd2
commit 56ba81e983
7 changed files with 135 additions and 59 deletions
-12
View File
@@ -21,7 +21,6 @@
import { getDynamicTheme } from './theme';
export let data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null };
export let currentTime: number = 0;
export let audioMode = false;
export let player: MediaPlayerElement;
export let isSyncing: boolean = false;
@@ -38,13 +37,6 @@
const proxyVideos = get(playerProxyVideos);
onMount(async () => {
try {
const defaultVolume = localStorage.getItem('volume');
if (defaultVolume) {
player.volume = Number(defaultVolume);
}
} catch {}
if (!data.video.hlsUrl) {
if (data.video.captions) {
data.video.captions.forEach(async (caption) => {
@@ -81,10 +73,6 @@
});
}
player.addEventListener('time-update', () => {
currentTime = player.currentTime;
});
player.addEventListener('pause', () => {
savePlayerPos();
});
+109 -38
View File
@@ -4,18 +4,24 @@
import { get } from 'svelte/store';
import {
deArrowEnabled,
interfacePreviewVideoOnHover,
playerProxyVideos,
playerSavePlaybackPosition,
syncPartyConnections,
syncPartyPeer
} from '../store';
import { getDeArrow, getThumbnail, getVideo } from './Api';
import type { Notification, PlaylistPageVideo, Video, VideoBase } from './Api/model';
import type { Notification, PlaylistPageVideo, Video, VideoBase, VideoPlay } from './Api/model';
import { cleanNumber, proxyVideoUrl, truncate, videoLength } from './misc';
import type { PlayerEvents } from './player';
export let video: VideoBase | Video | Notification | PlaylistPageVideo;
export let playlistId: string = '';
let showVideoPreview: boolean = false;
let videoPreview: VideoPlay | null = null;
let videoPreviewMuted: boolean = true;
let watchUrl = new URL(`${import.meta.env.VITE_DEFAULT_FRONTEND_URL}/watch/${video.videoId}`);
if (playlistId !== '') {
@@ -30,7 +36,6 @@
let loading = true;
let loaded = false;
let failed = false;
let img: HTMLImageElement;
@@ -118,7 +123,6 @@
};
img.onerror = () => {
loading = false;
failed = true;
};
});
@@ -139,47 +143,100 @@
});
}
}
async function previewVideo() {
if (!get(interfacePreviewVideoOnHover)) return;
showVideoPreview = true;
try {
videoPreview = await getVideo(video.videoId, get(playerProxyVideos));
} catch {
showVideoPreview = true;
}
}
</script>
<a
class="wave"
style="width: 100%; overflow: hidden;min-height:100px;"
href={watchUrl.toString()}
data-sveltekit-preload-data="off"
on:click={syncChangeVideo}
<div
style="position: relative;"
on:mouseover={previewVideo}
on:mouseleave={() => (showVideoPreview = false)}
on:focus={() => {}}
role="region"
>
{#if loading}
<progress class="circle"></progress>
{:else if loaded}
<img
class="responsive"
style="max-width: 100%;min-height: 160px;"
src={img.src}
alt="Thumbnail for video"
/>
{:else}
<p>{$_('thumbnail.failedToLoadImage')}</p>
{/if}
{#if progress}
<progress
class="absolute right bottom"
style="z-index: 1;"
value={progress}
max={video.lengthSeconds}
></progress>
{/if}
{#if !('liveVideo' in video) || !video.liveVideo}
{#if video.lengthSeconds !== 0}
<div class="absolute right bottom small-margin black white-text small-text thumbnail-corner">
&nbsp;{videoLength(video.lengthSeconds)}&nbsp;
<a
class="wave thumbnail"
href={watchUrl.toString()}
data-sveltekit-preload-data="off"
on:click={syncChangeVideo}
>
{#if loading}
<progress class="circle"></progress>
{:else if loaded}
{#if showVideoPreview && videoPreview}
<div style="max-width: 100%; max-height: 200px;">
<video
style="max-width: 100%; max-height: 200px;"
autoplay
poster={img.src}
muted={videoPreviewMuted}
volume={0.5}
src={videoPreview.formatStreams[0].url}
>
<track label="" kind="subtitles" srclang="" src="" default />
</video>
</div>
{:else}
<img
class="responsive"
style="max-width: 100%;min-height: 160px;"
src={img.src}
alt="Thumbnail for video"
/>
{/if}
{:else}
<p>{$_('thumbnail.failedToLoadImage')}</p>
{/if}
{#if progress}
<progress
class="absolute right bottom"
style="z-index: 1;"
value={progress}
max={video.lengthSeconds}
></progress>
{/if}
{#if !('liveVideo' in video) || !video.liveVideo}
{#if video.lengthSeconds !== 0}
<div
class="absolute right bottom small-margin black white-text small-text thumbnail-corner"
>
&nbsp;{videoLength(video.lengthSeconds)}&nbsp;
</div>
{/if}
{:else}
<div class="absolute right bottom small-margin red white-text small-text thumbnail-corner">
{$_('thumbnail.live')}
</div>
{/if}
{:else}
<div class="absolute right bottom small-margin red white-text small-text thumbnail-corner">
{$_('thumbnail.live')}
</div>
</a>
{#if showVideoPreview && videoPreview}
<button
class="no-padding"
style="position: absolute; bottom: 10px; left: 10px; width: 30px; height: 30px;"
on:click={() => {
videoPreviewMuted = !videoPreviewMuted;
}}
>
<i>
{#if videoPreviewMuted}
volume_off
{:else}
volume_up
{/if}
</i>
</button>
{/if}
</a>
</div>
<div class="small-padding">
<nav class="no-margin">
<div class="max">
@@ -200,3 +257,17 @@
<canvas id="canvas" style="display: none;"></canvas>
<div id="video-container" style="display: none;"></div>
<style>
.thumbnail {
width: 100%;
overflow: hidden;
max-height: 160px;
}
@media screen and (max-width: 1000px) {
.thumbnail {
max-height: 100%;
}
}
</style>
+6
View File
@@ -5,6 +5,7 @@ import {
deArrowEnabled,
deArrowInstance,
deArrowThumbnailInstance,
interfacePreviewVideoOnHover,
interfaceSearchSuggestions,
playerAlwaysLoop,
playerAutoPlay,
@@ -87,6 +88,11 @@ const persistedStores = [
store: interfaceSearchSuggestions,
type: 'boolean'
},
{
name: 'previewVideoOnHover',
store: interfacePreviewVideoOnHover,
type: 'boolean'
},
{
name: 'sponsorBlock',
store: sponsorBlock,
@@ -79,6 +79,7 @@
"color": "Color"
},
"searchSuggestions": "Search suggestions",
"previewVideoOnHover": "Preview video on hover",
"dataPreferences": {
"dataPreferences": "Data preferences",
"content": "Looking to import/export subscriptions, change password or delete account? Click here and scroll to the bottom of the page."
+17
View File
@@ -24,6 +24,7 @@
deArrowEnabled,
deArrowInstance,
deArrowThumbnailInstance,
interfacePreviewVideoOnHover,
interfaceSearchSuggestions,
playerAlwaysLoop,
playerAutoPlay,
@@ -384,6 +385,22 @@
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.previewVideoOnHover')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfacePreviewVideoOnHover}
on:click={() => interfacePreviewVideoOnHover.set(!$interfacePreviewVideoOnHover)}
/>
<span></span>
</label>
</nav>
</div>
</div>
<div class="settings">
@@ -46,7 +46,6 @@
let theatreMode = get(playerTheatreModeByDefault);
let audioMode = get(playerListenByDefault);
let currentTime: number;
let seekTo: (time: number) => void;
let player: MediaPlayerElement;
@@ -357,14 +356,7 @@
<div class="grid">
<div class={`s12 m12 l${theatreMode ? '12' : '10'}`}>
{#key data.video.videoId}
<Player
{data}
{audioMode}
isSyncing={$syncPartyPeer !== null}
bind:seekTo
bind:currentTime
bind:player
/>
<Player {data} {audioMode} isSyncing={$syncPartyPeer !== null} bind:seekTo bind:player />
{/key}
<h5>{data.video.title}</h5>
+1
View File
@@ -24,6 +24,7 @@ export const playerAutoplayNextByDefault = persisted('autoplayNextByDefault', fa
export const returnYtDislikes = persisted('returnYtDislikes', true);
export const interfaceSearchSuggestions = persisted('searchSuggestions', true);
export const interfacePreviewVideoOnHover = persisted('previewVideoOnHover', true);
export const auth: Writable<null | { username: string; token: string; }> = persisted(
'authToken',