Started video comments

This commit is contained in:
WardPearce
2024-03-08 17:24:33 +13:00
parent eb5d785e5e
commit 4c7c7f2540
10 changed files with 117 additions and 28 deletions
+14 -10
View File
@@ -1,12 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="%sveltekit.assets%/style.css">
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
+8 -1
View File
@@ -1,6 +1,6 @@
import { get } from 'svelte/store';
import { invidiousInstance, returnYTDislikesInstance } from '../../store';
import type { ReturnYTDislikes, Video, VideoPlay } from './model';
import type { Comments, ReturnYTDislikes, Video, VideoPlay } from './model';
export function buildPath(path: string): string {
return `${get(invidiousInstance)}/api/v1/${path}`;
@@ -19,4 +19,11 @@ export async function getVideo(videoId: string): Promise<VideoPlay> {
export async function getDislikes(videoId: string): Promise<ReturnYTDislikes> {
const resp = await fetch(`${get(returnYTDislikesInstance)}/Votes?videoId=${videoId}`);
return await resp.json();
}
export async function getComments(videoId: string, parameters: { sortBy: "top" | "new", source: "youtube" | "reddit", continuation?: string; }): Promise<Comments> {
const path = new URL(buildPath(`comments/${videoId}`));
path.search = new URLSearchParams(parameters).toString();
const resp = await fetch(path);
return await resp.json();
}
+31 -1
View File
@@ -17,6 +17,7 @@ export interface VideoBase {
title: string;
videoThumbnails: Thumbnail[];
author: string;
authorId: string;
lengthSeconds: number;
viewCountText: string;
}
@@ -24,7 +25,6 @@ export interface VideoBase {
export interface Video extends VideoBase {
type: "video";
title: string;
authorId: string;
authorUrl: string;
authorVerified: boolean;
description: string;
@@ -106,4 +106,34 @@ export interface ReturnYTDislikes {
rating: number;
viewCount: number;
deleted: boolean;
}
export interface Comment {
author: string;
authorThumbnails: AuthorThumbnails[];
authorID: string;
authorUrl: string;
isEdited: boolean;
isPinned: boolean;
content: string;
contentHtml: string;
published: number;
publishedText: string;
likeCount: number;
authorIsChannelOwner: boolean;
creatorHeart: {
creatorThumbnail: string;
creatorName: string;
};
replies: {
replyCount: number;
continuation: string;
};
}
export interface Comments {
commentCount: number;
videoId: string;
continuation?: string;
comments: Comment[];
}
+1 -7
View File
@@ -68,7 +68,7 @@
<div class="max">
<a href={`/watch/${video.videoId}`}><div class="bold">{truncate(video.title)}</div></a>
<div>
{video.author}{#if !('publishedText' in video)}
<a href={`/channel/${video.authorId}`}>{video.author}</a>{#if !('publishedText' in video)}
&nbsp;• {video.viewCountText}{/if}
</div>
{#if 'publishedText' in video}
@@ -76,12 +76,6 @@
{video.viewCountText} • {video.publishedText}
</div>{/if}
</div>
<button class="circle transparent" data-ui="#menu-1"
><i>more_vert</i><menu class="left no-wrap" id="menu-1" data-ui="#menu-1"
><a>Save to paylist</a>
<a>Download</a></menu
></button
>
</nav>
</div>
</article>
+1 -1
View File
@@ -1 +1 @@
export let ssr = false;
export let ssr = false;
@@ -16,6 +16,7 @@
const cleanRound = (number: number) => Number.parseFloat(number.toString()).toFixed(1);
$: video = data.video;
let player;
onMount(() => {
player = new Plyr('#player');
@@ -46,11 +47,19 @@
<h5>{data.video.title}</h5>
<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>
<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">
@@ -84,12 +93,46 @@
</button>
</nav>
<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>
<p>
<span class="bold">{comment.author}</span>
<span class="secondary-text">{comment.publishedText}</span>
</p>
<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>
{/each}
</div>
<div class="s12 m12 l2">
{#each data.video.recommendedVideos as recommendedVideo}
@@ -103,6 +146,14 @@
--plyr-color-main: var(--primary);
}
.comment {
display: flex;
}
.comment img {
margin: 0.5em 1em 0 1em;
}
.grid {
padding: 1em 10em;
}
@@ -1,5 +1,5 @@
import { getDislikes, getVideo } from '$lib/Api/index.js';
import { getComments, getDislikes, getVideo } from '$lib/Api/index.js';
export async function load({ params }) {
return { video: await getVideo(params.slug), returnYTDislikes: await getDislikes(params.slug) };
return { video: await getVideo(params.slug), returnYTDislikes: await getDislikes(params.slug), comments: await getComments(params.slug, { sortBy: "top", source: "youtube" }) };
};
+3
View File
@@ -0,0 +1,3 @@
.plyr--full-ui.plyr--video .plyr__control--overlaid {
display: flex !important;
}