diff options
Diffstat (limited to '.config/awesome/services')
| -rw-r--r-- | .config/awesome/services/backlight.lua | 6 | ||||
| -rw-r--r-- | .config/awesome/services/battery.lua | 8 | ||||
| -rw-r--r-- | .config/awesome/services/common.lua | 60 | ||||
| -rw-r--r-- | .config/awesome/services/mpris/init.lua | 100 | ||||
| -rw-r--r-- | .config/awesome/services/playerctl.lua | 612 |
5 files changed, 716 insertions, 70 deletions
diff --git a/.config/awesome/services/backlight.lua b/.config/awesome/services/backlight.lua index 02ed808..bb9bedc 100644 --- a/.config/awesome/services/backlight.lua +++ b/.config/awesome/services/backlight.lua @@ -1,10 +1,8 @@ local backlight = require("lib.lit").backlight.new("amdgpu_bl1", 5) local gobject = require "gears.object" -local gtimer = require "gears.timer" -local naughty = require "naughty" local phosphor = require "assets.phosphor" +local qcolor = require "quarrel.color" local qmath = require "quarrel.math" -local qvars = require "quarrel.vars" local backlight_wrapper = gobject { class = { @@ -27,7 +25,7 @@ backlight:connect_signal("brightness", function(_, brightness) { 255 }, }) - backlight_wrapper:emit_signal("icon", phosphor[icon_data .. "_fill"], qvars.colors.fg) + backlight_wrapper:emit_signal("icon", phosphor[icon_data .. "_fill"], qcolor.palette.fg()) backlight_wrapper:emit_signal("value", brightness) end) diff --git a/.config/awesome/services/battery.lua b/.config/awesome/services/battery.lua index 98bacc9..d10714e 100644 --- a/.config/awesome/services/battery.lua +++ b/.config/awesome/services/battery.lua @@ -2,8 +2,8 @@ local UPower = require("lgi").UPowerGlib local gobject = require "gears.object" local gtimer = require "gears.timer" local phosphor = require "assets.phosphor" +local qcolor = require "quarrel.color" local qmath = require "quarrel.math" -local qvars = require "quarrel.vars" local upower = assert(UPower.Client.new_full()) local inner @@ -51,7 +51,11 @@ local function icon_handler() { 100 }, }) - battery:emit_signal("icon", phosphor["battery_vertical_" .. icon_data[1] .. "_fill"], qvars.colors[icon_data[2]]) + battery:emit_signal( + "icon", + phosphor["battery_vertical_" .. icon_data[1] .. "_fill"], + qcolor.palette[icon_data[2]]() + ) end local function level_handler() diff --git a/.config/awesome/services/common.lua b/.config/awesome/services/common.lua index ad1f998..9657c8a 100644 --- a/.config/awesome/services/common.lua +++ b/.config/awesome/services/common.lua @@ -1,12 +1,10 @@ local gfs = require "gears.filesystem" local gtimer = require "gears.timer" local phosphor = require "assets.phosphor" +local qcolor = require "quarrel.color" local qjson = require "quarrel.json" local qmath = require "quarrel.math" local qnative = require "quarrel.native" -local qstore = require "quarrel.store" -local qvars = require "quarrel.vars" -local UPower = require("lgi").UPowerGlib --- Register a service ---@param name string @@ -39,9 +37,9 @@ if not gfs.file_readable "/tmp/wp_audio_status" then assert(io.open("/tmp/wp_audio_status", "w")):write("{}"):close() end -qstore.audio_file = qnative.util.open_file "/tmp/wp_audio_status" -qstore.brightness_file = qnative.util.open_file "/sys/class/backlight/amdgpu_bl1/actual_brightness" -qstore.wifi_file = qnative.util.open_file "/proc/net/wireless" +local audio_file = qnative.util.open_file "/tmp/wp_audio_status" +-- qstore.brightness_file = qnative.util.open_file "/sys/class/backlight/amdgpu_bl1/actual_brightness" +local wifi_file = qnative.util.open_file "/proc/net/wireless" -- follows the format `service = { provider, icon_provider }` ---@class Service @@ -53,7 +51,7 @@ local services = { audio = { -- volume, muted function() - local audio_status = qjson.decode(read(qstore.audio_file)) + local audio_status = qjson.decode(read(audio_file)) local default_sink = audio_status["G435 Wireless Gaming Headset Analog Stereo"] if not default_sink then @@ -64,7 +62,7 @@ local services = { end, function(volume, muted) if muted or not volume then - return phosphor.speaker_simple_x_fill, qvars.colors.red + return phosphor.speaker_simple_x_fill, qcolor.palette.red() end local icon_data = qmath.step_value(volume, { @@ -75,32 +73,32 @@ local services = { { 100 }, }) - return phosphor["speaker_simple_" .. icon_data .. "_fill"], qvars.colors.fg - end, - }, - brightness = { - -- brightness - function() - return read(qstore.brightness_file, "n") - end, - function(brightness) - local icon_data = qmath.step_value(brightness, { - { 0, "cloud_moon" }, - { 51, "moon" }, - { 102, "sun_horizon" }, - { 153, "sun_dim" }, - { 204, "sun" }, - { 255 }, - }) - - return phosphor[icon_data .. "_fill"], qvars.colors.fg + return phosphor["speaker_simple_" .. icon_data .. "_fill"], qcolor.palette.fg() end, }, + -- brightness = { + -- -- brightness + -- function() + -- return read(qstore.brightness_file, "n") + -- end, + -- function(brightness) + -- local icon_data = qmath.step_value(brightness, { + -- { 0, "cloud_moon" }, + -- { 51, "moon" }, + -- { 102, "sun_horizon" }, + -- { 153, "sun_dim" }, + -- { 204, "sun" }, + -- { 255 }, + -- }) + -- + -- return phosphor[icon_data .. "_fill"], qcolor.palette.fg() + -- end, + -- }, wifi = { -- essid, strength, connected function() - local lines = qstore.wifi_file:lines() - qstore.wifi_file:rewind() + local lines = wifi_file:lines() + wifi_file:rewind() if not lines[3] then return nil, 0, false @@ -112,7 +110,7 @@ local services = { end, function(_, strength, connected) if not connected then - return phosphor.wifi_x_fill, qvars.colors.red + return phosphor.wifi_x_fill, qcolor.palette.red() end local icon_data = qmath.step_value(strength, { @@ -123,7 +121,7 @@ local services = { { 100 }, }) - return phosphor["wifi_" .. icon_data[1] .. "_fill"], qvars.colors[icon_data[2]] + return phosphor["wifi_" .. icon_data[1] .. "_fill"], qcolor.palette[icon_data[2]]() end, }, } diff --git a/.config/awesome/services/mpris/init.lua b/.config/awesome/services/mpris/init.lua new file mode 100644 index 0000000..3659a3a --- /dev/null +++ b/.config/awesome/services/mpris/init.lua @@ -0,0 +1,100 @@ +local gobject = require "gears.object" +local playerctl = require "services.mpris.playerctl" +local qpersistent = require "quarrel.persistent" + +---@class ServiceMpris : gears.object +---@field inner Playerctl +---@field index number the index of the currently active player +local M = {} + +---@param self ServiceMpris +---@param player Playerctl.data +local function update_player(self, player) + self:emit_signal("player::metadata", player) + self:emit_signal("player::position", player) + -- handle_position(nil, player) +end + +---@param diff_player Playerctl.data +local function recalculate_active_player(diff_player, vanished) + if type(diff_player) ~= "table" then + return + end + -- if #layout.children == 0 then + -- M.active_player_index = 1 + -- update_player() + -- return + -- end + + local active_player = players[M.active_player_index] + if not active_player then -- we're recovering from a state with no players + update_player(diff_player) + return + end + + if diff_player.instance == active_player.instance and vanished then -- active player vanished; fall back to previous player + M.previous_player() + else -- non-active player appeared/vanished; try to find active player + for i, p in ipairs(playerctl:list()) do + if p.instance == active_player.instance then + M.active_player_index = i + update_player(p) + return + end + end + + gdebug.print_warning( + "failed to find active player:\n " .. gdebug.dump_return(active_player, nil, 2):gsub("\n", "\n ") + ) + M.active_player_index = 1 + update_player(playerctl:list()[M.active_player_index]) + end +end + +function M:next_player() + local players = self.inner:list() + + if #players == 0 then + return + elseif self.index + 1 > #players then + self.index = 1 + else + self.index = self.index + 1 + end + + -- update_player(playerctl:list()[M.active_player_index]) + local player = players[self.index] + self:emit_signal("player::metadata", player) + self:emit_signal("player::position", player) +end + +function M:previous_player() + local players = self.inner:list() + if #players == 0 then + return + elseif self.index - 1 < #players then + self.index = #players + else + self.index = self.index - 1 + end + + local player = players[self.index] + self:emit_signal("player::metadata", player) + self:emit_signal("player::position", player) +end + +M.inner = playerctl.new { + players = {}, + metadata = { + album = "xesam:album", + title = "xesam:title", + artist = "xesam:artist", + art = "mpris:artUrl", + }, +} + +local instance = gobject { class = M } +instance:connect_signal("property::index", function(self, index) + qpersistent.store("active_player_index", self.index) +end) +return instance diff --git a/.config/awesome/services/playerctl.lua b/.config/awesome/services/playerctl.lua index f6ee71a..c4be74b 100644 --- a/.config/awesome/services/playerctl.lua +++ b/.config/awesome/services/playerctl.lua @@ -1,45 +1,591 @@ -local playerctl = require("lib.bling.signal.playerctl").lib { - player = { "spotify", "%any" }, +-- CREDIT: https://github.com/kosorin/awesome-rice/blob/b6813bd1bbb3fdd697a1b994784b72daaeaf405d/services/media/playerctl.lua + +-- DEPENDENCIES: playerctl + +local type = type +local pairs = pairs +local ipairs = ipairs +local gobject = require "gears.object" +local gtable = require "gears.table" +local gtimer = require "gears.timer" +local lgi_playerctl = require("lgi").Playerctl -- /usr/share/gtk-doc/html/playerctl/index.html + +---@alias Playerctl.selector +---|>nil # Select primary player +---| string # Select player by `instance` +---| "%all" # Select all players + +---@class Playerctl.position_data +---@field position? integer +---@field length? integer + +---@class Playerctl.data +---@field name string +---@field instance string +---@field playback_status lgi.Playerctl.PlaybackStatus +---@field position integer +---@field shuffle boolean +---@field loop_status lgi.Playerctl.LoopStatus +---@field volume number +---@field metadata table<string, any> +---@field package _position_timer? gears.timer + +local playerctl = { + lowest_priority = math.maxinteger, + any = { name = "%any" }, + all = { name = "%all" }, } -playerctl:connect_signal("metadata", function(_, ...) - awesome.emit_signal("services::playerctl::metadata", ...) -end) +---@class Playerctl : gears.object +---@field unit integer # Number of microseconds in a second. +---@field package primary_player_data? Playerctl.data +---@field package player_data table<string, Playerctl.data> +---@field package tracked_metadata table<string, string> +---@field package excluded_players table<string, boolean> +---@field package player_priorities table<string, integer> +---@field package manager lgi.Playerctl.PlayerManager +playerctl.object = { unit = 1000000 } + +---@param player_data? Playerctl.data +---@return Playerctl.position_data|nil +function playerctl.object:get_position_data(player_data) + player_data = player_data or self.primary_player_data + return (player_data and player_data.metadata) + and { + position = player_data.position, + length = player_data.metadata.length, + } +end + +---@param self Playerctl +---@param instance string +---@return lgi.Playerctl.Player|nil +local function find_player_by_instance(self, instance) + for _, player in ipairs(self.manager.players) do + if player.player_instance == instance then + return player + end + end +end + +---@param self Playerctl +---@param player_selector Playerctl.selector +---@param action fun(player: lgi.Playerctl.Player) +local function for_each_player(self, player_selector, action) + local players + if not player_selector then + local player_data = self.primary_player_data + if player_data then + players = { find_player_by_instance(self, player_data.instance) } + end + elseif player_selector == playerctl.all.name then + players = self.manager.players + elseif type(player_selector) == "string" then + players = { find_player_by_instance(self, player_selector) } + end + + if players then + for _, p in ipairs(players) do + action(p) + end + end +end + +---@param player_selector Playerctl.selector +function playerctl.object:play_pause(player_selector) + for_each_player(self, player_selector, function(p) + p:play_pause() + end) +end + +---@param player_selector Playerctl.selector +function playerctl.object:play(player_selector) + for_each_player(self, player_selector, function(p) + p:play() + end) +end + +---@param player_selector Playerctl.selector +function playerctl.object:pause(player_selector) + for_each_player(self, player_selector, function(p) + p:pause() + end) +end + +---@param player_selector Playerctl.selector +function playerctl.object:stop(player_selector) + for_each_player(self, player_selector, function(p) + p:stop() + end) +end + +---@param player_selector Playerctl.selector +function playerctl.object:previous(player_selector) + for_each_player(self, player_selector, function(p) + p:previous() + end) +end + +---@param player_selector Playerctl.selector +function playerctl.object:next(player_selector) + for_each_player(self, player_selector, function(p) + p:next() + end) +end + +---@param offset integer +---@param player_selector Playerctl.selector +function playerctl.object:skip(offset, player_selector) + if offset > 0 then + self:next(player_selector) + else + self:previous(player_selector) + end +end + +---@param offset integer +---@param player_selector Playerctl.selector +function playerctl.object:rewind(offset, player_selector) + self:seek(-offset, player_selector) +end + +---@param offset integer +---@param player_selector Playerctl.selector +function playerctl.object:fast_forward(offset, player_selector) + self:seek(offset, player_selector) +end + +---@param offset integer +---@param player_selector Playerctl.selector +function playerctl.object:seek(offset, player_selector) + for_each_player(self, player_selector, function(p) + p:seek(offset) + end) +end + +---@param loop_status lgi.Playerctl.LoopStatus +---@param player_selector Playerctl.selector +function playerctl.object:set_loop_status(loop_status, player_selector) + loop_status = loop_status:upper() + for_each_player(self, player_selector, function(p) + p:set_loop_status(loop_status) + end) +end + +---@param player_selector Playerctl.selector +function playerctl.object:cycle_loop_status(player_selector) + for_each_player(self, player_selector, function(p) + if p.loop_status == "NONE" then + p:set_loop_status "TRACK" + elseif p.loop_status == "TRACK" then + p:set_loop_status "PLAYLIST" + elseif p.loop_status == "PLAYLIST" then + p:set_loop_status "NONE" + end + end) +end + +---@param position integer +---@param player_selector Playerctl.selector +function playerctl.object:set_position(position, player_selector) + for_each_player(self, player_selector, function(p) + p:set_position(position) + end) +end + +---@param shuffle boolean +---@param player_selector Playerctl.selector +function playerctl.object:set_shuffle(shuffle, player_selector) + for_each_player(self, player_selector, function(p) + p:set_shuffle(shuffle) + end) +end + +---@param player_selector Playerctl.selector +function playerctl.object:toggle_shuffle(player_selector) + for_each_player(self, player_selector, function(p) + p:set_shuffle(not p.shuffle) + end) +end + +---@param volume number +---@param player_selector Playerctl.selector +function playerctl.object:set_volume(volume, player_selector) + for_each_player(self, player_selector, function(p) + p:set_volume(volume) + end) +end + +---@param player_data? Playerctl.data +---@return boolean +function playerctl.object:is_primary_player(player_data) + return self.primary_player_data == player_data +end + +---@return Playerctl.data|nil +function playerctl.object:get_primary_player_data() + return self.primary_player_data +end + +---@param self Playerctl +---@param player? lgi.Playerctl.Player +local function update_primary_player(self, player) + if player then + self.manager:move_player_to_top(player) + end + + local primary_player = self.manager.players[1] + + local old = self.primary_player_data + local new = self.player_data[primary_player and primary_player.player_instance] + if old ~= new then + self.primary_player_data = new + self:emit_signal("player::primary", new, old) + end +end + +---@param player_data Playerctl.data +---@return boolean +function playerctl.object:is_pinned(player_data) + local player_instance = player_data and player_data.instance or nil + return self.pinned_player_instance == player_instance +end + +---@param player_data? Playerctl.data +function playerctl.object:pin(player_data) + local player_instance = player_data and player_data.instance or nil + if self.pinned_player_instance ~= player_instance then + self.pinned_player_instance = player_instance + self:emit_signal("player::pinned", self.pinned_player_instance) + + local player + if player_data then + player = find_player_by_instance(self, player_data.instance) + elseif self.primary_player_data then + player = find_player_by_instance(self, self.primary_player_data.instance) + end + + update_primary_player(self, player) + end +end + +---@return Playerctl.data[] +function playerctl.object:list() + ---@type Playerctl.data[] + local players = {} + for _, p in ipairs(self.manager.players) do + players[#players + 1] = self.player_data[p.player_instance] + end + return players +end + +---@param player_data Playerctl.data +local function refresh_position_timer(player_data) + if player_data.playback_status == "PLAYING" then + player_data._position_timer:again() + else + player_data._position_timer:stop() + end +end + +---@param self Playerctl +---@param player_data Playerctl.data +---@param by_timer? boolean +local function update_position(self, player_data, by_timer) + local player = find_player_by_instance(self, player_data.instance) + if player then + player_data.position = player:get_position() + self:emit_signal("player::position", player_data, by_timer) + end +end -playerctl:connect_signal("position", function(_, ...) - awesome.emit_signal("services::playerctl::position", ...) -end) +---@param self Playerctl +---@param player_data Playerctl.data +---@param metadata lgi.Playerctl.Metadata +---@return boolean +---@return table<string, boolean> +local function update_metadata(self, player_data, metadata) + metadata = metadata and metadata.value or {} -playerctl:connect_signal("playback_status", function(_, ...) - awesome.emit_signal("services::playerctl::playback_status", ...) -end) + local changed = false + local changed_data = {} -playerctl:connect_signal("seeked", function(_, ...) - awesome.emit_signal("services::playerctl::seeked", ...) -end) + local function mark_changed(name) + changed = true + changed_data[name] = true + end -playerctl:connect_signal("volume", function(_, ...) - awesome.emit_signal("services::playerctl::volume", ...) -end) + local target_metadata = player_data.metadata + for name, mpris_name in pairs(self.tracked_metadata) do + local value = metadata[mpris_name] + local value_type = type(value) + if value_type == "nil" or value_type == "boolean" or value_type == "number" or value_type == "string" then + if target_metadata[name] ~= value then + target_metadata[name] = value + mark_changed(name) + end + elseif value_type == "userdata" and value.type == "as" then + local old = target_metadata[name] + if type(old) ~= "table" then + old = {} + end -playerctl:connect_signal("loop_status", function(_, ...) - awesome.emit_signal("services::playerctl::loop_status", ...) -end) + local new = {} + for _, s in value:ipairs() do + new[#new + 1] = s + end -playerctl:connect_signal("shuffle", function(_, ...) - awesome.emit_signal("services::playerctl::shuffle", ...) -end) + target_metadata[name] = new -playerctl:connect_signal("exit", function(_, ...) - awesome.emit_signal("services::playerctl::exit", ...) -end) + if #old ~= #new then + mark_changed(name) + else + for i = 1, #new do + if old[i] ~= new[i] then + mark_changed(name) + break + end + end + end + else + if target_metadata[name] ~= nil then + target_metadata[name] = nil + mark_changed(name) + end + end + end -playerctl:connect_signal("exit", function(_, ...) - awesome.emit_signal("services::playerctl::exit", ...) -end) + return changed, changed_data +end -playerctl:connect_signal("no_players", function() - awesome.emit_signal "services::playerctl::no_players" -end) +---@param self Playerctl +---@param full_name lgi.Playerctl.PlayerName +---@return lgi.Playerctl.Player +local function manage_player(self, full_name) + local new_player = lgi_playerctl.Player.new_from_name(full_name) -return playerctl + function new_player.on_metadata(p, metadata) + local player_data = self.player_data[p.player_instance] + if player_data and update_metadata(self, player_data, metadata) then + self:emit_signal("player::metadata", player_data) + + update_position(self, player_data) + refresh_position_timer(player_data) + end + end + + function new_player.on_playback_status(p, playback_status) + update_primary_player(self, p) + + local player_data = self.player_data[p.player_instance] + if player_data and player_data.playback_status ~= playback_status then + player_data.playback_status = playback_status + self:emit_signal("player::playback_status", player_data) + + update_position(self, player_data) + refresh_position_timer(player_data) + end + end + + function new_player.on_seeked(p, position) + local player_data = self.player_data[p.player_instance] + if player_data and player_data.position ~= position then + player_data.position = position + self:emit_signal("player::position", player_data) + + refresh_position_timer(player_data) + end + end + + function new_player.on_shuffle(p, shuffle) + local player_data = self.player_data[p.player_instance] + if player_data and player_data.shuffle ~= shuffle then + player_data.shuffle = shuffle + self:emit_signal("player::shuffle", player_data) + end + end + + function new_player.on_loop_status(p, loop_status) + local player_data = self.player_data[p.player_instance] + if player_data and player_data.loop_status ~= loop_status then + player_data.loop_status = loop_status + self:emit_signal("player::loop_status", player_data) + end + end + + function new_player.on_volume(p, volume) + local player_data = self.player_data[p.player_instance] + if player_data and player_data.volume ~= volume then + player_data.volume = volume + self:emit_signal("player::volume", player_data) + end + end + + self.manager:manage_player(new_player) + + return new_player +end + +---@param self Playerctl +---@param player_name string +---@return boolean +local function filter_name(self, player_name) + if self.excluded_players[player_name] then + return false + end + if self.player_priorities[playerctl.any] or self.player_priorities[player_name] then + return true + end + return false +end + +---@param self Playerctl +---@param player_a lgi.Playerctl.Player +---@param player_b lgi.Playerctl.Player +---@return sign +local function compare_players(self, player_a, player_b) + if player_a.player_name < player_b.player_name then + return -1 + elseif player_a.player_name > player_b.player_name then + return 1 + else + return 0 + end +end + +---@param self Playerctl +local function initialize_manager(self) + self.player_data = {} + + self.manager = lgi_playerctl.PlayerManager() + self.manager:set_sort_func(function(a, b) + local player_a = lgi_playerctl.Player(a) + local player_b = lgi_playerctl.Player(b) + return compare_players(self, player_a, player_b) + end) + + ---@param full_name lgi.Playerctl.PlayerName + ---@return lgi.Playerctl.Player|nil + local function try_manage(full_name) + if filter_name(self, full_name.name) then + return manage_player(self, full_name) + end + end + + function self.manager.on_name_appeared(_, full_name) + try_manage(full_name) + end + + function self.manager.on_player_appeared(_, player) + ---@type Playerctl.data + local player_data = { + name = player.player_name, + instance = player.player_instance, + playback_status = player.playback_status, + position = player.position, + shuffle = player.shuffle, + loop_status = player.loop_status, + volume = player.volume, + metadata = {}, + } + update_metadata(self, player_data, player.metadata) + + player_data._position_timer = gtimer { + timeout = 1, + callback = function() + update_position(self, player_data, true) + end, + } + refresh_position_timer(player_data) + + self.player_data[player_data.instance] = player_data + self:emit_signal("player::appeared", player_data) + + update_primary_player(self, player) + end + + function self.manager.on_player_vanished(_, player) + update_primary_player(self) + + if self.pinned_player_instance == player.player_instance then + self:pin(nil) + end + + local player_data = self.player_data[player.player_instance] + if player_data then + player_data._position_timer:stop() + self:emit_signal("player::vanished", player_data) + self.player_data[player.player_instance] = nil + end + end + + for _, full_name in ipairs(self.manager.player_names) do + try_manage(full_name) + end + + update_primary_player(self) +end + +---@param self Playerctl +---@param args? Playerctl.new.args +local function parse_args(self, args) + args = args or {} + + self.tracked_metadata = args.metadata or {} + + -- Always track length + self.tracked_metadata.length = "mpris:length" + + local excluded_players = {} + if type(args.excluded_players) == "string" then + excluded_players[args.excluded_players] = true + elseif args.excluded_players then + for _, name in ipairs(args.excluded_players) do + excluded_players[name] = true + end + end + self.excluded_players = excluded_players + + local function get_priority_key(name) + return name == playerctl.any.name and playerctl.any or name + end + local player_priorities + if type(args.players) == "string" then + player_priorities = { [get_priority_key(args.players)] = 1 } + elseif type(args.players) == "table" and #args.players > 0 then + player_priorities = {} + for i, name in ipairs(args.players) do + player_priorities[get_priority_key(name)] = i + end + else + player_priorities = { [playerctl.any] = 1 } + end + self.player_priorities = player_priorities +end + +---@class Playerctl.new.args +---@field players string[] +---@field excluded_players? string[] +---@field metadata table<string, string> + +---@param args? Playerctl.new.args +---@return Playerctl +function playerctl.new(args) + local self = gtable.crush(gobject {}, playerctl.object, true) --[[@as Playerctl]] + + parse_args(self, args) + + initialize_manager(self) + + return self +end + +return playerctl.new { + players = {}, + metadata = { + album = "xesam:album", + title = "xesam:title", + artist = "xesam:artist", + art = "mpris:artUrl", + }, +} |
