airplay 1.0.0.beta3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +0 -1
- data/Gemfile.lock +1 -1
- data/README.md +23 -7
- data/Rakefile +1 -1
- data/airplay-cli.gemspec +1 -1
- data/bin/air +26 -16
- data/doc/documentation.md +4 -0
- data/doc/img/cli_list.png +0 -0
- data/doc/img/cli_play.png +0 -0
- data/doc/usage.md +14 -4
- data/lib/airplay.rb +39 -1
- data/lib/airplay/browser.rb +13 -3
- data/lib/airplay/cli.rb +32 -55
- data/lib/airplay/cli/image_viewer.rb +67 -0
- data/lib/airplay/cli/version.rb +2 -2
- data/lib/airplay/configuration.rb +4 -0
- data/lib/airplay/connection.rb +20 -10
- data/lib/airplay/device.rb +48 -0
- data/lib/airplay/device/features.rb +1 -1
- data/lib/airplay/devices.rb +25 -3
- data/lib/airplay/group.rb +20 -0
- data/lib/airplay/logger.rb +3 -1
- data/lib/airplay/playable.rb +13 -3
- data/lib/airplay/player.rb +65 -4
- data/lib/airplay/server.rb +18 -0
- data/lib/airplay/version.rb +1 -1
- data/lib/airplay/viewable.rb +4 -0
- data/lib/airplay/viewer.rb +17 -1
- metadata +9 -9
- data/SPEC.md +0 -34
- data/examples/demo.rb +0 -32
- data/examples/image.rb +0 -9
- data/examples/video.rb +0 -24
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -24,11 +24,11 @@ much as you want in: https://gumroad.com/l/airplay
|
|
24
24
|
|
25
25
|
### Library
|
26
26
|
|
27
|
-
`gem install airplay
|
27
|
+
`gem install airplay`
|
28
28
|
|
29
29
|
## CLI
|
30
30
|
|
31
|
-
`gem install airplay-cli
|
31
|
+
`gem install airplay-cli`
|
32
32
|
|
33
33
|
## Usage
|
34
34
|
|
@@ -37,6 +37,9 @@ much as you want in: https://gumroad.com/l/airplay
|
|
37
37
|
#### View devices
|
38
38
|
|
39
39
|
`air list`
|
40
|
+
|
41
|
+
![air list](doc/img/cli_list.png)
|
42
|
+
|
40
43
|
```text
|
41
44
|
* Apple TV (AppleTV2,1 running 11A502)
|
42
45
|
ip: 192.168.1.12
|
@@ -46,6 +49,9 @@ much as you want in: https://gumroad.com/l/airplay
|
|
46
49
|
#### Play a video
|
47
50
|
|
48
51
|
`air play [url to video or local file]`
|
52
|
+
|
53
|
+
![air play](doc/img/cli_play.png)
|
54
|
+
|
49
55
|
```text
|
50
56
|
Playing http://movietrailers.apple.com/movies/universal/rush/rush-tlr3_480p.mov?width=848&height=352
|
51
57
|
Time: 00:00:13 [===== ] 7% Apple TV
|
@@ -63,8 +69,8 @@ Time: 00:00:13 [===== ] 7% Apple TV
|
|
63
69
|
Airplay.configure do |config|
|
64
70
|
config.log_level # Log4r levels (Default: Log4r::ERROR)
|
65
71
|
config.autodiscover # Allows to search for nodes (Default: true)
|
66
|
-
config.host # In which host bind the server (Default: 0.0.0.0)
|
67
|
-
config.port # In which port bind the server (Default: 1337)
|
72
|
+
config.host # In which host to bind the server (Default: 0.0.0.0)
|
73
|
+
config.port # In which port to bind the server (Default: 1337)
|
68
74
|
config.output # Where to log (Default: Log4r::Outputter.stdout)
|
69
75
|
end
|
70
76
|
```
|
@@ -77,11 +83,15 @@ require "airplay"
|
|
77
83
|
Airplay.devices.each do |device|
|
78
84
|
puts device.name
|
79
85
|
end
|
86
|
+
```
|
80
87
|
|
81
|
-
|
88
|
+
#### Accesing and Grouping
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# You can access a known device easily
|
82
92
|
device = Airplay["Apple TV"]
|
83
93
|
|
84
|
-
# Or you can group known devices to have them do a given action
|
94
|
+
# Or you can group known devices to have them do a given action together
|
85
95
|
Airplay.group["Backyard"] << Airplay["Apple TV"]
|
86
96
|
Airplay.group["Backyard"] << Airplay["Room TV"]
|
87
97
|
|
@@ -165,8 +175,14 @@ player.progress -> progress {
|
|
165
175
|
}
|
166
176
|
```
|
167
177
|
|
178
|
+
## Documentation
|
179
|
+
|
180
|
+
All the documentation of the README can be found in the `doc` folder.
|
181
|
+
To generate an updated README based on the contents of `doc` please use `rake doc:generate`
|
182
|
+
|
168
183
|
## Contributors
|
169
184
|
|
170
185
|
* [sodabrew](http://github.com/sodabrew)
|
171
186
|
* [pote](http://github.com/pote)
|
172
|
-
|
187
|
+
* [janogonzalez](http://github.com/janogonzalez) - Who allowed me to release 1.0
|
188
|
+
from startech conf <3
|
data/Rakefile
CHANGED
@@ -84,7 +84,7 @@ task :test => [:spec]
|
|
84
84
|
|
85
85
|
namespace :doc do
|
86
86
|
task :generate do
|
87
|
-
structure = %w(header installation usage contributors)
|
87
|
+
structure = %w(header installation usage documentation contributors)
|
88
88
|
|
89
89
|
File.open("README.md", "w+") do |f|
|
90
90
|
structure.each { |part| f << File.read("doc/#{part}.md") + "\n" }
|
data/airplay-cli.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.homepage = "http://github.com/elcuervo/airplay"
|
14
14
|
s.files = %w(bin/air lib/airplay/cli.rb)
|
15
15
|
|
16
|
-
s.add_dependency("airplay", "= 1.0.0
|
16
|
+
s.add_dependency("airplay", "= 1.0.0")
|
17
17
|
s.add_dependency("clap", "~> 1.0.0")
|
18
18
|
s.add_dependency("ruby-progressbar", "~> 1.2.0")
|
19
19
|
end
|
data/bin/air
CHANGED
@@ -2,26 +2,36 @@
|
|
2
2
|
|
3
3
|
require "clap"
|
4
4
|
require "airplay/cli"
|
5
|
+
require "airplay/cli/version"
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
begin
|
8
|
+
Clap.run ARGV,
|
9
|
+
"--device" => lambda { |device_name| @device = Airplay[device_name] },
|
10
|
+
"--wait" => lambda { |sec| @wait = sec.to_i },
|
11
|
+
"--interactive" => lambda { @interactive = true },
|
10
12
|
|
11
|
-
|
13
|
+
"list" => Airplay::CLI.method(:list),
|
12
14
|
|
13
|
-
|
14
|
-
options = { device: @device || Airplay.devices.first }
|
15
|
-
Airplay::CLI.play(video, options)
|
16
|
-
},
|
15
|
+
"version" => lambda { puts Airplay::CLI::VERSION },
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
"play" => lambda { |video|
|
18
|
+
options = { device: @device || Airplay.devices.first }
|
19
|
+
Airplay::CLI.play(video, options)
|
20
|
+
},
|
21
|
+
|
22
|
+
"view" => lambda { |file|
|
23
|
+
options = {
|
24
|
+
device: @device || Airplay.devices.first,
|
25
|
+
interactive: @interactive || false,
|
26
|
+
wait: @wait || 3
|
27
|
+
}
|
28
|
+
Airplay::CLI.view(file, options)
|
23
29
|
}
|
24
|
-
|
25
|
-
|
30
|
+
|
31
|
+
rescue Airplay::Browser::NoDevicesFound
|
32
|
+
puts "No devices found."
|
33
|
+
rescue Interrupt
|
34
|
+
puts "Bye!"
|
35
|
+
end
|
26
36
|
|
27
37
|
# vim: ft=ruby
|
Binary file
|
Binary file
|
data/doc/usage.md
CHANGED
@@ -5,6 +5,9 @@
|
|
5
5
|
#### View devices
|
6
6
|
|
7
7
|
`air list`
|
8
|
+
|
9
|
+
![air list](doc/img/cli_list.png)
|
10
|
+
|
8
11
|
```text
|
9
12
|
* Apple TV (AppleTV2,1 running 11A502)
|
10
13
|
ip: 192.168.1.12
|
@@ -14,6 +17,9 @@
|
|
14
17
|
#### Play a video
|
15
18
|
|
16
19
|
`air play [url to video or local file]`
|
20
|
+
|
21
|
+
![air play](doc/img/cli_play.png)
|
22
|
+
|
17
23
|
```text
|
18
24
|
Playing http://movietrailers.apple.com/movies/universal/rush/rush-tlr3_480p.mov?width=848&height=352
|
19
25
|
Time: 00:00:13 [===== ] 7% Apple TV
|
@@ -31,8 +37,8 @@ Time: 00:00:13 [===== ] 7% Apple TV
|
|
31
37
|
Airplay.configure do |config|
|
32
38
|
config.log_level # Log4r levels (Default: Log4r::ERROR)
|
33
39
|
config.autodiscover # Allows to search for nodes (Default: true)
|
34
|
-
config.host # In which host bind the server (Default: 0.0.0.0)
|
35
|
-
config.port # In which port bind the server (Default: 1337)
|
40
|
+
config.host # In which host to bind the server (Default: 0.0.0.0)
|
41
|
+
config.port # In which port to bind the server (Default: 1337)
|
36
42
|
config.output # Where to log (Default: Log4r::Outputter.stdout)
|
37
43
|
end
|
38
44
|
```
|
@@ -45,11 +51,15 @@ require "airplay"
|
|
45
51
|
Airplay.devices.each do |device|
|
46
52
|
puts device.name
|
47
53
|
end
|
54
|
+
```
|
55
|
+
|
56
|
+
#### Accesing and Grouping
|
48
57
|
|
49
|
-
|
58
|
+
```ruby
|
59
|
+
# You can access a known device easily
|
50
60
|
device = Airplay["Apple TV"]
|
51
61
|
|
52
|
-
# Or you can group known devices to have them do a given action
|
62
|
+
# Or you can group known devices to have them do a given action together
|
53
63
|
Airplay.group["Backyard"] << Airplay["Apple TV"]
|
54
64
|
Airplay.group["Backyard"] << Airplay["Room TV"]
|
55
65
|
|
data/lib/airplay.rb
CHANGED
@@ -8,22 +8,44 @@ require "airplay/version"
|
|
8
8
|
#
|
9
9
|
module Airplay
|
10
10
|
class << self
|
11
|
+
# Public: General configuration
|
12
|
+
#
|
13
|
+
# &block - The block that will modify the configuration
|
14
|
+
#
|
15
|
+
# Returns the configuration file.
|
16
|
+
#
|
11
17
|
def configure(&block)
|
12
18
|
yield(configuration) if block
|
13
19
|
end
|
14
20
|
|
21
|
+
# Public: Access the server object
|
22
|
+
#
|
23
|
+
# Returns the Server object
|
24
|
+
#
|
15
25
|
def server
|
16
26
|
@_server ||= Server.new
|
17
27
|
end
|
18
28
|
|
29
|
+
# Public: Browses for devices in the current network
|
30
|
+
#
|
31
|
+
# Returns nothing.
|
32
|
+
#
|
19
33
|
def browse
|
20
34
|
browser.browse
|
21
35
|
end
|
22
36
|
|
37
|
+
# Public: Access or create a group based on a key
|
38
|
+
#
|
39
|
+
# Returns the Hash object.
|
40
|
+
#
|
23
41
|
def group
|
24
42
|
@_group ||= Hash.new { |h, k| h[k] = Group.new(k) }
|
25
43
|
end
|
26
44
|
|
45
|
+
# Public: Helper method to access all the devices
|
46
|
+
#
|
47
|
+
# Returns a Group with all the devices.
|
48
|
+
#
|
27
49
|
def all
|
28
50
|
@_all ||= begin
|
29
51
|
group = Group.new(:all)
|
@@ -32,23 +54,39 @@ module Airplay
|
|
32
54
|
end
|
33
55
|
end
|
34
56
|
|
35
|
-
# Public: Lists found devices
|
57
|
+
# Public: Lists found devices if autodiscover is enabled
|
58
|
+
#
|
59
|
+
# Returns an Array with all the devices
|
36
60
|
#
|
37
61
|
def devices
|
38
62
|
browse if browser.devices.empty? && configuration.autodiscover
|
39
63
|
browser.devices
|
40
64
|
end
|
41
65
|
|
66
|
+
# Public: Access the configuration object
|
67
|
+
#
|
68
|
+
# Returns the Configuration object
|
69
|
+
#
|
42
70
|
def configuration
|
43
71
|
@_configuration ||= Configuration.new
|
44
72
|
end
|
45
73
|
|
74
|
+
# Public: Access a device by name
|
75
|
+
#
|
76
|
+
# device_name - The name to search on the devices
|
77
|
+
#
|
78
|
+
# Returns the found device
|
79
|
+
#
|
46
80
|
def [](device_name)
|
47
81
|
devices.find_by_name(device_name)
|
48
82
|
end
|
49
83
|
|
50
84
|
private
|
51
85
|
|
86
|
+
# Private: Access the browser object
|
87
|
+
#
|
88
|
+
# Returns the momoized Browser object
|
89
|
+
#
|
52
90
|
def browser
|
53
91
|
@_browser ||= Browser.new
|
54
92
|
end
|
data/lib/airplay/browser.rb
CHANGED
@@ -8,6 +8,8 @@ module Airplay
|
|
8
8
|
# Public: Browser class to find Airplay-enabled devices in the network
|
9
9
|
#
|
10
10
|
class Browser
|
11
|
+
NoDevicesFound = Class.new(StandardError)
|
12
|
+
|
11
13
|
SEARCH = "_airplay._tcp."
|
12
14
|
|
13
15
|
def initialize
|
@@ -16,6 +18,8 @@ module Airplay
|
|
16
18
|
|
17
19
|
# Public: Browses in the search of devices and adds them to the nodes
|
18
20
|
#
|
21
|
+
# Returns nothing or raises NoDevicesFound if there are no devices
|
22
|
+
#
|
19
23
|
def browse
|
20
24
|
timeout(5) do
|
21
25
|
DNSSD.browse!(SEARCH) do |node|
|
@@ -23,10 +27,14 @@ module Airplay
|
|
23
27
|
break unless node.flags.more_coming?
|
24
28
|
end
|
25
29
|
end
|
30
|
+
rescue Timeout::Error => e
|
31
|
+
raise NoDevicesFound
|
26
32
|
end
|
27
33
|
|
28
34
|
# Public: Access to the node list
|
29
35
|
#
|
36
|
+
# Returns the Devices list object
|
37
|
+
#
|
30
38
|
def devices
|
31
39
|
@_devices ||= Devices.new
|
32
40
|
end
|
@@ -35,8 +43,8 @@ module Airplay
|
|
35
43
|
|
36
44
|
# Private: Resolves a node given a node and a resolver
|
37
45
|
#
|
38
|
-
#
|
39
|
-
#
|
46
|
+
# node - The given node
|
47
|
+
# resolver - The DNSSD::Server that is resolving nodes
|
40
48
|
#
|
41
49
|
# Returns if there are more nodes coming
|
42
50
|
#
|
@@ -56,7 +64,9 @@ module Airplay
|
|
56
64
|
|
57
65
|
# Private: Resolves the node information given a node
|
58
66
|
#
|
59
|
-
#
|
67
|
+
# node - The node from the DNSSD browsing
|
68
|
+
#
|
69
|
+
# Returns nothing
|
60
70
|
#
|
61
71
|
def resolve(node)
|
62
72
|
resolver = DNSSD::Service.new
|
data/lib/airplay/cli.rb
CHANGED
@@ -1,9 +1,18 @@
|
|
1
|
-
require "airplay"
|
2
1
|
require "ruby-progressbar"
|
2
|
+
require "airplay"
|
3
|
+
require "airplay/cli/image_viewer"
|
3
4
|
|
5
|
+
# Public: Airplay core module
|
6
|
+
#
|
4
7
|
module Airplay
|
8
|
+
# Public: Airplay CLI module
|
9
|
+
#
|
5
10
|
module CLI
|
6
11
|
class << self
|
12
|
+
# Public: Lists all the devices to STDOUT
|
13
|
+
#
|
14
|
+
# Returns nothing.
|
15
|
+
#
|
7
16
|
def list
|
8
17
|
Airplay.devices.each do |device|
|
9
18
|
puts <<-EOS.gsub(/^\s{12}/,'')
|
@@ -15,6 +24,14 @@ module Airplay
|
|
15
24
|
end
|
16
25
|
end
|
17
26
|
|
27
|
+
# Public: Plays a video given a device
|
28
|
+
#
|
29
|
+
# video - The url or file path to the video
|
30
|
+
# options - Options that include the device
|
31
|
+
# * device: The device in which it should run
|
32
|
+
#
|
33
|
+
# Returns nothing.
|
34
|
+
#
|
18
35
|
def play(video, options)
|
19
36
|
device = options[:device]
|
20
37
|
player = device.play(video)
|
@@ -31,74 +48,34 @@ module Airplay
|
|
31
48
|
player.wait
|
32
49
|
end
|
33
50
|
|
51
|
+
# Public: Show an image given a device
|
52
|
+
#
|
53
|
+
# file_or_dir - The url, file path or folder path to the image/s
|
54
|
+
# options - Options that include the device
|
55
|
+
# * device: The device in which it should run
|
56
|
+
# * interactive: Boolean flag to control playback with the
|
57
|
+
# arrow keys
|
58
|
+
#
|
59
|
+
# Returns nothing.
|
60
|
+
#
|
34
61
|
def view(file_or_dir, options)
|
35
62
|
device = options[:device]
|
36
|
-
|
63
|
+
viewer = ImageViewer.new(device, options)
|
37
64
|
|
38
65
|
if File.directory?(file_or_dir)
|
39
66
|
files = Dir.glob("#{file_or_dir}/*")
|
40
67
|
|
41
68
|
if options[:interactive]
|
42
|
-
|
69
|
+
viewer.interactive(files)
|
43
70
|
else
|
44
|
-
|
71
|
+
viewer.slideshow(files)
|
45
72
|
end
|
46
73
|
else
|
47
|
-
|
74
|
+
viewer.view(file_or_dir)
|
48
75
|
sleep
|
49
76
|
end
|
50
77
|
end
|
51
78
|
|
52
|
-
private
|
53
|
-
|
54
|
-
def view_interactive(files)
|
55
|
-
numbers = Array(0...files.count)
|
56
|
-
transition = "None"
|
57
|
-
|
58
|
-
i = 0
|
59
|
-
loop do
|
60
|
-
view_image(device, files[i], transition)
|
61
|
-
|
62
|
-
case read_char
|
63
|
-
# Right Arrow
|
64
|
-
when "\e[C"
|
65
|
-
i = i + 1 > numbers.count - 1 ? 0 : i + 1
|
66
|
-
transition = "SlideLeft"
|
67
|
-
when "\e[D"
|
68
|
-
i = i - 1 < 0 ? numbers.count - 1 : i - 1
|
69
|
-
transition = "SlideRight"
|
70
|
-
else
|
71
|
-
break
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def view_slideshow(files)
|
77
|
-
files.each do |file|
|
78
|
-
view_image(device, file)
|
79
|
-
sleep wait
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def read_char
|
84
|
-
STDIN.echo = false
|
85
|
-
STDIN.raw!
|
86
|
-
|
87
|
-
input = STDIN.getc.chr
|
88
|
-
if input == "\e" then
|
89
|
-
input << STDIN.read_nonblock(3) rescue nil
|
90
|
-
input << STDIN.read_nonblock(2) rescue nil
|
91
|
-
end
|
92
|
-
ensure
|
93
|
-
STDIN.echo = true
|
94
|
-
STDIN.cooked!
|
95
|
-
|
96
|
-
return input
|
97
|
-
end
|
98
|
-
|
99
|
-
def view_image(device, image, transition = "SlideLeft")
|
100
|
-
device.view(image, transition: transition)
|
101
|
-
end
|
102
79
|
end
|
103
80
|
end
|
104
81
|
end
|