From 4a2324deaf6ec9161c132959a992a3165c419877 Mon Sep 17 00:00:00 2001
From: MuslemRahimi
Date: Fri, 21 Jun 2024 12:54:06 +0200
Subject: [PATCH] add endpoint & update tos
---
src/routes/api/video/[filename].ts | 69 ++++++++++++++++++++++++++++
src/routes/terms-of-use/+page.svelte | 5 +-
2 files changed, 73 insertions(+), 1 deletion(-)
create mode 100644 src/routes/api/video/[filename].ts
diff --git a/src/routes/api/video/[filename].ts b/src/routes/api/video/[filename].ts
new file mode 100644
index 00000000..2dd40df7
--- /dev/null
+++ b/src/routes/api/video/[filename].ts
@@ -0,0 +1,69 @@
+import { promises as fsPromises } from 'fs';
+import fs from 'fs';
+import fetch from 'node-fetch';
+import type { RequestHandler } from '@sveltejs/kit';
+
+const CHUNK_SIZE = 1 * 1024 * 1024; // 1MB in bytes
+
+const parseRange = (range: string) => {
+ const [start, end] = range.replace(/bytes=/, '').split('-').map(Number);
+ return { start, end: end || undefined };
+};
+
+const createResponse = (body: ReadableStream | Buffer, headers: Record, status = 206) =>
+ new Response(body, { status, headers });
+
+const streamRemoteFile = async (url: string, range: string) => {
+ const { start, end } = parseRange(range);
+ const response = await fetch(url, {
+ headers: { Range: `bytes=${start}-${end || ''}` }
+ });
+
+ return createResponse(response.body, {
+ 'Content-Range': response.headers.get('Content-Range') || '',
+ 'Accept-Ranges': 'bytes',
+ 'Content-Length': response.headers.get('Content-Length') || '',
+ 'Content-Type': response.headers.get('Content-Type') || '',
+ });
+};
+
+const streamLocalFile = async (filePath: string, range: string) => {
+ const fileSize = (await fsPromises.stat(filePath)).size;
+ const { start, end } = parseRange(range);
+ const chunkEnd = end || Math.min(start + CHUNK_SIZE - 1, fileSize - 1);
+
+ if (start >= fileSize) {
+ return new Response(`Requested range not satisfiable\n${start} >= ${fileSize}`, {
+ status: 416,
+ headers: { 'Content-Range': `bytes */${fileSize}` },
+ });
+ }
+
+ const stream = fs.createReadStream(filePath, { start, end: chunkEnd });
+
+ return createResponse(stream, {
+ 'Content-Range': `bytes ${start}-${chunkEnd}/${fileSize}`,
+ 'Accept-Ranges': 'bytes',
+ 'Content-Length': (chunkEnd - start + 1).toString(),
+ 'Content-Type': 'video/mp4',
+ });
+};
+
+export const GET: RequestHandler = async ({ params, request }) => {
+ const { filename } = params;
+ const range = request.headers.get('range');
+
+ if (!range) {
+ return new Response('Requires Range header', { status: 400 });
+ }
+
+ try {
+ const videoPath = filename.startsWith('http') ? filename : `static/${filename}`;
+ return filename.startsWith('http') ?
+ await streamRemoteFile(videoPath, range) :
+ await streamLocalFile(videoPath, range);
+ } catch (error) {
+ console.error('Error streaming video:', error);
+ return new Response('File not found or error streaming video', { status: 404 });
+ }
+};
\ No newline at end of file
diff --git a/src/routes/terms-of-use/+page.svelte b/src/routes/terms-of-use/+page.svelte
index 65bddf35..2555be86 100644
--- a/src/routes/terms-of-use/+page.svelte
+++ b/src/routes/terms-of-use/+page.svelte
@@ -165,7 +165,10 @@
You may be held accountable for damages (including costs and attorneys' fees) for misrepresentation or bad-faith claims on the infringement of any Content found on and/or through Service on your copyright.
-
+
+
+ On this website we use some images on this website from FreePik.
+