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

runtimeCaching for Videos returned with 206 #793

Open
ExXTreMe315 opened this issue Nov 20, 2024 · 14 comments
Open

runtimeCaching for Videos returned with 206 #793

ExXTreMe315 opened this issue Nov 20, 2024 · 14 comments

Comments

@ExXTreMe315
Copy link

Hey, currently I am working on a simple Demo Page with some Images and Videos on it.
(I am using this small project as a POC)

I am working with Nuxt3 and configured the PWA in my Nuxt config (see below)

So I managed to cache the images with the Service Worker, but the Videos won't work correctly.
While trying to cache the Videos, the Browser returns: Uncaught (in promise) TypeError: Failed to execute 'put' on 'Cache': Partial response (status code 206) is unsupported

So I added “rangeRequest: true”, but that still didn't fix it.

Is there a way to do this?

Thanks for your help

My Config:

export default defineNuxtConfig({
   //...
  modules: [
    '@vite-pwa/nuxt',
    //....
  ],
  
  nitro: {
    prerender: {
      routes: [ '/' ]
    }
  },

  pwa: {
    devOptions:{
      enabled: true
    },
    manifest: false,
    registerType: 'autoUpdate',
    injectRegister: 'auto',
    workbox: {
      globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
      runtimeCaching: [
        {
          urlPattern: /^https:\/\/videos\.pexels\.com\/.*/i,
          handler: 'CacheFirst',
          options: {
            cacheName: 'pexels-video-cache',
            expiration: {
              maxEntries: 500,
              maxAgeSeconds: 60 * 60 * 24 * 365 // <== 365 days
            },
            cacheableResponse: {
              statuses: [0, 200, 206]
            },
            rangeRequests: true
          }
        },
        {
          urlPattern: /^https:\/\/images\.pexels\.com\/.*/i,
          handler: 'CacheFirst',
          options: {
            cacheName: 'pexels-image-cache',
            expiration: {
              maxEntries: 20,
              maxAgeSeconds: 60 * 60 * 24 * 365 // <== 365 days
            },
            cacheableResponse: {
              statuses: [0, 200]
            }
          }
        }
      ]
    }
  }
})

I tried also handler: NetworkFirst and configuring a manifest won't even change anything.
I am Using Chrome Version 131.0.6778.70 (Official Build) (arm64) on macOS Sequoia V15.1
While testing, I am using Incognito windows, so there are no extensions or other stuff blocking.
I also tried everything on localhost while running dev and deployed on CF pages as well.

@piotr-cz
Copy link

@ExXTreMe315
Copy link
Author

Hey @piotr-cz thanks for replying.

This article tells about a problem with versions of chrome lower than V87 (I got 131). They are recommending to use a workbox, what is actually right that what i am doing.
I even tell the Workbox to use the plugin for rangeRequests.

So from my view the only opportunity i get from the article is to modify the vite-pwa defaults or write my own?
Isn't there a Vite-PWA way of doing this?

Or did I misunderstand the article?

@ExXTreMe315
Copy link
Author

After a few tests without a success I added back my old config.
Now the error is gone and in he Network Tab of dev tools he tells 206 Partial Content (from service worker).
After I thought "Yes its working" I checked the Application Tab and there in the Cache Storage area is nothing.
The Cache pexels-video-cache exists but its empty.
So how is he using the Videos from the Cache if the Cache is empty?

@piotr-cz
Copy link

piotr-cz commented Nov 20, 2024

In my app I'm precaching videos at runtime like so:

// vite.config.ts
export default defineConfig({
  plugins: [
    preact(),
    VitePWA({
      srcDir: 'src',
      filename: 'sw.ts',
      manifestFilename: 'app.webmanifest',
      strategies: 'injectManifest',
      injectRegister: false,
      registerType: 'prompt',
      manifest: false,
      injectManifest: {
        globPatterns: [
          // Default
          '**/*.{js,css,html}',
          // Imported static assets
          'assets/*.{jpg,png,svg,woff2}',
          'assets/*.{mp3,mp4}',
          // Our buddy favicon
          'assets/icon/favicon.svg',
        ],
        globIgnores: ['mockServiceWorker.js'],
        // Tweak to cache videos
        maximumFileSizeToCacheInBytes: 2.3 * 1024 * 1024,
      },
    }),
  ],
})

However I had to create my own /src/sw.ts file.

So probably you just have to set up workbox to add mp4 files to build cache

@ExXTreMe315
Copy link
Author

ExXTreMe315 commented Nov 20, 2024

Okay, thanks,

so I guess I had to go the hard way and create an external file as long as there is no other way.

Is it to share a short snippet of what you do in your sw.ts so i don't need to code it from scratch?

@piotr-cz
Copy link

piotr-cz commented Nov 20, 2024

First try setting globPatterns with your setup

@ExXTreMe315
Copy link
Author

Wont change anything,
I took your config and updated it with my workbox settings (plus deleting manifest and fileNames).
Still same Issue (Failed to execute 'put' on 'Cache': Partial response (status code 206) is unsupported)

my new config:

pwa: {
    devOptions:{
      enabled: true
    },

    strategies: 'generateSW',
    injectRegister: false,
    registerType: 'prompt',

    manifest: false,
    injectManifest: {
      globPatterns: [
        // Default
        '**/*.{js,css,html}',
        // Imported static assets
        'assets/*.{jpg,png,svg,woff2}',
        'assets/*.{mp3,mp4}'
      ],
      // Tweak to cache videos
      maximumFileSizeToCacheInBytes: 2.3 * 1024 * 1024,
    },
    
    workbox: {
      globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
      runtimeCaching: [
        {
          urlPattern: /^https:\/\/videos\.pexels\.com\/.*/i,
          handler: 'CacheFirst',
          options: {
            cacheName: 'pexels-video-cache',
            expiration: {
              maxEntries: 500,
              maxAgeSeconds: 60 * 60 * 24 * 365 // <== 365 days
            },
            cacheableResponse: {
              statuses: [0, 200, 206]
            },
            rangeRequests: true
          }
        },
        {
          urlPattern: /^https:\/\/images\.pexels\.com\/.*/i,
          handler: 'CacheFirst',
          options: {
            cacheName: 'pexels-image-cache',
            expiration: {
              maxEntries: 20,
              maxAgeSeconds: 60 * 60 * 24 * 365 // <== 365 days
            },
            cacheableResponse: {
              statuses: [0, 200]
            }
          }
        }
      ]
    }
  }

@piotr-cz
Copy link

piotr-cz commented Nov 20, 2024

The sw.ts file I'm using is similar to this one: https://github.com/vite-pwa/vite-plugin-pwa/blob/v0.21.0/examples/preact-router/src/prompt-sw.ts

In your config you then have to set strategies: 'injectManifest',.

However both strategies should work, the key is to add mp4 files in globPatterns.

I remember having exactly similar problem when I was serving app locally with serve (there is not problem with vite preview).

Maybe your hosting is somehow configured to serve partial content instead of whole files?

When serving app, with serve, response for videos includes Accept-Ranges: bytes.
This header is not present when serving with vite preview or with my hosting.

Try this:

curl -I https://<host>/assets/<video file>.mp4

@piotr-cz
Copy link

piotr-cz commented Nov 21, 2024

If your case is like mine, then you are showing video using <video src="/assets/video.mp4" /> element right when app starts.

Browser fires a network request to stream video in chunks using header range: bytes=0- and receives first chunk with 206 Partial Content status and headers accept-ranges: byes and content-range: bytes 0-956124/956125 (more info here: HTTP range requests)

In the meantime service worker installs and downloads precached files.

For this probably browser re-uses previously made requests (including that video) and throws an error because it can only add requests to Cache that have 200 OK status.

Workarounds could be to fetch full video file:

  • Fetch full video, retrieve Blob and pass object url as <video src={objectUrl} />, as described here
  • Set <video src /> after service worker has finished
  • Disable partial responses on the server by unsetting accept-ranges header

It's possible to use <video poster /> property to show anything in meantime.

However users opening the app for the first time would have to wait for whole video to be downloaded to see it.

Proper solution would be that in case service worker would detect 206 Partial Content status, it would download full video, and optionally serve it as partial content (as described here: https://developer.chrome.com/docs/workbox/serving-cached-audio-and-video)

Anyway the issue is not directly related to this package, but to Workbox or Service Workers in general so you'd get better hints in Workbox repo.

If you find any working solution, please let me know.

@ExXTreMe315
Copy link
Author

Thank you for your answers. I have already tried some of your first reply, but it didn't work for me. I will try again and stick to your second message. If I get any solutions that work, I'll let you know.

@userquin
Copy link
Member

Looks like the server needs to add Access-Control-Expose-Headers: content-range: GoogleChrome/workbox#1644 (comment)

@piotr-cz
Copy link

Looks like the server needs to add Access-Control-Expose-Headers: content-range: GoogleChrome/workbox#1644 (comment)

Might be, but I'm able to reproduce the issue on localhost (no CORS involved) with

npm run build && npx serve -p 4173 dist

you need an app that uses <video src="/assets/some-video.mp4" /> on start page

@userquin
Copy link
Member

Beware using opaque responses with caches: check the warning here https://developer.chrome.com/docs/workbox/caching-resources-during-runtime#opaque_responses_and_the_navigatorstorage_api

@piotr-cz
Copy link

piotr-cz commented Nov 22, 2024

@ExXTreMe315

This with this custom plugin: GoogleChrome/workbox#3288 (comment).

I think that this issue may be closed and the discussion can continue on the workbox repo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants