ybabtsY
Denoβ€’3y ago
ybabts

HTTP 206 + HTML5 video streaming with Fresh

how do I implement an endpoint to stream a video to a HTML video player using HTTP 206 in Fresh? Here's what I have so far however it doesn't seem to be working.
import { HandlerContext } from "$fresh/server.ts";
import { Err, isErr, Ok } from "eav/mod.ts";

const video = await Deno.readFile("./static/assets/hero.mp4");

const chunkSize = 1024 * 1024 * 10;

export async function handler(
  req: Request,
  ctx: HandlerContext,
): Promise<Response> {
  if (req.headers.has("range")) {
    const range = Ok(parseRange(req.headers.get("range")!)) ??
      { start: 0, end: NaN };
    if (range.start === 0 && isNaN(range.end)) {
      return new Response(video.slice(0, chunkSize), {
        status: 206,
        headers: {
          "Content-Type": "video/mp4",
          "Content-Length": chunkSize.toString(),
          "Content-Range": `bytes 0-100/${video.length}`,
          "Accept-Ranges": "bytes",
        },
      });
    }
    const end = isNaN(range.end) ? range.start + chunkSize : range.end;
    return new Response(video.slice(range.start, end), {
      status: 206,
      headers: {
        "Content-Type": "video/mp4",
        "Content-Length": chunkSize.toString(),
        "Content-Range": `bytes ${range.start}-${end}/${video.length}`,
        "Accept-Ranges": "bytes",
      },
    });
  }
  return new Response(null, {
    status: 404,
  });
}

function parseRange(range: string) {
  if (!range.includes("bytes=")) {
    return new Err("invalid range");
  }
  const [_, rangeStr] = range.split("=");
  const [start, end] = rangeStr.split("-");
  return { start: parseInt(start), end: parseInt(end) };
}

I would bash my head against this some more but I'm going to sleep so I figure if someone knows it'll be useful. I plan on making a middleware to automatically stream any content that is over a certain size.
Was this page helpful?