alexander-yakushev / awesompd

Advanced mpd widget for Awesome WM

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Features for a more responsive feel (update_track changes)

firefish5000 opened this issue · comments

With update_track called only every 10 seconds by default, there seems to be a lag between song changes and awesompd updating. Small tracks (eg. 10sec- sound-effect track) may be missed completely. And Notify info on hover is slightly out of date.
Shorter intervals would just add unnecessary mpc calls. So I propose adding additional calls to update_track when:
mouse::enter event occurs
the current song is about to end
Mouse enter is simple enough to implement, just add 'instance:update_track' to mouse::enter before notify_track. The reasoning for adding it would be the user is looking for info on the current track, so we should make sure the info is accurate with an update.

Updating when the current song ends is just a bit complicated. However its advantage is near instant notifications of track changes almost ensuring that all tracks are noticed (still a 1-2 sec delay). To do it, we need update_track to, when state.PLAYING, note the track's current position and total length, calculate the time remaining in seconds, and add a one time timer calling update_track at that time (stopping/destroying previous timers).

In addition to this, I believe having the notify_track update the progress for the duration its displayed adds a nice feel to it, though that requires a call to update_track ~ every second, and may cause notify flickering. Perhaps as an optional feature.

And one last request, mouse::enter notification works great for most titles, but it would be nice if it stayed up until mouse::leave event was sent. This way we can read long titles without worrying of loosing our place every 5 seconds.

Here is a diff implementing the suggested features, feel free to use/discard/change any of them.

diff --git a/awesompd.lua b/awesompd.lua
index a095df8..c61b73e 100644
--- a/awesompd.lua
+++ b/awesompd.lua
@@ -148,6 +148,19 @@ local function non_empty(s)
    end
 end

+-- Converts mm:ss to seconds
+function awesompd.minsec_to_sec(s)
+   if s then
+       local min = string.match(s,"%d+%:")
+       min = string.match(min,"%d+")
+       local sec = string.match(s,"%:%d+")
+       sec = string.match(sec,"%d+")
+       return (min*60)+sec
+   else
+       return 0
+   end
+end
+
 -- Icons

 function awesompd.load_icons(path)
@@ -201,7 +214,10 @@ function awesompd:create()
    instance.background = beautiful.bg_normal
    instance.scrolling = true
    instance.output_size = 30
-   instance.update_interval = 10
+   -- update interval has been raised since smart_update handles responsiveness for continuous play.
+   -- this should only be needed for transitions (pause/play/fast-forward/rewind/skip) are done from
+   -- outside of awesompd.(eg. user intervention via mpc or ario)
+   instance.update_interval = 30
    instance.path_to_icons = ""
    instance.ldecorator = " "
    instance.rdecorator = " "
@@ -209,14 +225,33 @@ function awesompd:create()
    instance.show_album_cover = true
    instance.album_cover_size = 50
    instance.browser = "firefox"
-   
+-- Smart Update (sets timer to check/update widget near when the current track should end)
+   instance.track_position = "00:00"
+   instance.track_duration = "00:00"
+   instance.smart_update_timer = timer({ timeout = instance.continuous_notify_interval })
+   instance.smart_update_timer:connect_signal("timeout", function() instance:smart_update() end)
+-- Continuous Notify (updates notify every 0.5 sec until duration passed or told to stop, keeping track/time in for up to date)
+   instance.continuous_notify_interval = 0.5 -- How often to update when continuous is active
+   instance.continuous_notify_till = nil -- Seconds since epoche time when notify should be hidden
+   instance.continuous_notify_timer = timer({ timeout = instance.continuous_notify_interval })
+   instance.continuous_notify_timer:connect_signal("timeout", function()
+                                           instance:update_track()
+                                           instance:notify_track()
+                                           if (instance.continuous_notify_till) then
+                                               if (instance.continuous_notify_till <= os.time() ) then
+                                                   instance:continuous_notify_stop()
+                                               end
+                                           end
+                                       end)
+   -- When to stop notifying, epoche
+   -- awesompd widget mouse hover.
 -- Widget configuration
    instance.widget:connect_signal("mouse::enter", function(c)
-                                                 instance:notify_track()
-                                              end)
+       instance:continuous_notify_start()
+   end)
    instance.widget:connect_signal("mouse::leave", function(c)
-                                                 instance:hide_notification()
-                                              end)
+       instance:continuous_notify_stop()
+   end)
    return instance
 end

@@ -853,6 +888,26 @@ function awesompd:notify_track()
       self:show_notification(caption, nf_text, al_cover)
    end
 end
+function awesompd:continuous_notify_start(duration)
+   self:update_track()
+   self:notify_track()
+-- if scheduler then -- I dont understand/use scheduler
+--     scheduler.register_recurring("awesompd_continuous_notify", instance.continuous_notify_interval,
+--                             function() instance:notify_track() end)
+-- else
+       if duration then
+           self.continuous_notify_till = os.time() + duration
+       else -- You could set a default limit here, prehaps 60 sec, in case of an error like mouse::leave not sending.
+           self.continuous_notify_till = nil
+       end
+       self.continuous_notify_timer:start()
+-- end
+end
+
+function awesompd:continuous_notify_stop()
+   self.continuous_notify_timer:stop()
+   self:hide_notification()
+end

 function awesompd:notify_state(state_changed)
    state_array = { "Volume: " .. self.state_volume ,
@@ -926,7 +981,8 @@ end
 -- Checks if notification should be shown and shows if positive.
 function awesompd:check_notify()
    if self.to_notify then
-      self:notify_track()
+--      self:notify_track()
+       self:continuous_notify_start(5)
       self.to_notify = false
    end
 end
@@ -1025,6 +1081,11 @@ function awesompd:update_track(file)
             end
     end
     local tmp_pst = string.find(status_line,"%d+%:%d+%/")
+    local tmp_playtime = string.match(status_line,"%d+%:%d+%/%d+%:%d+")
+    local tmp_pos = string.match(tmp_playtime,"%d+%:%d+%/")
+    self.track_position = string.match(tmp_pos,"%d+%:%d+")
+    local tmp_dur = string.match(tmp_playtime,"%/%d+%:%d+")
+    self.track_duration = string.match(tmp_dur,"%d+%:%d+")
     local progress = self.find_pattern(status_line,"%#%d+/%d+") .. " " .. string.sub(status_line,tmp_pst)
          local new_status = awesompd.PLAYING
     if string.find(status_line,"paused") then
@@ -1039,6 +1100,27 @@ function awesompd:update_track(file)
     self.status_text = self.status .. " " .. progress
       end
    end
+   self:smart_update()
+end
+function awesompd:smart_update()
+   -- NOTE OneTime Timer. Prehaps there is another way to do this 
+   self.smart_update_timer:stop()
+   if (self.status == awesompd.PLAYING) then
+       local pos = awesompd.minsec_to_sec(self.track_position)
+       local dur = awesompd.minsec_to_sec(self.track_duration)
+       local rem = dur - pos
+       if (rem <= self.update_interval) then
+           if (rem >= 1) then -- Little time remaining, lets update when it runs out
+               self.smart_update_timer = timer({ timeout = rem })
+           else -- carefull of rem 0
+               self.smart_update_timer = timer({ timeout = 1 })
+           end
+           self.smart_update_timer:connect_signal("timeout", function() -- Update at predicted time
+                   self:update_track()
+               end)
+           self.smart_update_timer:start()
+       end
+   end
 end

 function awesompd:update_state(state_string)
@@ -1084,9 +1166,12 @@ end
 -- used.
 function awesompd.protect_string(str, for_menu)
    if for_menu then
-      return utf8.replace(str, awesompd.ESCAPE_MENU_SYMBOL_MAPPING)
+--      return utf8.replace(str, awesompd.ESCAPE_MENU_SYMBOL_MAPPING)
+     return awful.util.escape(str)
+
    else
-      return utf8.replace(str, awesompd.ESCAPE_SYMBOL_MAPPING)
+--   return utf8.replace(str, awesompd.ESCAPE_SYMBOL_MAPPING)
+     return awful.util.escape(str)
    end
 end

This is a very good idea. How about you make a pull request out of this and we will work towards its merge? I have a few comments on this but it's gonna be easier to voice them in a PR.