diff --git a/README.md b/README.md index ca99d2ba..59e9b7bd 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ Modern material design for Invidious - Audio only mode. ## Todo -- Complete channel view. - Playlist support. - PWA support. diff --git a/materialious/src/lib/Api/index.ts b/materialious/src/lib/Api/index.ts index 6fad8be5..40e87197 100644 --- a/materialious/src/lib/Api/index.ts +++ b/materialious/src/lib/Api/index.ts @@ -1,6 +1,6 @@ import { get } from 'svelte/store'; import { auth, returnYTDislikesInstance } from '../../store'; -import type { Channel, ChannelPage, Comments, Playlist, PlaylistPage, ReturnYTDislikes, SearchSuggestion, Subscription, Video, VideoPlay } from './model'; +import type { Channel, ChannelContentPlaylists, ChannelContentVideos, ChannelPage, Comments, Playlist, PlaylistPage, ReturnYTDislikes, SearchSuggestion, Subscription, Video, VideoPlay } from './model'; export function buildPath(path: string): string { return `${import.meta.env.VITE_DEFAULT_INVIDIOUS_INSTANCE}/api/v1/${path}`; @@ -54,6 +54,22 @@ export async function getChannel(channelId: string): Promise { return await resp.json(); } +export async function getChannelContent( + channelId: string, + parameters: { + type?: 'videos' | 'playlists' | 'streams' | 'shorts'; + continuation?: string; + }): Promise { + if (typeof parameters.type === 'undefined') parameters.type = 'videos'; + + const url = new URL(buildPath(`channels/${channelId}/${parameters.type}`)); + + if (typeof parameters.continuation !== 'undefined') url.searchParams.set('continuation', parameters.continuation); + + const resp = await fetch(url.toString()); + return await resp.json(); +} + export async function getSearchSuggestions(search: string): Promise { const path = new URL(buildPath("search/suggestions")); path.search = new URLSearchParams({ q: search }).toString(); diff --git a/materialious/src/lib/Api/model.ts b/materialious/src/lib/Api/model.ts index 4699060a..e396b2f0 100644 --- a/materialious/src/lib/Api/model.ts +++ b/materialious/src/lib/Api/model.ts @@ -185,6 +185,16 @@ export interface PlaylistPageVideo extends PlaylistVideo { viewCount: number; } +export interface ChannelContentVideos { + videos: Video[]; + continuation: string; +} + +export interface ChannelContentPlaylists { + playlists: PlaylistPage[]; + continuation: string; +} + export interface PlaylistPage extends Playlist { description: string; descriptionHtml: string; diff --git a/materialious/src/lib/PlaylistThumbnail.svelte b/materialious/src/lib/PlaylistThumbnail.svelte index 2727022a..a040f07c 100644 --- a/materialious/src/lib/PlaylistThumbnail.svelte +++ b/materialious/src/lib/PlaylistThumbnail.svelte @@ -13,7 +13,11 @@ onMount(() => { img = new Image(); - img.src = playlist.videos[0].videoThumbnails[4].url; + if (playlist.videos.length > 0) { + img.src = playlist.videos[0].videoThumbnails[4].url; + } else { + img.src = playlist.playlistThumbnail; + } img.onload = () => { loaded = true; diff --git a/materialious/src/routes/channel/[slug]/+page.svelte b/materialious/src/routes/channel/[slug]/+page.svelte index 36d9bf19..2f127e8a 100644 --- a/materialious/src/routes/channel/[slug]/+page.svelte +++ b/materialious/src/routes/channel/[slug]/+page.svelte @@ -1,11 +1,71 @@
@@ -24,28 +84,70 @@

{cleanNumber(data.channel.subCount)} subscribers

{data.channel.description}

- +
- {#each data.channel.tabs as tab} - - {tab} - - {/each} -
- -
- - +{#if displayContent} +
+
+
+ {#if 'videos' in displayContent} + {#each displayContent.videos as video} +
+
+ +
+
+ {/each} + {:else} + {#each displayContent.playlists as playlist} +
+
+ +
+
+ {/each} + {/if} +
+
+{:else} + +{/if} +