Skip to content
On this page

useMediaControls

Category
Last Changed
3 months ago

Reactive media controls for both audio and video elements

Demo

00:00
00:00 / 00:00
Off
Loop
2x
1x
currentTime: 0
duration: 0
waiting: false
seeking: false
ended: false
stalled: false
buffered: []
playing: false
rate: 1
volume: 1
muted: false
tracks: []
selectedTrack: -1
isPictureInPicture: false

Usage

Basic Usage

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useMediaControls } from '@vueuse/core'

const video = ref()
const { playing, currentTime, duration, volume } = useMediaControls(video, { 
  src: 'video.mp4',
})

// Change initial media properties
onMounted(() => {
  volume.value = 0.5
  currentTime.value = 60
})
</script>

<template>
  <video ref="video" />
  <button @click="playing = !playing">Play / Pause</button>
  <span>{{ currentTime }} / {{ duration }}</span>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useMediaControls } from '@vueuse/core'

const video = ref()
const { playing, currentTime, duration, volume } = useMediaControls(video, { 
  src: 'video.mp4',
})

// Change initial media properties
onMounted(() => {
  volume.value = 0.5
  currentTime.value = 60
})
</script>

<template>
  <video ref="video" />
  <button @click="playing = !playing">Play / Pause</button>
  <span>{{ currentTime }} / {{ duration }}</span>
</template>

Proving Captions, Subtitles, etc...

You can provide captions, subtitles, etc in the tracks options of the useMediaControlsfunction. The function will return an array of tracks along with two functions for controlling them, enableTrack, disableTrack, and selectedTrack. Using these you can manage the currently selected track. selectedTrack will be -1 if there is no selected track.

<script setup lang="ts">
const { tracks, enableTrack } = useMediaControls(video, { 
  src: 'video.mp4',
  tracks: [
    {
      default: true,
      src: './subtitles.vtt',
      kind: 'subtitles',
      label: 'English',
      srcLang: 'en',
    },
  ]
})
</script>

<template>
  <video ref="video" />
  <button v-for="track in tracks" :key="track.id" @click="enableTrack(track)">
    {{ track.label }}
  </button>
</template>
<script setup lang="ts">
const { tracks, enableTrack } = useMediaControls(video, { 
  src: 'video.mp4',
  tracks: [
    {
      default: true,
      src: './subtitles.vtt',
      kind: 'subtitles',
      label: 'English',
      srcLang: 'en',
    },
  ]
})
</script>

<template>
  <video ref="video" />
  <button v-for="track in tracks" :key="track.id" @click="enableTrack(track)">
    {{ track.label }}
  </button>
</template>

Type Declarations

Show Type Declarations
/**
 * Many of the jsdoc definitions here are modified version of the
 * documentation from MDN(https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement)
 */
export interface UseMediaSource {
  /**
   * The source url for the media
   */
  src: string
  /**
   * The media codec type
   */
  type?: string
}
export interface UseMediaTextTrackSource {
  /**
   * Indicates that the track should be enabled unless the user's preferences indicate
   * that another track is more appropriate
   */
  default?: boolean
  /**
   * How the text track is meant to be used. If omitted the default kind is subtitles.
   */
  kind: TextTrackKind
  /**
   * A user-readable title of the text track which is used by the browser
   * when listing available text tracks.
   */
  label: string
  /**
   * Address of the track (.vtt file). Must be a valid URL. This attribute
   * must be specified and its URL value must have the same origin as the document
   */
  src: string
  /**
   * Language of the track text data. It must be a valid BCP 47 language tag.
   * If the kind attribute is set to subtitles, then srclang must be defined.
   */
  srcLang: string
}
interface UseMediaControlsOptions extends ConfigurableDocument {
  /**
   * The source for the media, may either be a string, a `UseMediaSource` object, or a list
   * of `UseMediaSource` objects.
   */
  src?: MaybeRef<string | UseMediaSource | UseMediaSource[]>
  /**
   * A list of text tracks for the media
   */
  tracks?: MaybeRef<UseMediaTextTrackSource[]>
}
export interface UseMediaTextTrack {
  /**
   * The index of the text track
   */
  id: number
  /**
   * The text track label
   */
  label: string
  /**
   * Language of the track text data. It must be a valid BCP 47 language tag.
   * If the kind attribute is set to subtitles, then srclang must be defined.
   */
  language: string
  /**
   * Specifies the display mode of the text track, either `disabled`,
   * `hidden`, or `showing`
   */
  mode: TextTrackMode
  /**
   * How the text track is meant to be used. If omitted the default kind is subtitles.
   */
  kind: TextTrackKind
  /**
   * Indicates the track's in-band metadata track dispatch type.
   */
  inBandMetadataTrackDispatchType: string
  /**
   * A list of text track cues
   */
  cues: TextTrackCueList | null
  /**
   * A list of active text track cues
   */
  activeCues: TextTrackCueList | null
}
export declare function useMediaControls(
  target: MaybeRef<HTMLMediaElement | null | undefined>,
  options?: UseMediaControlsOptions
): {
  currentTime: Ref<number>
  duration: Ref<number>
  waiting: Ref<boolean>
  seeking: Ref<boolean>
  ended: Ref<boolean>
  stalled: Ref<boolean>
  buffered: Ref<[number, number][]>
  playing: Ref<boolean>
  rate: Ref<number>
  volume: Ref<number>
  muted: Ref<boolean>
  tracks: Ref<
    {
      id: number
      label: string
      language: string
      mode: TextTrackMode
      kind: TextTrackKind
      inBandMetadataTrackDispatchType: string
      cues: {
        [x: number]: {
          endTime: number
          id: string
          onenter: ((this: TextTrackCue, ev: Event) => any) | null
          onexit: ((this: TextTrackCue, ev: Event) => any) | null
          pauseOnExit: boolean
          startTime: number
          readonly track: {
            readonly activeCues: any | null
            readonly cues: any | null
            readonly id: string
            readonly inBandMetadataTrackDispatchType: string
            readonly kind: TextTrackKind
            readonly label: string
            readonly language: string
            mode: TextTrackMode
            oncuechange: ((this: TextTrack, ev: Event) => any) | null
            addCue: (cue: TextTrackCue) => void
            removeCue: (cue: TextTrackCue) => void
            addEventListener: {
              <K extends "cuechange">(
                type: K,
                listener: (this: TextTrack, ev: TextTrackEventMap[K]) => any,
                options?: boolean | AddEventListenerOptions | undefined
              ): void
              (
                type: string,
                listener: EventListenerOrEventListenerObject,
                options?: boolean | AddEventListenerOptions | undefined
              ): void
            }
            removeEventListener: {
              <K_1 extends "cuechange">(
                type: K_1,
                listener: (this: TextTrack, ev: TextTrackEventMap[K_1]) => any,
                options?: boolean | EventListenerOptions | undefined
              ): void
              (
                type: string,
                listener: EventListenerOrEventListenerObject,
                options?: boolean | EventListenerOptions | undefined
              ): void
            }
            dispatchEvent: (event: Event) => boolean
          } | null
          addEventListener: {
            <K_2 extends keyof TextTrackCueEventMap>(
              type: K_2,
              listener: (
                this: TextTrackCue,
                ev: TextTrackCueEventMap[K_2]
              ) => any,
              options?: boolean | AddEventListenerOptions | undefined
            ): void
            (
              type: string,
              listener: EventListenerOrEventListenerObject,
              options?: boolean | AddEventListenerOptions | undefined
            ): void
          }
          removeEventListener: {
            <K_3 extends keyof TextTrackCueEventMap>(
              type: K_3,
              listener: (
                this: TextTrackCue,
                ev: TextTrackCueEventMap[K_3]
              ) => any,
              options?: boolean | EventListenerOptions | undefined
            ): void
            (
              type: string,
              listener: EventListenerOrEventListenerObject,
              options?: boolean | EventListenerOptions | undefined
            ): void
          }
          dispatchEvent: (event: Event) => boolean
        }
        readonly length: number
        getCueById: (id: string) => TextTrackCue | null
        [Symbol.iterator]: () => IterableIterator<TextTrackCue>
      } | null
      activeCues: {
        [x: number]: {
          endTime: number
          id: string
          onenter: ((this: TextTrackCue, ev: Event) => any) | null
          onexit: ((this: TextTrackCue, ev: Event) => any) | null
          pauseOnExit: boolean
          startTime: number
          readonly track: {
            readonly activeCues: any | null
            readonly cues: any | null
            readonly id: string
            readonly inBandMetadataTrackDispatchType: string
            readonly kind: TextTrackKind
            readonly label: string
            readonly language: string
            mode: TextTrackMode
            oncuechange: ((this: TextTrack, ev: Event) => any) | null
            addCue: (cue: TextTrackCue) => void
            removeCue: (cue: TextTrackCue) => void
            addEventListener: {
              <K extends "cuechange">(
                type: K,
                listener: (this: TextTrack, ev: TextTrackEventMap[K]) => any,
                options?: boolean | AddEventListenerOptions | undefined
              ): void
              (
                type: string,
                listener: EventListenerOrEventListenerObject,
                options?: boolean | AddEventListenerOptions | undefined
              ): void
            }
            removeEventListener: {
              <K_1 extends "cuechange">(
                type: K_1,
                listener: (this: TextTrack, ev: TextTrackEventMap[K_1]) => any,
                options?: boolean | EventListenerOptions | undefined
              ): void
              (
                type: string,
                listener: EventListenerOrEventListenerObject,
                options?: boolean | EventListenerOptions | undefined
              ): void
            }
            dispatchEvent: (event: Event) => boolean
          } | null
          addEventListener: {
            <K_2 extends keyof TextTrackCueEventMap>(
              type: K_2,
              listener: (
                this: TextTrackCue,
                ev: TextTrackCueEventMap[K_2]
              ) => any,
              options?: boolean | AddEventListenerOptions | undefined
            ): void
            (
              type: string,
              listener: EventListenerOrEventListenerObject,
              options?: boolean | AddEventListenerOptions | undefined
            ): void
          }
          removeEventListener: {
            <K_3 extends keyof TextTrackCueEventMap>(
              type: K_3,
              listener: (
                this: TextTrackCue,
                ev: TextTrackCueEventMap[K_3]
              ) => any,
              options?: boolean | EventListenerOptions | undefined
            ): void
            (
              type: string,
              listener: EventListenerOrEventListenerObject,
              options?: boolean | EventListenerOptions | undefined
            ): void
          }
          dispatchEvent: (event: Event) => boolean
        }
        readonly length: number
        getCueById: (id: string) => TextTrackCue | null
        [Symbol.iterator]: () => IterableIterator<TextTrackCue>
      } | null
    }[]
  >
  selectedTrack: Ref<number>
  enableTrack: (
    track: number | UseMediaTextTrack,
    disableTracks?: boolean
  ) => void
  disableTrack: (track?: number | UseMediaTextTrack | undefined) => void
  supportsPictureInPicture: boolean | undefined
  togglePictureInPicture: () => Promise<unknown>
  isPictureInPicture: Ref<boolean>
  onSourceError: EventHookOn<Event>
}
export declare type UseMediaControlsReturn = ReturnType<typeof useMediaControls>
/**
 * Many of the jsdoc definitions here are modified version of the
 * documentation from MDN(https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement)
 */
export interface UseMediaSource {
  /**
   * The source url for the media
   */
  src: string
  /**
   * The media codec type
   */
  type?: string
}
export interface UseMediaTextTrackSource {
  /**
   * Indicates that the track should be enabled unless the user's preferences indicate
   * that another track is more appropriate
   */
  default?: boolean
  /**
   * How the text track is meant to be used. If omitted the default kind is subtitles.
   */
  kind: TextTrackKind
  /**
   * A user-readable title of the text track which is used by the browser
   * when listing available text tracks.
   */
  label: string
  /**
   * Address of the track (.vtt file). Must be a valid URL. This attribute
   * must be specified and its URL value must have the same origin as the document
   */
  src: string
  /**
   * Language of the track text data. It must be a valid BCP 47 language tag.
   * If the kind attribute is set to subtitles, then srclang must be defined.
   */
  srcLang: string
}
interface UseMediaControlsOptions extends ConfigurableDocument {
  /**
   * The source for the media, may either be a string, a `UseMediaSource` object, or a list
   * of `UseMediaSource` objects.
   */
  src?: MaybeRef<string | UseMediaSource | UseMediaSource[]>
  /**
   * A list of text tracks for the media
   */
  tracks?: MaybeRef<UseMediaTextTrackSource[]>
}
export interface UseMediaTextTrack {
  /**
   * The index of the text track
   */
  id: number
  /**
   * The text track label
   */
  label: string
  /**
   * Language of the track text data. It must be a valid BCP 47 language tag.
   * If the kind attribute is set to subtitles, then srclang must be defined.
   */
  language: string
  /**
   * Specifies the display mode of the text track, either `disabled`,
   * `hidden`, or `showing`
   */
  mode: TextTrackMode
  /**
   * How the text track is meant to be used. If omitted the default kind is subtitles.
   */
  kind: TextTrackKind
  /**
   * Indicates the track's in-band metadata track dispatch type.
   */
  inBandMetadataTrackDispatchType: string
  /**
   * A list of text track cues
   */
  cues: TextTrackCueList | null
  /**
   * A list of active text track cues
   */
  activeCues: TextTrackCueList | null
}
export declare function useMediaControls(
  target: MaybeRef<HTMLMediaElement | null | undefined>,
  options?: UseMediaControlsOptions
): {
  currentTime: Ref<number>
  duration: Ref<number>
  waiting: Ref<boolean>
  seeking: Ref<boolean>
  ended: Ref<boolean>
  stalled: Ref<boolean>
  buffered: Ref<[number, number][]>
  playing: Ref<boolean>
  rate: Ref<number>
  volume: Ref<number>
  muted: Ref<boolean>
  tracks: Ref<
    {
      id: number
      label: string
      language: string
      mode: TextTrackMode
      kind: TextTrackKind
      inBandMetadataTrackDispatchType: string
      cues: {
        [x: number]: {
          endTime: number
          id: string
          onenter: ((this: TextTrackCue, ev: Event) => any) | null
          onexit: ((this: TextTrackCue, ev: Event) => any) | null
          pauseOnExit: boolean
          startTime: number
          readonly track: {
            readonly activeCues: any | null
            readonly cues: any | null
            readonly id: string
            readonly inBandMetadataTrackDispatchType: string
            readonly kind: TextTrackKind
            readonly label: string
            readonly language: string
            mode: TextTrackMode
            oncuechange: ((this: TextTrack, ev: Event) => any) | null
            addCue: (cue: TextTrackCue) => void
            removeCue: (cue: TextTrackCue) => void
            addEventListener: {
              <K extends "cuechange">(
                type: K,
                listener: (this: TextTrack, ev: TextTrackEventMap[K]) => any,
                options?: boolean | AddEventListenerOptions | undefined
              ): void
              (
                type: string,
                listener: EventListenerOrEventListenerObject,
                options?: boolean | AddEventListenerOptions | undefined
              ): void
            }
            removeEventListener: {
              <K_1 extends "cuechange">(
                type: K_1,
                listener: (this: TextTrack, ev: TextTrackEventMap[K_1]) => any,
                options?: boolean | EventListenerOptions | undefined
              ): void
              (
                type: string,
                listener: EventListenerOrEventListenerObject,
                options?: boolean | EventListenerOptions | undefined
              ): void
            }
            dispatchEvent: (event: Event) => boolean
          } | null
          addEventListener: {
            <K_2 extends keyof TextTrackCueEventMap>(
              type: K_2,
              listener: (
                this: TextTrackCue,
                ev: TextTrackCueEventMap[K_2]
              ) => any,
              options?: boolean | AddEventListenerOptions | undefined
            ): void
            (
              type: string,
              listener: EventListenerOrEventListenerObject,
              options?: boolean | AddEventListenerOptions | undefined
            ): void
          }
          removeEventListener: {
            <K_3 extends keyof TextTrackCueEventMap>(
              type: K_3,
              listener: (
                this: TextTrackCue,
                ev: TextTrackCueEventMap[K_3]
              ) => any,
              options?: boolean | EventListenerOptions | undefined
            ): void
            (
              type: string,
              listener: EventListenerOrEventListenerObject,
              options?: boolean | EventListenerOptions | undefined
            ): void
          }
          dispatchEvent: (event: Event) => boolean
        }
        readonly length: number
        getCueById: (id: string) => TextTrackCue | null
        [Symbol.iterator]: () => IterableIterator<TextTrackCue>
      } | null
      activeCues: {
        [x: number]: {
          endTime: number
          id: string
          onenter: ((this: TextTrackCue, ev: Event) => any) | null
          onexit: ((this: TextTrackCue, ev: Event) => any) | null
          pauseOnExit: boolean
          startTime: number
          readonly track: {
            readonly activeCues: any | null
            readonly cues: any | null
            readonly id: string
            readonly inBandMetadataTrackDispatchType: string
            readonly kind: TextTrackKind
            readonly label: string
            readonly language: string
            mode: TextTrackMode
            oncuechange: ((this: TextTrack, ev: Event) => any) | null
            addCue: (cue: TextTrackCue) => void
            removeCue: (cue: TextTrackCue) => void
            addEventListener: {
              <K extends "cuechange">(
                type: K,
                listener: (this: TextTrack, ev: TextTrackEventMap[K]) => any,
                options?: boolean | AddEventListenerOptions | undefined
              ): void
              (
                type: string,
                listener: EventListenerOrEventListenerObject,
                options?: boolean | AddEventListenerOptions | undefined
              ): void
            }
            removeEventListener: {
              <K_1 extends "cuechange">(
                type: K_1,
                listener: (this: TextTrack, ev: TextTrackEventMap[K_1]) => any,
                options?: boolean | EventListenerOptions | undefined
              ): void
              (
                type: string,
                listener: EventListenerOrEventListenerObject,
                options?: boolean | EventListenerOptions | undefined
              ): void
            }
            dispatchEvent: (event: Event) => boolean
          } | null
          addEventListener: {
            <K_2 extends keyof TextTrackCueEventMap>(
              type: K_2,
              listener: (
                this: TextTrackCue,
                ev: TextTrackCueEventMap[K_2]
              ) => any,
              options?: boolean | AddEventListenerOptions | undefined
            ): void
            (
              type: string,
              listener: EventListenerOrEventListenerObject,
              options?: boolean | AddEventListenerOptions | undefined
            ): void
          }
          removeEventListener: {
            <K_3 extends keyof TextTrackCueEventMap>(
              type: K_3,
              listener: (
                this: TextTrackCue,
                ev: TextTrackCueEventMap[K_3]
              ) => any,
              options?: boolean | EventListenerOptions | undefined
            ): void
            (
              type: string,
              listener: EventListenerOrEventListenerObject,
              options?: boolean | EventListenerOptions | undefined
            ): void
          }
          dispatchEvent: (event: Event) => boolean
        }
        readonly length: number
        getCueById: (id: string) => TextTrackCue | null
        [Symbol.iterator]: () => IterableIterator<TextTrackCue>
      } | null
    }[]
  >
  selectedTrack: Ref<number>
  enableTrack: (
    track: number | UseMediaTextTrack,
    disableTracks?: boolean
  ) => void
  disableTrack: (track?: number | UseMediaTextTrack | undefined) => void
  supportsPictureInPicture: boolean | undefined
  togglePictureInPicture: () => Promise<unknown>
  isPictureInPicture: Ref<boolean>
  onSourceError: EventHookOn<Event>
}
export declare type UseMediaControlsReturn = ReturnType<typeof useMediaControls>

Source

SourceDemoDocs

Contributors

Anthony Fu
Alex Kozack
wheat
jelf
webfansplz
rimday
Shinigami

Changelog

v7.2.0 on 12/8/2021
417f8 - feat: playbackRate control (#1002)
v6.0.0-beta.2 on 8/9/2021
ff21b - feat: use tryOnScopeDispose instead of tryOnUnmounted
v4.11.2 on 5/30/2021
68c7d - feat(typedef): add return typedefs (#543) (#544)
v5.0.0-beta.1 on 5/25/2021
5d19a - refactor!: remove deprecated apis
v4.11.0 on 5/14/2021
4df9c - fix: Change onMediaError to onSourceError (#510)
d2e46 - feat: add error event (#509)
0b31b - fix: Doesn't rewrite default media properties (#500)
v4.9.3 on 5/11/2021
fca40 - fix: Removes tracks that have been inserted in html (#493)
7b1b7 - feat: expose source types (#495)
v4.11.0 on 5/14/2021
a1e3e - fix: Change onMediaError to onSourceError (#510)
a6442 - feat: add error event (#509)
720fc - fix: Doesn't rewrite default media properties (#500)
v4.9.3 on 5/11/2021
941ac - fix: Removes tracks that have been inserted in html (#493)
6b200 - feat: expose source types (#495)
v4.6.1 on 3/26/2021
db2a8 - fix: ssr compatibility
v4.5.0 on 3/25/2021
7b9b9 - feat: new function (#383)
useMediaControls has loaded