Skip to content

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

Closed as not planned
@serhii-chernenko

Description

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

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions