audio-tracker
Version:
A headless JavaScript library that gives you full control over web audio — playback, tracking, and Media Session integration made simple.
160 lines • 5.03 kB
JavaScript
/**
* Media Session module for AudioTracker
* Integrates with the browser's Media Session API to display playback controls in system UI
* @param tracker - AudioTracker instance to attach Media Session functionality
* @returns Cleanup function to remove Media Session handlers and metadata
* @example
* const tracker = new AudioTracker('audio.mp3', {
* mediaSession: {
* title: 'Song Title',
* artist: 'Artist Name',
* album: 'Album Name',
* artwork: [{ src: 'cover.jpg', sizes: '512x512', type: 'image/jpeg' }]
* }
* });
* tracker.use(mediaSessionModule);
*/
export function mediaSessionModule(tracker) {
if (!("mediaSession" in navigator)) {
console.warn("MediaSessionModule: Media Session API not supported");
return () => { };
}
if (tracker.options.mediaSession) {
navigator.mediaSession.metadata = new MediaMetadata(tracker.options.mediaSession);
}
/**
* Update Media Session metadata dynamically
* @param metadata - New metadata to display in system UI
* @example
* tracker.updateMediaSessionMetadata({
* title: 'New Song',
* artist: 'New Artist',
* artwork: [{ src: 'new-cover.jpg' }]
* });
*/
tracker.updateMediaSessionMetadata = (metadata) => {
try {
navigator.mediaSession.metadata = new MediaMetadata(metadata);
}
catch (error) {
console.warn("Failed to update MediaSession metadata:", error);
}
};
function updatePlaybackState(state) {
try {
navigator.mediaSession.playbackState = state;
}
catch (error) {
console.warn("Failed to update Media Session playback state:", error);
}
}
function updatePositionState() {
const { duration, playbackRate, currentTime } = tracker.audio;
if (duration && !isNaN(duration)) {
try {
navigator.mediaSession.setPositionState({
duration,
playbackRate,
position: currentTime,
});
}
catch (error) {
console.warn("Failed to update Media Session position:", error);
}
}
}
const actions = [
[
"play",
async () => {
await tracker.play();
updatePlaybackState("playing");
},
],
[
"pause",
() => {
tracker.pause();
updatePlaybackState("paused");
},
],
[
"seekbackward",
(details) => {
tracker.backward(details.seekOffset || 10);
},
],
[
"seekforward",
(details) => {
tracker.forward(details.seekOffset || 10);
},
],
[
"seekto",
(details) => {
if (details.fastSeek && "fastSeek" in tracker.audio) {
tracker.audio.fastSeek(details.seekTime);
}
else {
tracker.audio.currentTime = details.seekTime ?? 0;
}
updatePositionState();
},
],
[
"stop",
() => {
tracker.pause();
tracker.audio.currentTime = 0;
updatePlaybackState("paused");
},
],
];
// Register handlers
for (const [action, handler] of actions) {
try {
navigator.mediaSession.setActionHandler(action, handler);
}
catch {
console.warn(`Media Session action "${action}" not supported`);
}
}
// Playback event handlers
const handleEnded = () => {
updatePositionState();
updatePlaybackState("paused");
};
const handlePlay = () => {
updatePositionState();
updatePlaybackState("playing");
};
const handlePause = () => {
updatePositionState();
updatePlaybackState("paused");
};
const subscriptions = [
["loadedmetadata", updatePositionState],
["seeked", updatePositionState],
["ratechange", updatePositionState],
["timeupdate", updatePositionState],
["ended", handleEnded],
["play", handlePlay],
["pause", handlePause],
];
subscriptions.forEach(([event, handler]) => tracker.subscribe(event, handler));
return () => {
actions.forEach(([action]) => {
try {
navigator.mediaSession.setActionHandler(action, null);
}
catch {
// optionally log or ignore
}
});
subscriptions.forEach(([event, handler]) => tracker.unsubscribe(event, handler));
navigator.mediaSession.metadata = null;
navigator.mediaSession.playbackState = "none";
};
}
//# sourceMappingURL=mediaSessionModule.js.map