Started video comments
This commit is contained in:
+14
-10
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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[];
|
||||
}
|
||||
@@ -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)}
|
||||
• {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 @@
|
||||
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" }) };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.plyr--full-ui.plyr--video .plyr__control--overlaid {
|
||||
display: flex !important;
|
||||
}
|
||||
Reference in New Issue
Block a user