Skip to content

Instantly share code, notes, and snippets.

@progandy
Last active March 6, 2024 17:51
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save progandy/dc50c4b9a74cc8c74f816a500c7a7d87 to your computer and use it in GitHub Desktop.
Save progandy/dc50c4b9a74cc8c74f816a500c7a7d87 to your computer and use it in GitHub Desktop.
Minimal MPRIS2 support for mpv
local lgi = require 'lgi'
local GObject = lgi.require 'GObject'
local Gio = lgi.require 'Gio'
local GLib = lgi.require 'GLib'
local core = require 'lgi.core'
local mp = require "mp"
local MPRIS2_OBJPATH = "/org/mpris/MediaPlayer2"
local xml = [[<?xml version="1.0" encoding="UTF-8"?>
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg direction="in" type="s"/>
<arg direction="in" type="s"/>
<arg direction="out" type="v"/>
</method>
<method name="Set">
<arg direction="in" type="s"/>
<arg direction="in" type="s"/>
<arg direction="in" type="v"/>
</method>
<method name="GetAll">
<arg direction="in" type="s"/>
<arg direction="out" type="a{sv}"/>
</method>
<signal name="PropertiesChanged">
<arg type="s"/>
<arg type="a{sv}"/>
<arg type="as"/>
</signal>
</interface>
<interface name="org.mpris.MediaPlayer2">
<property name="Identity" type="s" access="read" />
<property name="DesktopEntry" type="s" access="read" />
<property name="SupportedMimeTypes" type="as" access="read" />
<property name="SupportedUriSchemes" type="as" access="read" />
<property name="HasTrackList" type="b" access="read" />
<property name="CanQuit" type="b" access="read" />
<property name="CanSetFullscreen" type="b" access="read" />
<property name="Fullscreen" type="b" access="readwrite" />
<property name="CanRaise" type="b" access="read" />
<method name="Quit" />
<method name="Raise" />
</interface>
<interface name="org.mpris.MediaPlayer2.Player">
<property name="Metadata" type="a{sv}" access="read" />
<property name="PlaybackStatus" type="s" access="read" />
<property name="LoopStatus" type="s" access="readwrite" />
<property name="Volume" type="d" access="readwrite" />
<property name="Shuffle" type="d" access="readwrite" />
<property name="Position" type="i" access="read" />
<property name="Rate" type="d" access="readwrite" />
<property name="MinimumRate" type="d" access="readwrite" />
<property name="MaximumRate" type="d" access="readwrite" />
<property name="CanControl" type="b" access="read" />
<property name="CanPlay" type="b" access="read" />
<property name="CanPause" type="b" access="read" />
<property name="CanSeek" type="b" access="read" />
<method name="Previous" />
<method name="Next" />
<method name="Stop" />
<method name="Play" />
<method name="Pause" />
<method name="PlayPause" />
<method name="Seek">
<arg type="x" direction="in" />
</method> <method name="OpenUri">
<arg type="s" direction="in" />
</method>
<method name="SetPosition">
<arg type="o" direction="in" />
<arg type="x" direction="in" />
</method>
</interface>
<interface name="org.mpris.MediaPlayer2.TrackList">
<property name="Tracks" type="ao" access="read" />
<property name="CanEditTracks" type="b" access="read" />
<method name="GetTracksMetadata">
<arg type="ao" direction="in" />
<arg type="aa{sv}" direction="out" />
</method>
<method name="AddTrack">
<arg type="s" direction="in" />
<arg type="o" direction="in" />
<arg type="b" direction="in" />
</method>
<method name="RemoveTrack">
<arg type="o" direction="in" />
</method>
<method name="GoTo">
<arg type="o" direction="in" />
</method>
<signal name="TrackListReplaced">
<arg type="ao" />
<arg type="o" />
</signal>
<signal name="TrackAdded">
<arg type="a{sv}" />
<arg type="o" />
</signal>
<signal name="TrackRemoved">
<arg type="o" />
</signal>
<signal name="TrackMetadataChanged">
<arg type="o" />
<arg type="a{sv}" />
</signal>
</interface>
</node>
]]
local node_info = Gio.DBusNodeInfo.new_for_xml(xml)
function getPid()
require "io"
local f=io.open("/proc/self/stat")
local pid=f:read("*n")
f:close()
return pid
end
function handle_call(conn, sender, obj_path, intf_name, meth_name, params, invoc)
if obj_path == MPRIS2_OBJPATH then
if intf_name == "org.mpris.MediaPlayer2" then
if meth_name == "Quit" then
mp.command("quit")
end
elseif intf_name == "org.mpris.MediaPlayer2.Player" then
if meth_name == "Play" then
mp.set_property_native("pause", false)
invoc:return_value(nil)
return
elseif meth_name == "Pause" then
mp.set_property_native("pause", true)
invoc:return_value(nil)
return
elseif meth_name == "Stop" then
mp.command("stop")
invoc:return_value(nil)
return
elseif meth_name == "Previous" then
mp.command("playlist-prev")
invoc:return_value(nil)
return
elseif meth_name == "Next" then
mp.command("playlist-next")
invoc:return_value(nil)
return
elseif meth_name == "Seek" then
if params and params:get_type_string() == "(x)" then
local us = params.value[1]
mp.commandv("seek", string.format("%d", us / 1000000), "relative")
invoc:return_value(nil)
return
end
elseif meth_name == "PlayPause" then
local pause = mp.get_property_native("pause")
mp.set_property_native("pause", not pause)
invoc:return_value(nil)
return
end
elseif intf_name == "org.freedesktop.DBus.Properties" then
if meth_name == "Get" then
if params and params:get_type_string() == "(ss)" then
local p= params.value
print("[NYI] Property Get:", p[1], p[2])
-- TODO implement getter
-- invoc:return_value(TODO)
-- return
end
elseif meth_name == "Set" then
if params and params:get_type_string() == "(ssv)" then
local p= params.value
print("[NYI] Property Set:", p[1], p[2])
-- TODO implement setter
-- invoc:return_value(nil)
-- return
end
end
end
end
invoc:return_dbus_error("org.freedesktop.DBus.Error.UnknownMethod", "No such method '" .. meth_name .. "' in interface '" .. intf_name .. "'")
end
local _m = {}
local last_timeout = nil;
_m.dispatch_events = function()
mp.dispatch_events()
local time = mp.get_next_timeout();
if time then
if time == last_timeout then return true end
GLib.timeout_add(time, _m.dispatch_timeout)
end
last_timeout = time
return false
end
_m.dispatch = function(fd)
local _ = _m.dispatch
local st = _m.ioc:read_line_string(_m.buf, 0)
_m.dispatch_events()
if not mp.keep_running then return false end
return true
end
function quit()
Gio.bus_unown_name(_m.name_id)
_m.mainloop:quit()
end
_m.mainloop = GLib.MainLoop()
_m.node_info = node_info
_m.fd = mp.get_wakeup_pipe()
_m.ioc = GLib.IOChannel.unix_new(_m.fd)
_m.buf = GLib.String()
_m.source = GLib.unix_fd_add_full(0,
_m.fd,
GLib.IOCondition{"IN", "HUP", "ERR", "OUT"},
_m.dispatch, nil)
_G.mp_event_loop = function()
mp.register_event("shutdown", quit)
_m.mainloop:run()
end
BUSNAME = string.format("org.mpris.MediaPlayer2.mpv.pid%s", getPid())
_m.name_id = Gio.bus_own_name(Gio.BusType.SESSION, BUSNAME, Gio.BusNameOwnerFlags.NONE,
GObject.Closure(function(conn, name)
-- print("Got Bus")
for i = 1, #node_info.interfaces do
conn:register_object(
MPRIS2_OBJPATH,
node_info.interfaces[i],
GObject.Closure(handle_call),
GObject.Closure(function() print "get_property_closure" end),
GObject.Closure(function() print "set_property_closure" end)
)
end
end),
GObject.Closure(function()
-- print("Got Name")
end),
GObject.Closure(function()
-- print("Name Lost")
end)
)
# PKGBUILD for lua52-lgi
# Maintainer: NONE
# lua-lgi Maintainer: speps <speps at aur dot archlinux dot org>
# lua-lgi Maintainer: Sébastien Luttringer
_pkgbase=lgi
pkgname=lua52-lgi
pkgver=0.9.1
pkgrel=1
pkgdesc='Lua 5.2 bindings for gnome/gobject using gobject-introspection library'
arch=(i686 x86_64)
url='https://github.com/pavouk/lgi'
license=('custom:MIT')
depends=('glibc' 'glib2' 'libffi' 'lua52' 'gobject-introspection-runtime')
makedepends=('gobject-introspection')
replaces=('lgi')
conflicts=('lgi')
source=("$_pkgbase-$pkgver.tar.gz::https://github.com/pavouk/$_pkgbase/archive/$pkgver.tar.gz")
md5sums=('0c85f177e4b8192bd849b4e508c8bdf0')
build() {
cd $_pkgbase-$pkgver
make PREFIX=/usr LUA_VERSION=5.2 LUA_CFLAGS="$(pkg-config --cflags lua52) -O2"
}
package() {
cd $_pkgbase-$pkgver
make \
LUA_LIBDIR=/usr/lib/lua/5.2 \
LUA_SHAREDIR=/usr/share/lua/5.2 \
DESTDIR="$pkgdir/" install
# dump typelib tool
install -Dm755 tools/dump-typelib.lua \
"$pkgdir/usr/bin/dump-typelib-lua5.2"
# docs
install -d "$pkgdir/usr/share/doc/$pkgname"
install -Dm644 docs/* \
"$pkgdir/usr/share/doc/$pkgname"
# samples
install -d "$pkgdir/usr/share/$pkgname/samples/gtk-demo"
install -Dm644 samples/*.lua \
"$pkgdir/usr/share/$pkgname/samples"
install -Dm644 samples/gtk-demo/* \
"$pkgdir/usr/share/$pkgname/samples/gtk-demo"
# license
install -Dm644 LICENSE \
"$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}
# vim:set ts=2 sw=2 et:
@quequotion
Copy link

This plugin provides a working mpris2 interface, but the interface doesn't call itself "mpv" or use mpv's icon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment