Plugins UIAnime/Library PlaybackInteract with the playback and auto-tracking system in Seanime.
Seanime provides APIs for controlling media playback and interacting with the underlying Playback Manager and MPV. These APIs give plugins access to the same functionality Seanime uses internally for media tracking and player control.
Permissions
playback
permission is required
Copy {
//...
"plugin": {
"permissions": {
"scopes": ["playback"]
}
}
}
Core methods
playUsingMediaPlayer(filePath)
Plays a local file using the configured media player, with automatic tracking.
Parameters:
filePath
: String - Path to a scanned local video file
Note: This only works with files properly scanned by Seanime. Using it with unscanned files will result in tracking errors.
Example:
Copy // Play a local file with tracking
try {
await ctx.playback.playUsingMediaPlayer("/anime/One Piece/One Piece - 1015.mkv")
console.log("Playback started successfully")
} catch (error) {
console.error("Playback error:", error)
}
streamUsingMediaPlayer(windowTitle, streamUrl, anime, aniDbEpisode)
Streams a video from a URL using the configured media player, with automatic tracking.
Parameters:
windowTitle
: String - Title for the player window
streamUrl
: String - URL of the video stream
anime
: AL_BaseAnime - AniList anime object
aniDbEpisode
: String - AniDB episode number
Example:
Copy // Stream an episode with proper tracking
const anime = $anilist.getAnime(21) // One Piece
try {
await ctx.playback.streamUsingMediaPlayer(
"One Piece - Episode 1015",
"https://example.com/streams/one-piece-1015.mkv",
anime,
"1015"
)
console.log("Stream started successfully")
} catch (error) {
console.error("Stream error:", error)
}
registerEventListener
registerEventListener(id, callback)
Registers a listener for playback events.
Parameters:
id
: String - Unique identifier for the listener
callback
: Function(event: PlaybackEvent) - Function called when an event occurs
Example:
Copy // Listen for playback events
// Callback triggered every 1-3 seconds
const unsubscribe = ctx.playback.registerEventListener((event) => {
//
// Local file playback
//
if (event.isVideoStarted || event.isVideoCompleted || event.isIsVideoStopped) {
// Video started
if (event.isVideoStated) {
console.log(event.startedEvent?.filename)
return
}
// Video completed
if (event.isVideoCompleted) {
console.log(event.completedEvent?.filename)
return
}
// Video stopped
if (event.isIsVideoStopped) {
console.log(event.stoppedEvent?.reason)
return
}
// The playback state
if (event.state) {
console.log("Media title", event.state.mediaTitle)
console.log("Episode number", event.state.episodeNumber)
console.log("Completion percentage", event.state.completionPercentage)
}
if(event.status) {
console.log("Is Playing", event.status.playing)
console.log("Current time", event.status.currentTimeInSeconds)
console.log("Duration", event.status.durationInSeconds)
}
}
//
// Stream playback
//
if (event.isStreamStarted || event.isStreamCompleted || event.isStreamStopped) {
// Stream started
if (event.isStreamStarted) {
console.log(event.startedEvent?.filename)
return
}
// Stream completed
if (event.isStreamCompleted) {
console.log(event.completedEvent?.filename)
return
}
// Stream stopped
if (event.isStreamStopped) {
console.log(event.stoppedEvent?.reason)
return
}
// The stream playback state
if (event.state) {
console.log("Media title", event.state.mediaTitle)
console.log("Episode number", event.state.episodeNumber)
console.log("Completion percentage", event.state.completionPercentage)
}
if(event.status) {
console.log("Is Playing", event.status.playing)
console.log("Current time", event.status.currentTimeInSeconds)
console.log("Duration", event.status.durationInSeconds)
}
}
})
// Later, to stop listening
unsubscribe()
pause
Pauses the current playback.
Example:
Copy try {
ctx.playback.pause()
console.log("Playback paused")
} catch (error) {
console.error("Could not pause:", error)
}
resume
Resumes the paused playback.
Example:
Copy // Resume after pausing
try {
ctx.playback.resume()
console.log("Playback resumed")
} catch (error) {
console.error("Could not resume:", error)
}
seek
Seeks to a specific position in the current playback.
Parameters:
seconds
: Number - The position to seek to in seconds
Example:
Copy // Skip ahead 30 seconds
try {
ctx.playback.seek(currentTimeInSeconds + 30)
console.log("Skipped forward 30 seconds")
} catch (error) {
console.error("Could not seek:", error)
}
cancel
Cancels the current playback tracking (does not stop the player).
Example:
Copy // Cancel tracking without stopping playback
try {
ctx.playback.cancel()
console.log("Tracking canceled")
} catch (error) {
console.error("Could not cancel tracking:", error)
}
getNextEpisode
Gets the next episode to play for the current media.
Example:
Copy // Check if there's a next episode
try {
const nextEpisode = await ctx.playback.getNextEpisode()
if (nextEpisode) {
console.log(`Next episode: ${nextEpisode.name}`)
} else {
console.log("No next episode available")
}
} catch (error) {
console.error("Error getting next episode:", error)
}
playNextEpisode
Plays the next episode for the current media.
Example:
Copy // Play next episode when current is almost done
ctx.playback.registerEventListener((event) => {
if (event.status && event.status.completionPercentage > 95) {
try {
ctx.playback.playNextEpisode()
} catch(e) {}
}
})
Best Practices
The playback API is designed for tracked media files that are part of the Seanime library:
Copy // Good practice: Play scanned files for proper tracking
const localFile = getScannedFile() // Get a file that's in the library
ctx.playback.playUsingMediaPlayer(localFile.path)
// Bad practice: Playing unscanned files won't track properly
ctx.playback.playUsingMediaPlayer("/random/video.mp4") // Will cause tracking errors