airplay 1.0.0.beta1 → 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +9 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +3 -7
  5. data/README.md +2 -1
  6. data/Rakefile +74 -0
  7. data/airplay-cli.gemspec +19 -0
  8. data/airplay.gemspec +5 -8
  9. data/examples/demo.rb +5 -8
  10. data/lib/airplay.rb +20 -2
  11. data/lib/airplay/cli/version.rb +5 -0
  12. data/lib/airplay/configuration.rb +8 -3
  13. data/lib/airplay/connection.rb +0 -7
  14. data/lib/airplay/devices.rb +4 -1
  15. data/lib/airplay/group.rb +33 -0
  16. data/lib/airplay/group/players.rb +31 -0
  17. data/lib/airplay/playable.rb +7 -3
  18. data/lib/airplay/{protocol/player.rb → player.rb} +37 -17
  19. data/lib/airplay/player/media.rb +22 -0
  20. data/lib/airplay/player/playback_info.rb +51 -0
  21. data/lib/airplay/player/playlist.rb +41 -0
  22. data/lib/airplay/player/timers.rb +27 -0
  23. data/lib/airplay/server.rb +56 -0
  24. data/lib/airplay/server/app.rb +17 -0
  25. data/lib/airplay/version.rb +3 -0
  26. data/lib/airplay/viewable.rb +2 -2
  27. data/lib/airplay/{protocol/viewer.rb → viewer.rb} +2 -2
  28. metadata +22 -73
  29. data/lib/airplay/protocol.rb +0 -12
  30. data/lib/airplay/protocol/app.rb +0 -52
  31. data/lib/airplay/protocol/message.rb +0 -8
  32. data/lib/airplay/protocol/playback_info.rb +0 -49
  33. data/lib/airplay/protocol/reverse.rb +0 -69
  34. data/lib/airplay/protocol/slideshow.rb +0 -66
  35. data/lib/airplay/protocol/timers.rb +0 -25
  36. data/test/fixtures/cassettes/airplay/listing_slideshow_features.yml +0 -201
  37. data/test/fixtures/cassettes/airplay/play_an_entire_video.yml +0 -100
  38. data/test/fixtures/cassettes/airplay/sending_a_video.yml +0 -71
  39. data/test/fixtures/cassettes/airplay/sending_an_image.yml +0 -26439
  40. data/test/fixtures/cassettes/airplay/stop_any_transmission.yml +0 -8851
  41. data/test/integration/discovery_test.rb +0 -13
  42. data/test/integration/features_test.rb +0 -14
  43. data/test/integration/send_media_test.rb +0 -37
  44. data/test/integration/slideshow_test.rb +0 -26
  45. data/test/test_helper.rb +0 -42
  46. data/test/unit/node_test.rb +0 -17
  47. data/test/unit/protocol_test.rb +0 -44
@@ -5,10 +5,13 @@ require "celluloid"
5
5
  require "cfpropertylist"
6
6
 
7
7
  require "airplay/connection"
8
- require "airplay/protocol/timers"
9
- require "airplay/protocol/playback_info"
8
+ require "airplay/server"
9
+ require "airplay/player/timers"
10
+ require "airplay/player/media"
11
+ require "airplay/player/playback_info"
12
+ require "airplay/player/playlist"
10
13
 
11
- module Airplay::Protocol
14
+ module Airplay
12
15
  # Public: The class that handles all the video playback
13
16
  #
14
17
  class Player
@@ -17,10 +20,16 @@ module Airplay::Protocol
17
20
 
18
21
  def_delegators :@machine, :state, :on
19
22
 
23
+ attr_reader :device
24
+
20
25
  def initialize(device)
21
26
  @device = device
22
27
  end
23
28
 
29
+ def playlist
30
+ @_playlist ||= Playlist.new
31
+ end
32
+
24
33
  # Public: Plays a given url or file.
25
34
  # Creates a new persistent connection to ensure that
26
35
  # the socket will be kept alive
@@ -28,20 +37,19 @@ module Airplay::Protocol
28
37
  # file_or_url - The url or file to be reproduced
29
38
  # options - Optional starting time
30
39
  #
31
- def play(file_or_url, options = {})
40
+ def play(media_to_play = "playlist", options = {})
32
41
  start_the_machine
33
42
  check_for_playback_status
34
43
 
35
- media_url = case true
36
- when File.exists?(file_or_url)
37
- when !!(file_or_url =~ URI::regexp)
38
- file_or_url
39
- else
40
- raise Errno::ENOENT, file_or_url
41
- end
44
+ media = case true
45
+ when media_to_play.is_a?(Media) then media_to_play
46
+ when media_to_play == "playlist" && playlist.any?
47
+ playlist.next
48
+ else Media.new(media_to_play)
49
+ end
42
50
 
43
51
  content = {
44
- "Content-Location" => media_url,
52
+ "Content-Location" => media,
45
53
  "Start-Position" => options.fetch(:time, 0.0)
46
54
  }
47
55
 
@@ -65,6 +73,18 @@ module Airplay::Protocol
65
73
  end
66
74
  end
67
75
 
76
+ def next
77
+ video = playlist.next
78
+ play(video) if video
79
+ video
80
+ end
81
+
82
+ def previous
83
+ video = playlist.previous
84
+ play(video) if video
85
+ video
86
+ end
87
+
68
88
  # Public: Shows the current playback time if a video is being played.
69
89
  #
70
90
  # Returns a hash with the :duration and current :position
@@ -117,6 +137,11 @@ module Airplay::Protocol
117
137
  cleanup
118
138
  end
119
139
 
140
+ def cleanup
141
+ timers.cancel
142
+ persistent.close
143
+ end
144
+
120
145
  private
121
146
 
122
147
  def timers
@@ -131,11 +156,6 @@ module Airplay::Protocol
131
156
  @_persistent ||= Airplay::Connection.new(@device, keep_alive: true)
132
157
  end
133
158
 
134
- def cleanup
135
- timers.cancel
136
- persistent.close
137
- end
138
-
139
159
  def check_for_playback_status
140
160
  timers << every(1) do
141
161
  case true
@@ -0,0 +1,22 @@
1
+ module Airplay
2
+ class Player
3
+ class Media
4
+ attr_reader :url
5
+
6
+ def initialize(file_or_url)
7
+ @url = case true
8
+ when File.exists?(file_or_url)
9
+ Airplay.server.serve(File.expand_path(file_or_url))
10
+ when !!(file_or_url =~ URI::regexp)
11
+ file_or_url
12
+ else
13
+ raise Errno::ENOENT, file_or_url
14
+ end
15
+ end
16
+
17
+ def to_s
18
+ @url
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,51 @@
1
+ module Airplay
2
+ class Player
3
+ PlaybackInfo = Struct.new(:info) do
4
+ def uuid
5
+ info["uuid"]
6
+ end
7
+
8
+ def duration
9
+ info["duration"]
10
+ end
11
+
12
+ def position
13
+ info["position"]
14
+ end
15
+
16
+ def percent
17
+ return unless position && duration
18
+ (position * 100 / duration).floor
19
+ end
20
+
21
+ def likely_to_keep_up?
22
+ info["playbackLikelyToKeepUp"]
23
+ end
24
+
25
+ def stall_count
26
+ info["stallCount"]
27
+ end
28
+
29
+ def ready_to_play?
30
+ info["readyToPlay"]
31
+ end
32
+
33
+ def stopped?
34
+ info.empty?
35
+ end
36
+
37
+ def playing?
38
+ info.has_key?("rate") && info.fetch("rate", false) && !info["rate"].zero?
39
+ end
40
+
41
+ def paused?
42
+ !playing?
43
+ end
44
+
45
+ def played?
46
+ # This is weird. I know. Bear with me.
47
+ info.keys.size == 2
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,41 @@
1
+ require "forwardable"
2
+ require "airplay/player/media"
3
+
4
+ module Airplay
5
+ class Player
6
+ class Playlist
7
+ include Enumerable
8
+ extend Forwardable
9
+
10
+ def_delegators :@items, :each, :size, :empty?
11
+
12
+ def initialize
13
+ @items = []
14
+ @position = 0
15
+ end
16
+
17
+ def to_ary
18
+ @items
19
+ end
20
+
21
+ def <<(file_or_url)
22
+ @items << Media.new(file_or_url)
23
+ end
24
+
25
+ def next
26
+ return nil if @position + 1 > @items.size
27
+
28
+ item = @items[@position]
29
+ @position += 1
30
+ item
31
+ end
32
+
33
+ def previous
34
+ return nil if @position - 1 < 0
35
+
36
+ @position -= 1
37
+ @items[@position]
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,27 @@
1
+ module Airplay
2
+ class Player
3
+ class Timers
4
+ include Enumerable
5
+ extend Forwardable
6
+
7
+ def_delegators :@timers, :each, :size, :empty?
8
+
9
+ def initialize
10
+ @timers = []
11
+ end
12
+
13
+ def <<(timer)
14
+ @timers << timer
15
+ end
16
+
17
+ def reset
18
+ @timers.each { |t| t.reset }
19
+ @timers = []
20
+ end
21
+
22
+ def cancel
23
+ @timers.each { |t| t.cancel }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,56 @@
1
+ require "rack"
2
+ require "socket"
3
+ require "uuid"
4
+
5
+ require "airplay/logger"
6
+ require "airplay/server/app"
7
+
8
+ module Airplay
9
+ class Server
10
+ include Celluloid
11
+
12
+ def initialize
13
+ @port = Airplay.configuration.port
14
+ @logger = Airplay::Logger.new("airplay::server")
15
+ @server = Rack::Server.new(
16
+ server: :webrick,
17
+ Host: private_ip,
18
+ Port: @port,
19
+ Logger: @logger,
20
+ AccessLog: [],
21
+ app: App.app
22
+ )
23
+
24
+ start!
25
+ end
26
+
27
+ def serve(file)
28
+ sleep 0.1 until running?
29
+ asset_id = App.settings[:assets][file]
30
+
31
+ "http://#{private_ip}:#{@port}/assets/#{asset_id}"
32
+ end
33
+
34
+ def start!
35
+ Thread.start { @server.start }
36
+ end
37
+
38
+ private
39
+
40
+ def running?
41
+ begin
42
+ socket = TCPSocket.new(private_ip, @port)
43
+ socket.close unless socket.nil?
44
+ true
45
+ rescue Errno::ECONNREFUSED, Errno::EBADF, Errno::EADDRNOTAVAIL
46
+ false
47
+ end
48
+ end
49
+
50
+ def private_ip
51
+ @_ip ||= Socket.ip_address_list.detect do |addr|
52
+ addr.ipv4_private?
53
+ end.ip_address
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,17 @@
1
+ require "cuba"
2
+
3
+ module Airplay
4
+ class Server
5
+ class App < Cuba
6
+ settings[:assets] ||= Hash.new do |h, k|
7
+ h[k] = UUID.generate
8
+ end
9
+
10
+ define do
11
+ on "assets/:uuid" do |uuid|
12
+ run Rack::File.new(settings[:assets].key(uuid))
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Airplay
2
+ VERSION = "1.0.0.beta2"
3
+ end
@@ -1,5 +1,5 @@
1
1
  require "forwardable"
2
- require "airplay/protocol/viewer"
2
+ require "airplay/viewer"
3
3
 
4
4
  module Airplay
5
5
  module Viewable
@@ -10,7 +10,7 @@ module Airplay
10
10
  private
11
11
 
12
12
  def viewer
13
- @_viewer ||= Airplay::Protocol::Viewer.new(self)
13
+ @_viewer ||= Airplay::Viewer.new(self)
14
14
  end
15
15
  end
16
16
  end
@@ -1,6 +1,6 @@
1
1
  require "open-uri"
2
2
 
3
- module Airplay::Protocol
3
+ module Airplay
4
4
  # Public: The class to handle image broadcast to a device
5
5
  #
6
6
  class Viewer
@@ -8,7 +8,7 @@ module Airplay::Protocol
8
8
 
9
9
  def initialize(device)
10
10
  @device = device
11
- @logger = Airplay::Logger.new("airplay::protocol::image")
11
+ @logger = Airplay::Logger.new("airplay::viewer")
12
12
  end
13
13
 
14
14
  # Public: Broadcasts the content to the device
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: 1.0.0.beta1
4
+ version: 1.0.0.beta2
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-10 00:00:00.000000000 Z
12
+ date: 2013-10-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: dnssd
@@ -128,17 +128,17 @@ dependencies:
128
128
  requirement: !ruby/object:Gem::Requirement
129
129
  none: false
130
130
  requirements:
131
- - - ~>
131
+ - - '='
132
132
  - !ruby/object:Gem::Version
133
- version: 0.0.10
133
+ version: 0.0.12
134
134
  type: :runtime
135
135
  prerelease: false
136
136
  version_requirements: !ruby/object:Gem::Requirement
137
137
  none: false
138
138
  requirements:
139
- - - ~>
139
+ - - '='
140
140
  - !ruby/object:Gem::Version
141
- version: 0.0.10
141
+ version: 0.0.12
142
142
  - !ruby/object:Gem::Dependency
143
143
  name: net-http-digest_auth
144
144
  requirement: !ruby/object:Gem::Requirement
@@ -155,38 +155,6 @@ dependencies:
155
155
  - - ~>
156
156
  - !ruby/object:Gem::Version
157
157
  version: 1.2.1
158
- - !ruby/object:Gem::Dependency
159
- name: clap
160
- requirement: !ruby/object:Gem::Requirement
161
- none: false
162
- requirements:
163
- - - ~>
164
- - !ruby/object:Gem::Version
165
- version: 1.0.0
166
- type: :runtime
167
- prerelease: false
168
- version_requirements: !ruby/object:Gem::Requirement
169
- none: false
170
- requirements:
171
- - - ~>
172
- - !ruby/object:Gem::Version
173
- version: 1.0.0
174
- - !ruby/object:Gem::Dependency
175
- name: ruby-progressbar
176
- requirement: !ruby/object:Gem::Requirement
177
- none: false
178
- requirements:
179
- - - ~>
180
- - !ruby/object:Gem::Version
181
- version: 1.2.0
182
- type: :runtime
183
- prerelease: false
184
- version_requirements: !ruby/object:Gem::Requirement
185
- none: false
186
- requirements:
187
- - - ~>
188
- - !ruby/object:Gem::Version
189
- version: 1.2.0
190
158
  - !ruby/object:Gem::Dependency
191
159
  name: minitest
192
160
  requirement: !ruby/object:Gem::Requirement
@@ -238,11 +206,12 @@ dependencies:
238
206
  description: Send image/video to an airplay enabled device
239
207
  email:
240
208
  - yo@brunoaguirre.com
241
- executables:
242
- - air
209
+ executables: []
243
210
  extensions: []
244
211
  extra_rdoc_files: []
245
212
  files:
213
+ - .gitignore
214
+ - .travis.yml
246
215
  - Gemfile
247
216
  - Gemfile.lock
248
217
  - HUGS
@@ -250,6 +219,7 @@ files:
250
219
  - README.md
251
220
  - Rakefile
252
221
  - SPEC.md
222
+ - airplay-cli.gemspec
253
223
  - airplay.gemspec
254
224
  - bin/air
255
225
  - examples/demo.rb
@@ -258,6 +228,7 @@ files:
258
228
  - lib/airplay.rb
259
229
  - lib/airplay/browser.rb
260
230
  - lib/airplay/cli.rb
231
+ - lib/airplay/cli/version.rb
261
232
  - lib/airplay/configuration.rb
262
233
  - lib/airplay/connection.rb
263
234
  - lib/airplay/connection/authentication.rb
@@ -266,36 +237,26 @@ files:
266
237
  - lib/airplay/device/features.rb
267
238
  - lib/airplay/device/info.rb
268
239
  - lib/airplay/devices.rb
240
+ - lib/airplay/group.rb
241
+ - lib/airplay/group/players.rb
269
242
  - lib/airplay/logger.rb
270
243
  - lib/airplay/playable.rb
271
- - lib/airplay/protocol.rb
272
- - lib/airplay/protocol/app.rb
273
- - lib/airplay/protocol/message.rb
274
- - lib/airplay/protocol/playback_info.rb
275
- - lib/airplay/protocol/player.rb
276
- - lib/airplay/protocol/reverse.rb
277
- - lib/airplay/protocol/slideshow.rb
278
- - lib/airplay/protocol/timers.rb
279
- - lib/airplay/protocol/viewer.rb
244
+ - lib/airplay/player.rb
245
+ - lib/airplay/player/media.rb
246
+ - lib/airplay/player/playback_info.rb
247
+ - lib/airplay/player/playlist.rb
248
+ - lib/airplay/player/timers.rb
249
+ - lib/airplay/server.rb
250
+ - lib/airplay/server/app.rb
280
251
  - lib/airplay/structure.rb
252
+ - lib/airplay/version.rb
281
253
  - lib/airplay/viewable.rb
282
- - test/fixtures/cassettes/airplay/listing_slideshow_features.yml
283
- - test/fixtures/cassettes/airplay/play_an_entire_video.yml
284
- - test/fixtures/cassettes/airplay/sending_a_video.yml
285
- - test/fixtures/cassettes/airplay/sending_an_image.yml
286
- - test/fixtures/cassettes/airplay/stop_any_transmission.yml
254
+ - lib/airplay/viewer.rb
287
255
  - test/fixtures/files/logo.png
288
256
  - test/fixtures/files/transition_0.png
289
257
  - test/fixtures/files/transition_1.png
290
258
  - test/fixtures/files/transition_2.png
291
259
  - test/fixtures/files/transition_3.png
292
- - test/integration/discovery_test.rb
293
- - test/integration/features_test.rb
294
- - test/integration/send_media_test.rb
295
- - test/integration/slideshow_test.rb
296
- - test/test_helper.rb
297
- - test/unit/node_test.rb
298
- - test/unit/protocol_test.rb
299
260
  homepage: http://github.com/elcuervo/airplay
300
261
  licenses:
301
262
  - MIT
@@ -323,20 +284,8 @@ signing_key:
323
284
  specification_version: 3
324
285
  summary: Airplay client
325
286
  test_files:
326
- - test/fixtures/cassettes/airplay/listing_slideshow_features.yml
327
- - test/fixtures/cassettes/airplay/play_an_entire_video.yml
328
- - test/fixtures/cassettes/airplay/sending_a_video.yml
329
- - test/fixtures/cassettes/airplay/sending_an_image.yml
330
- - test/fixtures/cassettes/airplay/stop_any_transmission.yml
331
287
  - test/fixtures/files/logo.png
332
288
  - test/fixtures/files/transition_0.png
333
289
  - test/fixtures/files/transition_1.png
334
290
  - test/fixtures/files/transition_2.png
335
291
  - test/fixtures/files/transition_3.png
336
- - test/integration/discovery_test.rb
337
- - test/integration/features_test.rb
338
- - test/integration/send_media_test.rb
339
- - test/integration/slideshow_test.rb
340
- - test/test_helper.rb
341
- - test/unit/node_test.rb
342
- - test/unit/protocol_test.rb