Old playerctl cli backend added as option (#49)

* Old playerctl cli backend added as option

* Move files into directory and only require playerctl lib on enable

* Added wrapper functions to select backend based on args

* Switched 'player_stopped' signal to 'no_players' in CLI backend

* Added support for setting interval directly in enable() for CLI backend

* Update docs for both backends
This commit is contained in:
HumblePresent 2021-04-24 04:37:06 -05:00 committed by GitHub
parent 397edcec6b
commit fb8bf62fae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 177 additions and 2 deletions

View File

@ -99,7 +99,17 @@ end)
```
### Theme Variables and Configuration
By default, this module will output signals from the most recently active player. If you wish to customize the behavior furthur, the following configuration options are available.
By default, this module will output signals from the most recently active player. If you wish to customize the behavior furthur, the following configuration options are available depending on the selected backend. Here is a summary of the two backends and which configuration options they support.
| Option | playerctl_cli | playerctl_lib |
| ------------------- | ------------------ | ------------------ |
| backend | :heavy_check_mark: | :heavy_check_mark: |
| ignore | | :heavy_check_mark: |
| player | | :heavy_check_mark: |
| update_on_activity | | :heavy_check_mark: |
| interval | :heavy_check_mark: | :heavy_check_mark: |
- `backend`: This is a string containing the name of the backend that will be used to produce the playerctl signals, either `playerctl_cli` or `playertl_lib`. `playerctl_cli` is used by default because it is supported on most if not all systems. That said, if the playerctl package for your distribution supports the `playerctl_lib` backend, it is recommended, because it supports all the configuration options as seen in the table above and uses less system resources. If you are not sure if your package supports the `playerctl_lib` backend you can simply try it and will receive an error message from Awesome upon calling `bling.signal.playerctl.enable()` if it is not supported. See the examples below for how to set configuration options.
- `ignore`: This option is either a string with a single name or a table of strings containing names of players that will be ignored by this module. It is empty by default.
@ -111,6 +121,7 @@ By default, this module will output signals from the most recently active player
These options can be set through a call to `bling.signal.playerctl.enable()` or these theme variables:
```lua
theme.playerctl_backend = "playerctl_cli"
theme.playerctl_ignore = {}
theme.playerctl_player = {}
theme.playerctl_update_on_activity = true
@ -121,18 +132,25 @@ theme.playerctl_position_update_interval = 1
```lua
-- Prioritize ncspot over all other players and ignore firefox players (e.g. YouTube and Twitch tabs) completely
bling.signal.playerctl.enable {
backend = "playerctl_lib",
ignore = "firefox",
player = {"ncspot", "%any"}
}
-- OR in your theme file:
-- Same config as above but with theme variables
theme.playerctl_backend = "playerctl_lib"
theme.playerctl_ignore = "firefox"
theme.playerctl_player = {"ncspot", "%any"}
-- Prioritize vlc over all other players and deprioritize spotify
theme.playerctl_backend = "playerctl_lib"
theme.playerctl_player = {"vlc", "%any", "spotify"}
-- Disable priority of most recently active players
theme.playerctl_backend = "playerctl_lib"
theme.playerctl_update_on_activity = false
-- Only emit the position signal every 2 seconds
theme.playerctl_position_update_interval = 2
```

View File

@ -15,6 +15,7 @@ theme.flash_focus_start_opacity = 0.6 -- the starting opacity
theme.flash_focus_step = 0.01 -- the step of animation
-- playerctl signal
theme.playerctl_backend = "playerctl_cli" -- backend to use
theme.playerctl_ignore = {} -- list of players to be ignored
theme.playerctl_player = {} -- list of players to be used in priority order
theme.playerctl_update_on_activity = true -- whether to prioritize the most recently active players or not

19
signal/playerctl/init.lua Normal file
View File

@ -0,0 +1,19 @@
local beautiful = require("beautiful")
-- Use CLI backend as default as it is supported on most if not all systems
local backend_config = beautiful.playerctl_backend or "playerctl_cli"
local backends = {
playerctl_cli = require(... .. ".playerctl_cli"),
playerctl_lib = require(... .. ".playerctl_lib")
}
local function enable_wrapper(args)
backend_config = (args and args.backend) or backend_config
backends[backend_config].enable(args)
end
local function disable_wrapper()
backends[backend_config].disable()
end
return {enable = enable_wrapper, disable = disable_wrapper}

View File

@ -0,0 +1,133 @@
--
-- Provides:
-- bling::playerctl::status
-- playing (boolean)
-- bling::playerctl::title_artist_album
-- title (string)
-- artist (string)
-- album_path (string)
-- bling::playerctl::position
-- interval_sec (number)
-- length_sec (number)
-- bling::playerctl::no_players
--
local awful = require("awful")
local beautiful = require("beautiful")
local interval = beautiful.playerctl_position_update_interval or 1
local function emit_player_status()
local status_cmd = "playerctl status -F"
-- Follow status
awful.spawn.easy_async({
"pkill", "--full", "--uid", os.getenv("USER"), "^playerctl status"
}, function()
awful.spawn.with_line_callback(status_cmd, {
stdout = function(line)
local playing = false
if line:find("Playing") then
playing = true
else
playing = false
end
awesome.emit_signal("bling::playerctl::status", playing)
end
})
collectgarbage("collect")
end)
end
local function emit_player_info()
local art_script = [[
sh -c '
tmp_dir="$XDG_CACHE_HOME/awesome/"
if [ -z ${XDG_CACHE_HOME} ]; then
tmp_dir="$HOME/.cache/awesome/"
fi
tmp_cover_path=${tmp_dir}"cover.png"
if [ ! -d $tmp_dir ]; then
mkdir -p $tmp_dir
fi
link="$(playerctl metadata mpris:artUrl)"
curl -s "$link" --output $tmp_cover_path
echo "$tmp_cover_path"
']]
-- Command that lists artist and title in a format to find and follow
local song_follow_cmd =
"playerctl metadata --format 'artist_{{artist}}title_{{title}}' -F"
-- Progress Cmds
local prog_cmd = "playerctl position"
local length_cmd = "playerctl metadata mpris:length"
awful.widget.watch(prog_cmd, interval, function(_, interval)
awful.spawn.easy_async_with_shell(length_cmd, function(length)
local length_sec = tonumber(length) -- in microseconds
local interval_sec = tonumber(interval) -- in seconds
if length_sec and interval_sec then
if interval_sec >= 0 and length_sec > 0 then
awesome.emit_signal("bling::playerctl::position",
interval_sec, length_sec / 1000000)
end
end
end)
collectgarbage("collect")
end)
-- Follow title
awful.spawn.easy_async({
"pkill", "--full", "--uid", os.getenv("USER"), "^playerctl metadata"
}, function()
awful.spawn.with_line_callback(song_follow_cmd, {
stdout = function(line)
local album_path = ""
awful.spawn.easy_async_with_shell(art_script, function(out)
-- Get album path
album_path = out:gsub('%\n', '')
-- Get title and artist
local artist = line:match('artist_(.*)title_')
local title = line:match('title_(.*)')
-- If the title is nil or empty then the players stopped
if title and title ~= "" then
awesome.emit_signal(
"bling::playerctl::title_artist_album", title,
artist, album_path)
else
awesome.emit_signal("bling::playerctl::no_players")
end
end)
collectgarbage("collect")
end
})
collectgarbage("collect")
end)
end
-- Emit info
-- emit_player_status()
-- emit_player_info()
local enable = function(args)
interval = (args and args.interval) or interval
emit_player_status()
emit_player_info()
end
local disable = function()
awful.spawn.with_shell("pkill --full --uid " .. os.getenv("USER") ..
" '^playerctl status -F'")
awful.spawn.with_shell("pkill --full --uid " .. os.getenv("USER") ..
" '^playerctl metadata --format'")
end
return {enable = enable, disable = disable}

View File

@ -19,7 +19,7 @@
local gears = require("gears")
local awful = require("awful")
local beautiful = require("beautiful")
local Playerctl = require("lgi").Playerctl
local Playerctl = nil
local manager = nil
local position_timer = nil
@ -270,6 +270,9 @@ local function playerctl_enable(args)
args.interval = args.interval or beautiful.playerctl_position_update_interval
parse_args(args)
-- Grab playerctl library
Playerctl = require("lgi").Playerctl
-- Ensure main event loop has started before starting player manager
gears.timer.delayed_call(start_manager)
end

View File

@ -15,6 +15,7 @@ theme.flash_focus_start_opacity = 0.6 -- the starting opacity
theme.flash_focus_step = 0.01 -- the step of animation
-- playerctl signal
theme.playerctl_backend = "playerctl_cli" -- backend to use
theme.playerctl_ignore = {} -- list of players to be ignored
theme.playerctl_player = {} -- list of players to be used in priority order
theme.playerctl_update_on_activity = true -- whether to prioritize the most recently active players or not