From a0441387d09fb0d84f47d443600a00993c4b64eb Mon Sep 17 00:00:00 2001 From: mbaharip <62494292+mbahArip@users.noreply.github.com> Date: Sun, 5 May 2024 23:47:17 +0700 Subject: [PATCH] Test new stream logic --- src/app/@preview.video.tsx | 7 +- src/app/api/stream/[encryptedId]/route.ts | 150 ++++++++++++++++------ 2 files changed, 110 insertions(+), 47 deletions(-) diff --git a/src/app/@preview.video.tsx b/src/app/@preview.video.tsx index df6e10c..73f15aa 100644 --- a/src/app/@preview.video.tsx +++ b/src/app/@preview.video.tsx @@ -8,8 +8,6 @@ import { cn } from "~/utils"; import Icon from "~/components/Icon"; -import { decryptData } from "~/utils/encryptionHelper/hash"; - import { CreateDownloadToken } from "./actions"; const Plyr = dynamic(() => import("plyr-react"), { @@ -32,10 +30,6 @@ export default function PreviewVideo({ file }: Props) { return; } const token = await CreateDownloadToken(); - const id = await decryptData(file.encryptedId); - // setVideoSrc( - // `https://drive.usercontent.google.com/download?id=${id}&export=download&authuser=0`, - // ); setVideoSrc(`/api/stream/${file.encryptedId}?token=${token}`); } catch (error) { const e = error as Error; @@ -85,6 +79,7 @@ export default function PreviewVideo({ file }: Props) { }, ], }} + // crossOrigin='anonymous' options={{ toggleInvert: true, settings: ["quality", "speed"], diff --git a/src/app/api/stream/[encryptedId]/route.ts b/src/app/api/stream/[encryptedId]/route.ts index f720c12..71448c0 100644 --- a/src/app/api/stream/[encryptedId]/route.ts +++ b/src/app/api/stream/[encryptedId]/route.ts @@ -7,8 +7,6 @@ import { gdriveNoCache as gdrive } from "~/utils/gdriveInstance"; import config from "~/config/gIndex.config"; -import { streamFile } from "./helper"; - export const dynamic = "force-dynamic"; export async function GET( @@ -35,12 +33,33 @@ export async function GET( if (!tokenValidity.success) throw new Error(tokenValidity.message); const decryptedId = await decryptData(encryptedId); - const _fileMeta = gdrive.files.get({ + const fileMeta = await gdrive.files.get({ fileId: decryptedId, fields: "id, name, mimeType, size, fileExtension, webContentLink", supportsAllDrives: config.apiConfig.isTeamDrive, }); - const _fileContent = gdrive.files.get( + + const fileSize = Number(fileMeta.data.size || 0); + if (!fileMeta.data.webContentLink) + throw new Error("No download link found"); + + const ranges = request.headers.get("Range") || "bytes=0-"; + let rangeStart = 0; + let rangeEnd = fileSize - 1; + const rangeRegex = /bytes=(\d+)-(\d+)?/; + const rangeSize = ranges.match(rangeRegex); + if (rangeSize) { + rangeStart = parseInt(rangeSize[1], 10); + rangeEnd = rangeSize ? parseInt(rangeSize[2], 10) : fileSize - 1; + } + + const contentRange = `bytes=${rangeStart}-${Math.min( + rangeEnd, + fileSize - 1, + )}/${fileSize}`; + const contentLength = rangeEnd ? rangeEnd - rangeStart + 1 : fileSize; + + const fileContent = await gdrive.files.get( { fileId: decryptedId, alt: "media", @@ -48,49 +67,98 @@ export async function GET( }, { responseType: "stream", + headers: { + "Accept-Ranges": "bytes", + "Range": ranges, + }, }, ); - const [fileMeta, fileContent] = await Promise.all([ - _fileMeta, - _fileContent, - ]); + const stream = fileContent.data as NodeJS.ReadableStream; + const fileRange = fileContent.headers["content-range"]; + const fileLength = fileContent.headers["content-length"]; - const fileSize = Number(fileMeta.data.size || 0); - if (!fileMeta.data.webContentLink) - throw new Error("No download link found"); - - const stream: ReadableStream = streamFile(fileContent.data); - const ranges = request.headers.get("Range"); - if (ranges) { - const [start, end] = ranges.replace(/bytes=/, "").split("-"); - const startByte = parseInt(start, 10); - const endByte = end ? parseInt(end, 10) : fileSize - 1; - return new NextResponse(stream, { - status: 206, - headers: { - "Content-Type": fileMeta.data.mimeType || "application/octet-stream", - "Content-Disposition": `attachment; filename="${encodeURIComponent( - fileMeta.data.name || `Untitled.${fileMeta.data.fileExtension}`, - )}"`, - "Content-Length": (endByte - startByte + 1).toString(), - "Accept-Ranges": "bytes", - "Content-Range": `bytes ${startByte}-${endByte}/${fileSize}`, - }, - }); - } - - return new NextResponse(stream, { - status: 200, - headers: { - "Content-Type": fileMeta.data.mimeType || "application/octet-stream", - "Content-Disposition": `attachment; filename="${encodeURIComponent( - fileMeta.data.name || `Untitled.${fileMeta.data.fileExtension}`, - )}"`, - "Content-Length": fileSize.toString(), - "Accept-Ranges": "bytes", + const readable = new ReadableStream({ + start(controller) { + stream.on("data", (chunk) => { + controller.enqueue(chunk); + }); + stream.on("end", () => { + controller.close(); + }); + stream.on("error", (error) => { + controller.error(error); + }); }, }); + + return new NextResponse(readable, { + status: 206, + headers: { + "Content-Range": fileRange || contentRange, + "Content-Length": fileLength || contentLength.toString(), + "Content-Type": fileMeta.data.mimeType || "application/octet-stream", + "Accept-Ranges": "bytes", + // 'Content-Disposition': `attachment; filename="${encodeURIComponent(fileMeta.data.name || `Untitled.${fileMeta.data.fileExtension}`)}"`, + }, + }); + + // async function* readStream() { + // for await (const chunk of fileContent.data as any) { + // yield chunk; + // } + // } + // function iteratorToStream(iterator: AsyncGenerator) { + // return new ReadableStream({ + // async pull(controller) { + // const { done, value } = await iterator.next(); + // if (done) { + // controller.close(); + // } else { + // controller.enqueue(value); + // } + // }, + // }); + // } + // const stream: ReadableStream = iteratorToStream(readStream()); + + // const res = new Response(stream, { + // status: 206, + // headers: { + // "Content-Type": fileMeta.data.mimeType || "application/octet-stream", + // "Content-Disposition": `attachment; filename="${encodeURIComponent( + // fileMeta.data.name || `Untitled.${fileMeta.data.fileExtension}`, + // )}"`, + // "Content-Length": contentLength.toString(), + // "Content-Range": ranges ? contentRange : "", + // }, + // }); + // NextResponse.next(res); + // new NextResponse(stream, { + // status: request.headers.get("Range") ? 206 : 200, + // headers: { + // "Content-Type": fileMeta.data.mimeType || "application/octet-stream", + // "Content-Disposition": `attachment; filename="${encodeURIComponent( + // fileMeta.data.name || `Untitled.${fileMeta.data.fileExtension}`, + // )}"`, + // "Content-Length": fileSize.toString(), + // "Accept-Ranges": "bytes", + // "Range": ranges ? `bytes ${rangeStart}-${rangeEnd}/${fileSize}` : "", + // }, + // }); + + // return new NextResponse(stream, { + // status: 206, + // headers: { + // "Content-Type": fileMeta.data.mimeType || "application/octet-stream", + // "Content-Disposition": `attachment; filename="${encodeURIComponent( + // fileMeta.data.name || `Untitled.${fileMeta.data.fileExtension}`, + // )}"`, + // "Content-Length": fileSize.toString(), + // "Accept-Ranges": "bytes", + // "Range": ranges ? `bytes ${rangeStart}-${rangeEnd}/${fileSize}` : "", + // }, + // }); } catch (error) { const e = error as Error; console.error(e.message);