Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mpris2 support #661

Closed
ghost opened this issue Mar 22, 2014 · 46 comments
Closed

mpris2 support #661

ghost opened this issue Mar 22, 2014 · 46 comments

Comments

@ghost
Copy link

ghost commented Mar 22, 2014

I was wondering if it's a possibility in the future.

@ghost ghost added the enhancement label Mar 22, 2014
@ghost
Copy link

ghost commented Mar 22, 2014

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).

@arclance
Copy link

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.
6759941
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.

  1. A python (bash would also work) script to assemble the command (small four line bash script for the master branch) to start mpv so I only have to type the mpv options out each time.
  2. A python script run from a conky Lua script to parse the redirected stderr output and format it before sending it to the Lua script.
    Lua (v5.1) chokes on the terminal commands so I needed a intermediary script here.
  3. A small bit of Lua to run the parsing python script and display the results in a conky window.

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.

@ghost
Copy link

ghost commented Mar 23, 2014

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).

@arclance
Copy link

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

@ghost
Copy link

ghost commented Mar 23, 2014

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.

https://github.com/LuaLanes/lanes

I don't think they would add much value in this case.

@arclance
Copy link

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.

@ghost
Copy link

ghost commented Mar 23, 2014

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.

@arclance
Copy link

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.

@ghost
Copy link

ghost commented Mar 23, 2014

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.

@arclance
Copy link

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.

@ghost ghost added the low priority label Apr 8, 2014
@progandy
Copy link

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

@ghedo
Copy link
Member

ghedo commented Apr 10, 2014

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.

@progandy
Copy link

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)
        )

@ghost
Copy link

ghost commented Apr 10, 2014

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?

@progandy
Copy link

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.

@ghost
Copy link

ghost commented Apr 12, 2014

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.

@ghost
Copy link

ghost commented Apr 12, 2014

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.

ghost pushed a commit that referenced this issue Apr 12, 2014
Pretty much experimental for issue #661.
@ghost
Copy link

ghost commented Apr 12, 2014

OK added some stuff. See the commit github mentioned, and following commits.

@nerijus
Copy link

nerijus commented Mar 6, 2015

Is is possible to use mpris interface (with some lua scripts?) now?

@ghost
Copy link

ghost commented Mar 6, 2015

Not that I know of.

@majewsky
Copy link

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.

@dodo
Copy link
Contributor

dodo commented Apr 21, 2015

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.

@ghost
Copy link

ghost commented Apr 22, 2015

So does this script solve the issue?

(Also I think the installation instructions for this script might be busted.)

@dodo
Copy link
Contributor

dodo commented Apr 22, 2015

Thanks for the hint. Added some install instructions to the readme.

Yes, that script solves this issue.

@ghost
Copy link

ghost commented May 25, 2015

@dodo: added your script to the wiki. I think I hereby consider this issue resolved.

@CounterPillow
Copy link
Contributor

@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.

@ghost
Copy link

ghost commented Mar 21, 2016

I just want no dbus code in mpv.

@CounterPillow
Copy link
Contributor

@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?

@mathstuf
Copy link
Contributor

mathstuf commented Apr 6, 2016

There's the https://github.com/gnome-mpv/gnome-mpv wrapper which has an MPRIS service.

@haasn
Copy link
Member

haasn commented Apr 6, 2016

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.

@mathstuf
Copy link
Contributor

mathstuf commented Apr 6, 2016

That is also another possible solution. Not sure what you do with multiple mpv instances though…

@ghost
Copy link

ghost commented Apr 6, 2016

Not sure what you do with multiple mpv instances though…

Every instance would obviously need its own json socket.

@z3ntu
Copy link

z3ntu commented Oct 8, 2016

I want mpv but I also want mpris support 😞

@mibli
Copy link

mibli commented Jan 7, 2017

@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.

@orschiro
Copy link

orschiro commented Jan 7, 2017

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.

@haasn
Copy link
Member

haasn commented Jan 7, 2017

@orschiro For that you'd probably want to use the JSON IPC mechanism

@mibli
Copy link

mibli commented Jan 7, 2017

@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.

@orschiro
Copy link

orschiro commented Jan 8, 2017

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

@progandy
Copy link

progandy commented Jan 9, 2017

I have finally got a minimal working MPRIS2 plugin using the lgi library for lua
https://gist.github.com/progandy/dc50c4b9a74cc8c74f816a500c7a7d87

@orschiro
Copy link

orschiro commented Jan 9, 2017

Nice. :-)

How does one use it?

-R

@progandy
Copy link

progandy commented Jan 9, 2017

@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.

@ghost
Copy link

ghost commented Jan 12, 2017

OK I added C plugin support:

44e06b7
mpv-player/mpv-examples@f2e24d5

If anyone really wants mpris2 support and hates Lua, he could try writing a mpris2 plugin with this.

@hoyon
Copy link
Contributor

hoyon commented Jun 7, 2017

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

@majewsky
Copy link

majewsky commented Jun 7, 2017

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.)

@ghost
Copy link

ghost commented Jun 7, 2017

@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

@hoyon
Copy link
Contributor

hoyon commented Jun 7, 2017

@wm4 Added it to the wiki

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests