Co-authored-by: unknown <unknown@bigvault>
This commit is contained in:
parent
4e836fab63
commit
9948c42038
118
recipes/mpc.lua
118
recipes/mpc.lua
|
@ -2,6 +2,8 @@ local lgi = require "lgi"
|
||||||
local GLib = lgi.GLib
|
local GLib = lgi.GLib
|
||||||
local Gio = lgi.Gio
|
local Gio = lgi.Gio
|
||||||
|
|
||||||
|
local gears = require "gears"
|
||||||
|
|
||||||
local mpc = {}
|
local mpc = {}
|
||||||
|
|
||||||
local function parse_password(host)
|
local function parse_password(host)
|
||||||
|
@ -13,42 +15,63 @@ local function parse_password(host)
|
||||||
return string.sub(host, position + 1), string.sub(host, 1, position - 1)
|
return string.sub(host, position + 1), string.sub(host, 1, position - 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mpc.new(host, port, password, error_handler, ...)
|
function mpc.new(host, port, password, error_handler, reconnect_interval, ...)
|
||||||
host = host or os.getenv("MPD_HOST") or "localhost"
|
host = host or os.getenv("MPD_HOST") or "localhost"
|
||||||
port = port or os.getenv("MPD_PORT") or 6600
|
port = port or os.getenv("MPD_PORT") or 6600
|
||||||
if not password then
|
if not password then
|
||||||
host, password = parse_password(host)
|
host, password = parse_password(host)
|
||||||
end
|
end
|
||||||
|
|
||||||
local self = setmetatable({
|
local self = setmetatable({
|
||||||
_host = host,
|
_host = host,
|
||||||
_port = port,
|
_port = port,
|
||||||
_password = password,
|
_password = password,
|
||||||
_error_handler = error_handler or function() end,
|
_error_handler = error_handler or function() end,
|
||||||
_connected = false,
|
_connected = false,
|
||||||
_try_reconnect = false,
|
_idle_commands = { ... },
|
||||||
_idle_commands = { ... }
|
_conn = nil,
|
||||||
|
_output = nil,
|
||||||
|
_input = nil,
|
||||||
|
_reconnect_timer = nil,
|
||||||
|
_reconnect_interval = reconnect_interval,
|
||||||
|
_try_reconnect = true
|
||||||
}, { __index = mpc })
|
}, { __index = mpc })
|
||||||
|
|
||||||
self:_connect()
|
self:_connect()
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function mpc:_error(err)
|
function mpc:_error(err)
|
||||||
|
self._error_handler(err, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mpc:_reset()
|
||||||
|
self._output = nil
|
||||||
|
self._input = nil
|
||||||
|
self._reply_handlers = {}
|
||||||
|
self._pending_reply = {}
|
||||||
|
self._idle_commands_pending = false
|
||||||
|
self._idle = false
|
||||||
self._connected = false
|
self._connected = false
|
||||||
self._error_handler(err)
|
end
|
||||||
self._try_reconnect = not self._try_reconnect
|
|
||||||
if self._try_reconnect then
|
function mpc:_reconnect()
|
||||||
self:_connect()
|
if not self._reconnect_timer then
|
||||||
|
self:_error("cannot reconnect")
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not self._try_reconnect then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self._reconnect_timer:again()
|
||||||
end
|
end
|
||||||
|
|
||||||
function mpc:_connect()
|
function mpc:_connect()
|
||||||
if self._connected then return end
|
if self._connected then return end
|
||||||
-- Reset all of our state
|
-- Reset all of our state
|
||||||
self._reply_handlers = {}
|
self:_reset()
|
||||||
self._pending_reply = {}
|
|
||||||
self._idle_commands_pending = false
|
|
||||||
self._idle = false
|
|
||||||
self._connected = true
|
|
||||||
|
|
||||||
-- Set up a new connection
|
-- Set up a new connection
|
||||||
local address
|
local address
|
||||||
|
@ -60,13 +83,28 @@ function mpc:_connect()
|
||||||
address = Gio.NetworkAddress.new(self._host, self._port)
|
address = Gio.NetworkAddress.new(self._host, self._port)
|
||||||
end
|
end
|
||||||
local client = Gio.SocketClient()
|
local client = Gio.SocketClient()
|
||||||
local conn, err = client:connect(address)
|
|
||||||
|
local conn
|
||||||
|
if not self._conn then
|
||||||
|
if not self._reconnect_timer and self._try_reconnect then
|
||||||
|
-- timer requires positive value
|
||||||
|
local interval = self._reconnect_interval >= 0 and self._reconnect_interval or 0
|
||||||
|
self._reconnect_timer = gears.timer.start_new(interval, function()
|
||||||
|
-- user disabled reconnect
|
||||||
|
if self._reconnect_interval < 0 then
|
||||||
|
self._try_reconnect = false
|
||||||
|
end
|
||||||
|
|
||||||
|
self:_reset()
|
||||||
|
conn, err = client:connect(address)
|
||||||
|
|
||||||
if not conn then
|
if not conn then
|
||||||
self:_error(err)
|
self:_error(err)
|
||||||
return false
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self._connected = true
|
||||||
|
|
||||||
local input, output = conn:get_input_stream(), conn:get_output_stream()
|
local input, output = conn:get_input_stream(), conn:get_output_stream()
|
||||||
self._conn, self._output, self._input = conn, output, Gio.DataInputStream.new(input)
|
self._conn, self._output, self._input = conn, output, Gio.DataInputStream.new(input)
|
||||||
|
|
||||||
|
@ -77,10 +115,24 @@ function mpc:_connect()
|
||||||
self:_send("password " .. self._password)
|
self:_send("password " .. self._password)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
self:_reconnect()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self._reconnect_timer:connect_signal("stop", function()
|
||||||
|
self:do_read()
|
||||||
|
-- To synchronize the state on startup, send the idle commands now. As a
|
||||||
|
-- side effect, this will enable idle state.
|
||||||
|
self:_send_idle_commands(true)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mpc:do_read()
|
||||||
-- Set up the reading loop. This will asynchronously read lines by
|
-- Set up the reading loop. This will asynchronously read lines by
|
||||||
-- calling itself.
|
-- calling itself.
|
||||||
local do_read
|
|
||||||
do_read = function()
|
|
||||||
self._input:read_line_async(GLib.PRIORITY_DEFAULT, nil, function(obj, res)
|
self._input:read_line_async(GLib.PRIORITY_DEFAULT, nil, function(obj, res)
|
||||||
local line, err = obj:read_line_finish(res)
|
local line, err = obj:read_line_finish(res)
|
||||||
-- Ugly API. On success we get string, length-of-string
|
-- Ugly API. On success we get string, length-of-string
|
||||||
|
@ -88,12 +140,16 @@ function mpc:_connect()
|
||||||
-- behave differently.
|
-- behave differently.
|
||||||
if line == nil or tostring(line) == "" then
|
if line == nil or tostring(line) == "" then
|
||||||
err = "Connection closed"
|
err = "Connection closed"
|
||||||
end
|
|
||||||
if type(err) ~= "number" then
|
|
||||||
self._output, self._input = nil, nil
|
|
||||||
self:_error(err)
|
self:_error(err)
|
||||||
|
self:_reconnect()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(err) ~= "number" then
|
||||||
|
self:_error(err)
|
||||||
|
self:_reconnect()
|
||||||
else
|
else
|
||||||
do_read()
|
self:do_read()
|
||||||
line = tostring(line)
|
line = tostring(line)
|
||||||
if line == "OK" or line:match("^ACK ") then
|
if line == "OK" or line:match("^ACK ") then
|
||||||
local success = line == "OK"
|
local success = line == "OK"
|
||||||
|
@ -115,14 +171,6 @@ function mpc:_connect()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
|
||||||
do_read()
|
|
||||||
|
|
||||||
-- To synchronize the state on startup, send the idle commands now. As a
|
|
||||||
-- side effect, this will enable idle state.
|
|
||||||
self:_send_idle_commands(true)
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mpc:_send_idle_commands(skip_stop_idle)
|
function mpc:_send_idle_commands(skip_stop_idle)
|
||||||
|
@ -148,6 +196,7 @@ end
|
||||||
|
|
||||||
function mpc:_start_idle()
|
function mpc:_start_idle()
|
||||||
if self._idle then
|
if self._idle then
|
||||||
|
self:_error("still idle?!")
|
||||||
error("Still idle?!")
|
error("Still idle?!")
|
||||||
end
|
end
|
||||||
self:_send("idle", function(success, reply)
|
self:_send("idle", function(success, reply)
|
||||||
|
@ -161,6 +210,7 @@ end
|
||||||
|
|
||||||
function mpc:_stop_idle()
|
function mpc:_stop_idle()
|
||||||
if not self._idle then
|
if not self._idle then
|
||||||
|
self:_error("Not idle?!")
|
||||||
error("Not idle?!")
|
error("Not idle?!")
|
||||||
end
|
end
|
||||||
self._output:write("noidle\n")
|
self._output:write("noidle\n")
|
||||||
|
@ -169,6 +219,7 @@ end
|
||||||
|
|
||||||
function mpc:_send(command, callback)
|
function mpc:_send(command, callback)
|
||||||
if self._idle then
|
if self._idle then
|
||||||
|
self:_error("Still idle in send()?!")
|
||||||
error("Still idle in send()?!")
|
error("Still idle in send()?!")
|
||||||
end
|
end
|
||||||
self._output:write(command .. "\n")
|
self._output:write(command .. "\n")
|
||||||
|
@ -176,12 +227,14 @@ function mpc:_send(command, callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mpc:send(...)
|
function mpc:send(...)
|
||||||
self:_connect()
|
if not self._conn then
|
||||||
if not self._connected then
|
self:_reconnect()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local args = { ... }
|
local args = { ... }
|
||||||
if not self._idle then
|
if not self._idle then
|
||||||
|
self:_error("Something is messed up, we should be idle here...")
|
||||||
error("Something is messed up, we should be idle here...")
|
error("Something is messed up, we should be idle here...")
|
||||||
end
|
end
|
||||||
self:_stop_idle()
|
self:_stop_idle()
|
||||||
|
@ -204,9 +257,10 @@ end
|
||||||
--[[
|
--[[
|
||||||
|
|
||||||
-- Example on how to use this (standalone)
|
-- Example on how to use this (standalone)
|
||||||
|
-- set negative reconnect_interval to disable reconnect in case initial connection failed
|
||||||
|
|
||||||
local host, port, password = nil, nil, nil
|
local host, port, password, reconnect_interval = nil, nil, nil, 0
|
||||||
local m = mpc.new(host, port, password, error,
|
local m = mpc.new(host, port, password, error_handler, reconnect_interval
|
||||||
"status", function(success, status) print("status is", status.state) end)
|
"status", function(success, status) print("status is", status.state) end)
|
||||||
|
|
||||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, function()
|
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, function()
|
||||||
|
|
|
@ -39,7 +39,6 @@ updates when the current MPD state changes.
|
||||||
|
|
||||||
local mpc = require("mpc")
|
local mpc = require("mpc")
|
||||||
local textbox = require("wibox.widget.textbox")
|
local textbox = require("wibox.widget.textbox")
|
||||||
local timer = require("gears.timer")
|
|
||||||
local mpd_widget = textbox()
|
local mpd_widget = textbox()
|
||||||
local state, title, artist, file = "stop", "", "", ""
|
local state, title, artist, file = "stop", "", "", ""
|
||||||
local function update_widget()
|
local function update_widget()
|
||||||
|
@ -53,15 +52,14 @@ updates when the current MPD state changes.
|
||||||
end
|
end
|
||||||
mpd_widget.text = text
|
mpd_widget.text = text
|
||||||
end
|
end
|
||||||
local connection
|
|
||||||
local function error_handler(err)
|
local function error_handler(err)
|
||||||
mpd_widget:set_text("Error: " .. tostring(err))
|
mpd_widget:set_text("Error: " .. tostring(err))
|
||||||
-- Try a reconnect soon-ish
|
|
||||||
timer.start_new(10, function()
|
|
||||||
connection:send("ping")
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
connection = mpc.new(nil, nil, nil, error_handler,
|
|
||||||
|
local reconnect_interval = 1 -- negative to disable
|
||||||
|
local connection
|
||||||
|
connection = mpc.new(nil, nil, nil, error_handler, reconnect_interval,
|
||||||
"status", function(_, result)
|
"status", function(_, result)
|
||||||
state = result.state
|
state = result.state
|
||||||
end,
|
end,
|
||||||
|
|
Loading…
Reference in New Issue