SwAudioPlayer

SwAudioPlayer is an audio player built on HLS.js + native <audio>. Its design philosophy is fully aligned with SwVideoPlayer — suitable for audiobooks, podcasts, broadcasts, and background music. SSR-safe.

Audio Source (one of two)

  • Direct URL mode: pass url + type for a single audio address
  • Multi-track mode: pass links for a track list, paired with v-model:linkCode

Basic Usage

Simplest form: local m4a direct link + native controls (type must match the url; :on-history="null" since the docs site has no backend).

Basic playback (m4a + native controls)

加载中...
<!-- Multi-track (recommended; matches backend API shape) -->
<SwAudioPlayer :links="audio.play_links" />
<!-- Direct URL; autoplay must pair with muted (hard mobile-browser requirement, see SVP-001) -->
<SwAudioPlayer url="https://example.com/song.m3u8" autoplay muted />

Multi-track Switching with v-model:linkCode

<script setup lang="ts">
const currentCode = ref('hq')
const links = [
  { code: 'hq', id: 1, lid: 1, m3u8_url: 'https://cdn.example.com/hq.m3u8' },
  { code: 'lq', id: 1, lid: 2, m3u8_url: 'https://cdn.example.com/lq.m3u8' },
]
</script>

<template>
  <SwAudioPlayer v-model:link-code="currentCode" :links="links" />
  <button @click="currentCode = 'lq'">Switch to LQ</button>
</template>

When switching tracks, the current playback position is preserved automatically — recordings currentTime → destroys old hls instance → rebuilds and attaches new source → restores position and plays.

Resume Playback

<script setup lang="ts">
const savedTime = ref(Number(localStorage.getItem('audio_seek') || 0))

function handleTimeupdate(currentTime: number) {
  localStorage.setItem('audio_seek', String(currentTime))
}
</script>

<template>
  <SwAudioPlayer
    :links="links"
    :start-time="savedTime"
    @timeupdate="handleTimeupdate"
  />
</template>

Event listening

play / pause / 1s-throttled timeupdate / ended + v-model:currentTime.

Event log + v-model:currentTime

加载中...

History Reporting (onHistory tri-state)

onHistory is zero-config by default — built-in posts to POST /audio/doHistory (legacy backend convention, without the event field).

  • undefined (default): built-in reporting
  • null: completely disable reporting
  • function: override built-in implementation (receives full AudioHistoryParams including event/view_time/code)
<!-- Default -->
<SwAudioPlayer :links="links" />

<!-- Disabled -->
<SwAudioPlayer :links="links" :on-history="null" />

<!-- Custom -->
<SwAudioPlayer
  :links="links"
  :on-history="(p) => stat.report('audio_play', p)"
/>

Imperative Control via ref

<script setup lang="ts">
const audioRef = ref()

function pause() {
  audioRef.value?.pause()
}
function jumpToBookmark() {
  audioRef.value?.seek(120)
}
function switchLine() {
  audioRef.value?.switchLink('lq')
}
function getNativeElement() {
  const el = audioRef.value?.getElement()
  if (el) console.log(el.duration)
}
</script>

<template>
  <SwAudioPlayer ref="audioRef" :links="links" />
</template>

Hide Native Controls (Custom UI)

:controls=false + ref-driven custom UI

加载中...

Multi-track v-model:linkCode resume

加载中...

loop + volume + preload combo

加载中...
<SwAudioPlayer
  ref="audioRef"
  :links="links"
  :controls="false"
  @play="isPlaying = true"
  @pause="isPlaying = false"
  @timeupdate="(t) => (currentTime = t)"
/>

Props

NameTypeRequiredDefaultDescription
urlstring—Direct URL (preferred over links)
typestring'm3u8'Paired with url
linksPlayLink[][]Track list (primary input)
linkCodestring—Current track code, supports v-model:linkCode
startTimenumber0Initial seek seconds
volumenumber1Volume (0-1)
mutedbooleanfalseMuted
autoplaybooleanfalseAutoplay
loopbooleanfalseLoop
preload'none' | 'metadata' | 'auto''metadata'Preload strategy
controlsbooleantrueWhether to render native <audio controls>
onHistory((p: AudioHistoryParams) => void) | nullundefinedTri-state: undefined → built-in /audio/doHistory; null → disable; function → override

Events

NamePayloadDescription
readyel: HTMLAudioElementaudio element ready and HLS attached
loadedmetadata—Metadata loaded
canplay—Can play
play—Play
pause—Pause
seekedcurrentTime: numberSeek complete
timeupdatecurrentTime: number1s-throttled progress
update:currentTimecurrentTime: number500ms-throttled, for v-model:currentTime
ended—Playback ended
errorerr: unknownPlayback error. Fatal errors only — non-fatal ones (segment 404, transient network) are retried by hls.js and no longer bother consumers
update:linkCodecode: stringTrack switched, sync v-model

defineExpose

MethodDescription
play()Start playback
pause()Pause
seek(time: number)Seek to seconds
destroy()Destroy hls instance
switchLink(code: string)Switch to specified track (preserves progress)
getElement()Get the native <audio> DOM reference
interface PlayLink {
  code: string
  id: number | string
  lid: number | string
  m3u8_url: string
}

interface AudioHistoryParams {
  event: 'audio_play' | 'audio_complete'
  view_time: number
  id?: number | string
  lid?: number | string
  code: string
  time: number
}

Notes

  • No Artplayer: native <audio> + Hls.js is sufficient (no menu/PiP/fullscreen needed for audio); keeps the component simple
  • switchLink preserves progress: aligned with SwVideoPlayer; switching tracks doesn't restart from zero
  • Events bound only once: audio DOM events are bound in onMounted and never re-bound on track switch
  • autoplay restrictions: browsers typically require muted: true for autoplay
  • iOS Safari natively supports HLS (≥ 10), Hls.js not strictly required

See Also