Skip to content
星罗
Search
Switch language
Go back

Media Players

3 min read Edit on GitHub

Xingluo integrates APlayer (audio) and DPlayer (video), supporting two ways to create players in Markdown and MDX, all lazy-loaded.

Enabling

Toggle each player as needed in features.players in xingluo.config.ts:

ts
features: { players: { aplayer: true, // Enable APlayer audio player dplayer: true, // Enable DPlayer video player }, }

The two are independent. When disabled:

  • The remarkPlayers plugin is not injected (MD fences are not parsed)
  • The player client script is not loaded
  • The build output has no aplayer / dplayer chunks

Two Usage Modes

ModeApplicable toSyntax
MD fencePlain .md and .mdx```aplayer / ```dplayer + JSON config body
MDX component.mdx onlyimport { APlayer, DPlayer } from "@/components/mdx"

Both modes ultimately output the same placeholder div structure (<div class="xng-aplayer|xng-dplayer" data-config>), lazy-loaded and instantiated by src/scripts/players.ts.

APlayer Audio Player

MD Fence

markdown
```aplayer { "audio": [ { "name": "Song", "artist": "Artist", "url": "/audio/song.mp3", "cover": "/images/cover.jpg", "lrc": "[00:00.00] First lyric line" } ], "theme": "#b7daff", "loop": "all", "autoplay": false } ```

MDX Component

mdx
import { APlayer } from "@/components/mdx"; <APlayer audio={[ { name: "Song", artist: "Artist", url: "/audio/song.mp3", cover: "/images/cover.jpg", }, ]} theme="#b7daff" loop="all" />

Options

FieldTypeDefaultNotes
audioAudio | Audio[]requiredAudio object or list
themestring#b7daffPlayer theme color
loop"all" | "one" | "none"allLoop mode
order"list" | "random"listPlay order
volumenumber0.7Initial volume (0–1)
autoplaybooleanfalseAutoplay (subject to browser policy)
listFoldedbooleanfalseList folded
listMaxHeightstring—List max height (CSS value)
lrcType0 | 1 | 2 | 30Lyric type: 0 none / 1 string / 2 URL

audio Object

FieldNotes
nameTrack name (falls back to title, then 'Audio name')
artistArtist (falls back to author)
urlAudio URL (required)
coverCover (falls back to pic)
lrcLyrics (string or URL, paired with lrcType)
themePer-track theme color
typeAudio type: auto | hls | normal

DPlayer Video Player

MD Fence

markdown
```dplayer { "video": { "url": "/videos/demo.mp4", "pic": "/images/video-cover.jpg", "type": "auto" }, "theme": "#b7daff", "autoplay": false, "loop": false } ```

MDX Component

mdx
import { DPlayer } from "@/components/mdx"; <DPlayer video={{ url: "/videos/demo.mp4", pic: "/images/video-cover.jpg" }} theme="#b7daff" subtitle={{ url: "/subtitles.vtt", type: "webvtt" }} />

Options

FieldTypeDefaultNotes
videoVideorequiredVideo config
themestring#b7daffTheme color
autoplaybooleanfalseAutoplay
loopbooleanfalseLoop playback
screenshotbooleanfalseScreenshot feature
hotkeybooleantrueHotkeys
preload"none" | "metadata" | "auto"autoPreload
volumenumber0.7Initial volume
playbackSpeednumber[]—Playback speed list
subtitleSubtitle—Subtitles
danmakuDanmaku—Danmaku (bullet comments)
livebooleanfalseLive mode
mutexbooleantrueMutex (only one player per page)

video Object

FieldNotes
urlVideo URL (required)
picCover
thumbnailsThumbnail URL
typeVideo type: auto | hls | flv | dash | normal
qualityQuality list + defaultQuality index

subtitle Object

FieldNotes
urlSubtitle URL (required)
typewebvtt | ass
fontSizeFont size
bottomDistance from bottom
colorColor

danmaku Object

FieldNotes
idUnique danmaku pool ID (required)
apiDanmaku API URL (required)
userUser identifier
maximumMax danmaku count

Lazy Loading Mechanism

Players are lazy-loaded via IntersectionObserver: the placeholder div dynamically imports the player module and styles and instantiates only when within 200px of the viewport.

  • APlayer: dynamic import("aplayer") + import("aplayer/dist/APlayer.min.css")
  • DPlayer: dynamic import("dplayer") (styles are inlined in JS; no separate CSS needed)

Module loading uses a shared Promise cache to avoid repeated dynamic imports. Re-instantiation is prevented via dataset markers (xng-init, xng-observed).

View Transitions Adaptation

The player script listens for astro:page-load and re-scans placeholder divs after each page load. After a View Transitions page switch, the new page’s player placeholders are re-observed and lazy-loaded.

Performance

  • Zero bundle when players are disabled (remark plugin not injected, client script not loaded)
  • Zero runtime when enabled but no players on a page (script loads but does not instantiate)
  • Player modules are standalone chunks, loaded on demand only on pages that use them
  • CSS and JS are imported separately to ensure styles are ready before instantiation

Type Declarations

APlayer and DPlayer have no official TypeScript types; Xingluo provides loose module declarations in src/types/aplayer.d.ts and src/types/dplayer.d.ts, with options fields set optional for spread compatibility. MDX component Props have full type constraints.


Related posts

  • Deployment

    2 min read

    Xingluo deployment guide covering static hosting platforms (Netlify/Vercel/GitHub Pages), Nginx self-hosting, Docker, and environment variables.

  • Search

    1 min read

    Xingluo search guide covering Flexsearch full-text search integration, index generation, UI, multilingual search, and performance.