CLI tool
elcuervo/airplay avatar
elcuervo/airplay

Airplay: Ruby bindings for pushing video to an Apple TV

Airplay bindings to Ruby

1,062 stars67 forksRubyMIT

At a glance

What is it?
Airplay is a Ruby gem that discovers AirPlay receivers on the network and drives them from Ruby or from a small CLI, with video playback, image display with transitions, playlists and device groups. It suits a Ruby developer automating a screen, and it works with receivers that answer the protocol it implements.
Who is it for?
Adopt Airplay if you want to script an Apple TV from Ruby, because the group and playlist abstractions turn a set of receivers into something you can drive in a loop, and the CLI makes the same operations available without writing Ruby at all. Do not adopt it without checking compatibility: the README says the gem attempts to be compatible with the latest Apple TV firmware, but the last release dates from 2013.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A gem for talking to AirPlay receivers

Airplay is a Ruby library with two distributions, a library gem and a CLI gem, for discovering and controlling AirPlay receivers on a local network.

The description in the README is short: Airplay attempts to be compatible with the latest Apple TV firmware, and the author would like to add compatibility to other servers. That second half is the important framing. The project is written against the AirPlay protocol as Apple implemented it for the generation of receivers that existed at the time, and the compatibility goal is a moving target, because a receiver that gets a firmware update can change what it answers to.

The device discovery is a network operation. The CLI lists what it finds:

text
* Apple TV (AppleTV2,1 running 11A502)
  ip: 192.168.1.12
  resolution: 1280x720

So the receiver reports a name, a hardware identifier, a running firmware version, an IP address and a resolution. The name is the handle you use everywhere else in the API, which is what makes the grouping feature work.

The gem is MIT licensed, the primary language is Ruby, and the release history shows three tags, v1.0.0, v1.0.1 and v1.0.2, all from late 2013, with v1.0.2 named for Rails 3 and Rails 4 compatibility. The last push to the master branch was on 2026-10-19, so the repository is being touched even though the releases are more than a decade old.

Two gems, one for libraries and one for the terminal

The installation section splits the project in two, and the split reflects how it is meant to be used.

For the library:

code
gem install airplay

And for the command line tool:

code
gem install airplay-cli

Two gems rather than one is a deliberate packaging choice. The CLI depends on the library, and separating them means a Rails application that only wants to drive a receiver from application code does not pull in the terminal dependencies.

The CLI has three documented commands. `air list` shows the receivers it discovered, with the name, the IP, the resolution and the running firmware. `air play [url to video or local file]` sends something to play, and the sample output shows a progress bar with a percentage and the receiver name:

text
Playing http://movietrailers.apple.com/movies/universal/rush/rush-tlr3_480p.mov?width=848&height=352
Time: 00:00:13 [=====                                              ] 7% Apple TV

And `air view [url to image or image folder]` displays an image, which is a different code path from video and is worth distinguishing, because a photo frame on a television is a common use that has nothing to do with media playback.

For a quick check that the network path works, `air list` is the one command that needs no media and no authentication, and it is the fastest way to find out whether the receiver is speaking a protocol this gem understands.

The library API, and the device groups that make it useful

The library surface is small, and one method is the reason to choose this over scripting the CLI.

Configuration is a block with a handful of options:

ruby
Airplay.configure do |config|
  config.log_level      # Log4r levels (Default: Log4r::ERROR)
  config.autodiscover   # Allows to search for nodes (Default: true)
  config.host           # In which host to bind the server (Default: 0.0.0.0)
  config.port           # In which port to bind the server (Default: will find one)
  config.output         # Where to log (Default: Log4r::Outputter.stdout)
end

Two defaults stand out. Autodiscovery is on by default, which is why `air list` works with no setup, and the port default is to find one, so the gem does not collide with whatever else is listening on the machine.

Devices are addressed by name, and the enumeration is straightforward:

ruby
require "airplay"

Airplay.devices.each do |device|
  puts device.name
end

You can also fetch a known device directly and set its password if it is protected:

ruby
# You can access a known device easily
device = Airplay["Apple TV"]

# And add the password of the device if needed
device.password = "my super secret password"

And then there is the grouping, which is the feature that makes the library worth having rather than merely convenient:

ruby
# Or you can group known devices to have them do a given action together
Airplay.group["Backyard"] << Airplay["Apple TV"]
Airplay.group["Backyard"] << Airplay["Room TV"]

# The groups can later do some actions like:
Airplay.group["Backyard"].play("video")

Named groups turn a set of receivers into a single addressable object. A house with several screens becomes one call, and a script can create a group per room and act on it. That is the kind of thing you would otherwise write yourself in a loop, and it is the reason to use the library rather than shelling out to the CLI.

Images with transitions, and video with playlists

The two media types have different APIs, and the image side is the simpler one.

For images you send local files or remote ones, and you can pick a transition:

ruby
require "airplay"

apple_tv = Airplay["Apple TV"]

# You can send local files
apple_tv.view("my_image.png")

# Or use remote files
apple_tv.view("https://github.com/elcuervo/airplay/raw/master/doc/img/logo.png")

# And define a transition
apple_tv.view("url_to_the_image", transition: "Dissolve")

# View all transitions
apple_tv.transitions

The transitions list is queried from the receiver, which means the set of available transitions is a property of the device rather than of the library, and a script that picks a transition by name needs to handle the case where a given receiver does not offer it.

Video is richer, because the receiver exposes a player object with its own state. The play call returns that player:

ruby
require "airplay"

apple_tv = Airplay["Apple TV"]
trailer = "http://movietrailers.apple.com/movies/dreamworks/needforspeed/needforspeed-tlr1xxzzs2_480p.mov"

player = apple_tv.play(trailer)

The player can hold a playlist, and there can be several:

ruby
# You can also add videos to a playlist and let the library handle them
player.playlist << "video_url"
player.playlist << "video_path"
player.play

# Or control it yourself
player.next
player.previous
ruby
# Or if you prefer you can have several playlists
player = apple_tv.player
player.playlists["Star Wars Classic"] << "Star Wars Episode IV: A New Hope"

with player.use("Star Wars Classic") to select one and player.play to start it. The live controls cover pause, resume, stop, scrub, info and seek, and the progress callback reports the position against the duration:

ruby
player.progress -> progress {
  puts "I'm viewing #{progress.position} of #{progress.duration}"
}

So the same object that starts a video also reports where it is, which is what you need for a status display or a synchronised multi-screen setup.

The test suite needs the hardware it was written for

The testing section is short and unusual, and it tells you how the project was actually validated.

There are two kinds of tests, regular unit tests and integration tests, and the integration tests run against a real Apple TV that is currently accessible over the internet, thanks to what the README calls the magic of the internet and a raspberry pi. The Apple TV is password protected to avoid issues with the tests, and it is configured in Travis CI.

The consequence is stated plainly: you cannot run those tests if you do not have an Apple TV.

The rake tasks are the standard split:

code
rake test:unit
rake test:integration
rake test:all

The unit tests are the ones you can run anywhere, and the integration tests are the ones that actually prove the protocol still works against a receiver. That is a real limitation for a contributor today, and it is also a statement about the project's quality bar: the protocol compatibility is checked against hardware, not against a mock.

The documentation is generated. All the documentation in the README can be found in the doc folder, and an updated README is generated from the contents of that folder with `rake doc:generate`. The repository layout matches, with a doc/ directory alongside lib/, test/, bin/, the Gemfile and Gemfile.lock, a Rakefile, a .ruby-version file and separate gemspec files for airplay and airplay-cli.

The maintainer asks for contributions and notes that the development of the gem takes time and there is a lot of research and hardware testing involved, and points at PayPal and Gumroad for anyone who wants to fund that time.

Editorial conclusion

Adopt Airplay if you want to script an Apple TV from Ruby, because the group and playlist abstractions turn a set of receivers into something you can drive in a loop, and the CLI makes the same operations available without writing Ruby at all. Do not adopt it without checking compatibility: the README says the gem attempts to be compatible with the latest Apple TV firmware, but the last release dates from 2013. Before you rely on it, check that your receiver still responds to the older protocol, and plan for the fact that the integration tests need real hardware on the network, a Raspberry Pi and an Apple TV that is password protected and wired into Travis CI.

Frequently asked questions

How do I install the Airplay Ruby library and its CLI?

The library is installed with `gem install airplay` and the command line tool with `gem install airplay-cli`. They are separate gems so that an application using the library does not pull in the terminal dependencies.

How do I control several Apple TV devices together with Airplay?

Use the group API. Add devices to a named group with `Airplay.group["Backyard"] << Airplay["Apple TV"]` and then act on the group, for example `Airplay.group["Backyard"].play("video")`. A device password can be set with `device.password` when the receiver is protected.

Can Airplay play a playlist and report playback progress?

Yes. The player returned by `apple_tv.play(trailer)` accepts a playlist, and several named playlists can exist on one player with `player.playlists["name"] << url` and `player.use("name")`. Controls include pause, resume, stop, scrub, info and seek, and `player.progress` gives a callback with the position and duration.

What do the Airplay integration tests require?

They require a real Apple TV reachable over the network, which the project runs through a Raspberry Pi configured in Travis CI, and the Apple TV is password protected. The README says you cannot run those tests without an Apple TV, though the unit tests run with `rake test:unit` and everything together with `rake test:all`.

Official sources

  1. elcuervo/airplay on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/elcuervo-airplay.svg)](https://hysenlabs.com/projects/elcuervo-airplay)

Was this useful?