2

I'm having some trouble loading an audio file in WAV format returned from the backend as a Blob in Angular.

I have in my services the following:

getFileBlob(uniformName: string, path: string) {
    const currentPath = path.replace(/\\/g, '/');
    return this.http.get(`${environment.api_url}/content/v1.4/${currentPath}/${uniformName}/_blob`, { responseType: 'blob' });
  }

Where in my Angular component, I have the following:

this.dService.getFileBlob().subscribe((response: Blob) => {
        const url = URL.createObjectURL(response);
        this.type.set(response.type);
        this.src.set(url);
      });

I have the correct mapping in my audio source tag: [screenshot from the inspect1

In my template, I have:

<audio #media [vgMedia]="$any(media)" id="myAudio" preload="{{ preload }}" crossorigin>
      <source src="{{ src() }}" type="{{ type() }}">
      <track
      src="assets/data/Downloaded_transcriptVVV.vtt"
      kind="metadata"
      label="Cue Points"
      default
      #metadataTrack
    />
  </audio>

When I paste the blob in the browser, I get the file downloaded - (blob:http://localhost:4200/7071f3e7-9738-4ad7-b08f-67ddb5852c53) The problem is that the native HTML audio does not play at all. One important note is that I'm using Angular in a zoneless mode. If, however, I put a direct relative URL in the place of the src, it works:

`this.src.set('/assets/audio/E_2024-10-07_H_100748_060.wav')

I would highly appreciate anyone's advice on what I'm doing wrong.

7
  • 1
    Can you try doing audioElement.load() after it has rerendered? Commented Nov 13, 2024 at 16:15
  • 1
    Not sure if it does help, but try once to change your binding syntax as follows: <source [src]="src()" [type]="type()">. Commented Nov 13, 2024 at 16:46
  • 1
    Also check the answer on a related question here, where it is suggested to change the data url to a binary and attach it as a source; maybe this will help you... Commented Nov 13, 2024 at 16:51
  • since it looks like you already have a file on the server, you should use that. Right now it's deep-linkable, so you might want to store it somewhere where that can't be done. Setup an endpoint that takes something like a UID as a parameter and returns a file. The src is then set to the endpoint. It's more secure that way so you can authenticate/authorize the request. It's also more efficient as you won't have to make a copy of the file locally. Commented Nov 13, 2024 at 18:46
  • There is an authentication done via interceptor. The endpoint mentioned returns a file. An Endpoint which return a file url however I don't have. It's external service Commented Nov 13, 2024 at 19:24

2 Answers 2

5

The issue is that the audio player is loaded without a source. The player doesn't listen for new or changed sources and is therefore unaware of the added audio. The fix is to use audio.load(), which forces the browser to recheck the sources and notice the now added audio.

The example below demonstrates that the browser doesn't play the audio if the src is set dynamically.

const source = document.querySelector('source');
const audio = document.querySelector('audio');

fetch('https://upload.wikimedia.org/wikipedia/commons/3/3d/Alcal%C3%A1_de_Henares_%28RPS_13-04-2024%29_canto_de_ruise%C3%B1or_%28Luscinia_megarhynchos%29_en_el_Soto_del_Henares.wav')
  .then(res => res.blob())
  .then(blob => {
    const url = URL.createObjectURL(blob);

    source.type = blob.type;
    source.src = url;
  })
<audio id="myAudio" preload="auto" crossorigin controls>
    <source />
</audio>

The fix is to use audio.load() to reset the player and force it to recheck the sources.

const source = document.querySelector('source');
const audio = document.querySelector('audio');

fetch('https://upload.wikimedia.org/wikipedia/commons/3/3d/Alcal%C3%A1_de_Henares_%28RPS_13-04-2024%29_canto_de_ruise%C3%B1or_%28Luscinia_megarhynchos%29_en_el_Soto_del_Henares.wav')
  .then(res => res.blob())
  .then(blob => {
    const url = URL.createObjectURL(blob);

    source.type = blob.type;
    source.src = url;

    // add this line
    audio.load();
  })
<audio id="myAudio" preload="auto" crossorigin controls>
    <source />
</audio>

Sign up to request clarification or add additional context in comments.

1 Comment

Great answer, learned something new...
0

Wrap src() in an if clause

When reading the answer from @lusc I realized that this would probably also work and might be even more correct since it won't render the audio link without a valid src:

@if (src() as src) { 
  <audio #media [vgMedia]="$any(media)" id="myAudio" [preload]="preload" crossorigin>
    <source [src]="src" [type]="type()">
    <track src="assets/data/Downloaded_transcriptVVV.vtt" kind="metadata" label="Cue Points" default #metadataTrack/> 
  </audio>
}

this will render the audio element only when you have a src hence there will be no need to call load() afterwards...

1 Comment

<ng-container *ngIf="src() as src"> <audio #media [vgMedia]="$any(media)" id="myAudio" preload="{{ preload }}" crossorigin> <source [src]="src" [type]="type()"> <track src="assets/data/Downloaded_transcriptVVV.vtt" kind="metadata" label="Cue Points" /> </audio> </ng-container> This somewhat doesn't work

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.