Bin
2025-12-17 1d710f844b65d9bfdf986a71a3b924cd70598a41
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
interface PatchedHTMLMediaElement extends HTMLMediaElement {
  _playPausePatched?: boolean;
}
 
type PatchableMethod = "play" | "pause";
const PATCHABLE_METHODS: PatchableMethod[] = ["play", "pause"];
 
/*
 * This patch prevents unhandled promise rejections in development mode
 * it will help in development with alerts that interrupt the flow
 * it will help in production with errors in the console
 * there is no option for just queuing the play/pause methods as it would cause issues (@see "Seeking is synced between audio, video when interacting with audio interface" test scenario)
 */
export function patchPlayPauseMethods<T extends HTMLMediaElement>(element: T): T & PatchedHTMLMediaElement {
  if (!(element instanceof HTMLMediaElement)) {
    throw new TypeError("patchPlayPauseMethods expects <audio> | <video>");
  }
 
  const patchedElement = element as T & PatchedHTMLMediaElement;
 
  if (patchedElement._playPausePatched) {
    return patchedElement;
  }
 
  const wrapMethod = <M extends PatchableMethod>(methodName: M) => {
    const originalMethod = patchedElement[methodName].bind(patchedElement);
    patchedElement[methodName] = (() => {
      let res = originalMethod();
      if (res instanceof Promise) {
        res = res.catch(() => {}); // catch any errors to avoid unhandled promise rejections
      }
      return res as ReturnType<(typeof patchedElement)[M]>;
    }) as unknown as (typeof patchedElement)[M];
  };
 
  PATCHABLE_METHODS.forEach(wrapMethod);
  patchedElement._playPausePatched = true;
 
  return patchedElement;
}