]> git.llucax.com Git - personal/ion3-config.git/commitdiff
Rewrite the MPD status monitor to use a persistent connection.
authorLeandro Lucarella <llucarella@integratech.com.ar>
Mon, 29 Oct 2007 19:32:48 +0000 (16:32 -0300)
committerLeandro Lucarella <llucarella@integratech.com.ar>
Mon, 29 Oct 2007 19:32:48 +0000 (16:32 -0300)
Now the MPD status monitor don't create a new connection for each
update_interval miliseconds, it uses a persistent netcat connection to get
the mpd status.

This status monitor is a "merge" between statusd_exec and statusd_mpd.

default/cfg_statusbar.lua
mpd_status.sh [deleted file]
statusd_mpd.lua

index 5d39b430a459bd66332487acbb1c57c43bd1bc40..d13c2c7d350b1af4366d4ad21c7a8a75ad1fa86b 100644 (file)
@@ -94,8 +94,11 @@ mod_statusbar.launch_statusd{
 
     -- MPD
     mpd = {
+        -- netcat path
+        netcat = 'nc',
+
        -- 500 or less makes seconds increment relatively smoothly while playing
-       update_interval = 1000,
+       update_interval = 500,
 
        -- mpd server info (localhost:6600 are mpd defaults)
        address = "localhost",
@@ -113,7 +116,8 @@ mod_statusbar.launch_statusd{
        --   track metadata: %artist, %title, %num, %album, %year, %len
        --   current track position: %pos
        --   escape for the percent character: %%
-        template = "%title by %artist, track %num from %album, %year (%pos/%len) [%volume]",
+        template = "%title by %artist, track %num from %album, %year " ..
+                    "(%pos/%len) [%volume]",
     },
 
     -- Memory monitor
diff --git a/mpd_status.sh b/mpd_status.sh
deleted file mode 100755 (executable)
index 8c329ba..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env python
-
-from socket import socket
-from time import sleep
-
-s = socket()
-s.connect(('localhost', 6600))
-
-volume = state = pos = len = ''
-
-while True:
-       s.send('status\ncurrentsong\n')
-       sleep(1)
-       for line in s.recv(4096).split('\n'):
-               if line.startswith('volume: '):
-                       volume = line[8:]
-               if line.startswith('state: '):
-                       state = line[7:]
-               if line.startswith('time: '):
-                       i = line.rindex(':')
-                       len = line[i+1:]
-                       pos = line[line.index(':')+2:i]
-               if line.startswith('Artist: '):
-                       artist = line[8:]
-               if line.startswith('Title: '):
-                       title = line[7:]
-               if line.startswith('Album: '):
-                       album = line[7:]
-               if line.startswith('Track: '):
-                       track = line[7:]
-               if line.startswith('Date: '):
-                       date = line[6:]
-               if line.startswith('Genre: '):
-                       genre = line[7:]
-       print volume, state, pos, len, artist, title, album, track, date, genre
-
index 57a11030b3a4682da92094bb111dbf469b276e0c..b2948f217da347c721c6192e8b918efc584ad0ff 100644 (file)
@@ -3,18 +3,19 @@
 
 -- requires that netcat is available in the path
 
-NETCAT='nc'
-
 local defaults={
+    -- netcat path
+    netcat = 'netcat',
+
     -- 500 or less makes seconds increment relatively smoothly while playing
-    update_interval=500,
+    update_interval = 1000,
 
     -- mpd server info (localhost:6600 are mpd defaults)
-    address="localhost",
-    port=6600,
+    address = "localhost",
+    port = 6600,
 
     -- mpd password (if any)
-    password=nil,
+    password = nil,
 
     -- seconds to consider the mpd in 'important' hint (0 never, -1 always)
     important_time = 10, -- seconds
@@ -31,141 +32,146 @@ local defaults={
     template = "%artist - %num - %title (%pos / %len)"
 }
 
-local settings=table.join(statusd.get_config("mpd"), defaults)
+local settings = table.join(statusd.get_config("mpd"), defaults)
 
-local success
-local last_success
+local timer
 local last_volume
 local last_state
 
-local function get_mpd_status()
-    local cmd_string = "status\ncurrentsong\nclose\n"
-    if settings.password ~= nil then
-        cmd_string = "password " .. settings.password .. "\n" .. cmd_string
-    end
-    cmd_string = string.format('echo -n "%s" | '..NETCAT..' %s %d',
-                               cmd_string, settings.address, settings.port)
+-- Function for starting and restarting execution of a process
 
-    last_success = success
-    success = false
+local function start_execution()
 
-    local mpd = io.popen(cmd_string, "r")
+    -- Set up a function for receiving the data
+    -- and informing the statusbar
 
-    -- welcome msg
-    local data = mpd:read()
-    if data == nil or string.sub(data,1,6) ~= "OK MPD" then
-        mpd:close()
-        statusd.inform("mpd_hint", "critical")
-        return "mpd not running"
-    end
+    local function process_input(data)
 
-    -- 'password' response (if necessary)
-    if settings.password ~= nil then
-        repeat
-            data = mpd:read()
-        until data == nil or string.sub(data,1,2) == "OK"
-                                or string.sub(data,1,3) == "ACK"
-        if data == nil or string.sub(data,1,2) ~= "OK" then
-            mpd:close()
-            statusd.inform("mpd_hint", "critical")
-            return "bad mpd password"
-        end
-    end
+        -- At first, we are not initialized
+        local inited = false
+
+        -- When we get new data, output the last complete line
+
+        while data do
 
-    local info = {}
-    local hint = "normal"
-
-    -- 'status' response
-    repeat
-        data = mpd:read()
-        if data == nil then break end
-
-        local _,_,attrib,val = string.find(data, "(.-): (.*)")
-        if attrib == "time" then
-            _,_,info.pos,info.len = string.find(val, "(%d+):(%d+)")
-            if settings.important_time == -1
-                            or info.pos+0 <= settings.important_time
-                                and settings.important_time ~= 0 then
-                hint = "important"
+            if not inited then
+                -- welcome msg
+                if string.sub(data, 1, 6) ~= "OK MPD" then
+                    statusd.inform("mpd_hint", "critical")
+                    statusd.inform("mpd", "bad mpd response on init")
+                end
+
+                -- 'password' response (if necessary)
+                if settings.password ~= nil then
+                    if string.sub(data, 1, 2) ~= "OK" then
+                        statusd.inform("mpd_hint", "critical")
+                        statusd.inform("mpd_hint", "bad mpd password")
+                    end
+                end
+
+                -- everything ok, receive more data
+                inited = true
+                data = coroutine.yield()
             end
-            info.pos = string.format("%d:%02d", math.floor(info.pos / 60),
-                    math.mod(info.pos, 60))
-            info.len = string.format("%d:%02d", math.floor(info.len / 60),
-                    math.mod(info.len, 60))
-        elseif attrib == "state" then
-            info.state = val
-            if info.state ~= last_state then
-                hint = "important"
+
+            local info = {}
+            local hint = "normal"
+
+            for attrib, val in string.gfind(data, "([^\n]-): ([^\n]*)") do
+
+                if attrib == "time" then
+                    _, _, info.pos, info.len = string.find(val, "(%d+):(%d+)")
+                    if settings.important_time == -1
+                                    or info.pos+0 <= settings.important_time
+                                        and settings.important_time ~= 0 then
+                        hint = "important"
+                    end
+                    info.pos = string.format("%d:%02d", math.floor(info.pos / 60),
+                            math.mod(info.pos, 60))
+                    info.len = string.format("%d:%02d", math.floor(info.len / 60),
+                            math.mod(info.len, 60))
+                elseif attrib == "state" then
+                    info.state = val
+                    if info.state ~= last_state then
+                        hint = "important"
+                    end
+                    last_state = info.state
+                elseif attrib == "volume" then
+                    info.volume = val .. "%"
+                    if info.volume ~= last_volume then
+                        hint = "important"
+                    end
+                    last_volume = info.volume
+                elseif attrib == "Artist" then
+                    info.artist = val
+                elseif attrib == "Title" then
+                    info.title = val
+                elseif attrib == "Album" then
+                    info.album = val
+                elseif attrib == "Track" then
+                    info.num = val
+                elseif attrib == "Date" then
+                    info.year = val
+                end
             end
-            last_state = info.state
-        elseif attrib == "volume" then
-            info.volume = val .. "%"
-            if info.volume ~= last_volume then
-                hint = "important"
+
+            -- done querying; now build the string
+            statusd.inform("mpd_hint", hint)
+            if info.state == "play" then
+                local mpd_st = settings.template
+                -- fill in %values
+                mpd_st = string.gsub(mpd_st, "%%([%w%_]+)",
+                        function (x) return(info[x]  or "") end)
+                mpd_st = string.gsub(mpd_st, "%%%%", "%%")
+                statusd.inform("mpd", mpd_st)
+            elseif info.state == "pause" then
+                statusd.inform("mpd", "Paused")
+            else
+                statusd.inform("mpd", "No song playing")
             end
-            last_volume = info.volume
-        end
-    until string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK"
-    if data == nil or string.sub(data,1,2) ~= "OK" then
-        mpd:close()
-        statusd.inform("mpd_hint", "critical")
-        return "error querying mpd status"
-    end
 
-    -- 'currentsong' response
-    repeat
-        data = mpd:read()
-        if data == nil then break end
-
-        local _,_,attrib,val = string.find(data, "(.-): (.*)")
-        if     attrib == "Artist" then info.artist = val
-        elseif attrib == "Title"  then info.title  = val
-        elseif attrib == "Album"  then info.album  = val
-        elseif attrib == "Track"  then info.num    = val
-        elseif attrib == "Date"   then info.year   = val
+            -- Wait for bgread to signal that more data
+            -- is available or the program has exited
+
+            data = coroutine.yield()
         end
-    until string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK"
-    if data == nil or string.sub(data,1,2) ~= "OK" then
-        mpd:close()
-        statusd.inform("mpd_hint", "critical")
-        return "error querying current song"
-    end
 
-    mpd:close()
-
-    success = true
-
-    -- done querying; now build the string
-    statusd.inform("mpd_hint", hint)
-    if info.state == "play" then
-        local mpd_st = settings.template
-        -- fill in %values
-        mpd_st = string.gsub(mpd_st, "%%([%w%_]+)",
-                function (x) return(info[x]  or "") end)
-        mpd_st = string.gsub(mpd_st, "%%%%", "%%")
-        return mpd_st
-    elseif info.state == "pause" then
-        return "Paused"
-    else
-        return "No song playing"
+        -- Program has exited.
+        -- Start a timer for a new execution if set.
+
+        timer:set(settings.update_interval,
+                  function() start_execution() end)
     end
-end
 
+    -- Set up a simple function for printing errors
 
-local mpd_timer
+    local function process_error(data)
+        while data do
+            io.stderr:write("ion-statusd [statusd_mpd]: " .. data)
+            io.stderr:flush()
+            data = coroutine.yield()
+        end
+    end
 
-local function update_mpd()
-    -- update unless there's an error that's not yet twice in a row, to allow
-    -- for transient errors due to load spikes
-    local mpd_st = get_mpd_status()
-    if success or not last_success then
-        statusd.inform("mpd", mpd_st)
+    -- Execute the program in the background and create
+    -- coroutine functions to handle input and error data
+    local sleep = settings.update_interval / 1000
+    sleep = string.gsub(sleep, '(%d+),(%d+)', '%1.%2')
+    local cmd = 'while sleep ' .. sleep .. '; do ' ..
+                'echo "status\ncurrentsong"; done | ' ..
+                settings.netcat .. ' ' .. settings.address .. ' ' ..
+                settings.port
+    if settings.password ~= nil then
+        cmd = "echo 'password " .. settings.password .. "'; " .. cmd
     end
-    mpd_timer:set(settings.update_interval, update_mpd)
+    statusd.popen_bgread(cmd, coroutine.wrap(process_input),
+                              coroutine.wrap(process_error))
+
 end
 
--- Init
-mpd_timer=statusd.create_timer()
-update_mpd()
+-- Now start execution of all defined processes
+
+timer = statusd.create_timer()
+start_execution()
 
 -- vim: set et sts=4 sw=4 :