Coder Social home page Coder Social logo

Support `idle` about ruby-mpd HOT 4 OPEN

Phrogz avatar Phrogz commented on June 21, 2024
Support `idle`

from ruby-mpd.

Comments (4)

archseer avatar archseer commented on June 21, 2024

https://github.com/archSeer/ruby-mpd#idle

from ruby-mpd.

Phrogz avatar Phrogz commented on June 21, 2024

https://github.com/archSeer/ruby-mpd#idle

To implement idle, what is needed is a lock that prevents sending commands to the daemon while waiting for the response (except noidle). An intermediate solution would be to queue the commands to send them later, when idle has returned the response.

That's not how I'd do it. Idle is ~useless to me if I can't do anything else once I invoke it. I'd implement idle by having each call to idle spawn another connection in another thread, ideally with an idleloop style option that allows the thread and connection to be re-used indefinitely if desired.

Some background on how I'm using idle, in case it helps shape the reason for my argument above:

My (web-based) music player shows the currently-playing song, progress, volume, and a list of all available playlists, and the "up next" live playlist. I want these to be exactly up-to-date.

I could have the web browser poll the web server, and have the web server poll MPD at high frequency. Turns out to be an ugly amount of network traffic and high server CPU usage. (There are currently over 17k songs in the music database. When all of them are added to the up-next queue, it takes several seconds to ask MPD for the current list.)

I could use ruby-mpd callbacks. However, polling the MPD server at 5Hz for changes seems ugly. I worry that it will also be CPU intensive (though I've not yet tried it.)

Instead, I want to write:

# Starts idling on another (identical) connection under another thread.
# Invokes the block once the idle command returns.
# The continuous:true option causes the idle command to be immediately re-issued
# in the same thread/connection (for convenience and minimal thread thrash).
@mpd.idle_until( 'stored_playlist', continuous:true ){ update_playlists }
@mpd.idle_until( 'playlist database', continuous:true ){ update_upnext }
@mpd.idle_until( 'player mixer options', continuous:true ){ update_status }

Right now I'm using EventMachine.defer to spawn a thread for each of these, and executing mpc idle. I use Faye to send updates to all clients when a change occurs. It's working well at first glance, but I'd rather not shell to mpc when I have ruby-mpd.

def initialize
  @mpd = MPD.new( ENV['MPD_HOST'], ENV['MPD_PORT'] )
  @mpd.connect
  @faye = Faye::Client.new("http://#{ENV['RB3JAY_HOST']}:#{ENV['RB3JAY_PORT']}/faye")
  watch_for_changes
end

def watch_for_changes
  watch_status
  watch_playlists
  watch_upnext
end

def watch_status
  # TODO: replace fast status updates with client-side "dead-reckoning"
  # of progress while playing; notify only for play/pause/song changes.
  # Run at higher than 1Hz so that the progress bar updates ~smoothly.
  EM.add_periodic_timer(0.25) do
    if (info=mpd_status) != @last_status
      send_status( @last_status=info )
    end
  end
end

def watch_playlists
  EM.defer(
    ->( ){ idle_until 'stored_playlist'    },
    ->(_){ send_playlists; watch_playlists }
  )
end

def watch_upnext
  EM.defer(
    ->( ){ idle_until 'playlist', 'database' },
    ->(_){ send_next; watch_upnext           }
  )
end

def idle_until(*events)
  `mpc -h #{ENV['MPD_HOST']} -p #{ENV['MPD_PORT']} idle #{events.join(' ')}`
end

def mpd_status
  @mpd.status
end
def send_status( info=mpd_status )
  @faye.publish '/status', info
end

def up_next
  @mpd.queue.slice(0,ENV['RB3JAY_LISTLIMIT'].to_i).map(&:details)
end
def send_next( songs=up_next )
  @faye.publish '/next', songs
end

def playlists
  @mpd.playlists.map(&:name).grep(/^(?!user-)/).sort
end
def send_playlists( lists=playlists )
  @faye.publish '/playlists', playlists
end

from ruby-mpd.

archseer avatar archseer commented on June 21, 2024

I haven't actively been using this library in the past 2 years so I'm unlikely to work on such features, however I will be more than happy to accept pull requests.

from ruby-mpd.

Phrogz avatar Phrogz commented on June 21, 2024

I haven't actively been using this library in the past 2 years so I'm unlikely to work on such features, however I will be more than happy to accept pull requests.

Both are good to know. I'll see what I can do. Any suggestion on how to spec or test what I described above (another thread waiting on idle from MPD)?

from ruby-mpd.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.