class Airplay::Player

Public: The class that handles all the video playback

Constants

PlaybackInfo

Attributes

device[R]

Public Class Methods

new(device) click to toggle source
# File lib/airplay/player.rb, line 25
def initialize(device)
  @device = device
end

Public Instance Methods

cleanup() click to toggle source

Public: Cleans up the player

Returns nothing

# File lib/airplay/player.rb, line 200
def cleanup
  timers.cancel
  persistent.close
end
info() click to toggle source

Public: checks current playback information

Returns a PlaybackInfo object with the playback information

# File lib/airplay/player.rb, line 142
def info
  response = connection.get("/playback-info").response
  plist = CFPropertyList::List.new(data: response.body)
  hash = CFPropertyList.native_types(plist.value)
  PlaybackInfo.new(hash)
end
loading?() click to toggle source
# File lib/airplay/player.rb, line 181
def loading?; state == :loading end
next() click to toggle source

Public: Plays the next video in the playlist

Returns the video that was selected or nil if none

# File lib/airplay/player.rb, line 111
def next
  video = playlist.next
  play(video) if video
  video
end
pause() click to toggle source

Public: Pauses a playing video

Returns nothing

# File lib/airplay/player.rb, line 161
def pause
  connection.async.post("/rate?value=0")
end
paused?() click to toggle source
# File lib/airplay/player.rb, line 183
def paused?;  state == :paused  end
play(media_to_play = "playlist", options = {}) click to toggle source

Public: Plays a given url or file.

Creates a new persistent connection to ensure that
the socket will be kept alive

file_or_url - The url or file to be reproduced options - Optional starting time

Returns nothing

# File lib/airplay/player.rb, line 69
def play(media_to_play = "playlist", options = {})
  start_the_machine
  check_for_playback_status

  media = case true
          when media_to_play.is_a?(Media) then media_to_play
          when media_to_play == "playlist" && playlist.any?
            playlist.next
          else Media.new(media_to_play)
          end

  content = {
    "Content-Location" => media,
    "Start-Position" => options.fetch(:time, 0.0)
  }

  data = content.map { |k, v| "#{k}: #{v}" }.join("\r\n")

  response = persistent.async.post("/play", data + "\r\n", {
    "Content-Type" => "text/parameters"
  })

  timers.reset
end
played?() click to toggle source
# File lib/airplay/player.rb, line 184
def played?;  state == :played  end
playing?() click to toggle source
# File lib/airplay/player.rb, line 182
def playing?; state == :playing end
playlist() click to toggle source

Public: Gets the current playlist

Returns the first Playlist if none defined or creates a new one

# File lib/airplay/player.rb, line 41
def playlist
  @_playlist ||= if playlists.any?
                   key, value = playlists.first
                   value
                 else
                   Playlist.new("Default")
                 end
end
playlists() click to toggle source

Public: Gets all the playlists

Returns the Playlists

# File lib/airplay/player.rb, line 33
def playlists
  @_playlists ||= Hash.new { |h,k| h[k] = Playlist.new(k) }
end
previous() click to toggle source

Public: Plays the previous video in the playlist

Returns the video that was selected or nil if none

# File lib/airplay/player.rb, line 121
def previous
  video = playlist.previous
  play(video) if video
  video
end
progress(callback) click to toggle source

Public: Handles the progress of the playback, the given &block get’s

executed every second while the video is played.

&block - Block to be executed in every playable second.

Returns nothing

# File lib/airplay/player.rb, line 101
def progress(callback)
  timers << every(1) do
    callback.call(info) if playing?
  end
end
resume() click to toggle source

Public: Resumes a paused video

Returns nothing

# File lib/airplay/player.rb, line 153
def resume
  connection.async.post("/rate?value=1")
end
scrub() click to toggle source

Public: Shows the current playback time if a video is being played.

Returns a hash with the :duration and current :position

# File lib/airplay/player.rb, line 131
def scrub
  return unless playing?
  response = connection.get("/scrub").response
  parts = response.body.split("\n")
  Hash[parts.collect { |v| v.split(": ") }]
end
seek(position) click to toggle source

Public: Seeks to the specified position (seconds) in the video

Returns nothing

# File lib/airplay/player.rb, line 177
def seek(position)
  connection.async.post("/scrub?position=#{position}")
end
stop() click to toggle source

Public: Stops the video

Returns nothing

# File lib/airplay/player.rb, line 169
def stop
  connection.post("/stop")
end
stopped?() click to toggle source
# File lib/airplay/player.rb, line 185
def stopped?; state == :stopped end
use(name) click to toggle source

Public: Sets a given playlist

name - The name of the playlist to be used

Returns nothing

# File lib/airplay/player.rb, line 56
def use(name)
  @_playlist = playlists[name]
end
wait() click to toggle source

Public: Locks the execution until the video gets fully played

Returns nothing

# File lib/airplay/player.rb, line 191
def wait
  sleep 1 while wait_for_playback?
  cleanup
end

Private Instance Methods

check_for_playback_status() click to toggle source

Private: Starts checking for playback status ever 1 second

Adds one timer to the pool

Returns nothing

# File lib/airplay/player.rb, line 245
def check_for_playback_status
  timers << every(1) do
    case true
    when info.stopped? && playing?  then @machine.trigger(:stopped)
    when info.played?  && playing?  then @machine.trigger(:played)
    when info.playing? && !playing? then @machine.trigger(:playing)
    when info.paused?  && playing?  then @machine.trigger(:paused)
    end
  end
end
connection() click to toggle source

Private: The connection

Returns the current connection to the device

# File lib/airplay/player.rb, line 228
def connection
  @_connection ||= Airplay::Connection.new(@device)
end
persistent() click to toggle source

Private: The persistent connection

Returns the persistent connection to the device

# File lib/airplay/player.rb, line 236
def persistent
  @_persistent ||= Airplay::Connection.new(@device, keep_alive: true)
end
start_the_machine() click to toggle source

Private: Get ready the state machine

Returns nothing

# File lib/airplay/player.rb, line 260
def start_the_machine
  @machine = MicroMachine.new(:loading)

  @machine.on(:stopped) { cleanup }
  @machine.on(:played)  do
    cleanup
    self.next if playlist.next?
  end

  @machine.when(:loading, :stopped => :loading)
  @machine.when(:playing, {
    :paused  => :playing,
    :loading => :playing,
    :stopped => :playing
  })

  @machine.when(:paused,  :loading => :paused,  :playing => :paused)
  @machine.when(:stopped, :playing => :stopped, :paused  => :stopped)
  @machine.when(:played,  :playing => :played,  :paused  => :played)
end
timers() click to toggle source

Private: The timers

Returns a Timers object

# File lib/airplay/player.rb, line 220
def timers
  @_timers ||= Timers.new
end
wait_for_playback?() click to toggle source

Private: Returns if we have to wait for playback

Returns a boolean if we need to wait

# File lib/airplay/player.rb, line 211
def wait_for_playback?
  return true if playlist.next?
  loading? || playing? || paused?
end