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

Safari 12 .mp4 support #1663

Closed
321ckatz123 opened this issue Sep 25, 2018 · 27 comments
Closed

Safari 12 .mp4 support #1663

321ckatz123 opened this issue Sep 25, 2018 · 27 comments

Comments

@321ckatz123
Copy link

Library Affected:
workbox-sw

Browser & Platform:
Safari 12

Issue or Feature Request Description:
No matter what strategy I use within Workbox, I cannot get a .mp4 to play via Safari that is cached with a service worker with a file served from AWS S3. I even wrote my own strategy using IndexedDb (one that converted to ArrayBuffer to get around known Safari issues with storage and one that didn't, https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/indexeddb-best-practices) to no avail.

I did remove the service worker to prove it worked and it played just fine. Images, JSON, and other text based content seemed to work just fine. I don't have any other video types to easily test with.

This does appear to be a known issue with Safari, per https://bugs.webkit.org/show_bug.cgi?id=184447, but they are basically claiming you need to fix it. Also, this is possibly a hole in the fetch spec to begin with, per whatwg/fetch#747, but the gist of it there seems to also be "you have to fix it". I did try adding the header they wanted in my custom strategy, but that also didn't work (although, granted, this is my first service worker, so I may have done something wrong). I'm also not sure if Workbox is eating my headers I am adding (although I can see the header on the response in the network tab, so I have to assume it is not).

const fetchedResponse = await fetch(url.href);
const blob = await fetchedResponse.blob();

const arrayBuffer = await blobToArrayBuffer(blob);
await db.assets.put({ sig: queryParams.sig, asset: arrayBuffer, type: blob.type });

const headers = new Headers(event.request.headers);
headers.append('Accept-Encoding', 'identity');
return new Response(blob, { headers });

Somewhat amusingly, if you right click on the video element and say "Download", it downloads almost immediately and works, implying it's pulling from cache.

In summation, I'm not 100% sure there is work that can be done, but raising awareness of the issue within workbox to see if there is a simple fix that can potentially be implemented for the strategies for this somewhat acute issue.

@jeffposnick
Copy link
Contributor

Sorry for the delay in getting back to you about this. I've got a couple of questions:

  • Which Workbox strategy are you using when handling .mp4 requests? staleWhileRevalidate, cacheFirst, etc.
  • Are you using workbox-range-requests as a plugin in that strategy?
  • Is the .mp4 asset cached ahead of time, or does it rely purely on runtime caching, with the initial request triggering the cache population?

If you could share your actual Workbox config or final service worker file, that would answer most of the questions. Thanks!

@simevidas
Copy link

simevidas commented Oct 17, 2018

Webpack config (I don’t use any additional workbox plugins):

config.plugins.push(new WorkboxPlugin.GenerateSW({
    importWorkboxFrom: 'local',
    swDest: 'sw.js',
    clientsClaim: true,
    skipWaiting: true,
    navigateFallback: '/index.html',
    navigateFallbackWhitelist: [/^\/issues/, /^\/about/],
    runtimeCaching: [{
        urlPattern: /\/(ajax|media)\//,
        handler: 'networkFirst',
        options: {
            cacheName: 'content-cache'
        }
    }]
}));

The videos have paths like /media/foo.mp4 and are only runtime-cached (not pre-cached) whenever new content loads that includes a video.

The service worker file is generated by workbox. I’ve copy-pasted it into this Gist if you want to take a look at it.

My site’s <video> elements appear as black boxes with crossed-out play icons in Safari (both iOS and Mac).

screen shot 2018-10-17 at 10 29 57 pm

@jeffposnick
Copy link
Contributor

There's unfortunately a lot of nuance needed to get this fully working with any service worker implementation.

Workbox can help, in that the logic needed to honor Range: requests when fulfilling a request using the Cache Storage API is pretty tedious. workbox-range-requests simplifies that.

Here's an example of a setup that I confirmed works in Safari 12: https://gist.github.com/jeffposnick/814b13353f16b8171999febfeb8b7938 (I'd normally use RawGit to host this sort of example with a live demo, but now that it's retired, you're better off making a copy and running it from a local web server.)

There are a number of "gotchas" called out in the comments, and for the same of completeness, I'm outlining them here:

  • You need to add workbox.rangeRequests.Plugin() into the mix. It's what detects when there's an incoming Range: request, and slices up the response to accommodate it.

  • You need to explicitly cache your video files outside of the runtime caching strategies that Workbox provides. The reason is that the runtime caching would just end up populating the cache with a partial response returned by the server, and that's obviously not want you want. You need the full video to be cached if you want to be able to satisfy subsequent requests with different Range: headers using the cached copy in the future. My example ends up caching the full video inside of an install handler, which is fine when there's a small number of small videos, but doesn't scale. I'm going to take some time and see about revising the example to use a custom handlerCallback instead of a built-in strategy, and have that handlerCallback be responsible for calling cache.add(videoUrl) when there's a cache-miss.

  • Your video response needs to be non-opaque. If it's opaque, the service worker won't be able to return slices of its body and fulfill future Range: requests.

These best practices are generally applicable outside of the Safari 12 context, but I'm guessing that other browsers are more flexible about dealing with unexpected video response bodies. We should definitely get this all written up for a section at https://developers.google.com/web/tools/workbox/guides/advanced-recipes

@321ckatz123
Copy link
Author

This might be a naive question (and likely is as I am not the most experienced with service workers), but, in your example is the Range header originating from my website or from the video source (so in my case S3)? It looks like workbox.rangeRequests.Plugin() honors the Range header from the website, but we have to cache outside of the normal workbox strategies. That implies that Workbox perpetuates the Range header to the video source and incorrectly has only partial data in the cache (which is likely what is causing the issues). Is that correct or am I way off base?

@jeffposnick
Copy link
Contributor

That implies that Workbox perpetuates the Range header to the video source and incorrectly has only partial data in the cache (which is likely what is causing the issues). Is that correct or am I way off base?

So that's what I initially thought last night, but after trying this out a bit more this morning, I (re)learned that attempting to cache a HTTP 206 response leads to a rejection—there was actually an earlier issue about this; see #1644.

Getting a complete copy of the vide file in the cache is still important if you want to use workbox-range-requests to slice it up and create partial HTTP 206 responses to fulfill the video playback Range: requests. But I now realize that "poisoning" the cache with a partial response is not likely to happen.

I'm working on some better guidance/recipe that would use runtime caching to populate the cache with a full response when there's a cache miss (while also passing the Range: request on to the network), and when there's a cache hit, respond with a proper HTTP 206. And also, ideally, take into account things like quota management that are critically important when you're caching what might be large resources.

Stay tuned for that.

@simevidas
Copy link

Relevant article (published today): Service workers: beware Safari's range request.

@cherukumilli
Copy link

@jeffposnick - We are running into this issue also. any update on the guidance/recipe will be greatly appreciated :)

@fi4sk0
Copy link

fi4sk0 commented Dec 19, 2018

I also ran into this problem and I'm happy to finally find somebody else sharing that problem. In my case (strategy cacheFirst) it seems not to work (event with the rangeRequest plugin), when the video is not in the cache yet, so it's forwarded to the server as a full request and answered as a full request.

Is it possible to answer the range request that the video tag in Safari with a partial response while still asking the full file from the server so not to poison the cache with said partial response?

@jeffposnick
Copy link
Contributor

Sorry for the lack of updates. I got a chance to try out a few scenarios today. Here's the current set of recommendations I could offer w.r.t. Workbox interaction, across all browsers. It would be great if folks could sanity check this with their own deployments, and then I could transfer the recommendations over to the Workbox documentation, and/or make some changes to the Workbox runtime (as appropriate).

First, I'd suggest that folks read @jakearchibald's "I discovered a browser bug" as background, as it explains some of what appears to be going on (and it's interesting!).

Second, take a look at the status of the "Range headers correctly preserved" web platform test, which @domenic was good enough to dig up for me. Unfortunately, all browsers are currently marked as FAIL, which means that when a service worker intercepts a no-cors request and attempts to pass it through via the equivalent of event.respondWith(fetch(event.request)), the Range headers will end up being dropped. That makes Safari, in particular, unhappy, but it's not a good thing in general.

Third (and this was something that I didn't realize), <video> playback of even a same-origin URL will result in no-cors request mode. You need to explicitly set crossOrigin="anonymous" to opt-in to cors mode on your <video> tag in order to work around that previously mentioned bug in all browsers that prevents the Range header from being preserved.

Fourth, optionally but to prevent some noise, you should make sure that Workbox doesn't attempt to cache HTTP 206 (partial content) responses received from the network. Calling cache.put() with a HTTP 206 response will fail anyway, so this won't really change behavior, but you'll cut down on logging noise by explicitly configuring Workbox to check for a HTTP 200 code. (The default right now is to check for response.ok, which will be true for any HTTP code in the 2xx range—we might want to change that default in a future Workbox release to look for 200 specifically.)

Fifth, and following from the fourth point, you have to explicitly cache the full video file ahead of time, via either precaching or a call to cache.add(), if you want workbox-range-requests to generate partial responses from you without going to the network. I can't think of any sane way to piece together a series of HTTP 206 partial content responses inside of a service worker into a complete Response body that could then be cached implicitly. I also am concerned that firing off a completely separate request for the entire contents of the video file whenever there's a cache-miss for a partial content request would end up leading to a bad user experience—your bandwidth would be split between both of those concurrent requests.

So... taken all together, here are the important parts:

<!-- In your page: -->
<!-- You currently need to set crossOrigin even for same-origin URLs! -->
<video src="movie.mp4" crossOrigin="anonymous"></video>


// In your service worker:
// It's up to you to either precache or explicitly call cache.add('movie.mp4')
// to populate the cache.
//
// This route will go against the network if there isn't a cache match,
// but it won't populate the cache at runtime.
// If there is a cache match, then it will properly serve partial responses.
workbox.routing.registerRoute(
  /.*\.mp4/,
  workbox.strategies.cacheFirst({
    cacheName: 'your-cache-name-here',
    plugins: [
      new workbox.cacheableResponse.Plugin({statuses: [200]}),
      new workbox.rangeRequests.Plugin(),
    ],
  }),
);

@jeffposnick
Copy link
Contributor

#1804 is a discussion around explicitly checking for response.status === 200 instead of response.ok in our default checks for cacheability. I think we're going to go ahead with that change for v4 (unless there are objections), which would mean not having to explicitly add

new workbox.cacheableResponse.Plugin({statuses: [200]})

to that recipe.

@xcv58
Copy link

xcv58 commented Dec 31, 2018

Thank you very much @jeffposnick !

Could you please provide an example for cache.add()?

@jeffposnick
Copy link
Contributor

@xcv58, it's up to you when you'd want to run this code (in response to the user pressing a "add to cache" button next to a thumbail, in the service worker's install hander, etc.), but it would look something like:

async function addToVideoCache(url) {
  const cache = await caches.open('your-cache-name-here');
  await cache.add(url);
}

And then just make sure to use the same 'your-cache-name-here' value in that code and in your Workbox code.

@jeffposnick
Copy link
Contributor

I've written up a summary at google/WebFundamentals#7050 and we'll use that as the official place to refer folks to when they run into similar issues.

(I wish it were easier, but...)

@freecates
Copy link

Using rangeRequest as a child options fails.

{ ValidationError: child "runtimeCaching" fails because ["runtimeCaching" at position 0 fails because [child "options" fails because ["rangeRequests" is not a supported parameter.]]]

workboxOpts: { runtimeCaching: [ { urlPattern: /.*\.mp4/, handler: 'cacheFirst', options: { cacheableResponse: { statuses: [200] }, rangeRequests: {} } } ] }
Is there a proper way to config?
I'm using workbox 4..1.0
Thanks.

@jeffposnick
Copy link
Contributor

There's only "out of the box" support for a few plugins when using GenerateSW and runtimeCaching:

const pluginsMapping = {
backgroundSync: 'workbox.backgroundSync.Plugin',
broadcastUpdate: 'workbox.broadcastUpdate.Plugin',
expiration: 'workbox.expiration.Plugin',
cacheableResponse: 'workbox.cacheableResponse.Plugin',
};

For more involved runtime caching scenarios, you'll need to switch to either InjectManifest mode and take control over your top-level service worker file, or alternatively, use the importScripts option in GenerateSW mode to pull in additional code to a top-level file that Workbox generates.

ZachGawlik added a commit to ZachGawlik/zachgawlik.com that referenced this issue May 16, 2019
GoogleChrome/workbox#1663
gatsbyjs/gatsby#10151

To make this even worse, the problem is only encountered on the built hosted website rather than locally
ZachGawlik added a commit to ZachGawlik/zachgawlik.com that referenced this issue May 16, 2019
See GoogleChrome/workbox#1663 and gatsbyjs/gatsby#10151 . Despite being same-origin, crossOrigin=anonymous needs to be set for Safari
@jadjoubran
Copy link
Collaborator

In case it's useful for anyone still having issues with this, Safari Tech Preview (96) just shipped with:

Fixed MP4 video element broken with Service Worker (r251594)

Release notes

@PhilJay
Copy link

PhilJay commented Jul 1, 2020

Hey @jeffposnick

I'm still facing the issue of videos not loading from cache in Safari (both macOS & iOS) in my create-react-app (3.4.1) with workbox-build (5.1.3); in Chrome everything is working fine. I am creating my own service worker (workboxBuild) and setting up the (I believe) proper caching rules. I am also using the required RangeRequestsPlugin:

package.json (scripts section)

"scripts": {
    "build-sw": "node ./src/sw-build.js",
    "start": "react-scripts start",
    "build": "GENERATE_SOURCEMAP=false react-scripts build && npm run build-sw",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  }

sw-build.js

const workboxBuild = require('workbox-build');

const buildSW = () => {
   workboxBuild
    .injectManifest({
      swSrc: 'src/sw-template.js', // sw template file
      swDest: 'build/service-worker.js', // same name as sw used by serviceWorker.js
      globDirectory: 'build',
      maximumFileSizeToCacheInBytes: 8000000,
      globPatterns: ['**\/*.{json,js,css,html,png,jpg,mp4}',], // precaching 
    })
    .then(({ count, size, warnings }) => {
      
      warnings.forEach(console.warn);
      console.log(`${count} files will be precached, totaling ${size} bytes.`);
    })
    .catch(console.error);
};
buildSW();

sw-template.js

if (typeof importScripts === 'function') {
  importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.3/workbox-sw.js');
 
  if (workbox) {
    console.log('Workbox is loaded');
    workbox.core.skipWaiting();

    /* injection point for manifest files.  */
    workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);

    workbox.routing.registerRoute(
      ({ url }) => url.pathname.endsWith('.mp4'),
      new workbox.strategies.CacheFirst({
        plugins: [
          new workbox.rangeRequests.RangeRequestsPlugin(),
        ],
      }),
      'GET'
    );
  }
}

serviceWorker.js (only excerpt, unchanged created by create-react-app)

// ...
window.addEventListener('load', () => {
      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
    // ...
}
// ...

My serviceWorker.js that was auto generated by create-react-app is unchanged.

Basically what happens is that my site initially works fine and videos are playing (because loaded from network). As soon as I refresh the page or visit a page that has already been loaded into cache the video no longer appears (blank screen).

When debugging, this is the response that is received from service worker:

Screenshot 2020-07-01 at 11 07 14

Is there anything you can see that I am doing wrong?

@jeffposnick
Copy link
Contributor

Since your call to precacheAndRoute() happens before your call to registerRoute(), the precaching route ends up "winning", and your custom response logic using workbox-range-requests won't be fired. You can swap things around like:

importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.3/workbox-sw.js');

workbox.core.skipWaiting();

// Your .mp4 file will likely have a WB_REVISION parameter appended to it
// when stored in the precache. This plugin will normalize the cach key:
const cacheKeyWillBeUsed = ({request}) => workbox.precaching.getCacheKeyForURL(request.url);

workbox.routing.registerRoute(
  ({url}) => url.pathname.endsWith('.mp4'),
  new workbox.strategies.CacheOnly({
    cacheName: workbox.core.cacheNames.precache,
    plugins: [
      {cacheKeyWillBeUsed},
      new workbox.rangeRequests.RangeRequestsPlugin(),
    ],
  })
);

workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);

Alternatively, instead of using workbox-precaching with your .mp4 files, you can just rely on runtime routing to handle serving them, potentially with a custom install handler that populates the cache ahead of time. In that case, the order of routes doesn't matter.

importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.3/workbox-sw.js');

workbox.core.skipWaiting();

const cacheName = 'videos';
self.addEventListener('install', (event) => {
  const cacheVideos = async () => {
    const cache = await caches.open(cacheName);
    await cache.add(videoURL);
  };
  event.waitUntil(cacheVideos());
});

workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);

workbox.routing.registerRoute(
  ({url}) => url.pathname.endsWith('.mp4'),
  new workbox.strategies.CacheFirst({
    cacheName,
    plugins: [
      new workbox.rangeRequests.RangeRequestsPlugin(),
    ],
  })
);

If you go with this second approach, it's up to your to properly version your .mp4 files and retrigger the service worker's install handler if you make a change to your videos that you want clients to pick up.

@PhilJay
Copy link

PhilJay commented Jul 1, 2020

Thank you for the quick response. I've tried your first approach, literally copy pasting it to test it & I've added logs to confirm that the code is actually run, which it is. I'm also getting the default success message from the service worker "Content is cached for offline use." - nevertheless, it still does not work. The even though the strategy now is CacheOnly, when clearing all Safari cache and re-visiting the website, all .mp4 files are loaded from network first (and playing/working) and again, upon another refresh loaded from service worker and no longer displaying. When debugging Safari Network I get the same output as the screenshot shown above.

Status code: 200
Source: Service Worker
Range: bytes=0-1

I believe that the bytes=0-1 is the issue, but isn't that what the RangeRequestsPlugin should handle? Anything else you can think of that might be the issue here? Really puzzled here.

@jeffposnick
Copy link
Contributor

Yes, the Range: bytes=0-1 bit should be handled by workbox-range-requests.

Are you able to share the URL for a version of your site with the above service worker deployed? I might be able to help more if I could debug something live.

@jeffposnick jeffposnick reopened this Jul 7, 2020
@PhilJay
Copy link

PhilJay commented Jul 8, 2020

After some more input from @jeffposnick I finally got it working - it appears the devil was in the detail. I had to change my service worker to this:

console.log('Starting Workbox...');
 
if (typeof importScripts === 'function') {
  importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.3/workbox-sw.js');

  if (workbox) {
    console.log('Yay - Workbox is loaded');
 
    workbox.core.skipWaiting();
 
    // Your .mp4 file will likely have a WB_REVISION parameter appended to it
    // when stored in the precache. This plugin will normalize the cach key:
    const cacheKeyWillBeUsed = ({ request }) => {
      const url = workbox.precaching.getCacheKeyForURL(request.url);
      return new Request(url, { headers: request.headers });
    };
 
    workbox.routing.registerRoute(
      ({ url }) => url.pathname.endsWith('.mp4'),
      new workbox.strategies.CacheFirst({
        cacheName: workbox.core.cacheNames.precache,
        plugins: [
          { cacheKeyWillBeUsed },
          new workbox.rangeRequests.RangeRequestsPlugin(),
        ],
      })
    );
 
    workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);
 
    console.log('Done');
 
  } else {
    console.log('Workbox could not be loaded. No Offline support');
  }
}

The important part is the new Request(url, { headers: request.headers });part. @jeffposnick explained that without creating a new request object and passing the original headers workbox does not know about the range header that was originally present and therefore cannot handle the request correctly.

Also important: workbox.precaching.precacheAndRoute(self.__WB_MANIFEST); must come after registering the route with the range requests plugin, otherwise caching also did not work for me.

AnakinYuen added a commit to AnakinYuen/personal-website that referenced this issue Aug 23, 2020
nukash added a commit to nukash/beatbox that referenced this issue Oct 1, 2020
precaching plugin
see: GoogleChrome/workbox#1663
@dmnsgn
Copy link

dmnsgn commented Nov 18, 2020

Also important: workbox.precaching.precacheAndRoute(self.__WB_MANIFEST); must come after registering the route with the range requests plugin, otherwise caching also did not work for me.

Hi, @jeffposnick is there anyway to tell the workbox-webpack-plugin to registerRoute before the call to precacheAndRoute ?

Adding @PhilJay rules to the runtimeCaching array will result in:

e.skipWaiting(), e.clientsClaim(), e.precacheAndRoute([ /* ... */ ]), e.registerRoute(/* ... */)

which prevents me to test the solution.

@jeffposnick
Copy link
Contributor

No, that's not possible using GenerateSW mode. You can do that using InjectManifest, though.

Something like https://github.com/cra-template/pwa/blob/master/packages/cra-template-pwa/template/src/service-worker.js is a good starting point that could be customized/re-ordered if you go that route.

@dmnsgn
Copy link

dmnsgn commented Nov 19, 2020

Thank you for the pointers to InjectManifest. It's a bit more manual work that I have anticipated so as a temporary workaround I'll just exclude mp4 (might help someone else landing here). They are loaded with a file-loader and the following webpack name option: "[name].[ext]?[hash]" so regex needs to be as follow:

exclude: [/\.map$/, /^manifest.*\.js$/, /\.mp4(?::(\d+))?/],

@Anders-Lunde
Copy link

Anders-Lunde commented Feb 3, 2021

Struggled for 2 days to make this work, but finally got it. My problem was the order of the methods, and the name of the cache. Matchoptions should maybe be turned off? I'm not sure what this does, but I think it enables the rangerequest for all resources. In my case that is good since it includes mp3 as well then.

@jeffposnick Maybe you should add the below to the docs (https://developers.google.com/web/tools/workbox/guides/advanced-recipes?authuser=0#cached-av). This really confused me: I left the "your-cache-name-here" in place since I'm so new to service workers and even caches.
cacheName: "your-cache-name-here", // for example workbox.core.cacheNames.precache to look in the precache if that's where your videos are cached

Also crossorigin="anonymous" does not seem to be necessary (in recent Safari versions at least: https://developers.google.com/web/tools/workbox/guides/advanced-recipes?authuser=0#cached-av)

This is for workbox 4.3.1 in a Vue2 app.

#service-worker.js

/* Use client claim to handle SW updates */
workbox.core.clientsClaim();

workbox.core.skipWaiting();
workbox.core.setCacheNameDetails({ prefix: "oslo_spell_apper" });

/* Cache files from _precacheManifest (generated by InjectManifest webpack plugin) */
self.__precacheManifest = [].concat(self.__precacheManifest || []);



/*
//https://developers.google.com/web/tools/workbox/guides/advanced-recipes?authuser=0#cached-av
//Not using imports: using namespaced methods instead
import { registerRoute } from "workbox-routing";
import { CacheFirst } from "workbox-strategies";
import { CacheableResponsePlugin } from "workbox-cacheable-response";
import { RangeRequestsPlugin } from "workbox-range-requests";
*/
workbox.routing.registerRoute(
  ({ url }) => url.pathname.includes(".mp4"), //Changed from .endsWith('.mp4') to "includes"
  new workbox.strategies.CacheFirst({
    cacheName: workbox.core.cacheNames.precache, //"your-cache-name-here",
    plugins: [
      new workbox.cacheableResponse.Plugin({ statuses: [200] }),
      new workbox.rangeRequests.Plugin(),
    ],
    //matchOptions: https://stackoverflow.com/questions/62022640/service-worker-with-range-requests-plugin-cannot-fetch-mp3-files-when-offline
    matchOptions: {
      ignoreSearch: true,
      ignoreVary: true,
    },
  })
);

/**
 * The workboxSW.precacheAndRoute() method efficiently caches and responds to
 * requests for URLs in the manifest.
 * See https://goo.gl/S9QRab
 */
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
#vue.config.js
  pwa: {
    themeColor: "#FF0000",
    msTileColor: "#FF0000",
    appleMobileWebAppCapable: "yes",
    appleMobileWebAppStatusBarStyle: "black-translucent",
    manifestOptions: { display: "fullscreen" },
    // configure the workbox plugin
    workboxPluginMode: "InjectManifest",
    workboxOptions: {
      swSrc: "src/service-worker.js",
    },
  },

@mavr1982
Copy link

is there any news on this ?
I have issues with videos (mp4, webm, ogv) on most of the browsers but Chrome.

@artola
Copy link

artola commented Apr 15, 2021

Someone can help me with GenerateSW config for video on iOS?

This is my config and the video is working in all browsers, except iOS Safari (without SW the video plays well, or just excluding with exclude: [/\.mp4$/].

        new WorkboxPlugin.GenerateSW({
          swDest: 'sw.js',
          cleanupOutdatedCaches: true,
          inlineWorkboxRuntime: true,
          clientsClaim: true,
          skipWaiting: true,
        }),

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