Added playlist thumbnails
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { get } from 'svelte/store';
|
||||
import { auth, returnYTDislikesInstance } from '../../store';
|
||||
import type { Channel, ChannelPage, Comments, ReturnYTDislikes, SearchSuggestion, Subscription, Video, VideoPlay } from './model';
|
||||
import type { Channel, ChannelPage, Comments, Playlist, ReturnYTDislikes, SearchSuggestion, Subscription, Video, VideoPlay } from './model';
|
||||
|
||||
export function buildPath(path: string): string {
|
||||
return `${import.meta.env.VITE_DEFAULT_INVIDIOUS_INSTANCE}/api/v1/${path}`;
|
||||
@@ -65,7 +65,7 @@ export async function getSearch(search: string, options: {
|
||||
sort_by?: "relevance" | "rating" | "upload_date" | "view_count",
|
||||
type?: "video" | "playlist" | "channel" | "all";
|
||||
page?: string;
|
||||
}): Promise<(Channel | Video)[]> {
|
||||
}): Promise<(Channel | Video | Playlist)[]> {
|
||||
if (typeof options.sort_by === "undefined") {
|
||||
options.sort_by = "relevance";
|
||||
}
|
||||
|
||||
@@ -159,6 +159,25 @@ export interface Channel {
|
||||
authorThumbnails: Image[];
|
||||
}
|
||||
|
||||
export interface PlaylistVideo {
|
||||
title: string;
|
||||
videoId: string;
|
||||
lengthSeconds: number;
|
||||
videoThumbnails: Thumbnail[];
|
||||
}
|
||||
|
||||
export interface Playlist {
|
||||
type: "playlist";
|
||||
title: string;
|
||||
playlistId: string;
|
||||
playlistThumbnail: string;
|
||||
author: string;
|
||||
authorId: string;
|
||||
authorVerified: boolean;
|
||||
videoCount: number;
|
||||
videos: PlaylistVideo[];
|
||||
}
|
||||
|
||||
export interface ChannelPage extends Channel {
|
||||
allowedRegions: string[];
|
||||
tabs: string[];
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { Channel } from './Api/model';
|
||||
import { cleanNumber, truncate } from './misc';
|
||||
|
||||
export let channel: Channel;
|
||||
</script>
|
||||
|
||||
<a href={`/channel/${channel.authorId}`}>
|
||||
<div class="padding">
|
||||
<div class="center-align">
|
||||
<img
|
||||
class="circle"
|
||||
style="width: 90px;height: 90px;"
|
||||
src={channel.authorThumbnails[0].url}
|
||||
alt={channel.author}
|
||||
/>
|
||||
</div>
|
||||
<h5 class="center-align">{truncate(channel.author, 14)}</h5>
|
||||
<h6 style="margin-top: 0;" class="center-align grey-text medium-text">
|
||||
{cleanNumber(channel.subCount)} subscribers
|
||||
</h6>
|
||||
<p>{channel.description}</p>
|
||||
</div>
|
||||
</a>
|
||||
@@ -0,0 +1,44 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import type { Channel } from './Api/model';
|
||||
import { cleanNumber, truncate } from './misc';
|
||||
|
||||
export let channel: Channel;
|
||||
|
||||
let loading = true;
|
||||
let loaded = false;
|
||||
let failed = false;
|
||||
|
||||
let img: HTMLImageElement;
|
||||
|
||||
onMount(() => {
|
||||
img = new Image();
|
||||
img.src = channel.authorThumbnails[0].url;
|
||||
|
||||
img.onload = () => {
|
||||
loaded = true;
|
||||
loading = false;
|
||||
};
|
||||
img.onerror = () => {
|
||||
loading = false;
|
||||
failed = true;
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<a href={`/channel/${channel.authorId}`} class="wave">
|
||||
<div class="padding">
|
||||
<div class="center-align">
|
||||
{#if loading}
|
||||
<progress class="circle"></progress>
|
||||
{:else}
|
||||
<img class="circle" style="width: 90px;height: 90px;" src={img.src} alt={channel.author} />
|
||||
{/if}
|
||||
</div>
|
||||
<h5 class="center-align">{truncate(channel.author, 14)}</h5>
|
||||
<h6 style="margin-top: 0;" class="center-align grey-text medium-text">
|
||||
{cleanNumber(channel.subCount)} subscribers
|
||||
</h6>
|
||||
<p>{channel.description}</p>
|
||||
</div>
|
||||
</a>
|
||||
@@ -0,0 +1,60 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import type { Playlist } from './Api/model';
|
||||
import { truncate } from './misc';
|
||||
|
||||
export let playlist: Playlist;
|
||||
|
||||
let loading = true;
|
||||
let loaded = false;
|
||||
let failed = false;
|
||||
|
||||
let img: HTMLImageElement;
|
||||
|
||||
onMount(() => {
|
||||
img = new Image();
|
||||
img.src = playlist.videos[0].videoThumbnails[4].url;
|
||||
|
||||
img.onload = () => {
|
||||
loaded = true;
|
||||
loading = false;
|
||||
};
|
||||
img.onerror = () => {
|
||||
loading = false;
|
||||
failed = true;
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<a
|
||||
href={`/playlist/${playlist.playlistId}`}
|
||||
style="width: 100%; overflow: hidden;min-height:100px;"
|
||||
class="wave"
|
||||
>
|
||||
{#if loading}
|
||||
<progress class="circle"></progress>
|
||||
{:else}
|
||||
<img
|
||||
class="responsive"
|
||||
style="max-width: 100%;height: 100%;"
|
||||
src={img.src}
|
||||
alt="Thumbnail for playlist"
|
||||
/>
|
||||
{/if}
|
||||
<div class="absolute right bottom small-margin black white-text small-text thumbnail-corner">
|
||||
{playlist.videoCount} videos
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="small-padding">
|
||||
<nav class="no-margin">
|
||||
<div class="max">
|
||||
<a href={`/playlist/${playlist.playlistId}`}
|
||||
><div class="bold">{truncate(playlist.title)}</div></a
|
||||
>
|
||||
<div>
|
||||
<a href={`/channel/${playlist.authorId}`}>{playlist.author}</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -11,6 +11,8 @@
|
||||
let loaded = false;
|
||||
let failed = false;
|
||||
|
||||
let img: HTMLImageElement;
|
||||
|
||||
let progress: string | null;
|
||||
if (get(playerSavePlaybackPosition)) {
|
||||
progress = localStorage.getItem(`v_${video.videoId}`);
|
||||
@@ -19,8 +21,8 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const img = new Image();
|
||||
img.src = video.videoThumbnails[3].url;
|
||||
img = new Image();
|
||||
img.src = video.videoThumbnails[4].url;
|
||||
|
||||
img.onload = () => {
|
||||
loaded = true;
|
||||
@@ -44,7 +46,7 @@
|
||||
<img
|
||||
class="responsive"
|
||||
style="max-width: 100%;height: 100%;"
|
||||
src={video.videoThumbnails[4].url}
|
||||
src={img.src}
|
||||
alt="Thumbnail for video"
|
||||
/>
|
||||
{:else}
|
||||
@@ -59,14 +61,11 @@
|
||||
></progress>
|
||||
{/if}
|
||||
{#if !('liveVideo' in video) || !video.liveVideo}
|
||||
<div class="absolute right bottom small-margin black white-text small-text">
|
||||
<div class="absolute right bottom small-margin black white-text small-text thumbnail-corner">
|
||||
{videoLength(video.lengthSeconds)}
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="absolute right bottom small-margin red white-text small-text"
|
||||
style="padding: 0 1em;"
|
||||
>
|
||||
<div class="absolute right bottom small-margin red white-text small-text thumbnail-corner">
|
||||
LIVE
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { getSearch } from '$lib/Api';
|
||||
import Channel from '$lib/Channel.svelte';
|
||||
import ChannelThumbnail from '$lib/ChannelThumbnail.svelte';
|
||||
import PlaylistThumbnail from '$lib/PlaylistThumbnail.svelte';
|
||||
import Thumbnail from '$lib/Thumbnail.svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { activePage } from '../../../store';
|
||||
@@ -9,7 +10,7 @@
|
||||
|
||||
let currentPage = 1;
|
||||
|
||||
let search = data.search;
|
||||
$: search = data.search;
|
||||
|
||||
activePage.set(null);
|
||||
|
||||
@@ -60,7 +61,9 @@
|
||||
{#if item.type === 'video'}
|
||||
<Thumbnail video={item} />
|
||||
{:else if item.type === 'channel'}
|
||||
<Channel channel={item} />
|
||||
<ChannelThumbnail channel={item} />
|
||||
{:else if item.type === 'playlist'}
|
||||
<PlaylistThumbnail playlist={item} />
|
||||
{/if}
|
||||
</article>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.thumbnail-corner {
|
||||
padding: .1em .4em;
|
||||
}
|
||||
Reference in New Issue
Block a user