diff --git a/.vscode/settings.json b/.vscode/settings.json index 4bf4637b..994bdcfd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,6 @@ "materialious/src/lib/i18n", "materialious/src/lib/i18n/locales" ], - "i18n-ally.extract.autoDetect": true + "i18n-ally.extract.autoDetect": true, + "i18n-ally.keystyle": "nested" } \ No newline at end of file diff --git a/materialious/src/lib/PlaylistThumbnail.svelte b/materialious/src/lib/PlaylistThumbnail.svelte index de81b825..64b029f1 100644 --- a/materialious/src/lib/PlaylistThumbnail.svelte +++ b/materialious/src/lib/PlaylistThumbnail.svelte @@ -69,7 +69,9 @@ >
{truncate(playlist.title)}
- {playlist.author} + {playlist.author}
diff --git a/materialious/src/lib/Thumbnail.svelte b/materialious/src/lib/Thumbnail.svelte index c8ee4874..5c97fbca 100644 --- a/materialious/src/lib/Thumbnail.svelte +++ b/materialious/src/lib/Thumbnail.svelte @@ -16,11 +16,15 @@ export let video: VideoBase | Video | Notification | PlaylistPageVideo; export let playlistId: string = ''; - let watchUrl = `/watch/${video.videoId}` + (playlistId ? `?playlist=${playlistId}` : ''); + let watchUrl = new URL(`${import.meta.env.VITE_DEFAULT_FRONTEND_URL}/watch/${video.videoId}`); + + if (playlistId !== '') { + watchUrl.searchParams.set('playlist', playlistId); + } syncPartyPeer.subscribe((peer) => { if (peer) { - watchUrl += (playlistId ? '&' : '?') + `sync=${peer.id}`; + watchUrl.searchParams.set('sync', peer.id); } }); @@ -140,7 +144,7 @@ @@ -179,7 +183,7 @@
-
+
-{#if search.length > 0} +{#if data.search.length > 0}
- {#each search as item} + {#each data.search as item}
{#key item}
diff --git a/materialious/src/routes/watch/[slug]/+page.svelte b/materialious/src/routes/watch/[slug]/+page.svelte index a8623fda..ecd65e35 100644 --- a/materialious/src/routes/watch/[slug]/+page.svelte +++ b/materialious/src/routes/watch/[slug]/+page.svelte @@ -12,7 +12,7 @@ import Comment from '$lib/Comment.svelte'; import Player from '$lib/Player.svelte'; import Thumbnail from '$lib/Thumbnail.svelte'; - import { cleanNumber, numberWithCommas } from '$lib/misc.js'; + import { cleanNumber, numberWithCommas, unsafeRandomItem } from '$lib/misc'; import type { PlayerEvents } from '$lib/player'; import type { DataConnection } from 'peerjs'; import { onDestroy, onMount } from 'svelte'; @@ -25,6 +25,7 @@ playerAutoplayNextByDefault, playerListenByDefault, playerTheatreModeByDefault, + playlistSettings, syncPartyConnections, syncPartyPeer } from '../../../store'; @@ -38,6 +39,9 @@ let playlistVideos: PlaylistPageVideo[] = []; let playlist: PlaylistPage | null = null; + let loopPlaylist: boolean = false; + let shufflePlaylist: boolean = false; + let theatreMode = get(playerTheatreModeByDefault); let audioMode = get(playerListenByDefault); @@ -45,6 +49,14 @@ let seekTo: (time: number) => void; let player: MediaPlayerElement; + playlistSettings.subscribe((playlistSetting) => { + if (!data.playlistId) return; + if (data.playlistId in playlistSetting) { + loopPlaylist = playlistSetting[data.playlistId].loop; + shufflePlaylist = playlistSetting[data.playlistId].shuffle; + } + }); + function playerSyncEvents(conn: DataConnection) { if (player) { conn.send({ @@ -195,12 +207,49 @@ if (player) { player.addEventListener('end', () => { - if ($playerAutoplayNextByDefault && !playlist) { - goto(`/watch/${data.video.recommendedVideos[0].videoId}`); + if (!playlistVideos) { + if ($playerAutoplayNextByDefault) { + goto(`/watch/${data.video.recommendedVideos[0].videoId}`); + } + return; } - if (data.playlistId) { - goToCurrentPlaylistItem(); + goToCurrentPlaylistItem(); + + const playlistVideoIds = playlistVideos.map((value) => { + return value.videoId; + }); + + let goToVideo: PlaylistPageVideo | undefined; + + if (shufflePlaylist) { + goToVideo = unsafeRandomItem(playlistVideos); + } else { + const currentVideoIndex = playlistVideoIds.indexOf(data.video.videoId); + const newIndex = currentVideoIndex + 1; + if (currentVideoIndex !== -1 && newIndex < playlistVideoIds.length) { + goToVideo = playlistVideos[newIndex]; + } else if (loopPlaylist) { + // Loop playlist on end + goToVideo = playlistVideos[0]; + } + } + + if (typeof goToVideo !== 'undefined') { + if ($syncPartyConnections) { + $syncPartyConnections.forEach((conn) => { + if (typeof goToVideo === 'undefined') return; + + conn.send({ + events: [ + { type: 'change-video', videoId: goToVideo.videoId }, + { type: 'playlist', playlistId: data.playlistId } + ] + } as PlayerEvents); + }); + } + + goto(`/watch/${goToVideo.videoId}?playlist=${data.playlistId}`); } }); } @@ -210,32 +259,6 @@ await loadPlaylist(data.playlistId); goToCurrentPlaylistItem(); - - if (playlistVideos && player) { - player.addEventListener('end', () => { - if (!playlistVideos) return; - - const playlistVideoIds = playlistVideos.map((value) => { - return value.videoId; - }); - - const currentVideoIndex = playlistVideoIds.indexOf(data.video.videoId); - const newIndex = currentVideoIndex + 1; - if (currentVideoIndex !== -1 && newIndex <= playlistVideoIds.length) { - if ($syncPartyConnections) { - $syncPartyConnections.forEach((conn) => { - conn.send({ - events: [ - { type: 'change-video', videoId: playlistVideos[newIndex].videoId }, - { type: 'playlist', playlistId: data.playlistId } - ] - } as PlayerEvents); - }); - } - goto(`/watch/${playlistVideos[newIndex].videoId}?playlist=${data.playlistId}`); - } - }); - } }); onDestroy(() => { @@ -533,6 +556,40 @@ {$_('videos')}

{playlist.author}

+ + +
@@ -545,7 +602,9 @@ id={playlistVideo.videoId} class:border={playlistVideo.videoId === data.video.videoId} > - + {#key playlistVideo.videoId} + + {/key} {/each} diff --git a/materialious/src/store.ts b/materialious/src/store.ts index 0e7fd1e5..6004937b 100644 --- a/materialious/src/store.ts +++ b/materialious/src/store.ts @@ -25,7 +25,7 @@ export const returnYtDislikes = persisted('returnYtDislikes', true); export const interfaceSearchSuggestions = persisted('searchSuggestions', true); -export const auth: Writable = persisted( +export const auth: Writable = persisted( 'authToken', null ); @@ -49,3 +49,5 @@ export const deArrowThumbnailInstance = persisted( export const syncPartyPeer: Writable = writable(null); export const syncPartyConnections: Writable = writable(); + +export const playlistSettings: Writable> = writable({}); \ No newline at end of file diff --git a/materialious/static/style.css b/materialious/static/style.css index ecb8876c..c58fac88 100644 --- a/materialious/static/style.css +++ b/materialious/static/style.css @@ -12,6 +12,12 @@ max-width: 200px !important; } +.vds-kb-action { + position: absolute !important; + top: 50% !important; + left: 50% !important; +} + .vds-chapter-radio { align-items: start !important; }