0

I am trying to build a service worker that retrieves a video from cache if available and fetches from online if it is not available. Here is my code for that:

self.addEventListener("fetch", function (event) {
  if (event.request.headers.get("range")) {
    caches.match(event.request.url).then(function (res) {
      if (!res) {
        log.debug(
          `Range request NOT found in cache for ${event.request.url}, activating fetch...`
        );
        return fetch(event.request);
      }
      returnRangeRequest(event);
    });
  } else {
    event.respondWith(
      caches.match(event.request).then((response) => {
        return response || fetch(event.request);
      })
    );
  }
});

function returnRangeRequest(event) {
  var rangeHeader = event.request.headers.get("range");
  var rangeMatch = rangeHeader.match(/^bytes\=(\d+)\-(\d+)?/);
  var pos = Number(rangeMatch[1]);
  var pos2 = rangeMatch[2];
  if (pos2) {
    pos2 = Number(pos2);
  }
  event.respondWith(
    caches
      .match(event.request.url)
      .then(function (res) {
        return res.arrayBuffer();
      })
      .then(function (ab) {
        let responseHeaders = {
          status: 206,
          statusText: "Partial Content",
          headers: [
            ["Content-Type", "video/mp4"],
            [
              "Content-Range",
              "bytes " +
                pos +
                "-" +
                (pos2 || ab.byteLength - 1) +
                "/" +
                ab.byteLength,
            ],
          ],
        };

        var abSliced = {};
        if (pos2 > 0) {
          abSliced = ab.slice(pos, pos2 + 1);
        } else {
          abSliced = ab.slice(pos);
        }

        log.debug(
          `Returning range request response`
        );

        return new Response(abSliced, responseHeaders);
      })
      .catch(function (err) {
        log.error(err);
      })
  );
}

When I am online and I try to play the video, it works fine and it prints the debug line Range request NOT found in cache for https://example.com/vid.mp4, activating fetch...

When I have cached the video url using cache.add("https://example.com/vid.mp4");, and I try to play it, the video plays fine.

The problem arises when I turn off the Wifi on the iPad. When I try to play the video after turning of wifi, the video stays at 0:00 with a total length of 0:00.

Some of my findings:

  1. When I have wifi on and I have the video cached, there are two requests made with bytes bytes=0-1 and then bytes=0-4444000.
  2. When I have wifi off, the request for bytes=0-1 is made, but it stops with that.

Where am I going wrong?

Shri
  • 709
  • 1
  • 7
  • 23

1 Answers1

0

Safari appears to have a very strict approach to range requests and similar issues to your problem are sometimes seen with regular online web servers.

In particular, Safari expects to see a '206' response when it sends a request with a byte range. If the server responds with a '200' request it appears Safari cannot handle this. Some other browsers seem to be ok with this - for example Chrome.

Apple provide some info on hoe to check for this:

If you are not sure whether your media server supports byte-range requests, you can open the Terminal application in OS X and use the curl command-line tool to download a short segment from a file on the server:

curl --range 0-99 http://example.com/test.mov -o /dev/null

If the tool reports that it downloaded 100 bytes, the media server correctly handled the byte-range request. If it downloads the entire file, you may need to update the media server. For more information on curl, see OS X Man Pages.

See this answer for more detail and background: https://stackoverflow.com/a/32998689/334402

Mick
  • 24,231
  • 1
  • 54
  • 120