lua-resty-mpd
一个 OpenResty/Luasocket/cqueues MPD 客户端库
$ opm get jprjr/lua-resty-mpd
lua-resty-mpd
尽管名为“lua-resty-mpd”,但它也适用于普通的 Lua!
这是一个用于通过 TCP 套接字或 Unix 套接字与 Music Player Daemon 交互的库。
它与 OpenResty 的 cosockets、cqueues 套接字 和 LuaSocket 兼容。它将尝试自动检测最合适的库,您也可以指定是否要使用特定库。
您可以在 nginx 和 cqueues 中同步或异步使用此库,在 Luasocket 上,您只能执行同步操作。
安装
您可以使用 luarocks
luarocks install lua-resty-mpd
或 OPM
opm get jprjr/lua-resty-mpd
或者从此仓库获取合并文件(在 lib/
下),它将该模块的所有源代码合并到一个文件中。
由于这可以使用多个套接字库,我没有将其列为依赖项,您需要自行安装 luasocket 或 cqueues。无需其他外部依赖项。
示例用法
这是一个循环调用 idle() 的脚本。
local mpd = require'resty.mpd'
local client = mpd()
client:settimeout(1000) -- set a low timeout just to demo idle timing out.
client:connect('tcp://127.0.0.1:6600')
-- loop until we've read 5 events
local events = 5
while events > 0 do
local res, err = client:idle()
if err and err ~= 'socket:timeout' then
print('Error: ' .. err)
os.exit(1)
end
for _,event in ipairs(res) do
print('Event: ' .. event)
events = events - 1
-- do something based on the event
end
end
client:close()
这是在 openresty 下使用 nginx 线程的异步用法示例。
local mpd = require'resty.mpd'
local client = mpd()
-- If MPD isn't running, bail
assert(client:connect('127.0.0.1'))
-- Holds references to our threads
local threads = {}
-- Each entry is the command to run and a
-- key that should be in the response.
--
-- We do this to verify that each thread is
-- getting the correct response (if we called
-- "status" but didn't get the "state" key, then
-- something went really, really wong.
local commands = {
{ 'status', 'state' },
{ 'stats', 'uptime' },
{ 'replay_gain_status','replay_gain_mode'},
}
-- Start a loop around client:idle().
-- This will write out any idle events (should be zero
-- unless you happen to do something to MPD in the
-- 2 seconds that this script runs for), and
-- exits if it receives an error.
table.insert(threads,ngx.thread.spawn(function()
while true do
print('calling client:idle()')
local events, err = client:idle()
if err and err ~= 'socket:timeout' then
print(string.format('client:idle() error %s',err))
return false, err
end
print(string.format('client:idle() returned %d events',#events))
for _,event in ipairs(events) do
print(string.format('client:idle() event: %s',event))
end
end
end))
-- Start threads to send individual commands.
-- These will interrupt the idle call and force
-- idle to return zero events.
for i=1,#commands do
table.insert(threads,ngx.thread.spawn(function()
local func = commands[i][1]
local key = commands[i][2]
print(string.format('calling client:%s()',func))
local res, err = client[func](client)
if err then
print(string.format('client:%s() error: %s',func,err))
return false, err
end
if not res[key] then
err = string.format('missing key %s',key)
print(string.format('client:%s() error: %s',func,err))
return false,err
end
print(string.format('client:%s() success',func))
return true
end))
end
-- Shut everything down after 2 seconds.
table.insert(threads,ngx.thread.spawn(function()
ngx.sleep(2)
print('calling client:close()')
local ok, err = client:close()
if err then
print(string.format('client:close() err: ' .. err))
end
end))
-- Rejoin all the threads
for i=1,#threads do
local ok, err = ngx.thread.wait(threads[i])
if not ok then error(err) end
end
这基本上与 nginx 示例相同,但使用 cqueues。由于它实际上与 nginx 示例相同,因此没有注释。
local cqueues = require'cqueues'
local mpd = require'resty.mpd'
local loop = cqueues.new()
local client = mpd.new()
assert(client:connect('127.0.0.1'))
local commands = {
{ 'status', 'state' },
{ 'stats', 'uptime' },
{ 'replay_gain_status','replay_gain_mode'},
}
loop:wrap(function()
while true do
print('calling client:idle()')
local events, err = client:idle()
if err and err ~= 'socket:timeout' then
print(string.format('client:idle() error %s',err))
return false, err
end
print(string.format('client:idle() returned %d events',#events))
for _,event in ipairs(events) do
print(string.format('client:idle() event: %s',event))
end
end
end)
for i=1,#commands do
loop:wrap((function()
local func = commands[i][1]
local key = commands[i][2]
print(string.format('calling client:%s()',func))
local res, err = client[func](client)
if err then
print(string.format('client:%s() error: %s',func,err))
return false, err
end
if not res[key] then
err = string.format('missing key %s',key)
print(string.format('client:%s() error: %s',func,err))
return false,err
end
print(string.format('client:%s() success',func))
return true
end))
end
loop:wrap(function()
cqueues.sleep(2)
print('calling client:close()')
local ok, err = client:close()
if err then
print(string.format('client:close() err: ' .. err))
end
end)
assert(loop:loop())
全局选项
lib = mpd:backend([name])
返回所有新客户端使用的套接字/条件变量库,name
是一个可选参数,用于选择特定库。有效的 name
值为
nginx
- nginx cosockets。cqueues
- cqueues。luasocket
- luasocket。
如果库不可用,它将返回默认库。
返回值是正在使用的库,您可以检查 .name
字段以查看它是哪个特定的库。示例
lib = mpd:backend('luasocket')
assert(lib.name == 'luasocket')
实例化客户端
client = mpd()
创建一个新的客户端实例。
您也可以将其称为 mpd.new()
lib = client:backend([name])
返回此特定客户端使用的套接字库,它的行为与上面的 mpd:backend
相同。
nginx
- nginx cosockets。cqueues
- cqueues。luasocket
- luasocket。
如果您的客户端已经调用了 connect
,您将无法更改库,您需要调用 close
,更改库,然后重新连接。
ok, err = client:connect(url)
连接到 MPD,支持 tcp 和 unix 套接字连接。
URL 应该采用以下两种格式之一
tcp://host:port
unix:/path/to/socket
host:port
host
(隐含端口 6600)tcp://host
(隐含端口 6600)path/to/socket
(不必是绝对路径)
对于 TCP 连接,您也可以将其称为 client:connect(host,port)
。
ok, err = client:settimeout(ms)
以毫秒为单位设置套接字超时,或使用 nil
表示无超时。
默认情况下,客户端没有超时,并且将永远阻塞,请注意这包括 nginx/OpenResty 后端。
(从技术上讲,OpenResty 不支持没有超时,因此它被设置为最大值)。
ok, err = client:close()
关闭连接,强制所有挂起的操作出错。
已实现的协议功能和错误处理
我过去曾经列出每个已实现的函数,相反,我建议您直接查阅 MPD 协议文档:https://www.musicpd.org/doc/protocol/command_reference.html
命令返回结果表或布尔值作为第一个返回值,以及错误(如果有)作为第二个返回值。
如果错误来自 MPD,则消息将以字符串 mpd:
开头,后跟错误号,以及括号中的错误消息,例如
mpd:50(No such file)
任何与套接字相关的错误消息都将以 socket:
开头,这些错误是不可恢复的(您应该断开连接/退出等)。idle
除外,见下文。
client:idle()
当 idle
超时时,它会自动发送 noidle
来取消当前的 idle
请求。否则,您的调度程序(nginx 线程、cqueues 等)可能会在您从应用程序中调用 noidle
之前发送命令,因为当 idle
结束时,下一个排队的命令将被调用。
这意味着 idle
将始终返回一个事件列表,该列表可能是超时情况下为空的表,您应该检查 err
的值以查看是否已超时(如果 err
为 nil
,则 idle
是通过另一个排队的命令故意取消的)。
一般用法
一般来说,您只需像 MPD 协议文档中列出的那样发送值。例如,MPD 协议文档对 list
命令有以下原型
list {TYPE} {FILTER} [group {GROUPTYPE}]
这将转换为
response, err = client:list(type,filter,'group',grouptype)
对于接受范围的函数,您对范围的每个部分使用单独的参数。例如,使用 find
命令,您可以指定 window
范围
find {FILTER} [sort {TYPE}] [window {START:END}]
这将变为
response, err = client:find(filter,'sort',type,'window',start,end)
对于可选参数,只需将其省略。如果您想仅使用过滤器和窗口调用 find
response, err = client:find(filter,'window',start,end)
或者仅使用过滤器
response, err = client:find(filter)
对于使用它们的命令,完全支持组(和嵌套组),组将返回类似数组的表而不是对象,因此,例如
local res, err = client:list('title','group','album','group','albumartist')
res
将是一个类似数组的表,每个条目将包含一个 title
、album
和 albumartist
键。
变更日志
版本 5.2.1
添加了三个缺少的命令
addtagid
outputset
volume
版本 5.2.0
行为更改 - MPD 可以返回具有相同键的多个响应。例如,如果 FLAC 文件列出了多个 COMPOSER
标签,MPD 将返回
Album: An Album
Artist: An Artist
Composer: First Composer
Composer: Second Composer
在版本 5.2.0 之前,lua-resty-mpd 只会返回最后一个标签,因此您的响应将类似于
{
album = "An Album",
artist = "An Artist",
composer = "Second Composer",
}
从版本 5.2.0 开始,如果检测到重复的键,则该值将被转换为表,例如
{
album = "An Album",
artist = "An Artist",
composer = { "First Composer", "Second Composer" },
}
版本 5.1.1
添加了一个缺少的命令:deleteid
版本 5.1.0
添加了新的 binarylimit
协议命令。
版本 5.0.1
小错误修复,如果未连接,则返回套接字错误。
版本 5.0.0
完全重写,客户端命令(list、play 等)应与旧版本兼容,但选择后端库的函数则不兼容。
这是为了异步操作而重写的,新版本可以根据需要自动调用 noidle
,而无需像版本 3 那样进行任何 hack。
版本 4.0.0
恢复通过 condvar/semaphore 的自动 noidle,事实证明这并不是一个好主意。
保留处理二进制响应并与 MPD 0.22.0 及其更高版本兼容的先前增强功能。
版本 3.0.1
关于条件变量/信号量的错误修复,现在似乎在调用 noidle 时更加可靠。
版本 3.0.0
主要版本升级。
版本 3.0.0 尝试检测是否在等待 IDLE
命令完成时发送了命令,并自动调用 noidle
,它通过 nginx 信号量和 cqueues 条件变量来完成此操作。
此新行为在 LuaSocket 上不受支持,您需要自己调用 noidle
。
还处理二进制响应,并且应该处理截至 MPD 0.22.0 的所有 MPD 协议函数。
版本 2.2.0
新功能,现在支持 cqueues 套接字库。
库使用以下优先级自动检测
nginx cosockets
cqueues
luasocket
这可以在全局级别或每个客户端级别覆盖。
版本 2.1.1
错误修复:在发送时转义引号/反斜杠。
版本 2.1.0
新功能:new
接受一个可选的表,请参阅文档。
版本 2.0.2
修复了 noidle
中的潜在竞争条件。
使用正确的套接字超时比例(luasocket 以秒为单位,nginx 以毫秒为单位)。
版本 2.0.1
修复了超时操作。
版本 2.0.0
重大变更
idle
更改
在以前的版本中,调用 idle
将返回一个字符串,如果使用 noidle
取消了空闲,则使用特殊字符串 (“interrupted”)。在 MPD 中,对 idle
的调用可以返回多个事件。
idle
现在返回一个事件数组,使用空数组表示取消了 idle
。
commands
和 notcommands
更改
以前的版本返回一个表,其中每个命令都是一个设置为 true
的键。
commands
和 notcommands
现在返回一个命令数组。
非重大变更
以前的版本要求 URL 匹配以下格式
tcp://host:port
unix:/path/to/socket
URL 还可以使用以下格式
host:port
host
(隐含端口 6600)tcp://host
(隐含端口 6600)path/to/socket
(不必是绝对路径)
我仍然建议使用 tcp://
或 unix:
前缀以明确表示
许可证
MIT 许可证(见 LICENSE
)
作者
John Regan
许可证
mit
依赖项
luajit
版本
-
jprjr/lua-resty-mpd 5.2.3一个 OpenResty/Luasocket/cqueues MPD 客户端库 2022-07-20 14:43:32
-
jprjr/lua-resty-mpd 5.2.2一个 OpenResty/Luasocket/cqueues MPD 客户端库 2022-05-06 18:00:28
-
jprjr/lua-resty-mpd 5.2.1一个 OpenResty/Luasocket/cqueues MPD 客户端库 2021-07-08 17:24:02
-
jprjr/lua-resty-mpd 5.2.0一个 OpenResty/Luasocket/cqueues MPD 客户端库 2021-07-08 14:59:24
-
jprjr/lua-resty-mpd 5.1.1一个 OpenResty/Luasocket/cqueues MPD 客户端库 2021-07-04 20:29:36
-
jprjr/lua-resty-mpd 5.1.0一个 OpenResty/Luasocket/cqueues MPD 客户端库 2021-04-02 18:15:33
-
jprjr/lua-resty-mpd 5.0.7一个 OpenResty/Luasocket/cqueues MPD 客户端库 2021-04-02 17:33:27
-
jprjr/lua-resty-mpd 5.0.6一个 OpenResty/Luasocket/cqueues MPD 客户端库 2020-12-07 23:08:07
-
jprjr/lua-resty-mpd 5.0.5一个 OpenResty/Luasocket/cqueues MPD 客户端库 2020-12-03 14:31:20
-
jprjr/lua-resty-mpd 5.0.4一个 OpenResty/Luasocket/cqueues MPD 客户端库 2020-12-03 14:16:30
-
jprjr/lua-resty-mpd 5.0.3一个 OpenResty/Luasocket/cqueues MPD 客户端库 2020-11-30 17:29:07
-
jprjr/lua-resty-mpd 5.0.2一个 OpenResty/Luasocket/cqueues MPD 客户端库 2020-11-26 20:52:05
-
jprjr/lua-resty-mpd 5.0.1一个 OpenResty/Luasocket/cqueues MPD 客户端库 2020-11-18 19:31:02
-
jprjr/lua-resty-mpd 5.0.0一个 OpenResty/Luasocket/cqueues MPD 客户端库 2020-11-18 19:23:22
-
jprjr/lua-resty-mpd 4.0.0一个 OpenResty/Luasocket MPD 客户端库 2020-11-15 22:01:46
-
jprjr/lua-resty-mpd 3.0.1一个 OpenResty/Luasocket MPD 客户端库 2020-11-12 00:54:48
-
jprjr/lua-resty-mpd 3.0.0一个 OpenResty/Luasocket MPD 客户端库 2020-11-11 20:40:38
-
jprjr/lua-resty-mpd 2.2.0一个 OpenResty/Luasocket MPD 客户端库 2020-11-11 15:25:25
-
jprjr/lua-resty-mpd 2.1.1一个 OpenResty/Luasocket MPD 客户端库 2020-11-10 22:45:16
-
jprjr/lua-resty-mpd 2.1.0一个 OpenResty/Luasocket MPD 客户端库 2020-05-10 13:25:32
-
jprjr/lua-resty-mpd 2.0.2一个 OpenResty/Luasocket MPD 客户端库 2020-05-10 12:34:06
-
jprjr/lua-resty-mpd 2.0.1一个 OpenResty/Luasocket MPD 客户端库 2020-05-09 21:16:03
-
jprjr/lua-resty-mpd 2.0.0一个 OpenResty/Luasocket MPD 客户端库 2020-05-09 18:46:21
-
jprjr/lua-resty-mpd 1.0.4一个 OpenResty/Luasocket MPD 客户端库 2017-09-06 19:25:34
-
jprjr/lua-resty-mpd 1.0.3一个 OpenResty/Luasocket MPD 客户端库 2017-09-06 18:21:09
-
jprjr/lua-resty-mpd 1.0.2一个 OpenResty/Luasocket MPD 客户端库 2017-09-06 13:44:38
-
jprjr/lua-resty-mpd 1.0.1一个 OpenResty/Luasocket MPD 客户端库 2017-03-25 16:40:15