Add mpris support
This commit is contained in:
parent
f011b376bb
commit
86f9f75bd7
|
@ -52,7 +52,7 @@
|
||||||
hwdec = "auto-safe";
|
hwdec = "auto-safe";
|
||||||
profile = "gpu-hq";
|
profile = "gpu-hq";
|
||||||
};
|
};
|
||||||
scripts = with pkgs.mpvScripts; [ thumbnail ];
|
scripts = with pkgs.mpvScripts; [ thumbnail mpris ];
|
||||||
scriptOpts = {
|
scriptOpts = {
|
||||||
mpv_thumbnail_script = {
|
mpv_thumbnail_script = {
|
||||||
autogenerate = false; # TODO It creates too many processes at once, crashing the system
|
autogenerate = false; # TODO It creates too many processes at once, crashing the system
|
||||||
|
|
|
@ -25,7 +25,7 @@ pkgs.python3Packages.buildPythonApplication {
|
||||||
pulsectl
|
pulsectl
|
||||||
pyinotify
|
pyinotify
|
||||||
];
|
];
|
||||||
makeWrapperArgs = [ "--prefix PATH : ${pkgs.lib.makeBinPath ([ lemonbar ] ++ (with pkgs; [ wirelesstools ]))}" ];
|
makeWrapperArgs = [ "--prefix PATH : ${pkgs.lib.makeBinPath ([ lemonbar ] ++ (with pkgs; [ wirelesstools playerctl ]))}" ];
|
||||||
|
|
||||||
src = ./.;
|
src = ./.;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ def run() -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO Middle
|
# TODO Middle
|
||||||
Bar.addSectionAll(fp.MpdProvider(theme=9), BarGroupType.LEFT)
|
Bar.addSectionAll(fp.MprisProvider(theme=9), BarGroupType.LEFT)
|
||||||
|
# Bar.addSectionAll(fp.MpdProvider(theme=9), BarGroupType.LEFT)
|
||||||
# Bar.addSectionAll(I3WindowTitleProvider(), BarGroupType.LEFT)
|
# Bar.addSectionAll(I3WindowTitleProvider(), BarGroupType.LEFT)
|
||||||
|
|
||||||
# TODO Computer modes
|
# TODO Computer modes
|
||||||
|
|
|
@ -849,3 +849,96 @@ class MpdProvider(Section, ThreadedUpdater):
|
||||||
self.connect()
|
self.connect()
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
log.error(e, exc_info=True)
|
log.error(e, exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
|
class MprisProviderSection(Section, Updater):
|
||||||
|
def __init__(self, parent: "MprisProvider"):
|
||||||
|
Updater.__init__(self)
|
||||||
|
Section.__init__(self, theme=parent.theme)
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
|
||||||
|
class MprisProvider(Section, ThreadedUpdater):
|
||||||
|
# TODO Controls (select player at least)
|
||||||
|
# TODO Use the Python native thing for it:
|
||||||
|
# https://github.com/altdesktop/playerctl?tab=readme-ov-file#using-the-library
|
||||||
|
# TODO Make it less sucky
|
||||||
|
|
||||||
|
SECTIONS = [
|
||||||
|
"{{ playerName }} {{ status }}",
|
||||||
|
"{{ album }}",
|
||||||
|
"{{ artist }}",
|
||||||
|
"{{ duration(position) }}|{{ duration(mpris:length) }}"
|
||||||
|
" {{ title }}",
|
||||||
|
]
|
||||||
|
|
||||||
|
# nf-fd icons don't work (UTF-16?)
|
||||||
|
SUBSTITUTIONS = {
|
||||||
|
"Playing": "",
|
||||||
|
"Paused": "",
|
||||||
|
"Stopped": "",
|
||||||
|
"mpd": "",
|
||||||
|
"firefox": "",
|
||||||
|
"chromium": "",
|
||||||
|
"mpv": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
ICONS = {
|
||||||
|
1: "",
|
||||||
|
2: "",
|
||||||
|
3: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, theme: int | None = None):
|
||||||
|
ThreadedUpdater.__init__(self)
|
||||||
|
Section.__init__(self, theme)
|
||||||
|
|
||||||
|
self.line = ""
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
self.sections: list[Section] = []
|
||||||
|
|
||||||
|
def fetcher(self) -> Element:
|
||||||
|
create = not len(self.sections)
|
||||||
|
populate = self.line
|
||||||
|
split = self.line.split("\t")
|
||||||
|
|
||||||
|
lastSection: Section = self
|
||||||
|
for i in range(len(self.SECTIONS)):
|
||||||
|
if create:
|
||||||
|
section = Section(theme=self.theme)
|
||||||
|
lastSection.appendAfter(section)
|
||||||
|
lastSection = section
|
||||||
|
self.sections.append(section)
|
||||||
|
else:
|
||||||
|
section = self.sections[i]
|
||||||
|
|
||||||
|
if populate:
|
||||||
|
text = split[i]
|
||||||
|
if i == 0:
|
||||||
|
for key, val in self.SUBSTITUTIONS.items():
|
||||||
|
text = text.replace(key, val)
|
||||||
|
if text:
|
||||||
|
if i in self.ICONS:
|
||||||
|
text = f"{self.ICONS[i]} {text}"
|
||||||
|
section.updateText(text)
|
||||||
|
else:
|
||||||
|
section.updateText(None)
|
||||||
|
else:
|
||||||
|
section.updateText(None)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def loop(self) -> None:
|
||||||
|
cmd = [
|
||||||
|
"playerctl",
|
||||||
|
"metadata",
|
||||||
|
"--format",
|
||||||
|
"\t".join(self.SECTIONS),
|
||||||
|
"--follow",
|
||||||
|
]
|
||||||
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||||
|
assert p.stdout
|
||||||
|
while p.poll() is None:
|
||||||
|
self.line = p.stdout.readline().decode().strip()
|
||||||
|
self.refreshData()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import functools
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
@ -11,8 +12,8 @@ import coloredlogs
|
||||||
import i3ipc
|
import i3ipc
|
||||||
import pyinotify
|
import pyinotify
|
||||||
|
|
||||||
from frobar.display import Element
|
|
||||||
from frobar.common import notBusy
|
from frobar.common import notBusy
|
||||||
|
from frobar.display import Element
|
||||||
|
|
||||||
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
|
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
|
||||||
log = logging.getLogger()
|
log = logging.getLogger()
|
||||||
|
|
|
@ -6,26 +6,36 @@
|
||||||
ashuffle
|
ashuffle
|
||||||
mpc-cli
|
mpc-cli
|
||||||
vimpc
|
vimpc
|
||||||
|
playerctl
|
||||||
];
|
];
|
||||||
sessionVariables = {
|
sessionVariables = {
|
||||||
MPD_PORT = "${toString config.services.mpd.network.port}";
|
MPD_PORT = "${toString config.services.mpd.network.port}";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
services.mpd = {
|
services = {
|
||||||
enable = true;
|
mpd = {
|
||||||
network = {
|
enable = true;
|
||||||
listenAddress = "0.0.0.0"; # Can be controlled remotely, determined with firewall
|
network = {
|
||||||
startWhenNeeded = true;
|
listenAddress = "0.0.0.0"; # Can be controlled remotely, determined with firewall
|
||||||
|
startWhenNeeded = true;
|
||||||
|
};
|
||||||
|
extraConfig = ''
|
||||||
|
restore_paused "yes"
|
||||||
|
audio_output {
|
||||||
|
type "pipewire"
|
||||||
|
name "PipeWire Sound Server"
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
# UPST auto audio_output ?
|
||||||
|
musicDirectory = "${config.home.homeDirectory}/Musiques";
|
||||||
};
|
};
|
||||||
extraConfig = ''
|
# Expose mpd to mpris
|
||||||
restore_paused "yes"
|
# mpd-mpris also exists but is MIT and make playerctld not pick up on play/pause events
|
||||||
audio_output {
|
mpdris2.enable = true;
|
||||||
type "pipewire"
|
# Allow control from headset
|
||||||
name "PipeWire Sound Server"
|
mpris-proxy.enable = true;
|
||||||
}
|
# Remember the last player
|
||||||
'';
|
playerctld.enable = true;
|
||||||
# UPST auto audio_output ?
|
|
||||||
musicDirectory = "${config.home.homeDirectory}/Musiques";
|
|
||||||
};
|
};
|
||||||
xdg = {
|
xdg = {
|
||||||
configFile = {
|
configFile = {
|
||||||
|
@ -45,9 +55,9 @@
|
||||||
};
|
};
|
||||||
xsession.windowManager.i3.config.keybindings =
|
xsession.windowManager.i3.config.keybindings =
|
||||||
{
|
{
|
||||||
"XF86AudioPrev" = "exec ${pkgs.mpc-cli}/bin/mpc prev";
|
"XF86AudioPrev" = "exec ${lib.getExe pkgs.playerctl} previous";
|
||||||
"XF86AudioPlay" = "exec ${pkgs.mpc-cli}/bin/mpc toggle";
|
"XF86AudioPlay" = "exec ${lib.getExe pkgs.playerctl} play-pause";
|
||||||
"XF86AudioNext" = "exec ${pkgs.mpc-cli}/bin/mpc next";
|
"XF86AudioNext" = "exec ${lib.getExe pkgs.playerctl} next";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue