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+typefor a single audio address - Multi-track mode: pass
linksfor a track list, paired withv-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 reportingnull: completely disable reportingfunction: override built-in implementation (receives fullAudioHistoryParamsincludingevent/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
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | — | Direct URL (preferred over links) | |
type | string | 'm3u8' | Paired with url | |
links | PlayLink[] | [] | Track list (primary input) | |
linkCode | string | — | Current track code, supports v-model:linkCode | |
startTime | number | 0 | Initial seek seconds | |
volume | number | 1 | Volume (0-1) | |
muted | boolean | false | Muted | |
autoplay | boolean | false | Autoplay | |
loop | boolean | false | Loop | |
preload | 'none' | 'metadata' | 'auto' | 'metadata' | Preload strategy | |
controls | boolean | true | Whether to render native <audio controls> | |
onHistory | ((p: AudioHistoryParams) => void) | null | undefined | Tri-state: undefined → built-in /audio/doHistory; null → disable; function → override |
Events
| Name | Payload | Description |
|---|---|---|
ready | el: HTMLAudioElement | audio element ready and HLS attached |
loadedmetadata | — | Metadata loaded |
canplay | — | Can play |
play | — | Play |
pause | — | Pause |
seeked | currentTime: number | Seek complete |
timeupdate | currentTime: number | 1s-throttled progress |
update:currentTime | currentTime: number | 500ms-throttled, for v-model:currentTime |
ended | — | Playback ended |
error | err: unknown | Playback error. Fatal errors only — non-fatal ones (segment 404, transient network) are retried by hls.js and no longer bother consumers |
update:linkCode | code: string | Track switched, sync v-model |
defineExpose
| Method | Description |
|---|---|
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 |
PlayLink / AudioHistoryParams Types
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
onMountedand never re-bound on track switch - autoplay restrictions: browsers typically require
muted: truefor autoplay - iOS Safari natively supports HLS (≥ 10), Hls.js not strictly required
See Also
- SwVideoPlayer — video player (Artplayer + HLS)
- SwVideoPreview — silent looped preview