python-mpv

repository·main·Indexed 20 days ago

https://github.com/jaseg/python-mpv

A ctypes-based Python interface to the mpv media player (v1.0.8) providing control over mpv features similar to the Lua interface. It supports playback control, property observation, custom key bindings, and playlist management. The library allows embedding mpv in PyQT5 and PyGObject (GTK) windows and supports feeding raw data via Python generators. Requires Python >= 3.9 and the libmpv shared library.

Tokens
2.5K
Snippets
8
Records
13
Agent score
21%

What's inside python-mpv

  1. Supported Platforms and known issues

    main

    Linux

    Works fine.

    Windows

    Works fine, but shared library handling can be difficult. Ensure libmpv is in your %PATH% or the module directory.

    OSX

    There are known bugs in the event logic (see issues #36 and #61). A common workaround is to create a PyQT window and have mpv draw into it.

  2. How threading and event handling work in python-mpv

    main

    The mpv module automatically starts one background thread for event handling to ensure MPV events are processed quickly.

    If you need to manage threading manually (e.g., to integrate with an external event loop like asyncio), you can pass start_event_thread=False to the MPV constructor and manually call the object's _loop function.

    All API functions are thread-safe.

  3. Embedding mpv in PyQT5

    main

    To embed mpv in a PyQT5 window, pass the window ID (wid) to the MPV constructor.

    CRITICAL: Because PyQT stomps over locale settings required by libmpv, you must call locale.setlocale(locale.LC_NUMERIC, 'C') after importing PyQT but before creating the first mpv.MPV instance.

    import mpv
    import sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    
    class Test(QMainWindow):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.container = QWidget(self)
            self.setCentralWidget(self.container)
            self.container.setAttribute(Qt.WA_DontCreateNativeAncestors)
            self.container.setAttribute(Qt.WA_NativeWindow)
            player = mpv.MPV(wid=str(int(self.container.winId())),
                    vo='x11', 
                    log_handler=print,
                    loglevel='debug')
            player.play('test.webm')
    
    app = QApplication(sys.argv)
    
    # Required: Fix locale before creating MPV instance
    import locale
    locale.setlocale(locale.LC_NUMERIC, 'C')
    
    win = Test()
    win.show()
    sys.exit(app.exec_())
  4. Basic Usage of python-mpv

    main

    To use python-mpv, import the mpv module and instantiate the MPV class. You can enable ytdl support (for YouTube/etc.) by passing ytdl=True to the constructor. Use .play() to start playback and .wait_for_playback() to block until the media finishes.

    import mpv
    player = mpv.MPV(ytdl=True)
    player.play('https://youtu.be/DOmdB7D-pUU')
    player.wait_for_playback()
  5. Embedding mpv in PyGObject (GTK)

    main

    To embed mpv in a GTK window, pass the XID of the widget to the wid parameter.

    CRITICAL: Similar to PyQT, you must call locale.setlocale(locale.LC_NUMERIC, 'C') after importing Gtk but before creating the first mpv.MPV instance.

    Note: The MPV instance must be created after the widget is shown, otherwise the window property may be None.

    import gi
    import mpv
    import locale
    
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk
    
    class MainClass(Gtk.Window):
        def __init__(self):
            super(MainClass, self).__init__()
            self.set_default_size(600, 400)
            self.connect("destroy", self.on_destroy)
            widget = Gtk.Frame()
            self.add(widget)
            self.show_all()
            # Must be created after widget is shown
            self.mpv = mpv.MPV(wid=str(widget.get_property("window").get_xid()))
            self.mpv.play("test.webm")
    
        def on_destroy(self, widget, data=None):
            self.mpv.terminate()
            Gtk.main_quit()
    
    if __name__ == "__main__":
        # Required: Fix locale before creating MPV instance
        locale.setlocale(locale.LC_NUMERIC, 'C')
        
        application = MainClass()
        Gtk.main()
  6. Enabling the built-in MPV GUI (OSC and Keyboard)

    main

    By default, libmpv disables GUI features like the On-Screen Controller (OSC). To enable them, use these initialization options:

    Standard GUI:

    player = mpv.MPV(input_default_bindings=True, input_vo_keyboard=True, osc=True)

    Pseudo-GUI (Floating Box style):

    player = mpv.MPV(player_operation_mode='pseudo-gui',
                     script_opts='osc-layout=box,osc-seekbarstyle=bar,osc-deadzonesize=0,osc-minmousemove=3',
                     input_default_bindings=True,
                     input_vo_keyboard=True,
                     osc=True)
  7. Loading external subtitles

    main

    There are two ways to load subtitles:

    1. During loadfile: Pass the sub_file argument to player.loadfile().
    2. At runtime: Use player.sub_add(filename). Note that sub_add can only be called after the player has finished loading the file and started playing (e.g., after waiting for core-idle).
    # Method 1: During loadfile
    player.loadfile('test.webm', sub_file='test.srt')
    
    # Method 2: At runtime
    player.play('test.webm')
    player.wait_until_playing()
    player.sub_add('test.srt')
  8. Requirements for python-mpv

    main

    To use python-mpv, you must satisfy the following requirements:

    libmpv

    You must have libmpv.so (or the equivalent shared library for your platform) available either in your current working directory or in your system's library search path.

    Windows Specifics:

    • You can place libmpv anywhere in your %PATH% (e.g., next to python.exe) or next to the mpv.py module.
    • python-mpv uses the DLL search order built into ctypes, which differs from the standard Windows internal search order. You can modify %PATH% before importing python-mpv to control where the DLL is located.

    Python Version

    • Requires Python >= 3.9. The project only supports stable releases from the last few years.
  9. Feeding data from Python to mpv via streams

    main

    You can feed raw data from a Python generator directly into mpv using the @player.python_stream(name) decorator. You then play the stream using the python://name URI scheme.

    import mpv
    
    player = mpv.MPV()
    @player.python_stream('foo')
    def reader():
        with open('test.webm', 'rb') as f:
            while True:
                yield f.read(1024*1024)
    
    player.play('python://foo')
    player.wait_for_playback()
  10. Handling key bindings and screenshots

    main

    You can register custom Python functions to run when specific keys are pressed using the @player.on_key_press(key) decorator. You can also capture screenshots using player.screenshot_raw(), which returns a Pillow image object.

    @player.on_key_press('s')
    def my_s_binding():
        pillow_img = player.screenshot_raw()
        pillow_img.save('screenshot.png')
  11. Accessing and observing MPV properties

    main

    Properties (like metadata, fullscreen, or loop_playlist) can be accessed directly as attributes on the MPV instance.

    • Runtime changes: Many properties can be changed at runtime via attribute assignment (e.g., player.fullscreen = True).
    • Options: Some configuration options require the core to reinitialize and should be set using dictionary-style access (e.g., player['vo'] = 'gpu').
    • Observers: You can use the @player.property_observer(name) decorator to react to changes in a specific property. For example, observing time-pos provides the fractional seconds since the beginning of the file.
    @player.property_observer('time-pos')
    def time_observer(_name, value):
        # value is None if nothing is playing, or a float of fractional seconds
        print('Now playing at {:.2f}s'.format(value))
    
    player.fullscreen = True
    player['vo'] = 'gpu'