Kako napraviti <audio> koji radi u Safariju?

Kako napraviti <audio> koji radi u Safariju?


Da biste napravili audio HTML5 tag, koji može da pušta zapise u Chrome pretraživaču na primer dovoljno je da napišete.

<audio src=’/putanja/do/fajla.wav’></audio>

Problem sa ovakvim remote učitavanjem fajla je što ne možete da prikažete odmah koliko vaš audio zapis traje, kao i da preskočimo negde na sredinu zapisa. Postoji bug koji nas sprečava da to uradimo, ali postoji način da rešimo taj problem.

const audioWithDurationLoaded = new Promise<HTMLAudioElement>((resolve, reject) => {
    audio.addEventListener("loadedmetadata", () => {
      // Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=642012
      if (audio.duration === Infinity) {
        audio.currentTime = Number.MAX_SAFE_INTEGER;
        audio.ontimeupdate = () => {
          audio.ontimeupdate = null;
          resolve(audio);
          audio.currentTime = 0;
        };
      }
      // Normal behavior
      else {
        resolve(audio);
      }
    });
    audio.onerror = (event) => reject(event);
  });

Audio u gornjem isečku predstavlja referencu na HTML5 audio element koji smo kreirali. Audio element pravimo dinamički: const audio = document.createElement("audio");

Ovim ćemo rešiti problem u ostalim pretraživačima, međutim, Safari i dalje neće puštati audio zapis koji se ne nalazi na istom serveru kao vaša aplikacija. Lokalni Blob fajl koji biste kreirali u vašoj klijentskoj aplikaciji biste mogli da pustite, ali remote fajlovi bi ostali neučitani zbog načina na koji Safari učitava fajlove i pravi pozive na server.

Postoji rešenje za ovaj problem, a to je da dinamički napravimo i <source> element kojem ćemo proslediti url do našeg audio zapisa. Od izuzetne je važnost da odredite tip zapisa PRE nego što postavite url.

const audio = document.createElement("audio");
const source = document.createElement("source");

source.type = "audio/wav";
audio.appendChild(source);

Da bi smo ostavili mogućnost da naš player pušta i zapise sa drugog servera, kao i audio zapise koje pravimo u našoj aplikaciji, source moramo setovati na dva načina:

source.src =
    typeof blob === "string" || blob instanceof String
      ? (blob as string)
      : window.URL.createObjectURL(blob);

Na kraju nam ostaje samo da vratimo naš novi audio element koji funkcioniše u svim pretraživačima.

Ceo kod:

export const getBlobPlayer = async (
  blob: String
): Promise<HTMLAudioElement> => {
  const audio = document.createElement("audio");
  const source = document.createElement("source");

  source.type = "audio/wav";
  audio.appendChild(source);

  const audioWithDurationLoaded = new Promise<HTMLAudioElement>((resolve, reject) => {
    audio.addEventListener("loadedmetadata", () => {
      // Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=642012
      if (audio.duration === Infinity) {
        audio.currentTime = Number.MAX_SAFE_INTEGER;
        audio.ontimeupdate = () => {
          audio.ontimeupdate = null;
          resolve(audio);
          audio.currentTime = 0;
        };
      }
      // Normal behavior
      else {
        resolve(audio);
      }
    });
    audio.onerror = (event) => reject(event);
  });

  source.src =
    typeof blob === "string" || blob instanceof String
      ? (blob as string)
      : window.URL.createObjectURL(blob);

  return audioWithDurationLoaded;
}

Ako imate predlog kako možemo optimizovati kod, pišite u komentarima.