I was wondering if it's a possibility in the future.
Unlikely.
It requires dbus. We certainly don't want to depend on dbus or any dbus client libraries. Also, these dbus client libraries are either horrible to use, or depend on entire toolkits (Qt/GTK).
If you want to get the type of data available from mpris2 you can use
--status-msg=<string>
Print out a custom string during playback instead of the standard status line. Expands properties. SeeProperty Expansion_.
to format terminal output in a easily script parsible format.
The "See Property Expansion_." link in the manual seems to be broken (and the section I remember on it appears to be missing or moved) though so it could be hard to find what the available options are.
This is written to stderr so if you redirect stderr to somewhere where the program you wanted to get the mpris2 data can read and parse it you can get the data that way.
This can be used to do things like displaying progress bars in other programs, in this case conky.
http://sta.sh/09yz3gdelvw
I would not use this method for anything but personal scripts however since redirecting stderr prevents status output (like real error messages) from being displayed to the user in the terminal.
It used to be possible to use bash redirection to show stderr in the terminal and send it elsewhere but that stopped working in the master branch most likely due to this commit.
6759941fca5820d62dbc30948b4863f49df21419
Using '2>&1 | tee "/tmp/filename.txt"' spams the status message to the console instead of refreshing the line after that change.
It prints to the console fine if you don't use that redirection trick though.
It is also kind of complex since I had to use three scripts to make my example work.
It may be possible to use the Lua scripting added with the OSD to write the pertinent data out to a text file as well but I have not investigated that possibility yet.
I wrote my conky status bar script before the OSD was functional so the Lua scripting in mpv was not a option at the time.
I also agree with you wm4, dbus libraries are horrible to use.
I wrote a similar conky status bar for smplayer2 using the python2 dbus library to access its mpris2 data and it was not a good experience.
I found the documentation woefully lacking it realistic examples of the libraries use.
The "See Property Expansion_." link in the manual seems to be broken (and the section I remember on it appears to be missing or moved) though so it could be hard to find what the available options are.
Github's rst render is broken or doesn't support this. Anyway, it's a section input.rst.
It may be possible to use the Lua scripting added with the OSD to write the pertinent data out to a text file as well but I have not investigated that possibility yet.
Yes that should definitely be possible. In context of this issue, a Lua script could even load dbus libraries and implement mpris in Lua (if there is a Lua dbus library; I don't know).
Github's rst render is broken or doesn't support this. Anyway, it's a section input.rst.
Thanks I see it there, I made a bookmark to remind me when I go looking for it again.
Yes that should definitely be possible. In context of this issue, a Lua script could even load dbus libraries and implement mpris in Lua (if there is a Lua dbus library; I don't know).
There is a Lua dbus library. https://github.com/xs-embedded-llc/l2dbus
I don't know how well it works though.
Depending on how lua scripts are run in mpv it may not be useable with mpv however.
If they work like Lua scripts in conky where the script is run once every update and unless you write your script very carefully even global variables can be reset if you make a change to your lua script while conky is running.
If it works like that I suspect the dbus library would not work because the dbus connection would die and be reopened every time the script runs (every screen update?).
This would prevent other programs from communicating over the dbus interface with the script reliably (or at all possibly).
It would also likely cause lag each time the script setup a new dbus interface.
You could possibly work around this using one the multithreading Lua libraries.
I know that LuaLanes works with conky Lua scripts but requires some "Fun" control logic to handle sending data between the main thread and the child thread due to the way Lua scripts are run by conky. https://github.com/LuaLanes/lanes
Depending on how lua scripts are run in mpv it may not be useable with mpv however.
If they work like Lua scripts in conky where the script is run once every update and unless you write your script very carefully even global variables can be reset if you make a change to your lua script while conky is running.
In git master, Lua scripts run in their own thread. By default, Lua a script is loaded only once. After initialization it enters an event loop, which waits for mpv event and dispatches them to callbacks registered by the script. So, usually, the script sets up event handlers, and then returns to C, and then C calls the event loop (which is written in Lua, see player/lua/defaults.lua, mp_event_loop). But in theory, a script could do something else, like enter a dbus dispatch loop (?) and poll for mpv events instead.
(Note that this is only so in git master. mpv 0.3.x is entirely different.)
There is a Lua dbus library. https://github.com/xs-embedded-llc/l2dbus
Looks like this requires a complicated "mainloop" abstraction, and there are pre-made glib and libev mainloop implementations. Sounds like a pain to integrate.
I don't think they would add much value in this case.
In git master, Lua scripts run in their own thread. By default, Lua a script is loaded only once. After initialization it enters an event loop, which waits for mpv event and dispatches them to callbacks registered by the script. So, usually, the script sets up event handlers, and then returns to C, and then C calls the event loop (which is written in Lua, see player/lua/defaults.lua, mp_event_loop). But in theory, a script could do something else, like enter a dbus dispatch loop (?) and poll for mpv events instead.
In that case what I think you would do is have your Lua script on load create a Lua table with dummy entires to be used to make replies to dbus queries.
This table would be populated with real values and updated by functions run by callbacks registered to mpv events (probably file opens, screen draws (position updates only), setting changes (like volume adjustments), and Playing/Paused/Stopped/Other state changes).
Then you would create and attach the dbus interface to the session bus and start listening for queries.
When you received a query over dbus it would trigger a function bound to the corresponding query from the mpris2 specification that would use the data in the Lua table to format and send the correct mpris2 response for the query.
If the query was not from the mpris2 specification you would send a error response of some kind.
Learning to use the dbus library and writing the functions to implement the mpris2 specification would be the hard and annoying part.
Looks like this requires a complicated "mainloop" abstraction, and there are pre-made glib and libev mainloop implementations. Sounds like a pain to integrate.
It was the only one I found that was still being maintained, the other one was from 2009.
This table would be populated with real values and updated by functions run by callbacks registered to mpv events (probably file opens, screen draws (position updates only), setting changes (like volume adjustments), and Playing/Paused/Stopped/Other state changes).
Well, the main problem is that both the dbus code and the mpv code want to wait for events. So they have to work somehow together, on some level. How that can be accomplished depends entirely on the dbus code. The mpv code could be changed to help with that.
For example, it might be possible that the dbus API allows listening to an additional user provided file descriptor. Then mpv could provide a "wakeup pipe" that gets written if a mpv event happens, which would wakeup the dbus event loop, and would allow the Lua script to check for mpv events.
Anyway, trying to integrate dbus and Lua (or dbus and anything) is probably messy.
For example, it might be possible that the dbus API allows listening to an additional user provided file descriptor. Then mpv could provide a "wakeup pipe" that gets written if a mpv event happens, which would wakeup the dbus event loop, and would allow the Lua script to check for mpv events.
I doubt that is possible since we are not actually setting up all of dbus (the session bus is created when you login).
What this script would do would be to attach to the already created session bus for the current user and listen for messages intended for it.
When it catches one sent to its "address" (or whatever they call it in dbus) it checks if it matches one of the commands that activate a function hook and if it matches runs the corresponding function.
Some functions send a reply back to the client ("The current file is "12345.mp3") or possibly change a setting in the program (Toggle the Play/Pause state).
If it is possible to request data from mpv at anytime from a Lua script and not on a mpv event you could have the Lua script just get that data from mpv inside the functions called by the dbus hooks.
I think it is more trouble than it's worth to work on though since dbus is just annoying in general.
I doubt that is possible since we are not actually setting up all of dbus (the session bus is created when you login).
What this script would do would be to attach to the already created session bus for the current user and listen for messages intended for it.
That doesn't really have to do with anything. I'm talking about dbus API usage (whatever that API is, there are multiple). Not really sure what you're talking about, maybe you're misunderstanding something, or we're somehow talking past each other.
When it catches one sent to its "address" (or whatever they call it in dbus) it checks if it matches one of the commands that activate a function hook and if it matches runs the corresponding function.
The main problem is that the dbus API has to do this "catching" somewhere, like a loop that calls select() or poll() on a dbus socket, and reads messages from there. So where does it do that? It could start a thread, but then you need some major synchronization. Or it could require a mainloop abstraction, like glib... then code using that dbus API has to use dbus too. And so on...
If it is possible to request data from mpv at anytime from a Lua script and not on a mpv event you could have the Lua script just get that data from mpv inside the functions called by the dbus hooks.
Yes, that's possible. But again, the question is how you integrate dbus operation (i.e. event loop) with the mpv/Lua event loop. How easy or hard this is depends on many things.
Not really sure what you're talking about, maybe you're misunderstanding something, or we're somehow talking past each other.
I think it is a little of both.
I don't really do any programming as low level as you are thinking at so you were not being quite specific enough for me to know what issues you were trying to get at.
Would it be possible to expose a mp.has_event function? Then you can easily create your own GSource for mpv events and use lua-lgi with a glib mainloop. You can already work with it, but it might not be really easy. I'll try to throw something together.
Edit: has_event doesn't really make a good GSource basis. A filedescriptor would be better
FWIW, I did look into this a while ago, but I ended up writing https://github.com/ghedo/grooved (warning, ugly code ahead) because I needed additional features.
The libdbus is actually rather "lightweight" (at least in terms of dependencies, since it only depends on libc), and it gets pulled in anyway due to libpulse. The mpris support could be made so that if there's no dbus daemon running, it simply is disabled.
The basic libdbus API is kinda convoluted and it requires a bit of glue code to make it work with an external IO loop, but it's doable and it shouldn't interfere much with the rest of mpv.
Here is a basic example for mixing a glib context with the lua mpv loop. Sadly this forces us to use active polling. Would it be possible to trigger a file descriptor when events arrive?
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 xml=[[<?xml version="1.0" encoding="UTF-8"?>
<node name="/org/pa/Test">
<interface name="org.pa.Test">
<method name="add">
<arg name="result" direction="out" type="d"/>
<arg name="a" direction="in" type="d"/>
<arg name="b" direction="in" type="d"/>
</method>
</interface>
</node>]]
local _m = {}
_m.main_context = GLib.MainContext.new()
_m.main_context:push_thread_default()
require "mp"
--mp = {keep_running = true, wait_event=function(a) return {event="none"} end }
_m.mp_event_loop = function(_m)
while mp.keep_running do
_m.main_context:iteration(false)
local e = mp.wait_event(0.01)
if (e.event ~= "none") then
mp.suspend()
repeat
if (e.event == "shutdown") then return; end
print(e.event)
print("!! Default lua api handlers won't work anymore!!")
e = mp.wait_event(0)
until (e.event == "none")
mp.resume()
end
end
end
_G.mp_event_loop = function() _m:mp_event_loop() end
function handle_call(conn, sender, obj_path, intf_name, meth_name, params, invoc)
print("-----")
if obj_path == "/org/pa/Test" then
if intf_name == "org.pa.Test" then
if meth_name == "add" then
if params and params:get_type_string() == "(dd)" then
local p = params.value
local r = (p[1] + p[2])
invoc:return_value(GLib.Variant('(d)', {r}))
return
end
end
end
end
invoc:return_dbus_error("org.freedesktop.DBus.Error.UnknownMethod", "No such method '" .. meth_name .. "'")
end
local handle_call_guard, handle_call_addr = core.marshal.callback(Gio.DBusInterfaceMethodCallFunc, handle_call)
local vtable = Gio.DBusInterfaceVTable { method_call=handle_call_addr }
local node_info = Gio.DBusNodeInfo.new_for_xml(xml)
_m.__gcguard = { handle_call_addr, handle_call_guard, vtable, node_info }
Gio.bus_own_name(Gio.BusType.SESSION, "org.pa.Test", Gio.BusNameOwnerFlags.NONE,
GObject.Closure(function(conn, name)
print("Got Bus")
conn:register_object("/org/pa/Test", node_info.interfaces[1], vtable)
end),
GObject.Closure(function() print("Got Name") end),
GObject.Closure(function() print("Name Lost") end)
)
Would it be possible to trigger a file descriptor when events arrive?
Sure. Does glib provide something? Or do we have to create a "wakeup pipe" ourselves?
Sure. Does glib provide something? Or do we have to create a "wakeup pipe" ourselves?
mpv should provide a pipe that is triggered during wakeup_client. You could do all communication over this pipe (much work in API changes) or simply use it as a signal. Maybe create the pipe only as response to a call to e.g. mpv_get_fd.
In a nutshell glib does poll() on filedescriptors. if an event arrives, the callback registered for the filedescriptor is called.
Edit: lua-lgi is somehow incapable of creating a GSource (at least I cannot do it), so that may require an additional lua library written in c, but that doesn't have to be part of mpv.
Well, I can add such a pipe. Of course mpv itself can't provide code for adding a GSource (unless we add a dependency on glib, which we definitely don't want).
I'll also look whether it's possible to reuse the Lua event loop provided by mpv in such a scenario.
The basic libdbus API is kinda convoluted and it requires a bit of glue code to make it work with an external IO loop, but it's doable and it shouldn't interfere much with the rest of mpv.
That might be something for external C plugins, except that we don't provide such a thing yet. As for libdbus, I want to avoid it as direct mpv dependency as far as possible.
OK added some stuff. See the commit github mentioned, and following commits.
Is is possible to use mpris interface (with some lua scripts?) now?
Not that I know of.
This would be really great to have. MPRIS2 is being used for usecases that are just magical. For example, when my phone rings, KDE Connect uses MPRIS2 to stop running video or music playback on my media center PC, and resumes playback once the call is finished. This works like a charm with e.g. VLC, but sadly not with mpv.
Hi, i wrote https://github.com/dodo/lua-mpris to overcome this problem (using kdeconnect as well) and to add mpris controls to the awesome window manager.
Sadly most of the documentation is missing and the ldbus package needs installation by hand (plus my PR is required). But if you know lua and dbus and stuff you can start using it, yay.
So does this script solve the issue?
(Also I think the installation instructions for this script might be busted.)
Thanks for the hint. Added some install instructions to the readme.
Yes, that script solves this issue.
@dodo: added your script to the wiki. I think I hereby consider this issue resolved.
@dodo's script appears to be unmaintained, as it's currently buggy but hasn't received any work to resolve it in months.
Seeing as mpv is a media player and mpris2 is an API designed for media players on Unix desktops, wouldn't it make more sense to have mpris2 support built-in?
As for libdbus, I want to avoid it as direct mpv dependency as far as possible.
Any external script providing mpris2 support will most likely use libdbus at some point; making libdbus an optional dependency doesn't seem too much of a horrible solution considering you already have quite a few optional dependencies on things such as samba or youtube-dl.
I just want no dbus code in mpv.
@wm4 I can understand that. DBus certainly isn't pretty as an API and the library isn't either. However, sadly it is currently pretty much the standard for desktop-oriented inter-process-communication on Linux.
Seeing as libmpv is now a thing, do you think this functionality would be more appropriate for something wrapping mpv into a Linux desktop oriented player?
There's the https://github.com/gnome-mpv/gnome-mpv wrapper which has an MPRIS service.
The easiest way would be to use a third party bridge wrapping MPRIS to the mpv JSON RPC, I guess?
I use such an MPRIS <-> mpd wrapper on my phone, so the lockscreen's player controls can interact with the mpd audio player.
For the record I also want no dbus code in mpv. Dbus doesn't need more support than it's already getting, and the mpv JSON RPC works fine for its purpose.
That is also another possible solution. Not sure what you do with multiple mpv instances though…
Not sure what you do with multiple mpv instances though…
Every instance would obviously need its own json socket.
I want mpv but I also want mpris support 😞
@mathstuf You run the music in one and play a stream in the other for example. Or keep multiple streams open and muted, while listening to one. The first reason is why I've come here looking.
Is mpris support related to having mpv use global media hotkey shortcuts?
I would like to be able to control mpv via, for instance, Fn+F11 to pause/play.
@orschiro For that you'd probably want to use the JSON IPC mechanism
@haasn the problem with json ipc is that it deosn't preserve configuration.
@orschiro For that I've written mpvctl (still WIP)
With the mpris, there are already wrappers doing just that.
Thank you @mibli!
How would I use your approach, for instance on Ubuntu 16.10?
How do I map your commands to my media playback buttons without disabling these hotkeys for my other applications?
-R
I have finally got a minimal working MPRIS2 plugin using the lgi library for lua
https://gist.github.com/progandy/dc50c4b9a74cc8c74f816a500c7a7d87
Nice. :-)
How does one use it?
-R
@orschiro, I ran it with mpv --script mpv-mpris2.lua {FILES...}
I tested it with playerctl [play|pause|play-pause|stop|next|previous]
By the way, stop is the same as quit in default mpv.
It should be possible to implement the code for status, metadata, volume and so on, but I had enough for now.
Edit: You'll need the packages dbus-glib, gobject-introspection, and lua52-lgi.
I ran with an older mpv-git, lua51 and luajit-lgi, so there might be some syntax errors with lua52
Update: I added the PKGBUILD for lua52-lgi. my mpris2 implementation seems to work well with it.
OK I added C plugin support:
https://github.com/mpv-player/mpv/commit/44e06b70d504d5a9073990851a353820370f3667
https://github.com/mpv-player/mpv-examples/commit/f2e24d5f8ab2237341d7435b0ba766e4bbc6bf3f
If anyone really wants mpris2 support and hates Lua, he could try writing a mpris2 plugin with this.
If anyone is still interested, I have created a C plugin for this fully implementing the basic MPRIS2 spec https://github.com/hoyon/mpv-mpris
Very nice. Will inquire with my distribution if they can --enable-cplugins so that I can try this out and maybe package it up. (See also #4491.)
@hoyon probably best to create a cplugins section on this page, and add a link to it: https://github.com/mpv-player/mpv/wiki/User-Scripts
@wm4 Added it to the wiki
Most helpful comment
If anyone is still interested, I have created a C plugin for this fully implementing the basic MPRIS2 spec https://github.com/hoyon/mpv-mpris