Added video playlist support

This commit is contained in:
WardPearce
2024-04-01 10:30:40 +13:00
parent 5c61e72fe5
commit abeefe755a
6 changed files with 78 additions and 13 deletions
+3 -1
View File
@@ -48,7 +48,9 @@
![Preview of chapters](./previews/chapter-previews.png)
### Playlists
Waiting
![Preview of playlist page](./previews/playlist-preview.png)
![Preview of playlist on video page](./previews/playlist-preview-2.png)
# Have any questions?
[Join our Matrix space](https://matrix.to/#/#ward:matrix.org)
+19 -2
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import 'vidstack/bundle';
import { goto } from '$app/navigation';
import { SponsorBlock, type Category } from 'sponsorblock-api';
import { onDestroy, onMount } from 'svelte';
import { get } from 'svelte/store';
@@ -15,13 +16,14 @@
sponsorBlockCategories,
sponsorBlockUrl
} from '../store';
import type { VideoPlay } from './Api/model';
import type { PlaylistPageVideo, VideoPlay } from './Api/model';
import { videoLength, type PhasedDescription } from './misc';
import { getDynamicTheme } from './theme';
export let data: { video: VideoPlay; content: PhasedDescription };
export let data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null };
export let currentTime: number = 0;
export let audioMode = false;
export let playlistVideos: PlaylistPageVideo[] | null = null;
let player: MediaPlayerElement;
let src: PlayerSrc = [];
@@ -36,6 +38,21 @@
const proxyVideos = get(playerProxyVideos);
onMount(async () => {
if (playlistVideos) {
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) {
goto(`/watch/${playlistVideos[newIndex].videoId}?playlist=${data.playlistId}`);
}
});
}
if (!data.video.hlsUrl) {
if (data.video.captions) {
data.video.captions.forEach(async (caption) => {
@@ -1,10 +1,12 @@
<script lang="ts">
import { deleteUnsubscribe, getComments, postSubscribe } from '$lib/Api/index.js';
import { deleteUnsubscribe, getComments, getPlaylist, postSubscribe } from '$lib/Api/index.js';
import type { PlaylistPage, PlaylistPageVideo } from '$lib/Api/model.js';
import Comment from '$lib/Comment.svelte';
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 { onMount } from 'svelte';
import { get } from 'svelte/store';
import { activePage, playerListenByDefault } from '../../../store.js';
@@ -14,6 +16,30 @@
activePage.set(null);
let playlistVideos: PlaylistPageVideo[] = [];
let playlist: PlaylistPage | null = null;
onMount(async () => {
if (!data.playlistId) return;
for (let page = 1; page < Infinity; page++) {
console.log(page);
const newPlaylist = await getPlaylist(data.playlistId, page);
if (page === 1) {
playlist = newPlaylist;
}
const newVideos = newPlaylist.videos;
if (newVideos.length === 0) {
break;
}
playlistVideos = [...playlistVideos, ...newVideos].sort(
(a: PlaylistPageVideo, b: PlaylistPageVideo) => {
return a.index < b.index ? -1 : 1;
}
);
}
});
async function loadMoreComments() {
if (!comments) {
return;
@@ -47,7 +73,7 @@
<div class="grid">
<div class="s12 m12 l10">
{#key data.video.videoId}
<Player {data} {audioMode} bind:seekTo bind:currentTime />
<Player {data} {audioMode} {playlistVideos} bind:seekTo bind:currentTime />
{/key}
<h5>{data.video.title}</h5>
@@ -180,12 +206,31 @@
{/if}
</div>
<div class="s12 m12 l2">
{#if data.video.recommendedVideos}
{#each data.video.recommendedVideos as recommendedVideo}
<article class="no-padding">
<Thumbnail video={recommendedVideo} />
</article>
{/each}
{#if !data.playlistId}
{#if data.video.recommendedVideos}
{#each data.video.recommendedVideos as recommendedVideo}
<article class="no-padding">
<Thumbnail video={recommendedVideo} />
</article>
{/each}
{/if}
{:else if playlist}
<article style="height: 75vh;" class="scroll">
<h6>{playlist.title}</h6>
<p>{cleanNumber(playlist.viewCount)} views • {playlist.videoCount} videos</p>
<div class="divider"></div>
<div class="space"></div>
{#each playlistVideos as playlistVideo}
<article
class="no-padding primary-border"
class:border={playlistVideo.videoId === data.video.videoId}
>
<Thumbnail video={playlistVideo} playlistId={data.playlistId} />
</article>
{/each}
</article>
{/if}
</div>
</div>
@@ -4,7 +4,7 @@ import { error } from '@sveltejs/kit';
import { get } from 'svelte/store';
import { auth, playerProxyVideos, returnYtDislikes } from '../../../store';
export async function load({ params }) {
export async function load({ params, url }) {
let video;
video = await getVideo(params.slug, get(playerProxyVideos));
@@ -39,6 +39,7 @@ export async function load({ params }) {
returnYTDislikes: returnYTDislikes,
comments: comments,
subscribed: await amSubscribed(video.authorId),
content: phaseDescription(video.description)
content: phaseDescription(video.description),
playlistId: url.searchParams.get('playlist')
};
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB