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

player-mpris-tail: should sort players by recency #335

Open
victorz opened this issue Jul 2, 2021 · 13 comments · May be fixed by #379
Open

player-mpris-tail: should sort players by recency #335

victorz opened this issue Jul 2, 2021 · 13 comments · May be fixed by #379

Comments

@victorz
Copy link

victorz commented Jul 2, 2021

When I'm playing Spotify and another thing, e.g. Pocket Casts web player, or YouTube, etc., there are multiple players. If I click the player-mpris-tail module to pause say currently-playing Pocket Casts, then Spotify suddenly shows up in the module. Possibly due to the formatted output by the module? But pressing unpause on my media keys again actually plays Pocket Casts again, yet clicking the module would resume Spotify playback instead.

It would be good to sort the players which have been "touched"/interacted with by recency, so that the module doesn't flip between players when interacting with it. The only thing that should change the order is by manually starting or stopping another player externally.

Thanks for your consideration.

@x70b1
Copy link
Member

x70b1 commented Jul 2, 2021

Do you want to work on a PR?

@victorz
Copy link
Author

victorz commented Jul 2, 2021

Do you want to work on a PR?

Sure? I need someone to explain how the plugin works though. I tried to study the code but I can't follow along in it. Been a while since I did any python.

@x70b1
Copy link
Member

x70b1 commented Nov 22, 2021

Maybe its an idea for @Cybolic or somebody else.

@zjeffer
Copy link

zjeffer commented Jun 25, 2022

@victorz I use a workaround for this: a script that keeps the most recent player in a file, and my media keys will interact with that player. Here is my script:

#!/bin/sh

get_players(){
    # gets a list of MPRIS compatible players
    echo `qdbus | egrep -i 'org.mpris.MediaPlayer2|plasma.browser_integration'`
}

get_status(){
    # get the current playback status of the given player
    echo `qdbus $1 /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.PlaybackStatus 2>&1`
}

setFirstPausedPlayer() {
    for player in $(get_players) ; do
        if [[ $(get_status $player) == 'Paused' ]]; then
            echo "$player" > ~/.config/activePlayer/currentPlaying.txt
            break
        fi
    done
}

while true ; do
    for player in $(get_players) ; do
        if [[ $(get_status $player) == 'Playing' ]]; then
            # if the player is playing, set it as the current playing player
			echo $player > ~/.config/activePlayer/currentPlaying.txt
            break
        elif [[ $(get_status $player) == 'Stopped' ]]; then
            # if a player is stopped, we don't want to start it again
			setFirstPausedPlayer
		fi
    done
    # if the current player does not exist anymore, look for a new one
    currentPlayer=`cat ~/.config/activePlayer/currentPlaying.txt`
    if [[ $(get_status $currentPlayer) == *"does not exist"* ]]; then
        # if a player is stopped, search for the next paused player
        setFirstPausedPlayer
    fi
	sleep 0.25
done

This script runs in the background and is started when I log in to my X session.

To control that latest player, I use this script:

#!/bin/sh

#Get command
case $1 in
	'play-pause')
		cmd='/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.PlayPause';;
	'next')
		cmd='/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Next';;
	'previous')
		cmd='/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Previous';;
	'stop')
		cmd='/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Stop';;
	'skipForward')
		# skip 5 second forward
		cmd='/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Seek 5000000';;
	'skipBackward')
		# skip 5 second backward
		cmd='/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Seek -5000000';;
	*)
esac

#Send command to qdbus
if [[ `qdbus | egrep -i 'org.mpris.MediaPlayer2|plasma-browser-integration' | wc -l` -eq 1 ]]; then
	# if only one player is detected
	qdbus `qdbus | egrep -i 'org.mpris.MediaPlayer2|plasma-browser-integration'` $cmd
else
	# if multiple players are detected, interact with the most recent active player
	qdbus `cat ~/.config/activePlayer/currentPlaying.txt` $cmd
fi

unset cmd

In sxhkdrc (the config file for bspwm's hotkey daemon), I define these actions:

########################################## MEDIA KEYS ##########################################

# Media control
XF86Audio{Play,Prev,Next,Stop}
	~/.config/activePlayer/control.sh {play-pause,previous,next,stop}

# Fastforward/Rewind x seconds
shift + XF86Audio{Next,Prev}
	~/.config/activePlayer/control.sh {skipForward, skipBackward}

@victorz
Copy link
Author

victorz commented Jun 25, 2022

@zjeffer

That's one solution. Although my issue is that this module does the wrong thing. My media keys do the right thing, and control the expected player (most recent). I use playerctl and set my keys up in my i3 config. I use playerctl without the player flag, so that I don't limit the command/keys to only one player. But this module seems to flip between players when I pause the current player externally (e.g. via media keys).

Expected behavior would be:

If one player is playing, show that.

If multiple players are playing, show the most recently interacted-with player.

If no players are playing, show the most recently interacted-with player.

I hope that makes sense and is achievable. Thanks!

@zjeffer
Copy link

zjeffer commented Jun 25, 2022

If one player is playing, show that.
If multiple players are playing, show the most recently interacted-with player.
If no players are playing, show the most recently interacted-with player.

This is what my script fixes, but indeed only with media keys.
To fix this in the module, we could implement the above bash script into the python script. I'm going to look into it.

@zjeffer
Copy link

zjeffer commented Jun 25, 2022

I managed to implement a way to keep the most recent players, the problem is that the module's buttons will start a new instance of the Python script, so that instance never knows which players were interacted with recently (as they are kept in memory).

I'll try saving the most recent players in a file for now, and reading them from that file every time a module button is pressed.

zjeffer added a commit to zjeffer/polybar-scripts that referenced this issue Jun 25, 2022
@zjeffer zjeffer linked a pull request Jun 25, 2022 that will close this issue
@victorz
Copy link
Author

victorz commented Jun 26, 2022

Would it be possible to use the "tail" feature of polybar instead? So that a single instance is always running, but each new line of output will be the new state in the bar? That way you could keep the player state in the script and not have to use a file on disk for this. I don't know, just spit-balling. :-)

@zjeffer
Copy link

zjeffer commented Jun 26, 2022

I thought so too, but I have no idea how to program that :)

@victorz
Copy link
Author

victorz commented Jun 26, 2022

I made a few custom scripts that do this, and all it requires is that the script keeps running, with a loop or has its input piped to it or something, and that it keeps outputting lines of statuses (separated by newlines). So if you can accomplish that with this script, I think we'd be golden.

Although obviously this would be a major version as it breaks existing configs. But maybe that's okay? I don't know how you feel about that.

@zjeffer
Copy link

zjeffer commented Jun 26, 2022

Doesn't sound that difficult, I'll look into that in the next couple of days.

Do you have a link to those scripts you made?

@victorz
Copy link
Author

victorz commented Jun 26, 2022

Hmm, I don't have them in a repo I don't think. I'll look into that tomorrow to see how I can best get those to you. 🙂 Cheers!

@victorz
Copy link
Author

victorz commented Aug 2, 2022

Sorry for the delay on this! Vacation came and went, finally have some time at the computer.

One example of this is a script I have called dbus-listen.sh, which looks like this:

#!/bin/bash
watchexpr="$1"
shift 1
dbus-monitor --profile "$watchexpr" |
  while read -r line
  do
    sh -c "$*"
  done

The script monitors a dbus interface given by the parameters to the script, and for every line that the monitor reads, it executes a command, also given by (the rest of) the parameters to the script.

dbus-monitor simply runs forever until killed, and the command is expected to print one polybar format line per dbus monitor trigger.

I use the script in my polybar for various things, one of which is to mute the microphone system-wide, like this:

[module/mic-mute]
type = custom/script
exec = dbus-listen.sh sender=user.victor,member=MicMuteToggle "pamixer --get-mute --default-source | sed -e 's/true/%{F#f05050}%{F-}/;>
tail = true
click-left = mic-mute-toggle.sh
click-right = pavucontrol -t 4

A simpler script for testing purposes would probably be something like this though:

#!/bin/bash

val=true
while true
do
  print $val
  if [[ val = true ]]
  then
    val=false
  else
    val=true
  fi
done

I hope this helps!

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

Successfully merging a pull request may close this issue.

3 participants