airplay 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  /pkg
2
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.md CHANGED
@@ -29,6 +29,16 @@ airplay.password "password"
29
29
  airplay.send_image("lolcatz.jpg")
30
30
  ```
31
31
 
32
+ ## Player
33
+
34
+ ```ruby
35
+ require 'airplay'
36
+
37
+ airplay = Airplay::Client.new
38
+ player = airplay.send_video("http://www.yo-yo.org/mp4/yu2.mp4")
39
+ player.stop
40
+ ```
41
+
32
42
  ## Useful methods
33
43
 
34
44
  ```ruby
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "airplay"
3
- s.version = "0.2.0"
3
+ s.version = "0.2.1"
4
4
  s.summary = "Airplay client"
5
5
  s.description = "Send image/video to an airplay enabled device"
6
6
  s.authors = ["elcuervo"]
@@ -13,7 +13,6 @@ Gem::Specification.new do |s|
13
13
  s.add_dependency("net-http-digest_auth")
14
14
 
15
15
  s.add_development_dependency("cutest")
16
- s.add_development_dependency("mocha")
17
16
  s.add_development_dependency("capybara")
18
17
  s.add_development_dependency("fakeweb")
19
18
  s.add_development_dependency("vcr")
@@ -4,9 +4,13 @@ require 'net/http/digest_auth'
4
4
  require 'uri'
5
5
 
6
6
  module Airplay; end;
7
+ require 'airplay/server'
8
+ require 'airplay/server/browser'
9
+ require 'airplay/server/node'
10
+
7
11
  require 'airplay/protocol'
8
12
  require 'airplay/protocol/image'
9
- require 'airplay/protocol/video'
13
+ require 'airplay/protocol/media'
10
14
  require 'airplay/protocol/scrub'
11
- require 'airplay/node'
15
+
12
16
  require 'airplay/client'
@@ -1,13 +1,14 @@
1
1
  class Airplay::Client
2
2
  attr_reader :servers, :active_server, :password
3
3
 
4
- def initialize(server = false)
4
+ def initialize(server = false, server_browser = Airplay::Server::Browser)
5
+ @server_browser = server_browser
5
6
  browse unless server
6
- use servers.first if servers.any?
7
+ use servers.first if !@servers.nil?
7
8
  end
8
9
 
9
10
  def use(server)
10
- @active_server = server.is_a?(Airplay::Node) ? server : find_by_name(server)
11
+ @active_server = server.is_a?(Airplay::Server::Node) ? server : @server_browser.find_by_name(server)
11
12
  end
12
13
 
13
14
  def password(password)
@@ -15,29 +16,11 @@ class Airplay::Client
15
16
  end
16
17
 
17
18
  def find_by_name(name)
18
- found_server = @servers.detect do |server|
19
- server if server.name == name
20
- end
21
- raise Airplay::Client::ServerNotFoundError unless found_server
22
- found_server
19
+ @server_browser.find_by_name(name)
23
20
  end
24
21
 
25
22
  def browse
26
- @servers = []
27
- DNSSD.browse!(Airplay::Protocol::SEARCH) do |reply|
28
- resolver = DNSSD::Service.new
29
- target, port = nil
30
- resolver.resolve(reply) do |resolved|
31
- port = resolved.port
32
- target = resolved.target
33
- break unless resolved.flags.more_coming?
34
- end
35
- info = Socket.getaddrinfo(target, nil, Socket::AF_INET)
36
- ip_address = info[0][2]
37
- @servers << Airplay::Node.new(reply.name, reply.domain, ip_address, port)
38
- break unless reply.flags.more_coming?
39
- end
40
- @servers
23
+ @servers = @server_browser.browse
41
24
  end
42
25
 
43
26
  def handler
@@ -49,7 +32,11 @@ class Airplay::Client
49
32
  end
50
33
 
51
34
  def send_video(video, position = 0)
52
- Airplay::Protocol::Video.new(handler).send(video, position)
35
+ Airplay::Protocol::Media.new(handler).send(video, position)
36
+ end
37
+
38
+ def send_audio(audio, position = 0)
39
+ Airplay::Protocol::Media.new(handler).send(audio, position)
53
40
  end
54
41
 
55
42
  def scrub
@@ -1,4 +1,4 @@
1
- class Airplay::Protocol::Video
1
+ class Airplay::Protocol::Media
2
2
 
3
3
  def initialize(protocol_handler)
4
4
  @http = protocol_handler
@@ -8,28 +8,31 @@ class Airplay::Protocol::Video
8
8
  "/play"
9
9
  end
10
10
 
11
- def position_body(position = 0)
12
- "Start-Position: #{position}\n"
11
+ def stop_resource
12
+ "/stop"
13
13
  end
14
14
 
15
- def location_body(video)
16
- "Content-Location: #{video}\n"
15
+ def position_body(position = 0)
16
+ "Start-Position: #{position}\n"
17
17
  end
18
18
 
19
- def send(video, position = 0)
20
- body = location_body(video)
21
- body += position_body(position.to_s)
22
- @http.post(resource, body)
19
+ def location_body(media)
20
+ "Content-Location: #{media}\n"
23
21
  end
24
22
 
25
- def play
23
+ def send(media, position = 0)
24
+ play(media, position)
25
+ self
26
26
  end
27
27
 
28
- def pause
29
- # TODO: know how to pause video
28
+ def play(media, position = 0)
29
+ body = location_body(media)
30
+ body += position_body(position.to_s)
31
+ @http.post(resource, body)
30
32
  end
31
33
 
32
34
  def stop
35
+ @http.post(stop_resource)
33
36
  end
34
37
 
35
38
  end
@@ -0,0 +1,2 @@
1
+ class Airplay::Server
2
+ end
@@ -0,0 +1,30 @@
1
+ module Airplay::Server::Browser
2
+ attr_reader :servers
3
+
4
+ def self.find_by_name(name)
5
+ found_server = @servers.detect do |server|
6
+ server if server.name == name
7
+ end
8
+ raise Airplay::Client::ServerNotFoundError unless found_server
9
+ found_server
10
+ end
11
+
12
+ def self.browse
13
+ @servers = []
14
+ DNSSD.browse!(Airplay::Protocol::SEARCH) do |reply|
15
+ resolver = DNSSD::Service.new
16
+ target, port = nil
17
+ resolver.resolve(reply) do |resolved|
18
+ port = resolved.port
19
+ target = resolved.target
20
+ break unless resolved.flags.more_coming?
21
+ end
22
+ info = Socket.getaddrinfo(target, nil, Socket::AF_INET)
23
+ ip_address = info[0][2]
24
+ @servers << Airplay::Server::Node.new(reply.name, reply.domain, ip_address, port)
25
+ break unless reply.flags.more_coming?
26
+ end
27
+ @servers
28
+ end
29
+
30
+ end
@@ -1,4 +1,4 @@
1
- class Airplay::Node
1
+ class Airplay::Server::Node
2
2
  attr_reader :name, :domain, :ip, :port
3
3
 
4
4
  def initialize(name, domain, ip, port)
@@ -1,17 +1,13 @@
1
1
  require File.expand_path("helper", File.dirname(__FILE__))
2
2
 
3
3
  scope do
4
- setup do
5
- @airplay = Airplay::Client.new
6
- end
7
-
8
4
  test "connect to an authenticated source" do
9
5
  VCR.use_cassette("authenticate all the things!") do
10
- @airplay.use("Apple TV")
11
- @airplay.password("password")
6
+ airplay = Airplay::Client.new(false, MockedBrowser)
7
+ airplay.password("password")
12
8
 
13
- assert @airplay.scrub.has_key?("duration")
14
- assert @airplay.scrub.has_key?("position")
9
+ assert airplay.scrub.has_key?("duration")
10
+ assert airplay.scrub.has_key?("position")
15
11
  end
16
12
  end
17
13
  end
@@ -3,7 +3,7 @@ require File.expand_path("helper", File.dirname(__FILE__))
3
3
  scope do
4
4
 
5
5
  setup do
6
- @airplay = Airplay::Client.new
6
+ @airplay = Airplay::Client.new(false, MockedBrowser)
7
7
  end
8
8
 
9
9
  test "browse for available airplay servers" do
@@ -11,8 +11,7 @@ scope do
11
11
  end
12
12
 
13
13
  test "find servers by name" do
14
- airplay = Airplay::Client.new
15
- assert @airplay.find_by_name("Apple TV").is_a?(Airplay::Node)
14
+ assert @airplay.find_by_name("Mock TV").is_a?(Airplay::Server::Node)
16
15
  end
17
16
 
18
17
  test "raise on not found" do
@@ -22,7 +21,7 @@ scope do
22
21
  end
23
22
 
24
23
  test "autoselect if only one server available" do
25
- assert_equal "Apple TV", @airplay.active_server.name
24
+ assert_equal "Mock TV", @airplay.active_server.name
26
25
  end
27
26
 
28
27
  end
@@ -0,0 +1,25 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :post
5
+ uri: http://10.1.0.220:7000/play
6
+ body: ! 'Content-Location: http://www.robtowns.com/music/blind_willie.mp3
7
+
8
+ Start-Position: 0
9
+
10
+ '
11
+ headers:
12
+ user-agent:
13
+ - MediaControl/1.0
14
+ response: !ruby/struct:VCR::Response
15
+ status: !ruby/struct:VCR::ResponseStatus
16
+ code: 200
17
+ message: OK
18
+ headers:
19
+ date:
20
+ - Wed, 14 Sep 2011 13:12:35 GMT
21
+ content-length:
22
+ - '0'
23
+ body: !!null
24
+ http_version: '1.1'
25
+ ignored: false
@@ -17,7 +17,27 @@
17
17
  message: OK
18
18
  headers:
19
19
  date:
20
- - Wed, 31 Aug 2011 14:36:39 GMT
20
+ - Thu, 15 Sep 2011 14:55:22 GMT
21
+ content-length:
22
+ - '0'
23
+ body: !!null
24
+ http_version: '1.1'
25
+ ignored: false
26
+ - !ruby/struct:VCR::HTTPInteraction
27
+ request: !ruby/struct:VCR::Request
28
+ method: :post
29
+ uri: http://10.1.0.220:7000/stop
30
+ body: !!null
31
+ headers:
32
+ user-agent:
33
+ - MediaControl/1.0
34
+ response: !ruby/struct:VCR::Response
35
+ status: !ruby/struct:VCR::ResponseStatus
36
+ code: 200
37
+ message: OK
38
+ headers:
39
+ date:
40
+ - Thu, 15 Sep 2011 14:55:22 GMT
21
41
  content-length:
22
42
  - '0'
23
43
  body: !!null
@@ -2,9 +2,25 @@ $:.unshift(File.expand_path("../lib", File.dirname(__FILE__)))
2
2
 
3
3
  require "airplay"
4
4
  require 'vcr'
5
- require 'mocha'
6
5
  require "cutest"
7
6
 
7
+ module MockedBrowser
8
+ attr_reader :servers
9
+
10
+ def self.browse
11
+ @servers = [Airplay::Server::Node.new("Mock TV", ".local", "10.1.0.220", 7000)]
12
+ end
13
+
14
+ def self.find_by_name(name)
15
+ if name == "Mock TV"
16
+ @servers.first
17
+ else
18
+ raise Airplay::Client::ServerNotFoundError
19
+ end
20
+ end
21
+
22
+ end
23
+
8
24
  VCR.config do |c|
9
25
  c.cassette_library_dir = 'test/fixtures/cassettes/airplay'
10
26
  c.default_cassette_options = { :record => :once }
@@ -9,12 +9,11 @@ end
9
9
 
10
10
  scope do
11
11
  setup do
12
- @airplay = Airplay::Client.new
12
+ @airplay = Airplay::Client.new(false, MockedBrowser)
13
13
  end
14
14
 
15
15
  test "send an image to the server" do
16
16
  VCR.use_cassette("send image to apple tv") do
17
- @airplay.use("Apple TV")
18
17
  file_path = "./test/fixtures/image2.gif"
19
18
  assert @airplay.send_image(file_path).kind_of?(String)
20
19
  assert @airplay.send_image(File.open(file_path)).kind_of?(String)
@@ -23,7 +22,6 @@ scope do
23
22
 
24
23
  test "send an image to the server doing a dissolve" do
25
24
  VCR.use_cassette("send image to apple tv") do
26
- @airplay.use("Apple TV")
27
25
  assert @airplay.send_image("./test/fixtures/image.gif", :dissolve).kind_of?(String)
28
26
  end
29
27
  end
@@ -0,0 +1,28 @@
1
+ require File.expand_path("helper", File.dirname(__FILE__))
2
+
3
+ scope do
4
+ test "check media protocol class" do
5
+ image_protocol = Airplay::Protocol::Media.new("127.0.0.1")
6
+ assert_equal "/play", image_protocol.resource
7
+ end
8
+ end
9
+
10
+ scope do
11
+ setup do
12
+ @airplay = Airplay::Client.new(false, MockedBrowser)
13
+ end
14
+
15
+ test "send a video to the server" do
16
+ VCR.use_cassette("send video to apple tv") do
17
+ player = @airplay.send_video("http://www.yo-yo.org/mp4/yu.mp4")
18
+ assert player
19
+ assert player.stop.kind_of?(String)
20
+ end
21
+ end
22
+
23
+ test "send audio to the server" do
24
+ VCR.use_cassette("send audio to apple tv") do
25
+ assert @airplay.send_audio("http://www.robtowns.com/music/blind_willie.mp3")
26
+ end
27
+ end
28
+ end
@@ -9,12 +9,11 @@ end
9
9
 
10
10
  scope do
11
11
  setup do
12
- @airplay = Airplay::Client.new
12
+ @airplay = Airplay::Client.new(false, MockedBrowser)
13
13
  end
14
14
 
15
15
  test "check scrub status" do
16
16
  VCR.use_cassette("get current scrub from apple tv") do
17
- @airplay.use("Apple TV")
18
17
  assert @airplay.scrub.has_key?("duration")
19
18
  assert @airplay.scrub.has_key?("position")
20
19
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airplay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-13 00:00:00.000000000 -03:00
12
+ date: 2011-09-15 00:00:00.000000000 -03:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: dnssd
17
- requirement: &70245624906500 !ruby/object:Gem::Requirement
17
+ requirement: &70210208536140 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: '0'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70245624906500
25
+ version_requirements: *70210208536140
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: net-http-digest_auth
28
- requirement: &70245624906060 !ruby/object:Gem::Requirement
28
+ requirement: &70210208535700 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *70245624906060
36
+ version_requirements: *70210208535700
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: cutest
39
- requirement: &70245624905640 !ruby/object:Gem::Requirement
39
+ requirement: &70210208535280 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,21 +44,10 @@ dependencies:
44
44
  version: '0'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *70245624905640
48
- - !ruby/object:Gem::Dependency
49
- name: mocha
50
- requirement: &70245624905220 !ruby/object:Gem::Requirement
51
- none: false
52
- requirements:
53
- - - ! '>='
54
- - !ruby/object:Gem::Version
55
- version: '0'
56
- type: :development
57
- prerelease: false
58
- version_requirements: *70245624905220
47
+ version_requirements: *70210208535280
59
48
  - !ruby/object:Gem::Dependency
60
49
  name: capybara
61
- requirement: &70245624904800 !ruby/object:Gem::Requirement
50
+ requirement: &70210208534860 !ruby/object:Gem::Requirement
62
51
  none: false
63
52
  requirements:
64
53
  - - ! '>='
@@ -66,10 +55,10 @@ dependencies:
66
55
  version: '0'
67
56
  type: :development
68
57
  prerelease: false
69
- version_requirements: *70245624904800
58
+ version_requirements: *70210208534860
70
59
  - !ruby/object:Gem::Dependency
71
60
  name: fakeweb
72
- requirement: &70245624904380 !ruby/object:Gem::Requirement
61
+ requirement: &70210208534440 !ruby/object:Gem::Requirement
73
62
  none: false
74
63
  requirements:
75
64
  - - ! '>='
@@ -77,10 +66,10 @@ dependencies:
77
66
  version: '0'
78
67
  type: :development
79
68
  prerelease: false
80
- version_requirements: *70245624904380
69
+ version_requirements: *70210208534440
81
70
  - !ruby/object:Gem::Dependency
82
71
  name: vcr
83
- requirement: &70245624903960 !ruby/object:Gem::Requirement
72
+ requirement: &70210208534020 !ruby/object:Gem::Requirement
84
73
  none: false
85
74
  requirements:
86
75
  - - ! '>='
@@ -88,7 +77,7 @@ dependencies:
88
77
  version: '0'
89
78
  type: :development
90
79
  prerelease: false
91
- version_requirements: *70245624903960
80
+ version_requirements: *70210208534020
92
81
  description: Send image/video to an airplay enabled device
93
82
  email:
94
83
  - yo@brunoaguirre.com
@@ -97,28 +86,32 @@ extensions: []
97
86
  extra_rdoc_files: []
98
87
  files:
99
88
  - .gitignore
89
+ - Gemfile
100
90
  - README.md
101
91
  - Rakefile
102
92
  - airplay.gemspec
103
93
  - lib/airplay.rb
104
94
  - lib/airplay/client.rb
105
- - lib/airplay/node.rb
106
95
  - lib/airplay/protocol.rb
107
96
  - lib/airplay/protocol/image.rb
97
+ - lib/airplay/protocol/media.rb
108
98
  - lib/airplay/protocol/scrub.rb
109
- - lib/airplay/protocol/video.rb
99
+ - lib/airplay/server.rb
100
+ - lib/airplay/server/browser.rb
101
+ - lib/airplay/server/node.rb
110
102
  - test/authentication.rb
111
103
  - test/discovery.rb
112
104
  - test/fixtures/cassettes/airplay/authenticate_all_the_things_.yml
113
105
  - test/fixtures/cassettes/airplay/get_current_scrub_from_apple_tv.yml
106
+ - test/fixtures/cassettes/airplay/send_audio_to_apple_tv.yml
114
107
  - test/fixtures/cassettes/airplay/send_image_to_apple_tv.yml
115
108
  - test/fixtures/cassettes/airplay/send_video_to_apple_tv.yml
116
109
  - test/fixtures/image.gif
117
110
  - test/fixtures/image2.gif
118
111
  - test/helper.rb
119
112
  - test/images.rb
113
+ - test/media.rb
120
114
  - test/scrub.rb
121
- - test/video.rb
122
115
  has_rdoc: true
123
116
  homepage: http://github.com/elcuervo/airplay
124
117
  licenses: []
@@ -1,21 +0,0 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
2
-
3
- scope do
4
- test "check video protocol class" do
5
- image_protocol = Airplay::Protocol::Video.new("127.0.0.1")
6
- assert_equal "/play", image_protocol.resource
7
- end
8
- end
9
-
10
- scope do
11
- setup do
12
- @airplay = Airplay::Client.new
13
- end
14
-
15
- test "send a video to the server" do
16
- VCR.use_cassette("send video to apple tv") do
17
- @airplay.use("Apple TV")
18
- assert @airplay.send_video("http://www.yo-yo.org/mp4/yu.mp4").kind_of?(String)
19
- end
20
- end
21
- end