workign
This commit is contained in:
627
package-lock.json
generated
627
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1 +1,6 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
|
||||
@import 'tailwindcss';
|
||||
|
||||
@theme {
|
||||
--font-display: "Montserrat", sans-serif;
|
||||
}
|
||||
@@ -6,6 +6,6 @@
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div class="contents">%sveltekit.body%</div>
|
||||
<div class="contents font-display">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
{#if href != null}
|
||||
<a class="
|
||||
text-white bg-indigo-400
|
||||
cursor-grab
|
||||
cursor-pointer
|
||||
hover:bg-indigo-500
|
||||
not-motion-reduce:transition-colors
|
||||
shadow shadow-stone-900
|
||||
|
||||
@@ -17,18 +17,14 @@
|
||||
} = $props();
|
||||
|
||||
title = title.trim()
|
||||
if (title.length > titleLength) {
|
||||
title = title.slice(0, titleLength).trim() + "...";
|
||||
}
|
||||
</script>
|
||||
|
||||
<a class="
|
||||
bg-white
|
||||
cursor-grab
|
||||
cursor-pointer
|
||||
shadow-sm shadow-black
|
||||
flex flex-col
|
||||
px-3 py-2
|
||||
min-w-md max-w-md
|
||||
rounded-md
|
||||
" href="/watch/{id}">
|
||||
{#if thumbnail != null}
|
||||
|
||||
66
src/lib/providers/iProvider.ts
Normal file
66
src/lib/providers/iProvider.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
type Video = {
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
mime: string
|
||||
|
||||
uploader?: string
|
||||
uploaderID?: string
|
||||
|
||||
views?: number
|
||||
likes?: number
|
||||
dislikes?: number
|
||||
|
||||
creationDate?: number
|
||||
|
||||
videoURL?: string
|
||||
thumbnailURL?: string
|
||||
|
||||
duration?: number
|
||||
hasVideo: boolean
|
||||
width?: number
|
||||
height?: number
|
||||
fps?: number
|
||||
};
|
||||
|
||||
type VideoComment = {
|
||||
id: string
|
||||
parentID: string
|
||||
videoID: string
|
||||
content: string
|
||||
|
||||
uploader: string
|
||||
uploaderID?: string
|
||||
|
||||
likes?: number
|
||||
dislikes?: number
|
||||
|
||||
creationDate: number
|
||||
editDate?: number
|
||||
};
|
||||
|
||||
interface IVideoList {
|
||||
videos: Video[]
|
||||
HasMore(): boolean
|
||||
HasPrevious(): boolean
|
||||
Next(): void
|
||||
Previous(): void
|
||||
};
|
||||
|
||||
interface ICommentList {
|
||||
id: string
|
||||
comments: VideoComment[]
|
||||
HasMore(): boolean
|
||||
Next(): void
|
||||
}
|
||||
|
||||
interface IProvider {
|
||||
GetVideos(): Promise<IVideoList | null>
|
||||
GetVideoInfo(id: string): Promise<Video | null>
|
||||
GetComments(id: string): Promise<ICommentList | null>
|
||||
SearchVideos(query: string): Promise<IVideoList | null>
|
||||
CanUpload(): Promise<boolean>
|
||||
// TODO: Upload support
|
||||
};
|
||||
|
||||
export { type ICommentList, type IProvider, type IVideoList, type Video, type VideoComment };
|
||||
135
src/lib/providers/youvideoProvider.ts
Normal file
135
src/lib/providers/youvideoProvider.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { type ICommentList, type IProvider, type IVideoList, type Video } from "./iProvider";
|
||||
|
||||
class YouvideoVideoList implements IVideoList {
|
||||
videos: Video[];
|
||||
|
||||
constructor(initialVideos: Video[]) {
|
||||
this.videos = initialVideos;
|
||||
}
|
||||
|
||||
HasMore(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
HasPrevious(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
Next(): void {
|
||||
}
|
||||
|
||||
Previous(): void {
|
||||
}
|
||||
}
|
||||
|
||||
type unprocessedVideo = {
|
||||
cached: boolean
|
||||
description: string
|
||||
extension?: string
|
||||
id: string
|
||||
metadata: {
|
||||
duration: number
|
||||
fps: number
|
||||
size: number[]
|
||||
}
|
||||
name: string
|
||||
};
|
||||
|
||||
class YouvideoProvider implements IProvider {
|
||||
instanceURL: URL
|
||||
|
||||
constructor(url: string) {
|
||||
this.instanceURL = new URL(url);
|
||||
}
|
||||
|
||||
async GetVideos(): Promise<IVideoList | null> {
|
||||
const resp = await fetch(new URL("/api/videos", this.instanceURL))
|
||||
if (!resp.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let initalVideos: Video[] = [];
|
||||
|
||||
const json: unprocessedVideo[] = await resp.json();
|
||||
json.forEach(video => {
|
||||
let convertedVideo: Video = {
|
||||
description: video.description,
|
||||
hasVideo: true,
|
||||
id: video.id,
|
||||
mime: "",
|
||||
title: video.name,
|
||||
fps: video.metadata.fps,
|
||||
duration: video.metadata.duration,
|
||||
uploader: "Youvideo Provider"
|
||||
};
|
||||
initalVideos.push(convertedVideo);
|
||||
});
|
||||
return new YouvideoVideoList(initalVideos);
|
||||
}
|
||||
|
||||
async GetVideoInfo(id: string): Promise<Video | null> {
|
||||
const resp = await fetch(new URL("/video/" + id, this.instanceURL));
|
||||
if (!resp.ok) {
|
||||
return null;
|
||||
}
|
||||
const video: unprocessedVideo = await resp.json();
|
||||
if (video.extension == null) {
|
||||
return null;
|
||||
}
|
||||
let convertedVideo: Video = {
|
||||
description: video.description,
|
||||
hasVideo: true,
|
||||
id: video.id,
|
||||
mime: "",
|
||||
title: video.name,
|
||||
fps: video.metadata.fps,
|
||||
duration: video.metadata.duration,
|
||||
uploader: "Youvideo Provider",
|
||||
videoURL: new URL("/youvideo/api/videofile/with_extension/" + id + video.extension, this.instanceURL).toString()
|
||||
};
|
||||
return convertedVideo;
|
||||
}
|
||||
|
||||
async GetComments(id: string): Promise<ICommentList | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async SearchVideos(query: string): Promise<IVideoList | null> {
|
||||
const resp = await fetch(new URL("/api/videos", this.instanceURL))
|
||||
if (!resp.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let initalVideos: Video[] = [];
|
||||
|
||||
const json: unprocessedVideo[] = await resp.json();
|
||||
json.forEach(video => {
|
||||
let contains = false;
|
||||
contains = video.description.includes(query) || video.name.includes(query);
|
||||
// This is like this to allow nicer things
|
||||
if (!contains) {
|
||||
return;
|
||||
}
|
||||
let convertedVideo: Video = {
|
||||
description: video.description,
|
||||
hasVideo: true,
|
||||
id: video.id,
|
||||
mime: "",
|
||||
title: video.name,
|
||||
fps: video.metadata.fps,
|
||||
duration: video.metadata.duration,
|
||||
uploader: "Youvideo Provider",
|
||||
width: video.metadata.size[0],
|
||||
height: video.metadata.size[1]
|
||||
};
|
||||
initalVideos.push(convertedVideo);
|
||||
});
|
||||
return new YouvideoVideoList(initalVideos);
|
||||
}
|
||||
|
||||
async CanUpload(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export { YouvideoProvider };
|
||||
@@ -12,7 +12,7 @@
|
||||
</svelte:head>
|
||||
|
||||
<header class="bg-neutral-100 p-2 rounded-b-xl flex flex-row justify-between">
|
||||
<a class="text-2xl" href="/">TheyWatch</a>
|
||||
<a class="text-2xl font-bold" href="/">TheyWatch</a>
|
||||
<nav class="flex flex-row align-middle">
|
||||
<Button href="/upload">Upload video</Button>
|
||||
</nav>
|
||||
|
||||
@@ -1,21 +1,35 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import VideoCard from "$lib/components/VideoCard.svelte";
|
||||
import type { IVideoList } from "$lib/providers/iProvider";
|
||||
import { YouvideoProvider } from "$lib/providers/youvideoProvider";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
const provider = new YouvideoProvider("https://youvideo.nonamesoft.xyz/youvideo");
|
||||
|
||||
let videolist: IVideoList | null = $state(null);
|
||||
let error = $state("");
|
||||
|
||||
onMount(async () => {
|
||||
const vl = await provider.GetVideos();
|
||||
if (vl == null) {
|
||||
error = "Failed to get videos";
|
||||
return
|
||||
}
|
||||
videolist = vl
|
||||
})
|
||||
</script>
|
||||
<h1>Welcome to SvelteKit</h1>
|
||||
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
|
||||
|
||||
<div class="flex flex-row gap-x-2">
|
||||
{#if error != ""}
|
||||
<p class="
|
||||
w-full p-1 rounded-lg
|
||||
text-white bg-red-500 text-center font-bold
|
||||
">{error}</p>
|
||||
{/if}
|
||||
<div class="grid grid-cols-5 gap-2">
|
||||
{#each videolist?.videos ?? [] as video}
|
||||
<VideoCard
|
||||
title="How I cloned YouVideo without getting a Cease and Decist"
|
||||
id="2137"
|
||||
views={9999999999}
|
||||
uploader="Ghost Fox"
|
||||
/>
|
||||
<VideoCard
|
||||
title="I got sued..."
|
||||
id="2138"
|
||||
views={2137420691337}
|
||||
uploader="Ghost Fox"
|
||||
id={video.id}
|
||||
title={video.title}
|
||||
uploader={"Youvideo"}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
33
src/routes/watch/[id]/+page.svelte
Normal file
33
src/routes/watch/[id]/+page.svelte
Normal file
@@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import Button from "$lib/components/Button.svelte";
|
||||
import type { Video } from "$lib/providers/iProvider";
|
||||
import { YouvideoProvider } from "$lib/providers/youvideoProvider";
|
||||
import { onMount } from "svelte";
|
||||
import type { PageProps } from "./$types";
|
||||
|
||||
let { params }: PageProps = $props();
|
||||
|
||||
const provider = new YouvideoProvider("https://youvideo.nonamesoft.xyz/youvideo");
|
||||
let video: Video | null = $state(null);
|
||||
let error: string = $state("");
|
||||
|
||||
onMount(async () => {
|
||||
const vid = await provider.GetVideoInfo(params.id);
|
||||
if (vid == null) {
|
||||
error = "Video not found!";
|
||||
return;
|
||||
}
|
||||
video = vid;
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if error != ""}
|
||||
<p class="text-center text-3xl font-bold">{error}</p>
|
||||
<Button href="/">Go back</Button>
|
||||
{:else}
|
||||
<video controls src={video?.videoURL ?? ""}>
|
||||
<track kind="captions">
|
||||
</video>
|
||||
<h3 class="font-bold text-xl">{video?.title}</h3>
|
||||
<h5 class="text-sm">{video?.description}</h5>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user