Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useHub().serve() doesn't work with long videos and the Content-Range response header #446

Closed
serhii-chernenko opened this issue Jan 29, 2025 · 1 comment
Labels
bug Something isn't working

Comments

@serhii-chernenko
Copy link

Describe the bug
I want to have a possibility to stream videos, especially long videos. Described in previous issue: #444

I uploaded short.mp4 (1.4MB) and long.mov (370MB) videos to the storage. In general, I'd like to upload videos with the size 1GB+.
Image

While the short one loads well, the long one seems broken because loaded only a small part:
Image

I tried to separate video range by the Content-Range header, but video is just not loaded after this.
Image
Image
Image

Steps to reproduce

<video
  muted="true"
  controls
  playsinline
  preload="preload"
  controlsList="noplaybackrate"
  src="/videos/ssstwitter.com_1717007701991.mp4"
/>
server/routes/videos/[pathname].get.ts
import { z } from 'zod'

const rules = z.object({
  pathname: z.string().nonempty().max(255),
})

export default defineEventHandler(async (event) => {
  const params = await getValidatedRouterParams(
    event,
    rules.safeParse,
  )

  if (params.error) {
    throw createError({
      statusCode: 400,
      statusMessage: 'Invalid URL parameters',
      data: params.error,
    })
  }

  const { pathname } = params.data

  const file = await hubBlob().head(pathname)

  if (!file) {
    throw createError({
      statusCode: 404,
      statusMessage: 'Video not found',
    })
  }

  const range = getRequestHeader(event, 'range') ?? 'bytes=0-'
  const videoSize = file.size
  const parts = range.replace(/bytes=/, '').split('-')
  const start = parseInt(parts[0], 10)
  const chunkSize = 10 * 1024 // 1MB
  const end = start < 2
    ? start + 1
    : Math.min(start + chunkSize, videoSize - 1)
  const contentLength = end - start + 1

  setResponseHeader(
    event,
    'Content-Range',
    `bytes ${start}-${end}/${videoSize}`,
  )
  setResponseHeader(event, 'Accept-Ranges', 'bytes')
  setResponseHeader(event, 'Content-Length', contentLength)
  setResponseHeader(event, 'Content-Type', file.contentType)
  setResponseHeader(event, 'Content-Security-Policy', 'default-src \'none\';')
  setResponseHeader(event, 'Content-Disposition', 'inline')
  setResponseStatus(event, 206)

  return hubBlob().serve(event, pathname)
})

Expected behavior

  1. Long videos have to be loaded via blob serve.
  2. Partial content has to work for streamed video.
  3. Perhaps serve function has to have further enhancements regarding the issue
    Add to the hubBlob().serve() method options object with the property onlyStream to prevent downloading videos #444
    and PR feat(blob serve): stream option implemented to prevent videos downloading #445
Copy link
Contributor

atinux commented Jan 30, 2025

I am wondering if this is not a limitation of CF Workers and the size limit in memory to serve data. For videos, I think it might be best to use CF videos service

@atinux atinux closed this as not planned Won't fix, can't repro, duplicate, stale Feb 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants