Added saving player pos

This commit is contained in:
WardPearce
2024-03-09 18:25:13 +13:00
parent 3e8d57f8b4
commit 4d81e63992
7 changed files with 327 additions and 152 deletions
+25 -6
View File
@@ -1,16 +1,33 @@
<script lang="ts">
import Plyr from 'plyr';
import Plyr, { type PlyrEvent } from 'plyr';
import 'plyr/dist/plyr.css';
import { onDestroy, onMount, tick } from 'svelte';
import { onDestroy, onMount } from 'svelte';
import { get } from 'svelte/store';
import { invidiousInstance } from '../store';
import {
invidiousInstance,
playerAlwaysLoop,
playerAutoPlay,
playerSavePlaybackPosition
} from '../store';
import type { VideoPlay } from './Api/model';
export let data: { video: VideoPlay };
let player: Plyr | undefined;
onMount(() => {
onMount(async () => {
const playerPos = localStorage.getItem(data.video.videoId);
player = new Plyr('#player');
player.on('loadeddata', (event: PlyrEvent) => {
if (get(playerSavePlaybackPosition) && playerPos) {
event.detail.plyr.currentTime = Number(playerPos);
}
});
player.autoplay = get(playerAutoPlay);
player.loop = get(playerAlwaysLoop);
player.source = {
type: 'video',
previewThumbnails: {
@@ -32,9 +49,11 @@
});
onDestroy(async () => {
if (get(playerSavePlaybackPosition) && player?.currentTime) {
localStorage.setItem(data.video.videoId, player.currentTime.toString());
}
player = undefined;
await tick();
console.log(document);
document.getElementsByClassName('plyr')[0]?.remove();
});
</script>
+147 -18
View File
@@ -1,11 +1,37 @@
<script lang="ts">
import Logo from '$lib/Logo.svelte';
import 'beercss';
import 'material-dynamic-colors';
import { onMount } from 'svelte';
import { get } from 'svelte/store';
import { darkMode, themeColor } from '../store';
import {
activePage,
darkMode,
playerAlwaysLoop,
playerAutoPlay,
playerDash,
playerListenByDefault,
playerProxyVideos,
playerSavePlaybackPosition,
themeColor
} from '../store';
let currentPage: string | null = '';
activePage.subscribe((page) => (currentPage = page));
let autoplay = false;
let alwaysLoop = false;
let proxyVideos = false;
let savePlayerPackPos = false;
let dash = false;
let listenByDefault = false;
playerAutoPlay.subscribe((value) => (autoplay = value));
playerAlwaysLoop.subscribe((value) => (alwaysLoop = value));
playerProxyVideos.subscribe((value) => (proxyVideos = value));
playerSavePlaybackPosition.subscribe((value) => (savePlayerPackPos = value));
playerDash.subscribe((value) => (dash = value));
playerListenByDefault.subscribe((value) => (listenByDefault = value));
const pages = [
{
@@ -91,7 +117,7 @@
<nav class="left m l small">
<header></header>
{#each pages as page}
<a href={page.href}
<a href={page.href} class:active={currentPage === page.name.toLowerCase()}
><i>{page.icon}</i>
<div>{page.name}</div>
</a>
@@ -120,24 +146,123 @@
<dialog class="right" id="dialog-settings">
<nav>
<h5 class="max">Settings</h5>
<h4 class="max">Settings</h4>
<button class="circle transparent" data-ui="#dialog-settings"><i>close</i></button>
</nav>
<p>Customize Materialious</p>
<button on:click={toggleDarkMode} class="no-margin">
{#if !$darkMode}
<i>dark_mode</i>
<span>Dark mode</span>
{:else}
<i>light_mode</i>
<span>Light mode</span>
{/if}
</button>
<button>
<i>palette</i>
<span>Color</span>
<input on:change={setColor} type="color" />
</button>
<div class="settings">
<h6>Theme</h6>
<button on:click={toggleDarkMode} class="no-margin">
{#if !$darkMode}
<i>dark_mode</i>
<span>Dark mode</span>
{:else}
<i>light_mode</i>
<span>Light mode</span>
{/if}
</button>
<button>
<i>palette</i>
<span>Color</span>
<input on:change={setColor} type="color" />
</button>
</div>
<div class="settings">
<h6>Player</h6>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>Autoplay</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={autoplay}
on:click={() => playerAutoPlay.set(!autoplay)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>Always loop</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={alwaysLoop}
on:click={() => playerAlwaysLoop.set(!alwaysLoop)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>Proxy videos</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={proxyVideos}
on:click={() => playerProxyVideos.set(!proxyVideos)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>Save playback position</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={savePlayerPackPos}
on:click={() => playerSavePlaybackPosition.set(!savePlayerPackPos)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>Listen by default</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={listenByDefault}
on:click={() => playerListenByDefault.set(!listenByDefault)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>Dash</div>
</div>
<label class="switch">
<input type="checkbox" bind:checked={dash} on:click={() => playerDash.set(!dash)} />
<span></span>
</label>
</nav>
</div>
</div>
</dialog>
<dialog class="right" id="dialog-notifications">
@@ -174,4 +299,8 @@
nav.left a {
font-size: 0.8em;
}
.settings h6 {
margin: 1em 0 0.3em 0;
}
</style>
@@ -1,8 +1,11 @@
<script lang="ts">
import { cleanNumber } from '$lib/misc';
import Thumbnail from '$lib/Thumbnail.svelte';
import { activePage } from '../../../store.js';
export let data;
activePage.set(null);
</script>
<div class="padding">
@@ -1,11 +1,14 @@
<script lang="ts">
import PageLoading from '$lib/PageLoading.svelte';
import Thumbnail from '$lib/Thumbnail.svelte';
import { activePage } from '../../store.js';
export let data;
activePage.set('trending');
</script>
{#if data.trending}
{#if data}
<div class="page right active">
<div class="space"></div>
<div class="grid">
+137 -125
View File
@@ -1,36 +1,61 @@
<script lang="ts">
import PageLoading from '$lib/PageLoading.svelte';
import Player from '$lib/Player.svelte';
import Thumbnail from '$lib/Thumbnail.svelte';
import { cleanNumber, numberWithCommas } from '$lib/misc.js';
import { activePage } from '../../../store.js';
export let data;
activePage.set(null);
</script>
<div class="grid">
<div class="s12 m12 l10">
{#key data.video.videoId}
<Player {data} />
{/key}
{#if data}
<div class="grid">
<div class="s12 m12 l10">
{#key data.video.videoId}
<Player {data} />
{/key}
<h5>{data.video.title}</h5>
<h5>{data.video.title}</h5>
<nav>
<a href={`/channel/${data.video.authorId}`}>
<nav>
<img
class="circle large"
src={data.video.authorThumbnails[2].url}
alt="Channel profile"
/>
<div>
<p class="bold">{data.video.author}</p>
<p>{data.video.subCountText}</p>
</div>
<nav>
<a href={`/channel/${data.video.authorId}`}>
<nav>
<img
class="circle large"
src={data.video.authorThumbnails[2].url}
alt="Channel profile"
/>
<div>
<p class="bold">{data.video.author}</p>
<p>{data.video.subCountText}</p>
</div>
</nav>
</a>
<button class="inverse-surface">Subscribe</button>
<div class="max"></div>
<nav class="no-space m l">
<button style="cursor: default;" class="border left-round">
<i class="small">thumb_up</i>
<span>{cleanNumber(data.returnYTDislikes.likes)}</span>
</button>
<button style="cursor: default;" class="border right-round">
<i class="small">thumb_down_alt</i>
<span>{cleanNumber(data.returnYTDislikes.dislikes)}</span>
</button>
</nav>
</a>
<button class="inverse-surface">Subscribe</button>
<div class="max"></div>
<nav class="no-space m l">
<button class="border m l" data-ui="#share"
><i>share</i> Share
<menu class="left no-wrap" id="share" data-ui="#share">
<a class="row"> <div class="min">Copy Materialious link</div></a><a class="row">
<div class="min">Copy Invidious redirect link</div></a
><a class="row"> <div class="min">Copy Youtube link</div></a></menu
></button
>
</nav>
<nav class="no-space s">
<button style="cursor: default;" class="border left-round">
<i class="small">thumb_up</i>
<span>{cleanNumber(data.returnYTDislikes.likes)}</span>
@@ -40,113 +65,100 @@
<span>{cleanNumber(data.returnYTDislikes.dislikes)}</span>
</button>
</nav>
<button class="border m l" data-ui="#share"
><i>share</i> Share
<menu class="left no-wrap" id="share" data-ui="#share">
<a class="row"> <div class="min">Copy Materialious link</div></a><a class="row">
<div class="min">Copy Invidious redirect link</div></a
><a class="row"> <div class="min">Copy Youtube link</div></a></menu
></button
>
</nav>
<nav class="no-space s">
<button style="cursor: default;" class="border left-round">
<i class="small">thumb_up</i>
<span>{cleanNumber(data.returnYTDislikes.likes)}</span>
</button>
<button style="cursor: default;" class="border right-round">
<i class="small">thumb_down_alt</i>
<span>{cleanNumber(data.returnYTDislikes.dislikes)}</span>
</button>
</nav>
<article class="medium scroll">
<p class="bold">
{numberWithCommas(data.video.viewCount)} views • {data.video.publishedText}
</p>
<p style="white-space: pre-line;word-wrap: break-word;">{data.video.description}</p>
</article>
<article class="medium scroll">
<p class="bold">
{numberWithCommas(data.video.viewCount)} views • {data.video.publishedText}
</p>
<p style="white-space: pre-line;word-wrap: break-word;">{data.video.description}</p>
</article>
<div class="space"></div>
<h6>{numberWithCommas(data.comments.commentCount)} comments</h6>
{#each data.comments.comments as comment}
<div class="comment">
<img class="circle small" src={comment.authorThumbnails[1].url} alt="comment profile" />
<div>
<div class="row">
<p>
<span class:bold={true} class:channel-owner={comment.authorIsChannelOwner}
>{comment.author}</span
>
<span class="secondary-text">{comment.publishedText}</span>
</p>
{#if comment.isPinned}
<i>push_pin</i>
{/if}
{#if comment.isEdited}
<i>edit</i>
{/if}
</div>
<p>
{comment.content}
</p>
<div style="display: flex;">
<p><i>thumb_up</i> {numberWithCommas(comment.likeCount)}</p>
{#if comment.creatorHeart}
<div>
<img
class="circle"
style="width: 25px; height: 25px"
src={comment.creatorHeart.creatorThumbnail}
alt="Creator profile"
/>
<i
style="font-size: 20px;margin-left: 5px;"
class="absolute left red-text bottom fill">favorite</i
>
<div class="space"></div>
{#if data.comments.comments}
<h6>{numberWithCommas(data.comments.commentCount)} comments</h6>
{#each data.comments.comments as comment}
<div class="comment">
<img class="circle small" src={comment.authorThumbnails[1].url} alt="comment profile" />
<div>
<div class="row">
<p>
<span class:bold={true} class:channel-owner={comment.authorIsChannelOwner}
>{comment.author}</span
>
<span class="secondary-text">{comment.publishedText}</span>
</p>
{#if comment.isPinned}
<i>push_pin</i>
{/if}
{#if comment.isEdited}
<i>edit</i>
{/if}
</div>
{/if}
<p>
{comment.content}
</p>
<div style="display: flex;">
<p><i>thumb_up</i> {numberWithCommas(comment.likeCount)}</p>
{#if comment.creatorHeart}
<div>
<img
class="circle"
style="width: 25px; height: 25px"
src={comment.creatorHeart.creatorThumbnail}
alt="Creator profile"
/>
<i
style="font-size: 20px;margin-left: 5px;"
class="absolute left red-text bottom fill">favorite</i
>
</div>
{/if}
</div>
</div>
</div>
</div>
</div>
{/each}
<button class="margin">Load more</button>
{/each}
<button class="margin">Load more</button>
{:else}
<h6>Comments disabled</h6>
{/if}
</div>
<div class="s12 m12 l2">
{#each data.video.recommendedVideos as recommendedVideo}
<Thumbnail video={recommendedVideo} />
{/each}
</div>
</div>
<div class="s12 m12 l2">
{#each data.video.recommendedVideos as recommendedVideo}
<Thumbnail video={recommendedVideo} />
{/each}
</div>
</div>
<style>
:root {
--plyr-color-main: var(--primary);
}
.comment {
display: flex;
}
.comment img {
margin: 0.5em 1em 0 1em;
}
.grid {
padding: 1em 10em;
}
.channel-owner {
background-color: var(--primary);
padding: 0 0.5em;
border-radius: 1em;
color: var(--surface-variant);
}
@media screen and (max-width: 1646px) {
.grid {
padding: 0;
<style>
:root {
--plyr-color-main: var(--primary);
}
}
</style>
.comment {
display: flex;
}
.comment img {
margin: 0.5em 1em 0 1em;
}
.grid {
padding: 1em 10em;
}
.channel-owner {
background-color: var(--primary);
padding: 0 0.5em;
border-radius: 1em;
color: var(--surface-variant);
}
@media screen and (max-width: 1646px) {
.grid {
padding: 0;
}
}
</style>
{:else}
<PageLoading />
{/if}
+11 -2
View File
@@ -1,7 +1,16 @@
import { persisted } from 'svelte-persisted-store';
import type { Writable } from 'svelte/store';
import { writable, type Writable } from 'svelte/store';
export const invidiousInstance = persisted('invidiousInstance', import.meta.env.VITE_DEFAULT_INVIDIOUS_INSTANCE);
export const returnYTDislikesInstance = persisted('returnYTDislikesInstance', import.meta.env.VITE_DEFAULT_RETURNYTDISLIKES_INSTANCE);
export const darkMode: Writable<null | boolean> = persisted("darkMode", null);
export const themeColor: Writable<null | string> = persisted("themeColor", null);
export const themeColor: Writable<null | string> = persisted("themeColor", null);
export const activePage: Writable<string | null> = writable("home");
export const playerAutoPlay = persisted("autoPlay", false);
export const playerAlwaysLoop = persisted("alwaysLoop", false);
export const playerProxyVideos = persisted("proxyVideos", false);
export const playerListenByDefault = persisted("listenByDefault", false);
export const playerSavePlaybackPosition = persisted("savePlaybackPosition", true);
export const playerDash = persisted("dash", true);