Big site update
This commit is contained in:
parent
072b2d38e2
commit
92e3b5f49f
265 changed files with 10549 additions and 877 deletions
401
content/projects/python-mpv/README.rst
Normal file
401
content/projects/python-mpv/README.rst
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
.. vim: tw=120 sw=4 et
|
||||
|
||||
python-mpv is a ctypes-based python interface to the mpv media player. It gives you more or less full control of all
|
||||
features of the player, just as the lua interface does.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
.. code:: bash
|
||||
|
||||
pip install mpv
|
||||
|
||||
|
||||
...though you can also realistically just copy `mpv.py`_ into your project as it's all nicely contained in one file.
|
||||
|
||||
Requirements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
libmpv
|
||||
......
|
||||
``libmpv.so`` either locally (in your current working directory) or somewhere in your system library search path. This
|
||||
module is somewhat lenient as far as ``libmpv`` versions are concerned but since ``libmpv`` is changing quite frequently
|
||||
you'll only get all the newest features when using an up-to-date version of this module. The unit tests for this module
|
||||
do some basic automatic version compatibility checks. If you discover anything missing here, please open an `issue`_ or
|
||||
submit a `pull request`_ on github.
|
||||
|
||||
On Windows you can place libmpv anywhere in your ``%PATH%`` (e.g. next to ``python.exe``) or next to this module's
|
||||
``mpv.py``. Before falling back to looking in the mpv module's directory, python-mpv uses the DLL search order built
|
||||
into ctypes, which is different to the one Windows uses internally. Consult `this stackoverflow post
|
||||
<https://stackoverflow.com/a/23805306>`__ for details.
|
||||
|
||||
Python >= 3.7 (officially)
|
||||
..........................
|
||||
The ``main`` branch officially only supports recent python releases (3.5 onwards), but there is the somewhat outdated
|
||||
but functional `py2compat branch`_ providing Python 2 compatibility.
|
||||
|
||||
.. _`py2compat branch`: https://github.com/jaseg/python-mpv/tree/py2compat
|
||||
.. _`issue`: https://github.com/jaseg/python-mpv/issues
|
||||
.. _`pull request`: https://github.com/jaseg/python-mpv/pulls
|
||||
|
||||
Supported Platforms
|
||||
...................
|
||||
|
||||
**Linux**, **Windows** and **OSX** all seem to work mostly fine. For some notes on the installation on Windows see
|
||||
`this comment`__. Shared library handling is quite bad on windows, so expect some pain there. On OSX there seems to be
|
||||
some bug int the event logic. See `issue 36`_ and `issue 61`_ for details. Creating a pyQT window and having mpv draw
|
||||
into it seems to be a workaround (about 10loc), but in case you want this fixed please weigh in on the issue tracker
|
||||
since right now there is not many OSX users.
|
||||
|
||||
.. __: https://github.com/jaseg/python-mpv/issues/60#issuecomment-352719773
|
||||
.. _`issue 61`: https://github.com/jaseg/python-mpv/issues/61
|
||||
.. _`issue 36`: https://github.com/jaseg/python-mpv/issues/36
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
.. code:: python
|
||||
|
||||
import mpv
|
||||
player = mpv.MPV(ytdl=True)
|
||||
player.play('https://youtu.be/DOmdB7D-pUU')
|
||||
player.wait_for_playback()
|
||||
|
||||
python-mpv mostly exposes mpv's built-in API to python, adding only some porcelain on top. Most "`input commands <https://mpv.io/manual/master/#list-of-input-commands>`_" are mapped to methods of the MPV class. Check out these methods and their docstrings in `the source <https://github.com/jaseg/python-mpv/blob/main/mpv.py>`__ for things you can do. Additional controls and status information are exposed through `MPV properties <https://mpv.io/manual/master/#properties>`_. These can be accessed like ``player.metadata``, ``player.fullscreen`` and ``player.loop_playlist``.
|
||||
|
||||
Threading
|
||||
~~~~~~~~~
|
||||
|
||||
The ``mpv`` module starts one thread for event handling, since MPV sends events that must be processed quickly. The
|
||||
event queue has a fixed maxmimum size and some operations can cause a large number of events to be sent.
|
||||
|
||||
If you want to handle threading yourself, you can pass ``start_event_thread=False`` to the ``MPV`` constructor and
|
||||
manually call the ``MPV`` object's ``_loop`` function. If you have some strong need to not use threads and use some
|
||||
external event loop (such as asyncio) instead you can do that, too with some work. The API of the backend C ``libmpv``
|
||||
has a function for producing a sort of event file descriptor for a handle. You can use that to produce a file descriptor
|
||||
that can be passed to an event loop to tell it to wake up the python-mpv event handler on every incoming event.
|
||||
|
||||
All API functions are thread-safe. If one is not, please file an issue on github.
|
||||
|
||||
Advanced Usage
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Logging, Properties, Python Key Bindings, Screenshots and youtube-dl
|
||||
....................................................................
|
||||
|
||||
.. code:: python
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import mpv
|
||||
|
||||
def my_log(loglevel, component, message):
|
||||
print('[{}] {}: {}'.format(loglevel, component, message))
|
||||
|
||||
player = mpv.MPV(log_handler=my_log, ytdl=True, input_default_bindings=True, input_vo_keyboard=True)
|
||||
|
||||
# Property access, these can be changed at runtime
|
||||
@player.property_observer('time-pos')
|
||||
def time_observer(_name, value):
|
||||
# Here, _value is either None if nothing is playing or a float containing
|
||||
# fractional seconds since the beginning of the file.
|
||||
print('Now playing at {:.2f}s'.format(value))
|
||||
|
||||
player.fullscreen = True
|
||||
player.loop_playlist = 'inf'
|
||||
# Option access, in general these require the core to reinitialize
|
||||
player['vo'] = 'gpu'
|
||||
|
||||
@player.on_key_press('q')
|
||||
def my_q_binding():
|
||||
print('THERE IS NO ESCAPE')
|
||||
|
||||
@player.on_key_press('s')
|
||||
def my_s_binding():
|
||||
pillow_img = player.screenshot_raw()
|
||||
pillow_img.save('screenshot.png')
|
||||
|
||||
player.play('https://youtu.be/DLzxrzFCyOs')
|
||||
player.wait_for_playback()
|
||||
|
||||
del player
|
||||
|
||||
Skipping silence using libav filters
|
||||
....................................
|
||||
|
||||
The following code uses the libav silencedetect filter to skip silence at the beginning of a file. It works by loading
|
||||
the filter, then parsing its output from mpv's log. Thanks to Sean DeNigris on github (#202) for the original code!
|
||||
|
||||
.. code:: python
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import mpv
|
||||
|
||||
p = mpv.MPV()
|
||||
p.play(sys.argv[1])
|
||||
|
||||
def skip_silence():
|
||||
p.set_loglevel('debug')
|
||||
p.af = 'lavfi=[silencedetect=n=-20dB:d=1]'
|
||||
p.speed = 100
|
||||
def check(evt):
|
||||
toks = evt['event']['text'].split()
|
||||
if 'silence_end:' in toks:
|
||||
return float(toks[2])
|
||||
p.time_pos = p.wait_for_event('log_message', cond=check)
|
||||
p.speed = 1
|
||||
p.af = ''
|
||||
|
||||
skip_silence()
|
||||
p.wait_for_playback()
|
||||
|
||||
Video overlays
|
||||
..............
|
||||
|
||||
.. code:: python
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import time
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
import mpv
|
||||
|
||||
player = mpv.MPV()
|
||||
|
||||
player.loop = True
|
||||
player.play('test.webm')
|
||||
player.wait_until_playing()
|
||||
|
||||
font = ImageFont.truetype('DejaVuSans.ttf', 40)
|
||||
|
||||
while not player.core_idle:
|
||||
|
||||
time.sleep(0.5)
|
||||
overlay = player.create_image_overlay()
|
||||
|
||||
for pos in range(0, 500, 5):
|
||||
ts = player.time_pos
|
||||
if ts is None:
|
||||
break
|
||||
|
||||
img = Image.new('RGBA', (400, 150), (255, 255, 255, 0))
|
||||
d = ImageDraw.Draw(img)
|
||||
d.text((10, 10), 'Hello World', font=font, fill=(0, 255, 255, 128))
|
||||
d.text((10, 60), f't={ts:.3f}', font=font, fill=(255, 0, 255, 255))
|
||||
|
||||
overlay.update(img, pos=(2*pos, pos))
|
||||
time.sleep(0.05)
|
||||
|
||||
overlay.remove()
|
||||
|
||||
|
||||
Playlist handling
|
||||
.................
|
||||
|
||||
.. code:: python
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import mpv
|
||||
|
||||
player = mpv.MPV(ytdl=True, input_default_bindings=True, input_vo_keyboard=True)
|
||||
|
||||
player.playlist_append('https://youtu.be/PHIGke6Yzh8')
|
||||
player.playlist_append('https://youtu.be/Ji9qSuQapFY')
|
||||
player.playlist_append('https://youtu.be/6f78_Tf4Tdk')
|
||||
|
||||
player.playlist_pos = 0
|
||||
|
||||
while True:
|
||||
# To modify the playlist, use player.playlist_{append,clear,move,remove}. player.playlist is read-only
|
||||
print(player.playlist)
|
||||
player.wait_for_playback()
|
||||
|
||||
Directly feeding mpv data from python
|
||||
.....................................
|
||||
|
||||
.. code:: python
|
||||
|
||||
#!/usr/bin/env python3
|
||||
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()
|
||||
|
||||
Using external subtitles
|
||||
........................
|
||||
|
||||
The easiest way to load custom subtitles from a file is to pass the ``--sub-file`` option to the ``loadfile`` call:
|
||||
|
||||
.. code:: python
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import mpv
|
||||
|
||||
player = mpv.MPV()
|
||||
player.loadfile('test.webm', sub_file='test.srt')
|
||||
player.wait_for_playback()
|
||||
|
||||
Note that you can also pass many other options to ``loadfile``. See the mpv docs for details.
|
||||
|
||||
If you want to add subtitle files or streams at runtime, you can use the ``sub-add`` command. ``sub-add`` can only be
|
||||
called once the player is done loading the file and starts playing. An easy way to wait for this is to wait for the
|
||||
``core-idle`` property.
|
||||
|
||||
.. code:: python
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import mpv
|
||||
|
||||
player = mpv.MPV()
|
||||
player.play('test.webm')
|
||||
player.wait_until_playing()
|
||||
player.sub_add('test.srt')
|
||||
player.wait_for_playback()
|
||||
|
||||
Using MPV's built-in GUI
|
||||
........................
|
||||
|
||||
python-mpv is using mpv via libmpv. libmpv is meant for embedding into other applications and by default disables most
|
||||
GUI features such as the OSD or keyboard input. To enable the built-in GUI, use the following options when initializing
|
||||
the MPV instance. See `Issue 102`_ for more details
|
||||
|
||||
.. _`issue 102`: https://github.com/jaseg/python-mpv/issues/61
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Enable the on-screen controller and keyboard shortcuts
|
||||
player = mpv.MPV(input_default_bindings=True, input_vo_keyboard=True, osc=True)
|
||||
|
||||
# Alternative version using the old "floating box" style on-screen controller
|
||||
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)
|
||||
|
||||
PyQT embedding
|
||||
..............
|
||||
|
||||
.. code:: python
|
||||
|
||||
#!/usr/bin/env python3
|
||||
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', # You may not need this
|
||||
log_handler=print,
|
||||
loglevel='debug')
|
||||
player.play('test.webm')
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
# This is necessary since PyQT stomps over the locale settings needed by libmpv.
|
||||
# This needs to happen after importing PyQT before creating the first mpv.MPV instance.
|
||||
import locale
|
||||
locale.setlocale(locale.LC_NUMERIC, 'C')
|
||||
win = Test()
|
||||
win.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
PyGObject embedding
|
||||
...................
|
||||
|
||||
.. code:: python
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import gi
|
||||
|
||||
import mpv
|
||||
|
||||
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< the widget is shown, else property 'window' will be None
|
||||
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__':
|
||||
# This is necessary since like Qt, Gtk stomps over the locale settings needed by libmpv.
|
||||
# Like with Qt, this needs to happen after importing Gtk but before creating the first mpv.MPV instance.
|
||||
import locale
|
||||
locale.setlocale(locale.LC_NUMERIC, 'C')
|
||||
|
||||
application = MainClass()
|
||||
Gtk.main()
|
||||
|
||||
Using OpenGL from PyGObject
|
||||
...........................
|
||||
|
||||
Just like it is possible to render into a GTK widget through X11 windows, it `also is possible to render into a GTK
|
||||
widget using OpenGL <https://gist.github.com/jaseg/657e8ecca3267c0d82ec85d40f423caa>`__ through this python API.
|
||||
|
||||
Using OpenGL from PyQt5/QML
|
||||
...........................
|
||||
|
||||
Robozman_ has mangaed to `make mpv render into a PyQt5/QML widget using OpenGL
|
||||
<https://gitlab.com/robozman/python-mpv-qml-example>`__ through this python API.
|
||||
|
||||
Using mpv inside imgui inside OpenGL via GLFW
|
||||
.............................................
|
||||
|
||||
dfaker_ has written a demo (`link <https://github.com/dfaker/imgui_glfw_pythonmpv_demo/blob/main/main.py>`__) that uses mpv to render video into an `imgui <https://github.com/ocornut/imgui>`__ UI running on an OpenGL context inside `GLFW <https://www.glfw.org/>`__. Check out their demo to see how to integrate with imgui/OpenGL and how to access properties and manage the lifecycle of an MPV instance.
|
||||
|
||||
Running tests
|
||||
-------------
|
||||
|
||||
Use pytest to run tests.
|
||||
|
||||
Coding Conventions
|
||||
------------------
|
||||
|
||||
The general aim is `PEP 8`_, with liberal application of the "consistency" section. 120 cells line width. Four spaces.
|
||||
No tabs. Probably don't bother making pure-formatting PRs except if you think it *really* helps readability or it
|
||||
*really* irks you if you don't.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
python-mpv inherits the underlying libmpv's license, which can be either GPLv2 or later (default) or LGPLv2.1 or later.
|
||||
For details, see `the mpv copyright page`_.
|
||||
|
||||
.. _`PEP 8`: https://www.python.org/dev/peps/pep-0008/
|
||||
.. _`mpv.py`: https://raw.githubusercontent.com/jaseg/python-mpv/main/mpv.py
|
||||
.. _cosven: https://github.com/cosven
|
||||
.. _Robozman: https://gitlab.com/robozman
|
||||
.. _dfaker: https://github.com/dfaker
|
||||
.. _`the mpv copyright page`: https://github.com/mpv-player/mpv/blob/master/Copyright
|
||||
|
||||
18
content/projects/python-mpv/index.rst
Normal file
18
content/projects/python-mpv/index.rst
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: "python-mpv"
|
||||
external_links:
|
||||
- name: Sources
|
||||
url: "https://git.jaseg.de/python-mpv.git"
|
||||
- name: Issues
|
||||
url: "https://github.com/jaseg/python-mpv/issues"
|
||||
- name: Docs
|
||||
url: "https://neinseg.gitlab.io/python-mpv"
|
||||
- name: PyPI
|
||||
url: "https://pypi.org/projects/mpv"
|
||||
summary: >
|
||||
python-mpv is a small, ctypes-based Python library wrapping the libmpv media player library. Despite its small size
|
||||
and simple API, python-mpv allows advanced control over libmpv and beyond simple remote control of mpv can be used
|
||||
to embed mpv in OpenGL, Qt, and GTK-based Python applications.
|
||||
---
|
||||
|
||||
.. include:: content/projects/python-mpv/README.rst
|
||||
Loading…
Add table
Add a link
Reference in a new issue