API Reference
Complete API reference for @bbplayer/orpheus including the native module, React hooks, enums, interfaces, and event system.
Module Export
import { Orpheus } from '@bbplayer/orpheus'
The Orpheus object is the main native module instance providing all playback functionality.
Properties
restorePlaybackPositionEnabled
Orpheus.restorePlaybackPositionEnabled: boolean
Enables automatic restoration of playback position on app restart.
loudnessNormalizationEnabled
Orpheus.loudnessNormalizationEnabled: boolean
Enables ReplayGain-style loudness normalization (Android only).
autoplayOnStartEnabled
Orpheus.autoplayOnStartEnabled: boolean
Automatically resumes playback when the app starts.
isDesktopLyricsShown
Orpheus.isDesktopLyricsShown: boolean
Returns whether desktop lyrics window is currently visible (Android only).
isDesktopLyricsLocked
Orpheus.isDesktopLyricsLocked: boolean
Returns whether desktop lyrics window is locked against dragging (Android only).
Playback Methods
play()
Starts or resumes playback.
pause()
Pauses playback.
seekTo()
seekTo(seconds: number): Promise<void>
Seeks to a specific position in seconds.
Target position in seconds
await Orpheus.seekTo(120) // Seek to 2:00
getPosition()
getPosition(): Promise<number>
Returns the current playback position in seconds.
const position = await Orpheus.getPosition()
getDuration()
getDuration(): Promise<number>
Returns the total duration of the current track in seconds.
const duration = await Orpheus.getDuration()
getBuffered()
getBuffered(): Promise<number>
Returns the buffered position in seconds.
const buffered = await Orpheus.getBuffered()
getIsPlaying()
getIsPlaying(): Promise<boolean>
Returns whether playback is currently active.
const isPlaying = await Orpheus.getIsPlaying()
Queue Methods
addToEnd()
addToEnd(
tracks: Track[],
startFromId?: string,
clearQueue?: boolean
): Promise<void>
Adds tracks to the end of the queue without deduplication.
Optional track ID to start playing immediately after adding
Whether to clear the existing queue before adding
await Orpheus.addToEnd(
[track1, track2, track3],
track1.id, // Start playing track1
true // Clear existing queue
)
playNext()
playNext(track: Track): Promise<void>
Inserts a track to play immediately after the current track.
await Orpheus.playNext(track)
removeTrack()
removeTrack(index: number): Promise<void>
Removes a track from the queue by index.
await Orpheus.removeTrack(3)
clear()
Clears the entire playback queue.
getQueue()
getQueue(): Promise<Track[]>
Returns the current playback queue.
const queue = await Orpheus.getQueue()
getCurrentTrack()
getCurrentTrack(): Promise<Track | null>
Returns the currently playing track, or null if none.
const track = await Orpheus.getCurrentTrack()
if (track) {
console.log('Now playing:', track.title)
}
getCurrentIndex()
getCurrentIndex(): Promise<number>
Returns the current queue index.
const index = await Orpheus.getCurrentIndex()
getIndexTrack(index: number): Promise<Track | null>
Returns the track at a specific queue index.
const track = await Orpheus.getIndexTrack(5)
skipTo()
skipTo(index: number): Promise<void>
Skips to a specific track by queue index.
Zero-based queue index to skip to
skipToNext()
skipToNext(): Promise<void>
Skips to the next track in the queue.
await Orpheus.skipToNext()
skipToPrevious()
skipToPrevious(): Promise<void>
Skips to the previous track in the queue.
await Orpheus.skipToPrevious()
Repeat and Shuffle
setRepeatMode()
setRepeatMode(mode: RepeatMode): Promise<void>
Sets the repeat mode.
One of: RepeatMode.OFF, RepeatMode.TRACK, RepeatMode.QUEUE
import { RepeatMode } from '@bbplayer/orpheus'
await Orpheus.setRepeatMode(RepeatMode.QUEUE)
getRepeatMode()
getRepeatMode(): Promise<RepeatMode>
Returns the current repeat mode.
const mode = await Orpheus.getRepeatMode()
setShuffleMode()
setShuffleMode(enabled: boolean): Promise<void>
Enables or disables shuffle mode.
Whether to enable shuffle
await Orpheus.setShuffleMode(true)
getShuffleMode()
getShuffleMode(): Promise<boolean>
Returns whether shuffle mode is enabled.
const shuffled = await Orpheus.getShuffleMode()
Bilibili Integration
setBilibiliCookie()
setBilibiliCookie(cookie: string): void
Sets the authentication cookie for Bilibili API requests.
Full cookie string including SESSDATA
Orpheus.setBilibiliCookie('SESSDATA=abc123; buvid3=xyz...')
Download Methods
downloadTrack()
downloadTrack(track: Track): Promise<void>
Downloads a single track for offline playback.
await Orpheus.downloadTrack(track)
multiDownload()
multiDownload(tracks: Track[]): Promise<void>
Batch downloads multiple tracks.
Array of tracks to download
await Orpheus.multiDownload([track1, track2, track3])
removeDownload()
removeDownload(id: string): Promise<void>
Removes a download task and its downloaded file.
await Orpheus.removeDownload('track-id')
removeDownloads()
removeDownloads(ids: string[]): Promise<void>
Batch removes multiple download tasks.
Array of track IDs to remove
await Orpheus.removeDownloads(['id1', 'id2', 'id3'])
removeAllDownloads()
removeAllDownloads(): Promise<void>
Removes all download tasks including completed files.
await Orpheus.removeAllDownloads()
getDownloads()
getDownloads(): Promise<DownloadTask[]>
Returns all download tasks.
Array of download task objects
const downloads = await Orpheus.getDownloads()
downloads.forEach(task => {
console.log(`${task.track?.title}: ${task.percentDownloaded}%`)
})
getDownloadStatusByIds()
getDownloadStatusByIds(ids: string[]): Promise<Record<string, DownloadState>>
Returns download states for specific track IDs.
const statuses = await Orpheus.getDownloadStatusByIds(['id1', 'id2'])
console.log(statuses) // { 'id1': DownloadState.COMPLETED, 'id2': DownloadState.DOWNLOADING }
clearUncompletedDownloadTasks()
clearUncompletedDownloadTasks(): Promise<void>
Removes all incomplete download tasks (keeps completed downloads).
await Orpheus.clearUncompletedDownloadTasks()
getUncompletedDownloadTasks()
getUncompletedDownloadTasks(): Promise<DownloadTask[]>
Returns only incomplete download tasks.
const pending = await Orpheus.getUncompletedDownloadTasks()
downloadMissingCovers()
downloadMissingCovers(): Promise<number>
Downloads cover art for tracks that have audio but no cover.
Number of covers started downloading
const count = await Orpheus.downloadMissingCovers()
console.log(`Downloading ${count} covers`)
getDownloadedCoverUri()
getDownloadedCoverUri(trackId: string): string | null
Returns the local file URI for a downloaded cover, or null if not available.
const uri = Orpheus.getDownloadedCoverUri('track-id')
if (uri) {
console.log('Cover at:', uri) // file:///...
}
Export Methods (Android)
selectDirectory()
selectDirectory(): Promise<string | null>
Opens Android’s directory picker and returns the selected SAF URI.
SAF URI string, or null if cancelled
const uri = await Orpheus.selectDirectory()
if (uri) {
console.log('Selected:', uri)
}
exportDownloads()
exportDownloads(
ids: string[],
destinationUri: string,
filenamePattern: string | null,
embedLyrics: boolean,
convertToLrc: boolean,
cropCoverArt: boolean
): Promise<void>
Batch exports downloaded tracks with metadata embedding.
SAF directory URI from selectDirectory()
Filename template using variables: {id}, {name}, {artist}, {bvid}, {cid}
Whether to embed cached lyrics into M4A metadata
Convert SPL lyrics to standard LRC (only if embedLyrics is true)
Crop cover art to square aspect ratio
const uri = await Orpheus.selectDirectory()
if (uri) {
await Orpheus.exportDownloads(
['id1', 'id2'],
uri,
'{artist} - {name}',
true, // Embed lyrics
false, // Keep SPL format
false // Keep original aspect ratio
)
}
Cache Methods
getLruCachedUris()
getLruCachedUris(uris: string[]): string[]
Checks which URIs are fully cached in the LRU stream cache.
Array of orpheus:// URIs to check
Subset of input URIs that are fully cached
const cached = Orpheus.getLruCachedUris([
'orpheus://bilibili?bvid=BV1xx411c7mD&cid=123456789',
'orpheus://bilibili?bvid=BV1yy422c8mE&cid=987654321'
])
console.log('Cached:', cached)
Desktop Lyrics Methods (Android)
checkOverlayPermission()
checkOverlayPermission(): Promise<boolean>
Checks if overlay permission is granted.
const hasPermission = await Orpheus.checkOverlayPermission()
requestOverlayPermission()
requestOverlayPermission(): Promise<void>
Opens system settings to request overlay permission.
await Orpheus.requestOverlayPermission()
showDesktopLyrics()
showDesktopLyrics(): Promise<void>
Shows the desktop lyrics floating window.
await Orpheus.showDesktopLyrics()
hideDesktopLyrics()
hideDesktopLyrics(): Promise<void>
Hides the desktop lyrics floating window.
await Orpheus.hideDesktopLyrics()
setDesktopLyrics()
setDesktopLyrics(lyricsJson: string): Promise<void>
Sets the lyrics content for desktop display.
JSON string containing lyrics data with timestamps
const lyrics = {
lines: [
{ time: 0, text: 'Line 1' },
{ time: 5000, text: 'Line 2' }
]
}
await Orpheus.setDesktopLyrics(JSON.stringify(lyrics))
Advanced Methods
setPlaybackSpeed()
setPlaybackSpeed(speed: number): Promise<void>
Sets playback speed multiplier.
Speed multiplier (typically 0.5 to 2.0)
await Orpheus.setPlaybackSpeed(1.25)
getPlaybackSpeed()
getPlaybackSpeed(): Promise<number>
Returns current playback speed.
const speed = await Orpheus.getPlaybackSpeed()
setSleepTimer()
setSleepTimer(durationMs: number): Promise<void>
Sets a sleep timer to pause playback after a duration.
await Orpheus.setSleepTimer(30 * 60 * 1000) // 30 minutes
getSleepTimerEndTime()
getSleepTimerEndTime(): Promise<number | null>
Returns the sleep timer end timestamp, or null if not set.
const endTime = await Orpheus.getSleepTimerEndTime()
if (endTime) {
console.log('Timer ends at:', new Date(endTime))
}
cancelSleepTimer()
cancelSleepTimer(): Promise<void>
Cancels the active sleep timer.
await Orpheus.cancelSleepTimer()
updateSpectrumData()
updateSpectrumData(destination: Float32Array): void
Synchronously updates a Float32Array with current FFT spectrum data (Android only).
Pre-allocated Float32Array of length SPECTRUM_SIZE (512)
import { SPECTRUM_SIZE } from '@bbplayer/orpheus'
const spectrum = new Float32Array(SPECTRUM_SIZE)
function animate() {
Orpheus.updateSpectrumData(spectrum)
// Use spectrum data for visualization
requestAnimationFrame(animate)
}
debugTriggerError()
debugTriggerError(): Promise<void>
Triggers a test error for debugging error handling.
await Orpheus.debugTriggerError()
React Hooks
useOrpheus()
function useOrpheus(): {
state: PlaybackState
isPlaying: boolean
position: number
duration: number
buffered: number
currentTrack: Track | null
currentIndex: number
}
All-in-one hook combining playback state, progress, and current track.
import { useOrpheus } from '@bbplayer/orpheus'
function Player() {
const { state, isPlaying, position, duration, currentTrack } = useOrpheus()
return (
<View>
<Text>{currentTrack?.title}</Text>
<Text>{position} / {duration}</Text>
</View>
)
}
useProgress()
function useProgress(): {
position: number
duration: number
buffered: number
}
Hook for tracking playback progress.
import { useProgress } from '@bbplayer/orpheus'
function ProgressBar() {
const { position, duration, buffered } = useProgress()
return <Slider value={position} max={duration} />
}
usePlaybackState()
function usePlaybackState(): PlaybackState
Hook for tracking playback state changes.
import { usePlaybackState, PlaybackState } from '@bbplayer/orpheus'
function LoadingIndicator() {
const state = usePlaybackState()
return state === PlaybackState.BUFFERING ? <Spinner /> : null
}
useIsPlaying()
function useIsPlaying(): boolean
Hook for tracking play/pause state.
import { useIsPlaying } from '@bbplayer/orpheus'
function PlayButton() {
const isPlaying = useIsPlaying()
return <Icon name={isPlaying ? 'pause' : 'play'} />
}
useCurrentTrack()
function useCurrentTrack(): {
track: Track | null
index: number
}
Hook for tracking the current track and queue index.
import { useCurrentTrack } from '@bbplayer/orpheus'
function NowPlaying() {
const { track, index } = useCurrentTrack()
return (
<View>
<Text>{track?.title}</Text>
<Text>Track {index + 1}</Text>
</View>
)
}
Headless Task
registerOrpheusHeadlessTask()
function registerOrpheusHeadlessTask(
task: (event: OrpheusHeadlessEvent) => Promise<void>
): void
Registers a background task handler for playback events.
Async function that handles headless events
import { registerOrpheusHeadlessTask } from '@bbplayer/orpheus'
registerOrpheusHeadlessTask(async (event) => {
switch (event.eventName) {
case 'onTrackStarted':
// Handle track start
break
case 'onTrackFinished':
// Handle track finish
break
case 'onTrackPaused':
// Handle pause
break
case 'onTrackResumed':
// Handle resume
break
}
})
Events
Orpheus uses an event emitter pattern for real-time notifications.
Event Listener Pattern
const subscription = Orpheus.addListener('eventName', (event) => {
// Handle event
})
// Later: remove listener
subscription.remove()
onPlaybackStateChanged
Orpheus.addListener('onPlaybackStateChanged', (event: { state: PlaybackState }) => {
console.log('State:', event.state)
})
Fired when playback state changes (IDLE, BUFFERING, READY, ENDED).
onTrackStarted
Orpheus.addListener('onTrackStarted', (event: {
trackId: string
reason: number
}) => {
console.log('Started:', event.trackId, 'Reason:', event.reason)
})
Fired when a track starts playing.
onTrackFinished
Orpheus.addListener('onTrackFinished', (event: {
trackId: string
finalPosition: number
duration: number
}) => {
console.log(`Finished ${event.trackId} at ${event.finalPosition}s`)
})
Fired when a track finishes playing.
onPositionUpdate
Orpheus.addListener('onPositionUpdate', (event: {
position: number
duration: number
buffered: number
}) => {
console.log(`${event.position}s / ${event.duration}s`)
})
Fired periodically with playback progress (typically every second).
onIsPlayingChanged
Orpheus.addListener('onIsPlayingChanged', (event: { status: boolean }) => {
console.log('Playing:', event.status)
})
Fired when play/pause state changes.
onPlayerError
Orpheus.addListener('onPlayerError', (event: PlaybackErrorEvent) => {
if (event.platform === 'android') {
console.error('Android error:', event.errorCodeName, event.message)
console.error('Stack:', event.stackTrace)
} else {
console.error('iOS error:', event.error)
}
})
Fired when a playback error occurs. Event structure differs by platform.
onDownloadUpdated
Orpheus.addListener('onDownloadUpdated', (event: DownloadTask) => {
console.log(`Download ${event.id}: ${event.percentDownloaded}%`)
console.log('State:', event.state)
})
Fired when download progress or state changes.
onCoverDownloadProgress
Orpheus.addListener('onCoverDownloadProgress', (event: {
current: number
total: number
trackId: string
status: 'success' | 'failed'
}) => {
console.log(`Cover ${event.current}/${event.total}: ${event.status}`)
})
Fired during batch cover art download operations.
onPlaybackSpeedChanged
Orpheus.addListener('onPlaybackSpeedChanged', (event: { speed: number }) => {
console.log('Speed:', event.speed)
})
Fired when playback speed changes.
onExportProgress
Orpheus.addListener('onExportProgress', (event: {
progress?: number
currentId: string
index?: number
total?: number
status: 'success' | 'error'
message?: string
}) => {
console.log(`Exporting [${event.index}/${event.total}]: ${event.currentId}`)
if (event.progress !== undefined) {
console.log(`Progress: ${event.progress}%`)
}
})
Fired during batch export operations.
onHeadlessEvent
Orpheus.addListener('onHeadlessEvent', (event: OrpheusHeadlessEvent) => {
console.log('Headless event:', event.eventName)
})
Fired for headless task events (iOS only - Android uses native headless service).
Types and Interfaces
Track
interface Track {
id: string
url: string
title?: string
artist?: string
artwork?: string
duration?: number
}
DownloadTask
interface DownloadTask {
id: string
state: DownloadState
percentDownloaded: number
bytesDownloaded: number
contentLength: number
track?: Track
}
PlaybackErrorEvent
type PlaybackErrorEvent = AndroidPlaybackErrorEvent | IosPlaybackErrorEvent
interface AndroidPlaybackErrorEvent {
platform: 'android'
errorCode: number
errorCodeName: string | null
timestamp: string
message: string | null
stackTrace: string
rootCauseClass: string
rootCauseMessage: string
}
interface IosPlaybackErrorEvent {
platform: 'ios'
error: string
}
OrpheusHeadlessEvent
type OrpheusHeadlessEvent =
| OrpheusHeadlessTrackStartedEvent
| OrpheusHeadlessTrackFinishedEvent
| OrpheusHeadlessTrackPausedEvent
| OrpheusHeadlessTrackResumedEvent
interface OrpheusHeadlessTrackStartedEvent {
eventName: 'onTrackStarted'
trackId: string
reason: number
}
interface OrpheusHeadlessTrackFinishedEvent {
eventName: 'onTrackFinished'
trackId: string
finalPosition: number
duration: number
}
interface OrpheusHeadlessTrackPausedEvent {
eventName: 'onTrackPaused'
}
interface OrpheusHeadlessTrackResumedEvent {
eventName: 'onTrackResumed'
}
Enums
PlaybackState
enum PlaybackState {
IDLE = 1,
BUFFERING = 2,
READY = 3,
ENDED = 4
}
RepeatMode
enum RepeatMode {
OFF = 0,
TRACK = 1,
QUEUE = 2
}
TransitionReason
enum TransitionReason {
REPEAT = 0,
AUTO = 1,
SEEK = 2,
PLAYLIST_CHANGED = 3
}
DownloadState
enum DownloadState {
QUEUED = 0,
STOPPED = 1,
DOWNLOADING = 2,
COMPLETED = 3,
FAILED = 4,
REMOVING = 5,
RESTARTING = 7
}
Constants
SPECTRUM_SIZE
export const SPECTRUM_SIZE = 512
The valid length for FFT spectrum data. Use this to initialize the Float32Array for updateSpectrumData().
import { SPECTRUM_SIZE } from '@bbplayer/orpheus'
const spectrum = new Float32Array(SPECTRUM_SIZE)