Ever since apple came out with their new standard airplay I have been very jelly
of it. It allows for a user to stream videos, music, and photos to a device, in
their design the appletv. XBMC, Totem, and many others though have implemented
the receiver protocol allowing for them to be airplay endpoints to be driven by
iphones and macbooks etc. Which is cool but doesn’t get me any love for my
gnu/linux thinkpad.
When the rasberry pi came out I decided I wanted to build a little xbmc box that I
could carry with me to do remote video playback. I found a ruby library that allows
for pushing airplay video, and images to airplay devices like the appletv and the pi.
I downloaded and got it working, but it didn’t support streaming files locally, only
via http. So I learned me a little ruby and wrote the script below. It takes an
http/s url or a local file url and serves them up for the airplay receiver. It
works great. give it a try
#!/usr/bin/ruby
#code is written by Matthew O'Gorman 2012 Licensed Gplv3
require 'webrick'
require 'airplay'
require 'uri'
require 'socket'
class Streamer < WEBrick::HTTPServlet::FileHandler
def initialize(server, v, f, l)
super(server, l)
@valid_ip = v
@valid_file = f
@local_path = l
end
def prevent_caching(res)
res['ETag'] = nil
res['Last-Modified'] = Time.now + 100**4
res['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
res['Pragma'] = 'no-cache'
res['Expires'] = Time.now - 100**4
end
def do_GET(req, res)
super
print "yup \n" + req.remote_ip + "\n" + req.path + "\n"
print "uh hu " + @valid_ip + " " + @valid_file + "\n"
path = req.path.slice!(0)
if (req.remote_ip != @valid_ip) then
if (path != @valid_file) then
print "invalid request\n"
res.status = 403
res['Content-Type'] = "text/html"
res.body = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><html><head><title>403 Forbidden</title></head><body><h1>Forbidden</h1><p>You are not the airplay server we are looking for!</p><hr><address>WEBrick airplay server 0.1</address></body></html>"
return res
end
end
prevent_caching(res)
end
end
def Local_ip
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
UDPSocket.open do |s|
s.connect '64.233.187.99', 1
s.addr.last
end
ensure
Socket.do_not_reverse_lookup = orig
end
client = Airplay::Client.new
client.browse
if (ARGV[0] =~ URI::regexp) then
print "playing url " + ARGV[0] + "\n"
player = client.send_video(ARGV[0])
# Correct URL
elsif (File.readable?(ARGV[0])) then
print "playing file " + ARGV[0] + "\n"
dir = File.dirname(ARGV[0])
Dir.chdir(dir)
s = WEBrick::HTTPServer.new(:IP => "0.0.0.0", :Port => 3000)
valid_ip = client.servers[0].ip
valid_file = File.basename(ARGV[0])
s.mount "/", Streamer, valid_ip, valid_file, Dir.pwd
trap('INT') { s.shutdown }
child = fork do
sleep 2
url = "http://" + Local_ip() + ":3000/" + valid_file
print url
print "\n"
player = client.send_video(url)
end
s.start
exit
else
print "Not a uri or a readable file. nothing to do\n"
end