Reputation: 51
I have an angular service worker and I have cached sounds (mp3 files) so that they can be played offline. Here is the ngsw-config-json
:
{
"index": "/index.html",
"assetGroups": [{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js",
"/*.svg",
"/*.eot",
"/*.woff",
"/*.woff2",
"/*.tff"
]
}
}, {
"name": "assets",
"installMode": "prefetch",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/sounds/character1/**",
"/assets/sounds/character2/**"
]
}
}]
}
In my web app, the sounds play like this:
let audioLink = `/assets/sounds/${character}/${soundURL}`;
let player = new Audio(audioLink);
player.play()
The website works offline. The HTML, CSS, JS, JSON files are all being cached. But the sounds don't play (when I try playing, the give an error I showed above).
In the Chrome Developer Tools > Application > Cache, I can see that a few of the sounds are there (the paths), but not all. But none of them are working offline.
Sometimes these errors arise:
GET "path/to/file" net::ERR_ABORTED 504 (Gateway Timeout)
Uncaught (in promise) DOMException
Failed to load resource: net::ERR_INTERNET_DISCONNECTED
It sometimes also says there is a problem with the manifest.json
, but there is nothing wrong as it shows up in the manifest section of the developer tools when online.
Is there any fix to this problem? It's not a Chrome bug, because I am seeing the same behavior in Safari and Firefox too. I know that there is a bug in a new chrome version, and because of this bug, the favicon.ico
does not show up offline
Not sure if this will help, but here is the link to the hosted web application. If you see the network tab in the Chrome Developer Tools, you can see that the mp3 files are 'downloading', but they don't play offline.
After generating the website for production (ng build --prod --base-href="/"
), I looked into the ngsw.json
and this is what I saw (some lines removed):
{
"name": "assets",
"installMode": "prefetch",
"updateMode": "prefetch",
"urls": [
"/assets/sounds/character1/sound one.mp3",
"/assets/sounds/character1/sound two.mp3",
"/assets/sounds/character2/sound three.mp3"
"/assets/sounds/character2/sound four.mp3"
],
"patterns": []
}
Here is the full generated ngsw.json
. Everything has been set to precache rather than cache lazily.
And all these asset URLs are also in the "hashTable"
object. I also see them being downloaded in the chrome developer tools (developer tools > network), but I only see some of them in the application cache section (maybe the first 20-30 out of 150 in total).
What am I doing wrong here? Could this possible be an Angular 7 bug? I've seen various other StackOverflow and Reddit questions about a similar issue.
Something I tried was changing
let audioLink = `/assets/sounds/${character}/${soundURL}`;
to
let audioLink = `assets/sounds/${character}/${soundURL}`;
(removed slash at the start), but that didn't make a difference.
I am completely sure that all the sound file paths are correct because there are abolutely no errors when the site is running online.
Apart from the above, I tried manually changing all the urls and inserting %20
where there was supposed to be a space. For example, changing "/assets/sounds/character1/sound one.mp3"
to "/assets/sounds/character1/sound%20one.mp3"
, but that did not work either.
One thing I've noticed is that if you visit the website for the first time, and wait for all the sounds to download, then immateriality after, close your internet connection and try out the site, all the sounds work. But only for some time usually around 10-15 minutes then they stop working. I have tested on Chrome, Chrome Android, Firefox, Safari, and IOS Safari.
EDIT: As suggested by someone, I tried changing
{
"name": "assets",
"installMode": "prefetch",
"updateMode": "prefetch",
"urls": [
"/assets/sounds/character1/sound one.mp3",
"/assets/sounds/character1/sound two.mp3",
"/assets/sounds/character2/sound three.mp3"
"/assets/sounds/character2/sound four.mp3"
],
"patterns": []
}
to
{
"name": "assets",
"installMode": "prefetch",
"updateMode": "prefetch",
"urls": [
"/assets/sounds/**"
],
"patterns": []
}
but the same problem persists: the sounds are cached offline for maybe 10-15 minutes, after which they do not play offline. I was only able to test this in Chrome and Chrome Android.
Upvotes: 2
Views: 4070
Reputation: 51
I found an answer to my question with the help of @codeninja.
Firstly, the audioLink
has to be a direct link to the sound. I'm hosting the site on Github, so the link looks something like this:
let url = `sounds/path_to_sound.mp3`
let audioLink = `https://raw.githubusercontent.com/username/repo_name/master/assets/sounds/${url}`;
Secondly, we cannot just use the sound.play()
with Audio()
method, we must retrieve the audio using HttpClient
. I am not sure why exactly, but codeninja suggested that the way ngsw
caches files is incompatible with the Audio
class. I'm not sure if this is true, but I found that when offline, I had to use the HttpClient
to retrieve the audio blob (demonstrated below). If not, you get a response not Ok
error. Even if I gave the direct link https://raw.githubusercontent.com/username/repo_name/master/assets/sounds/direct.mp3
, it would throw the same error.
All in all, the code looks like this:
First there is a getSound()
function:
getSound(url) {
return this.http.get(url, {responseType: 'blob'});
}
Then in another function (playSound(url)
), we have this:
playSound(url) {
let audioLink = `https://raw.githubusercontent.com/username/repo_name/master/assets/sounds/${url}`;
this.getSound(audioLink)
.subscribe(data => {
console.log(data);
// data is an audio blob
let url = URL.createObjectURL(data);
let sound = document.createElement('audio');
sound.src = url;
sound.play();
})
}
Additionally, I also had to change my ngsw-config.json
. I only changed the "assets" part so I'll show that:
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
],
"urls": [
"https://raw.githubusercontent.com/username/repo_name/master/assets/sounds/**"
]
}
}
I was also unable to get it to work with "prefetch". Although the cached sounds now play offline, they will only play if they have been played before, as it is set to "lazy".
I tested this solution on Chrome, Chrome Android, and Firefox, and it is working fine.
Upvotes: 3
Reputation: 9815
I've tried making a demo to isolate the error and seems to work. Have noticed some weird url in the app cache:
Not sure that's causing the issue. You've mentioned that the sound works after you make the device offline, I would guess that is the browser caching not the offline cache. When I tried refreshing the page the sound never works. My guess is the configuration with ngsw or the way ngsw caches the files isn't compatible with the Audio class.
You could test if it works by passing a blob into Audio by fetching it first using @angular/common/http
. I'm thinking this as the audio files seem to be in a different cache directory (see screenshot). The css/js files seem to be stored differently for some reason and the audio files only accessible (offline) to angular.
Posting as response as it's too long for a comment
Upvotes: 3