A python interface to the mpv media player
—
Comprehensive event system for monitoring player state changes, handling errors, responding to user interactions, and implementing custom event-driven logic.
Register callbacks to handle specific types of events from the mpv player.
def register_event_callback(self, callback):
"""
Register a callback for all mpv events.
Parameters:
- callback: Function that accepts MpvEvent objects
"""
def event_callback(self, *event_types):
"""
Decorator for registering event callbacks for specific event types.
Parameters:
- event_types: Event types to listen for (MpvEventID constants)
Returns:
Decorator function for callback registration
"""
def unregister_event_callback(self, callback):
"""
Remove an event callback.
Parameters:
- callback: Callback function to remove
"""Wait for specific events with optional conditions and timeouts.
def wait_for_event(self, *event_types, cond=lambda evt: True, timeout=None, catch_errors=True):
"""
Wait for specific event types with optional condition.
Parameters:
- event_types: Event types to wait for
- cond: Condition function that must return True for the event
- timeout: Maximum wait time in seconds
- catch_errors: Whether to catch and ignore errors
Returns:
The matching event object
"""
def prepare_and_wait_for_event(self, *event_types, cond=lambda evt: True, timeout=None, catch_errors=True):
"""
Context manager for waiting on events with preparation.
Parameters:
- event_types: Event types to wait for
- cond: Condition function for event matching
- timeout: Maximum wait time in seconds
- catch_errors: Whether to catch and ignore errors
Returns:
Context manager yielding Future object
"""Convenience methods for waiting on common playback state changes.
def wait_until_paused(self, timeout=None, catch_errors=True):
"""
Wait until playback is paused.
Parameters:
- timeout: Maximum wait time in seconds
- catch_errors: Whether to catch and ignore errors
"""
def wait_until_playing(self, timeout=None, catch_errors=True):
"""
Wait until playback starts.
Parameters:
- timeout: Maximum wait time in seconds
- catch_errors: Whether to catch and ignore errors
"""
def wait_for_playback(self, timeout=None, catch_errors=True):
"""
Wait until playback completes.
Parameters:
- timeout: Maximum wait time in seconds
- catch_errors: Whether to catch and ignore errors
"""
def wait_for_shutdown(self, timeout=None, catch_errors=True):
"""
Wait until mpv core shuts down.
Parameters:
- timeout: Maximum wait time in seconds
- catch_errors: Whether to catch and ignore errors
"""class MpvEvent:
"""Base class for all mpv events."""
@property
def data(self):
"""Event-specific data payload."""
def as_dict(self, decoder=identity_decoder) -> dict:
"""
Convert event to dictionary representation.
Parameters:
- decoder: Function to decode byte strings
Returns:
Dictionary representation of event
"""
def __str__(self) -> str:
"""String representation of the event."""class MpvEventProperty:
"""Event fired when an observed property changes."""
@property
def name(self) -> str:
"""Name of the property that changed."""
@property
def value(self):
"""New value of the property (None if unavailable)."""class MpvEventLogMessage:
"""Event containing log messages from mpv."""
@property
def prefix(self) -> str:
"""Log message prefix/module name."""
@property
def level(self) -> str:
"""Log level ('fatal', 'error', 'warn', 'info', 'debug', 'trace')."""
@property
def text(self) -> str:
"""Log message text."""class MpvEventStartFile:
"""Event fired when file loading starts."""
@property
def playlist_entry_id(self) -> int:
"""Playlist entry ID of the file being loaded."""
class MpvEventEndFile:
"""Event fired when file playback ends."""
@property
def reason(self) -> int:
"""Reason for file ending (see constants below)."""
@property
def playlist_entry_id(self) -> int:
"""Playlist entry ID of the file that ended."""
@property
def playlist_insert_id(self) -> int:
"""Playlist insertion ID."""
@property
def playlist_insert_num_entries(self) -> int:
"""Number of entries inserted."""
# End file reasons
EOF = 0 # End of file reached
RESTARTED = 1 # File restarted (looping)
ABORTED = 2 # Playback aborted
QUIT = 3 # Player quit
ERROR = 4 # Error occurred
REDIRECT = 5 # Redirected to different URLclass MpvEventCommand:
"""Event containing results of asynchronous commands."""
def unpack(self, decoder=identity_decoder):
"""
Unpack command result data.
Parameters:
- decoder: Function to decode byte strings
Returns:
Command result data
"""
@property
def result(self):
"""Command result value."""class MpvEventClientMessage:
"""Event for script messages and IPC communication."""
@property
def args(self) -> list:
"""List of message arguments."""class MpvEventHook:
"""Event for mpv hook mechanism."""
@property
def name(self) -> str:
"""Name of the hook."""Handle script messages and inter-process communication with mpv scripts and external applications.
def register_message_handler(self, target: str, handler=None):
"""
Register a message handler for specific target.
Parameters:
- target: Target name/identifier for message routing
- handler: Handler function (optional if using as decorator)
Returns:
Decorator function if handler not provided
"""
def unregister_message_handler(self, target_or_handler):
"""
Remove a message handler.
Parameters:
- target_or_handler: Target name or handler function to remove
"""
def message_handler(self, target: str):
"""
Decorator for registering message handlers.
Parameters:
- target: Target name for message routing
Returns:
Decorator function for handler registration
"""Send messages to mpv scripts and external applications.
def script_message(self, *args):
"""
Send a message to all scripts.
Parameters:
- args: Message arguments to broadcast to all scripts
"""
def script_message_to(self, target: str, *args):
"""
Send a message to a specific script or target.
Parameters:
- target: Target script name or identifier
- args: Message arguments to send to the target
"""class MpvEventID:
"""Event ID constants for different event types."""
NONE = 0
SHUTDOWN = 1 # Player shutdown
LOG_MESSAGE = 2 # Log message
GET_PROPERTY_REPLY = 3 # Property query response
SET_PROPERTY_REPLY = 4 # Property set response
COMMAND_REPLY = 5 # Command execution response
START_FILE = 6 # File loading started
END_FILE = 7 # File playback ended
FILE_LOADED = 8 # File successfully loaded
CLIENT_MESSAGE = 16 # Script/client message
VIDEO_RECONFIG = 17 # Video output reconfigured
AUDIO_RECONFIG = 18 # Audio output reconfigured
SEEK = 20 # Seek operation completed
PLAYBACK_RESTART = 21 # Playback restarted
PROPERTY_CHANGE = 22 # Property value changed
QUEUE_OVERFLOW = 24 # Event queue overflow
HOOK = 25 # Hook eventimport mpv
player = mpv.MPV()
# Register event callback for all events
def handle_all_events(event):
print(f"Event: {event}")
player.register_event_callback(handle_all_events)
# Register specific event callbacks using decorator
@player.event_callback('end-file')
def handle_end_file(event):
print(f"File ended: {event.data}")
@player.event_callback('property-change')
def handle_property_change(event):
print(f"Property {event.data.name} changed to {event.data.value}")
player.play('/path/to/video.mp4')# Monitor playback progress
@player.event_callback('playback-restart')
def playback_started(event):
print("Playback started/restarted")
@player.event_callback('seek')
def seek_completed(event):
print("Seek operation completed")
# Monitor errors
@player.event_callback('end-file')
def handle_end_file(event):
if event.data == mpv.MpvEventEndFile.ERROR:
print("Playback error occurred")
elif event.data == mpv.MpvEventEndFile.EOF:
print("Playback completed normally")# Custom log handler
@player.event_callback('log-message')
def handle_log(event):
log_msg = event.data
print(f"[{log_msg.level}] {log_msg.prefix}: {log_msg.text}")
# Or set log handler during initialization
def log_handler(loglevel, component, message):
print(f"[{loglevel}] {component}: {message}")
player = mpv.MPV(log_handler=log_handler, loglevel='info')# Wait for file to start playing
player.play('/path/to/video.mp4')
try:
player.wait_until_playing(timeout=10)
print("Playback started successfully")
except TimeoutError:
print("Playback did not start within 10 seconds")
# Wait for specific event with condition
def wait_for_specific_property_change():
event = player.wait_for_event('property-change',
cond=lambda e: e.data.name == 'time-pos' and e.data.value > 30)
print(f"Position passed 30 seconds: {event.data.value}")
# Wait for playback to complete
player.wait_for_playback()
print("Playback finished")# Context manager for event waiting
with player.prepare_and_wait_for_event('end-file') as end_file_future:
player.play('/path/to/video.mp4')
# Do other work while waiting
end_event = end_file_future.result(timeout=60)
print(f"File ended: {end_event.data}")
# Multiple event types
@player.event_callback('start-file', 'file-loaded', 'end-file')
def file_state_handler(event):
event_names = {
mpv.MpvEventID.START_FILE: "loading",
mpv.MpvEventID.FILE_LOADED: "loaded",
mpv.MpvEventID.END_FILE: "ended"
}
print(f"File {event_names.get(event.event_id, 'unknown')}")
# Event filtering and processing
def media_info_processor(event):
if event.event_id == mpv.MpvEventID.FILE_LOADED:
# File loaded - can now access media properties
print(f"Loaded: {player.filename}")
print(f"Duration: {player.duration}")
print(f"Resolution: {player.width}x{player.height}")
player.register_event_callback(media_info_processor)# Robust event handling with error catching
@player.event_callback('property-change')
def safe_property_handler(event):
try:
prop_event = event.data
if prop_event.name == 'time-pos' and prop_event.value is not None:
# Update UI or perform actions
update_progress_bar(prop_event.value)
except Exception as e:
print(f"Error handling property change: {e}")
# Handling event queue overflow
@player.event_callback('queue-overflow')
def handle_overflow(event):
print("Event queue overflow - some events may have been lost")
# Consider reducing event frequency or processing speedclass MediaPlayerController:
def __init__(self):
self.player = mpv.MPV()
self.setup_event_handlers()
def setup_event_handlers(self):
# Bind methods as event handlers
self.player.register_event_callback(self.process_event)
def process_event(self, event):
# Route events to specific handlers
handlers = {
mpv.MpvEventID.START_FILE: self.on_file_start,
mpv.MpvEventID.FILE_LOADED: self.on_file_loaded,
mpv.MpvEventID.END_FILE: self.on_file_end,
mpv.MpvEventID.PROPERTY_CHANGE: self.on_property_change
}
handler = handlers.get(event.event_id)
if handler:
handler(event)
def on_file_start(self, event):
print("Starting file load...")
def on_file_loaded(self, event):
print("File loaded successfully")
def on_file_end(self, event):
print("File playback ended")
def on_property_change(self, event):
prop = event.data
print(f"Property changed: {prop.name} = {prop.value}")Install with Tessl CLI
npx tessl i tessl/pypi-mpv