Subversion Repositories mildred

Rev

Blame | Compare with Previous | Last modification | View Log | RSS feed

require "mpd"


class MpdMonitor

  # Number of tracks to maintain in the upcoming queue.  If set to -1,
  # MpdMonitor will always return true from the playlist_too_short? method,
  # which can be useful for testing.
  QUEUE_SIZE = 3 # -1 for testing

  # Number of tracks to allow to remain in the queue's history.  It is useful
  # to leave this a bit long, since that way, if we crash, MPD will continue
  # to play, looping through the tracks in its playlist.  If that list is
  # long, it should be less annoying to unsuspecting listeners.
  HISTORY_SIZE = 60

  cattr_accessor :logger
  QBeat.setup_logging

  def initialize(logger=nil)
    # MpdMonitor is used by both QBeat, and Rails.  We allow Rails to override
    # the log file, so that MpdMonitor log info for the Rails site goes into
    # the Rails logfile
    @@logger = logger ? logger : RAILS_DEFAULT_LOGGER
  end

  # Given a track, append it to the MPD playlist.  Mark the track queued
  # whether we're successful or not.
  def append_track_to_playlist(track)
    results = conn.search("filename", track.filename)
    if results.size > 0 && results.first.file.size > 0 && conn.add(results.first.file.gsub("\\", "\\\\\\")).ok?
      logger.info("queued file: #{track.id} #{track.filename}")
    else
      logger.error(conn.error)
      Notifier.deliver_failed_to_queue_track(track.id, track.album.id, track.filename)
    end
    track.queued
  end

  def loop_pre
    @current_track = conn.current_song unless @current_track
    cs = conn.current_song
    if playing? && @current_track && cs && @current_track.id != cs.id
      @current_track = cs
      report_now_playing
      if track = Track.find_by_md5sum(@current_track.md5sum)
        track.played
      end
    end
  end

  def loop_post
    return unless playing?
    status = conn.status
    if status.song > HISTORY_SIZE
      (1..(status.song - HISTORY_SIZE)).each {conn.delete(1)}
    end
  end

  def playlist_too_short?
    return true if QUEUE_SIZE < 0 # used for testing
    status = conn.status
    if playing?
      queue_size = status.playlistlength - status.song
      return queue_size <= QUEUE_SIZE
    else
      return status.playlistlength <= QUEUE_SIZE
    end
  end

  def get_playlist_info
    info = {}
    info = {:currentsong => conn.current_song,
            :playlistinfo => conn.playlist_info,
            :status => conn.status,}
    begin
      next_up = conn.playlist_info(conn.current_song.pos + 1).first
      if next_up.empty?
        info[:next_up] = conn.playlist_info.first
      else
        info[:next_up] = next_up
      end
    rescue
      info[:next_up] = conn.playlist_info.first
    end
    info
  end


  private

  def playing?
    if conn.status.state == "play"
      return true
    elsif ListenersMonitor.listeners(true) > 0
      logger.warn("MPD not playing (#{conn.status.state}), " +
                  "attempting to begin playing.")
      start_playing
      return false
    end
  end

  def start_playing
    conn.play
    conn.repeat(1)
    conn.random(0)
  end

  def conn
    return @conn unless @conn.nil?
    @conn = Mpd::Connection.new(File.join(RAILS_ROOT, "config", "mpd.yml"))
  end

  def generate_track_filename(track)
    pos = sprintf("%02d", track.position)

    # XXX to fix the media merge differences
    if track.album.media.size > 1
      medium_name = " (#{track.medium.name})"
    else
      medium_name = ""
    end

    [pos, track.title, track.album.title + medium_name,
     track.artist.name].join("-")
  end

  # used to search by filename
  def prep(filename)
    filename.gsub("\"", "\\\\\"").gsub("/", "\\\\\\\\")
  end

  # used to add by filename
  def prep2(filename)
    filename.gsub("\\", "\\\\\\\\").gsub("\"", "\\\"")
  end

  def report_now_playing
    logger.info("Now playing \"#{conn.current_song.title}\" by " +
                  "#{conn.current_song.artist}")
  end

end