Subversion Repositories mildred

Rev

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

# A selector weights and selects tracks.  It has a rather simple public API.

class Selector

  cattr_accessor :logger

  # The penalty added to the weight of tracks which were last queued within
  # RECENTLY_QUEUED_DURATION
  RECENTLY_QUEUED_PENALTY = 5.0

  # The amount of time during which a track receives the
  # RECENTLY_QUEUED_PENALTY.
  if RAILS_ENV == "development"
    RECENTLY_QUEUED_DURATION = 15.minutes.ago
  else
    RECENTLY_QUEUED_DURATION = 1.hour.ago
  end

  MIN_TRACK_TIME = 45 # seconds
  MIN_TRACK_RATING = 2

  attr_accessor :new_block, :last_pblock

  def initialize(options={})
    @options = {:schedule => nil}.merge(options)
    @last_pblock = nil
    @new_pblock = false
  end

  def self.filter_track(track)
    return false if track.time < MIN_TRACK_TIME
    return false if track.disabled
    return false if track.rating < MIN_TRACK_RATING && track.rating != 0 && !track.rating.nil?
    true
  end

  def check_pblock(time=Time.now)
    pblock = load_pblock(time)
    @new_pblock = !pblock.equal?(@last_pblock)
    @last_pblock = pblock
    @new_pblock
  end

  # TODO refactor me out
  def normalize_last_queued(*args)
    self.class.normalize_last_queued(*args)
  end
 
  # TODO refactor me out
  def normalize_rating(*args)
    self.class.normalize_rating(*args)
  end
 
  def self.normalize_last_queued(x, min, max, inv_range)
    1.0 - (x.to_f - min.to_f) * inv_range
  end

  def self.normalize_rating(x, min, max, inv_range)
    (x.to_f - min.to_f) * inv_range
  end

  def select
    raise NotImplementedError.new("Override me!")
  end


  private
 
  def load_tracks(time)
    pblock = load_pblock(time)
    pblock.fetch
  end
 
  def gen_powerblock_from(track)
    tracks = track.album.tracks + track.artist.tracks +
      track.artist.albums.collect {|al| al.tracks}.flatten
    tracks.uniq!
    n_tracks = Kernel::rand(3) + 3
    logger.debug("=== generating power block of size: #{n_tracks} " +
                 "from #{track.inspect}")
    if tracks.size > n_tracks
      (1..n_tracks).collect {tracks.delete_at(Kernel::rand(tracks.size))}
    else
      tracks
    end
  end

  def load_pblock(time)
    sched = @options[:schedule] || # make me SCHEDULE_FILE or something
      File.join(RAILS_ROOT, "config", "schedule.yml")
    Schedule.load(sched, time)
  end

  def select_type_from_track(track)
    qt = @last_pblock.queue_type # TODO factor me out?
    case qt
    when ProgrammingBlock::QUEUE_MEDIUM
      medium = track.album.media[Kernel::rand(track.album.media.size-1)]
      tracks = medium.tracks.find_all {|track| self.class.filter_track(track)}
      return tracks.sort_by {|t| t.position}
    when ProgrammingBlock::QUEUE_POWER_BLOCK
      return gen_powerblock_from(track)
    when ProgrammingBlock::QUEUE_TRACK
      return [track]
    else
      raise "Unhandled queue_type response"
    end
  end
end