airplayer 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +15 -5
- data/Gemfile.lock +65 -42
- data/LICENSE +1 -1
- data/README.md +35 -15
- data/airplayer.gemspec +11 -4
- data/bin/console +7 -0
- data/bin/setup +5 -0
- data/{bin → exe}/airplayer +0 -0
- data/lib/airplayer.rb +3 -1
- data/lib/airplayer/app.rb +5 -0
- data/lib/airplayer/media.rb +22 -34
- data/lib/airplayer/playlist.rb +21 -18
- data/lib/airplayer/version.rb +1 -1
- data/lib/airplayer/youtube_dl.rb +100 -0
- metadata +40 -20
- data/spec/airplayer/app_spec.rb +0 -33
- data/spec/airplayer/controller_spec.rb +0 -35
- data/spec/airplayer/device_spec.rb +0 -56
- data/spec/airplayer/media_spec.rb +0 -51
- data/spec/airplayer/playlist_spec.rb +0 -58
- data/spec/spec_helper.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2b6af16b4d901fa48c2d07c409c89507be2c652
|
4
|
+
data.tar.gz: 37da9c298b6d1142b95353bb06d3cb42c3249f6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 042c297753ce32f4e4231f02c629f67aa33d5c6e579d4d487456f85c8b7bd856802e3eb238a440d1f039468bdddc6d88f6e009560eb437b4ab0a59a7b8bbeb22
|
7
|
+
data.tar.gz: 55308fd2bed65b39f41d969745c7470fad3b5eba8d4aee490c84994c21b5ddcd71d668561f30685bc3b0b09d71084d42aabead41c4c555512308e161a357754d
|
data/.travis.yml
CHANGED
@@ -1,8 +1,18 @@
|
|
1
|
-
|
1
|
+
sudo: false
|
2
|
+
language: objective-c
|
2
3
|
rvm:
|
3
|
-
-
|
4
|
-
|
4
|
+
- 2.2.3
|
5
|
+
|
6
|
+
cache: bundler
|
7
|
+
|
8
|
+
os:
|
9
|
+
- osx
|
10
|
+
|
5
11
|
before_install:
|
6
|
-
-
|
7
|
-
|
12
|
+
- brew install youtube-dl
|
13
|
+
|
14
|
+
before_script:
|
15
|
+
- which youtube-dl
|
16
|
+
- youtube-dl --version
|
17
|
+
|
8
18
|
script: bundle exec rake spec
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
airplayer (1.0
|
4
|
+
airplayer (1.1.0)
|
5
5
|
airplay (~> 1.0.2)
|
6
6
|
celluloid (= 0.15.2)
|
7
7
|
celluloid-io (= 0.15.0)
|
8
8
|
cuba (= 3.1.1)
|
9
9
|
http (= 0.5.0)
|
10
10
|
mime-types (~> 2.4.0)
|
11
|
+
nio4r (~> 1.1.0)
|
11
12
|
rack (~> 1.5.0)
|
12
13
|
reel (= 0.4.0)
|
13
14
|
reel-rack (= 0.1.0)
|
@@ -35,34 +36,43 @@ GEM
|
|
35
36
|
celluloid (>= 0.15.0)
|
36
37
|
nio4r (>= 0.5.0)
|
37
38
|
coderay (1.1.0)
|
38
|
-
coveralls (0.
|
39
|
-
|
40
|
-
rest-client
|
41
|
-
simplecov (
|
42
|
-
term-ansicolor
|
43
|
-
thor
|
39
|
+
coveralls (0.8.2)
|
40
|
+
json (~> 1.8)
|
41
|
+
rest-client (>= 1.6.8, < 2)
|
42
|
+
simplecov (~> 0.10.0)
|
43
|
+
term-ansicolor (~> 1.3)
|
44
|
+
thor (~> 0.19.1)
|
44
45
|
cuba (3.1.1)
|
45
46
|
rack
|
46
47
|
diff-lcs (1.2.5)
|
47
|
-
dnssd (2.0)
|
48
|
+
dnssd (2.0.1)
|
48
49
|
docile (1.1.5)
|
49
|
-
|
50
|
-
|
50
|
+
domain_name (0.5.24)
|
51
|
+
unf (>= 0.0.5, < 1.0.0)
|
52
|
+
fakefs (0.6.7)
|
53
|
+
ffi (1.9.10)
|
51
54
|
formatador (0.2.5)
|
52
|
-
guard (2.
|
55
|
+
guard (2.12.8)
|
53
56
|
formatador (>= 0.2.4)
|
54
|
-
listen (
|
57
|
+
listen (>= 2.7, <= 4.0)
|
55
58
|
lumberjack (~> 1.0)
|
59
|
+
nenv (~> 0.1)
|
60
|
+
notiffany (~> 0.0)
|
56
61
|
pry (>= 0.9.12)
|
62
|
+
shellany (~> 0.0)
|
57
63
|
thor (>= 0.18.1)
|
58
|
-
guard-
|
64
|
+
guard-compat (1.2.1)
|
65
|
+
guard-rspec (4.6.0)
|
59
66
|
guard (~> 2.1)
|
60
|
-
|
67
|
+
guard-compat (~> 1.1)
|
68
|
+
rspec (>= 2.99.0, < 4.0)
|
61
69
|
http (0.5.0)
|
62
70
|
http_parser.rb
|
71
|
+
http-cookie (1.0.2)
|
72
|
+
domain_name (~> 0.5)
|
63
73
|
http_parser.rb (0.6.0)
|
64
|
-
|
65
|
-
|
74
|
+
json (1.8.3)
|
75
|
+
listen (3.0.1)
|
66
76
|
rb-fsevent (>= 0.9.3)
|
67
77
|
rb-inotify (>= 0.9)
|
68
78
|
log4r (1.1.10)
|
@@ -70,21 +80,24 @@ GEM
|
|
70
80
|
method_source (0.8.2)
|
71
81
|
micromachine (1.0.4)
|
72
82
|
mime-types (2.4.3)
|
73
|
-
|
83
|
+
nenv (0.2.0)
|
74
84
|
net-http-digest_auth (1.2.1)
|
75
85
|
net-ptth (0.0.17)
|
76
86
|
celluloid-io (>= 0.15.0)
|
77
87
|
http_parser.rb (>= 0.6.0.beta.2)
|
78
88
|
rack (>= 1.4.5)
|
79
|
-
netrc (0.
|
80
|
-
nio4r (1.
|
89
|
+
netrc (0.10.3)
|
90
|
+
nio4r (1.1.1)
|
91
|
+
notiffany (0.0.6)
|
92
|
+
nenv (~> 0.1)
|
93
|
+
shellany (~> 0.0)
|
81
94
|
pry (0.10.1)
|
82
95
|
coderay (~> 1.1.0)
|
83
96
|
method_source (~> 0.8.1)
|
84
97
|
slop (~> 3.4)
|
85
|
-
rack (1.5.
|
86
|
-
rake (10.
|
87
|
-
rb-fsevent (0.9.
|
98
|
+
rack (1.5.5)
|
99
|
+
rake (10.4.2)
|
100
|
+
rb-fsevent (0.9.5)
|
88
101
|
rb-inotify (0.9.5)
|
89
102
|
ffi (>= 0.5.0)
|
90
103
|
reel (0.4.0)
|
@@ -96,34 +109,40 @@ GEM
|
|
96
109
|
reel-rack (0.1.0)
|
97
110
|
rack (>= 1.4.0)
|
98
111
|
reel (>= 0.4.0)
|
99
|
-
rest-client (1.
|
112
|
+
rest-client (1.8.0)
|
113
|
+
http-cookie (>= 1.0.2, < 2.0)
|
100
114
|
mime-types (>= 1.16, < 3.0)
|
101
115
|
netrc (~> 0.7)
|
102
|
-
rspec (3.
|
103
|
-
rspec-core (~> 3.
|
104
|
-
rspec-expectations (~> 3.
|
105
|
-
rspec-mocks (~> 3.
|
106
|
-
rspec-core (3.1
|
107
|
-
rspec-support (~> 3.
|
108
|
-
rspec-expectations (3.
|
116
|
+
rspec (3.3.0)
|
117
|
+
rspec-core (~> 3.3.0)
|
118
|
+
rspec-expectations (~> 3.3.0)
|
119
|
+
rspec-mocks (~> 3.3.0)
|
120
|
+
rspec-core (3.3.1)
|
121
|
+
rspec-support (~> 3.3.0)
|
122
|
+
rspec-expectations (3.3.0)
|
123
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
124
|
+
rspec-support (~> 3.3.0)
|
125
|
+
rspec-mocks (3.3.1)
|
109
126
|
diff-lcs (>= 1.2.0, < 2.0)
|
110
|
-
rspec-support (~> 3.
|
111
|
-
rspec-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
simplecov (0.9.1)
|
127
|
+
rspec-support (~> 3.3.0)
|
128
|
+
rspec-support (3.3.0)
|
129
|
+
ruby-progressbar (1.7.5)
|
130
|
+
shellany (0.0.1)
|
131
|
+
simplecov (0.10.0)
|
116
132
|
docile (~> 1.1.0)
|
117
|
-
|
118
|
-
simplecov-html (~> 0.
|
119
|
-
simplecov-html (0.
|
133
|
+
json (~> 1.8)
|
134
|
+
simplecov-html (~> 0.10.0)
|
135
|
+
simplecov-html (0.10.0)
|
120
136
|
slop (3.6.0)
|
121
|
-
term-ansicolor (1.3.
|
137
|
+
term-ansicolor (1.3.2)
|
122
138
|
tins (~> 1.0)
|
123
139
|
thor (0.19.1)
|
124
140
|
timers (1.1.0)
|
125
|
-
tins (1.
|
126
|
-
|
141
|
+
tins (1.5.4)
|
142
|
+
unf (0.1.4)
|
143
|
+
unf_ext
|
144
|
+
unf_ext (0.0.7.1)
|
145
|
+
websocket_parser (1.0.0)
|
127
146
|
|
128
147
|
PLATFORMS
|
129
148
|
ruby
|
@@ -134,5 +153,9 @@ DEPENDENCIES
|
|
134
153
|
coveralls
|
135
154
|
fakefs
|
136
155
|
guard-rspec
|
156
|
+
pry
|
137
157
|
rake
|
138
158
|
rspec
|
159
|
+
|
160
|
+
BUNDLED WITH
|
161
|
+
1.11.2
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -3,27 +3,37 @@ AirPlayer
|
|
3
3
|
|
4
4
|
Command-line AirPlay video client for Apple TV
|
5
5
|
|
6
|
-
[](https://codeclimate.com/github/Tomohiro/airplayer)
|
12
|
-
|
13
|
-
|
14
|
-
---
|
6
|
+
[](http://badge.fury.io/rb/airplayer)
|
7
|
+
[](https://gemnasium.com/Tomohiro/airplayer)
|
8
|
+
[](https://travis-ci.org/Tomohiro/airplayer)
|
9
|
+
[](https://coveralls.io/r/Tomohiro/airplayer)
|
10
|
+
[](https://codeclimate.com/github/Tomohiro/airplayer)
|
15
11
|
|
16
12
|
|
17
13
|
Requirements
|
18
14
|
-------------------------------------------------------------------------------
|
19
15
|
|
20
|
-
- OS X
|
21
|
-
- Ruby
|
16
|
+
- OS X, Ubuntu, Arch Linux
|
17
|
+
- Ruby 2.2 or later
|
18
|
+
- Bundler 1.10.0 or later
|
22
19
|
- AppleTV 2G or later
|
23
20
|
- [youtube-dl](http://rg3.github.com/youtube-dl/) (If you want to watch YouTube)
|
24
21
|
|
25
22
|
|
26
|
-
###
|
23
|
+
### For Arch Linux
|
24
|
+
|
25
|
+
[nss-mdns](https://www.archlinux.org/packages/extra/x86_64/nss-mdns/) package is required.
|
26
|
+
|
27
|
+
```sh
|
28
|
+
$ sudo pacman -S nss-mdns
|
29
|
+
```
|
30
|
+
or
|
31
|
+
|
32
|
+
```sh
|
33
|
+
$ yaourt -S nss-mdns
|
34
|
+
```
|
35
|
+
|
36
|
+
### For Ubuntu
|
27
37
|
|
28
38
|
```sh
|
29
39
|
$ sudo apt-get install rdnssd libavahi-compat-libdnssd-dev
|
@@ -156,6 +166,16 @@ $ airplayer play --device 1 '~/Movies/GHOST IN THE SHELL.mp4'
|
|
156
166
|
```
|
157
167
|
|
158
168
|
|
169
|
+
Advanced Usage
|
170
|
+
--------------------------------------------------------------------------------
|
171
|
+
|
172
|
+
### Register to OS X Service
|
173
|
+
|
174
|
+
You can create Automator Service, that opens URL from your browser in airplayer.
|
175
|
+
|
176
|
+

|
177
|
+
|
178
|
+
|
159
179
|
Supported MIME types
|
160
180
|
--------------------------------------------------------------------------------
|
161
181
|
|
@@ -172,6 +192,6 @@ File extension | MIME type | Ruby `mime-types`
|
|
172
192
|
LICENSE
|
173
193
|
--------------------------------------------------------------------------------
|
174
194
|
|
175
|
-
© 2012 -
|
176
|
-
|
177
|
-
See LICENSE for details.
|
195
|
+
© 2012 - 2016 Tomohiro TAIRA.
|
196
|
+
|
197
|
+
This project is licensed under the MIT license. See [LICENSE](LICENSE) for details.
|
data/airplayer.gemspec
CHANGED
@@ -8,15 +8,20 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = AirPlayer::VERSION
|
9
9
|
spec.authors = ['Tomohiro TAIRA']
|
10
10
|
spec.email = ['tomohiro.t@gmail.com']
|
11
|
-
|
11
|
+
|
12
12
|
spec.summary = 'Command-line AirPlay video client for Apple TV'
|
13
|
+
spec.description = 'Command-line AirPlay video client for Apple TV'
|
13
14
|
spec.homepage = 'https://github.com/Tomohiro/airplayer'
|
15
|
+
spec.license = 'MIT'
|
14
16
|
|
15
|
-
spec.files = `git ls-files`.split(
|
16
|
-
spec.
|
17
|
-
spec.
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^spec/}) }
|
18
|
+
spec.bindir = 'exe'
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}).map { |f| File.basename(f) }
|
18
20
|
spec.require_paths = ['lib']
|
19
21
|
|
22
|
+
# This gem will work with 2.2.0 or greater...
|
23
|
+
spec.required_ruby_version = '>= 2.2.0'
|
24
|
+
|
20
25
|
spec.add_runtime_dependency 'thor'
|
21
26
|
spec.add_runtime_dependency 'ruby-progressbar'
|
22
27
|
spec.add_runtime_dependency 'mime-types', '~> 2.4.0'
|
@@ -24,6 +29,7 @@ Gem::Specification.new do |spec|
|
|
24
29
|
spec.add_runtime_dependency 'airplay', '~> 1.0.2'
|
25
30
|
spec.add_runtime_dependency 'rack', '~> 1.5.0'
|
26
31
|
spec.add_runtime_dependency 'http', '= 0.5.0'
|
32
|
+
spec.add_runtime_dependency 'nio4r', '~> 1.1.0'
|
27
33
|
spec.add_runtime_dependency 'reel', '= 0.4.0'
|
28
34
|
spec.add_runtime_dependency 'reel-rack', '= 0.1.0'
|
29
35
|
spec.add_runtime_dependency 'celluloid', '= 0.15.2'
|
@@ -36,4 +42,5 @@ Gem::Specification.new do |spec|
|
|
36
42
|
spec.add_development_dependency 'guard-rspec'
|
37
43
|
spec.add_development_dependency 'fakefs'
|
38
44
|
spec.add_development_dependency 'coveralls'
|
45
|
+
spec.add_development_dependency 'pry'
|
39
46
|
end
|
data/bin/console
ADDED
data/bin/setup
ADDED
data/{bin → exe}/airplayer
RENAMED
File without changes
|
data/lib/airplayer.rb
CHANGED
@@ -3,9 +3,11 @@ Airplay.configuration.load
|
|
3
3
|
|
4
4
|
module AirPlayer
|
5
5
|
require 'airplayer/version'
|
6
|
-
require 'airplayer/app'
|
7
6
|
require 'airplayer/controller'
|
8
7
|
require 'airplayer/device'
|
9
8
|
require 'airplayer/playlist'
|
10
9
|
require 'airplayer/media'
|
10
|
+
require 'airplayer/youtube_dl'
|
11
|
+
|
12
|
+
require 'airplayer/app'
|
11
13
|
end
|
data/lib/airplayer/app.rb
CHANGED
@@ -2,16 +2,21 @@ require 'thor'
|
|
2
2
|
|
3
3
|
module AirPlayer
|
4
4
|
class App < Thor
|
5
|
+
class_option :youtube_dl, desc: 'path to youtube-dl', default: YoutubeDl.default_path
|
6
|
+
|
5
7
|
desc 'play <URI|PATH> [-r|--repeat] [-s|--shuffle] [-d=|--device=]', 'Play video(URI[Podcast URI, YouTube] or Path[local video file, directory])'
|
6
8
|
method_option :repeat, aliases: '-r', desc: 'Repeat play', type: :boolean
|
7
9
|
method_option :shuffle, aliases: '-s', desc: 'Shuffle play', type: :boolean
|
8
10
|
method_option :device, aliases: '-d', desc: 'Device number', type: :numeric
|
9
11
|
def play(target)
|
12
|
+
YoutubeDl.path = options[:youtube_dl]
|
10
13
|
controller = Controller.new(device: options.fetch('device', 0))
|
14
|
+
|
11
15
|
Playlist.new(options).add(target).entries do |media|
|
12
16
|
controller.play(media)
|
13
17
|
controller.pause
|
14
18
|
end
|
19
|
+
rescue Interrupt # capture Ctrl-C
|
15
20
|
end
|
16
21
|
|
17
22
|
desc 'devices', 'Show AirPlay devices'
|
data/lib/airplayer/media.rb
CHANGED
@@ -19,11 +19,6 @@ module AirPlayer
|
|
19
19
|
video/mpeg4
|
20
20
|
)
|
21
21
|
|
22
|
-
SUPPORTED_DOMAINS = %w(
|
23
|
-
youtube
|
24
|
-
youtu.be
|
25
|
-
)
|
26
|
-
|
27
22
|
class Media
|
28
23
|
attr_reader :title, :path, :type
|
29
24
|
|
@@ -35,26 +30,38 @@ module AirPlayer
|
|
35
30
|
@title = File.basename(path)
|
36
31
|
@type = :file
|
37
32
|
else
|
38
|
-
|
39
|
-
@
|
40
|
-
@title = online_media_title(uri)
|
33
|
+
@path = YoutubeDl.get_url(target)
|
34
|
+
@title = YoutubeDl.get_title(target)
|
41
35
|
@type = :url
|
42
36
|
end
|
43
37
|
end
|
44
38
|
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
class << self
|
40
|
+
def playable?(path)
|
41
|
+
if is_url?(path)
|
42
|
+
YoutubeDl.supports?(path) || supported_mime_type?(YoutubeDl.filename(path))
|
43
|
+
else
|
44
|
+
supported_mime_type?(path)
|
45
|
+
end
|
48
46
|
end
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
def is_url?(path)
|
49
|
+
uri = URI(path)
|
50
|
+
uri.scheme && uri.absolute?
|
51
|
+
rescue URI::InvalidURIError
|
52
|
+
false
|
53
53
|
end
|
54
54
|
|
55
|
-
|
55
|
+
def supported_mime_type?(path)
|
56
|
+
MIME::Types.of(path).map(&:simplified).each do |mimetype|
|
57
|
+
return SUPPORTED_MIME_TYPES.include?(mimetype)
|
58
|
+
end
|
59
|
+
|
60
|
+
false
|
61
|
+
end
|
56
62
|
end
|
57
63
|
|
64
|
+
|
58
65
|
def file?
|
59
66
|
@type == :file
|
60
67
|
end
|
@@ -62,24 +69,5 @@ module AirPlayer
|
|
62
69
|
def url?
|
63
70
|
@type == :url
|
64
71
|
end
|
65
|
-
|
66
|
-
private
|
67
|
-
def online_media_path(uri)
|
68
|
-
case URI.parse(uri).host
|
69
|
-
when /youtube|youtu\.be/
|
70
|
-
uri = `youtube-dl -g #{uri}`
|
71
|
-
else
|
72
|
-
uri
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def online_media_title(uri)
|
77
|
-
case URI.parse(uri).host
|
78
|
-
when /youtube|youtu\.be/
|
79
|
-
title = `youtube-dl -e #{uri}`
|
80
|
-
else
|
81
|
-
title = File.basename(uri)
|
82
|
-
end
|
83
|
-
end
|
84
72
|
end
|
85
73
|
end
|
data/lib/airplayer/playlist.rb
CHANGED
@@ -6,7 +6,7 @@ module AirPlayer
|
|
6
6
|
class Playlist < Array
|
7
7
|
def initialize(options = {})
|
8
8
|
@shuffle = options['shuffle'] || false
|
9
|
-
@repeat = options['repeat']
|
9
|
+
@repeat = options['repeat'] || false
|
10
10
|
end
|
11
11
|
|
12
12
|
def add(item)
|
@@ -18,6 +18,7 @@ module AirPlayer
|
|
18
18
|
when :url
|
19
19
|
push(Media.new(item))
|
20
20
|
end
|
21
|
+
|
21
22
|
self
|
22
23
|
end
|
23
24
|
|
@@ -30,26 +31,28 @@ module AirPlayer
|
|
30
31
|
end
|
31
32
|
|
32
33
|
private
|
33
|
-
def type(item)
|
34
|
-
if Dir.exists?(File.expand_path(item))
|
35
|
-
:local_dir
|
36
|
-
elsif Media.playable? item
|
37
|
-
:url
|
38
|
-
elsif item.match(/.+(xml|rss)$/)
|
39
|
-
:podcast
|
40
|
-
end
|
41
|
-
end
|
42
34
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
35
|
+
def type(item)
|
36
|
+
if Dir.exist?(File.expand_path(item))
|
37
|
+
:local_dir
|
38
|
+
elsif Media.playable?(item)
|
39
|
+
:url
|
40
|
+
elsif item.match(/.+(xml|rss)$/)
|
41
|
+
:podcast
|
47
42
|
end
|
43
|
+
end
|
48
44
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
45
|
+
def media_in_local(path)
|
46
|
+
Dir.entries(File.expand_path(path)).sort.map do |node|
|
47
|
+
Media.new(File.expand_path(node, path)) if Media.playable? node
|
48
|
+
end.compact
|
49
|
+
end
|
50
|
+
|
51
|
+
def media_in_podcast(path)
|
52
|
+
strict_parse = false
|
53
|
+
RSS::Parser.parse(path, strict_parse).items.map do |node|
|
54
|
+
Media.new(node.link)
|
53
55
|
end
|
56
|
+
end
|
54
57
|
end
|
55
58
|
end
|
data/lib/airplayer/version.rb
CHANGED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module AirPlayer
|
4
|
+
class YoutubeDl
|
5
|
+
|
6
|
+
attr_reader :path
|
7
|
+
|
8
|
+
def initialize(path = self.class.path || self.class.default_path)
|
9
|
+
@path = path
|
10
|
+
|
11
|
+
if @path
|
12
|
+
File.exist?(@path) or raise "youtube-dl could not be found at #{@path}"
|
13
|
+
end
|
14
|
+
|
15
|
+
@output = '2> /dev/null'.freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
def enabled?
|
19
|
+
@path && ! @path.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
EXTRACT_EXTRACTOR_AND_URL = %r{
|
23
|
+
(?<extractor>.+)\n # extractor name and new line
|
24
|
+
\s+(:?.+) # the url if the extractor supports it
|
25
|
+
}x
|
26
|
+
|
27
|
+
GENERIC_EXTRACTOR = 'generic'.freeze
|
28
|
+
|
29
|
+
def supports?(url)
|
30
|
+
extractors = list_extractors(url).scan(EXTRACT_EXTRACTOR_AND_URL).flatten
|
31
|
+
extractors.delete(GENERIC_EXTRACTOR)
|
32
|
+
! extractors.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
def list_extractors(*urls)
|
36
|
+
execute('--list-extractors', *urls)
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_url(url)
|
40
|
+
execute('--get-url', url)
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_filename(url)
|
44
|
+
execute('--get-filename', url)
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_title(url)
|
48
|
+
execute('--get-title', url)
|
49
|
+
end
|
50
|
+
|
51
|
+
def execute(*args)
|
52
|
+
return '' unless enabled?
|
53
|
+
escape = Shellwords.method(:escape)
|
54
|
+
parts = [ path, args.flat_map(&escape), @output ]
|
55
|
+
%x`#{parts.flatten.join(' ')}`.strip
|
56
|
+
end
|
57
|
+
|
58
|
+
class << self
|
59
|
+
attr_accessor :path
|
60
|
+
|
61
|
+
def enabled?
|
62
|
+
path && ! path.empty?
|
63
|
+
end
|
64
|
+
|
65
|
+
def default_path
|
66
|
+
@default_path ||= `which youtube-dl 2> /dev/null`.strip
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_url(uri)
|
70
|
+
if enabled?
|
71
|
+
new.get_url(uri)
|
72
|
+
else
|
73
|
+
uri
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def supports?(path)
|
78
|
+
if enabled?
|
79
|
+
new.supports?(path)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def filename(url)
|
84
|
+
if enabled?
|
85
|
+
new.get_filename(url)
|
86
|
+
else
|
87
|
+
File.basename(url)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_title(uri)
|
92
|
+
if enabled?
|
93
|
+
new.get_title(uri)
|
94
|
+
else
|
95
|
+
File.basename(uri)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: airplayer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomohiro TAIRA
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - '='
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 0.5.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: nio4r
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.1.0
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.1.0
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: reel
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -248,6 +262,20 @@ dependencies:
|
|
248
262
|
- - ">="
|
249
263
|
- !ruby/object:Gem::Version
|
250
264
|
version: '0'
|
265
|
+
- !ruby/object:Gem::Dependency
|
266
|
+
name: pry
|
267
|
+
requirement: !ruby/object:Gem::Requirement
|
268
|
+
requirements:
|
269
|
+
- - ">="
|
270
|
+
- !ruby/object:Gem::Version
|
271
|
+
version: '0'
|
272
|
+
type: :development
|
273
|
+
prerelease: false
|
274
|
+
version_requirements: !ruby/object:Gem::Requirement
|
275
|
+
requirements:
|
276
|
+
- - ">="
|
277
|
+
- !ruby/object:Gem::Version
|
278
|
+
version: '0'
|
251
279
|
description: Command-line AirPlay video client for Apple TV
|
252
280
|
email:
|
253
281
|
- tomohiro.t@gmail.com
|
@@ -267,7 +295,9 @@ files:
|
|
267
295
|
- README.md
|
268
296
|
- Rakefile
|
269
297
|
- airplayer.gemspec
|
270
|
-
- bin/
|
298
|
+
- bin/console
|
299
|
+
- bin/setup
|
300
|
+
- exe/airplayer
|
271
301
|
- lib/airplayer.rb
|
272
302
|
- lib/airplayer/app.rb
|
273
303
|
- lib/airplayer/controller.rb
|
@@ -275,14 +305,10 @@ files:
|
|
275
305
|
- lib/airplayer/media.rb
|
276
306
|
- lib/airplayer/playlist.rb
|
277
307
|
- lib/airplayer/version.rb
|
278
|
-
-
|
279
|
-
- spec/airplayer/controller_spec.rb
|
280
|
-
- spec/airplayer/device_spec.rb
|
281
|
-
- spec/airplayer/media_spec.rb
|
282
|
-
- spec/airplayer/playlist_spec.rb
|
283
|
-
- spec/spec_helper.rb
|
308
|
+
- lib/airplayer/youtube_dl.rb
|
284
309
|
homepage: https://github.com/Tomohiro/airplayer
|
285
|
-
licenses:
|
310
|
+
licenses:
|
311
|
+
- MIT
|
286
312
|
metadata: {}
|
287
313
|
post_install_message:
|
288
314
|
rdoc_options: []
|
@@ -292,7 +318,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
292
318
|
requirements:
|
293
319
|
- - ">="
|
294
320
|
- !ruby/object:Gem::Version
|
295
|
-
version:
|
321
|
+
version: 2.2.0
|
296
322
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
297
323
|
requirements:
|
298
324
|
- - ">="
|
@@ -300,14 +326,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
300
326
|
version: '0'
|
301
327
|
requirements: []
|
302
328
|
rubyforge_project:
|
303
|
-
rubygems_version: 2.
|
329
|
+
rubygems_version: 2.5.1
|
304
330
|
signing_key:
|
305
331
|
specification_version: 4
|
306
332
|
summary: Command-line AirPlay video client for Apple TV
|
307
|
-
test_files:
|
308
|
-
- spec/airplayer/app_spec.rb
|
309
|
-
- spec/airplayer/controller_spec.rb
|
310
|
-
- spec/airplayer/device_spec.rb
|
311
|
-
- spec/airplayer/media_spec.rb
|
312
|
-
- spec/airplayer/playlist_spec.rb
|
313
|
-
- spec/spec_helper.rb
|
333
|
+
test_files: []
|
data/spec/airplayer/app_spec.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module AirPlayer
|
4
|
-
describe App do
|
5
|
-
let (:airplayer) do
|
6
|
-
AirPlayer::App.new
|
7
|
-
end
|
8
|
-
|
9
|
-
describe '.new' do
|
10
|
-
it 'returns instance of Thor' do
|
11
|
-
expect(airplayer).to be_kind_of Thor
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
describe '.devices' do
|
16
|
-
before do
|
17
|
-
Airplay.devices << double_device
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'display devices' do
|
21
|
-
devices = capture(:stdout) { airplayer.devices }
|
22
|
-
expect(devices).to match double_device.name
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe '.version' do
|
27
|
-
it 'display version' do
|
28
|
-
version = capture(:stdout) { airplayer.version }
|
29
|
-
expect(version.chomp).to eq AirPlayer::VERSION
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module AirPlayer
|
4
|
-
describe Controller do
|
5
|
-
let (:controller) do
|
6
|
-
AirPlayer::Controller
|
7
|
-
end
|
8
|
-
|
9
|
-
before do
|
10
|
-
Airplay.devices << double_device
|
11
|
-
end
|
12
|
-
|
13
|
-
describe '.new' do
|
14
|
-
context 'with args' do
|
15
|
-
it 'returns instance of Controller' do
|
16
|
-
expect(controller.new(device: 0)).to be_kind_of Controller
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
context 'without args' do
|
21
|
-
it 'raise TypeError' do
|
22
|
-
expect{ controller.new }.to raise_error(TypeError)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
describe '.pause' do
|
28
|
-
context 'with not playing media' do
|
29
|
-
it 'do nothing' do
|
30
|
-
expect(controller.new(device: 0).pause).to be nil
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module AirPlayer
|
4
|
-
describe Device do
|
5
|
-
let (:device) do
|
6
|
-
AirPlayer::Device
|
7
|
-
end
|
8
|
-
|
9
|
-
before do
|
10
|
-
Airplay.devices << double_device
|
11
|
-
end
|
12
|
-
|
13
|
-
describe '#devices' do
|
14
|
-
it 'returns array' do
|
15
|
-
expect(device.devices).to be_kind_of Array
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
describe '#get' do
|
20
|
-
context 'with exist device number' do
|
21
|
-
it 'returns instance of device' do
|
22
|
-
expect(device.get(0)).to be_kind_of Airplay::Device
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context 'with not exist device number' do
|
27
|
-
it 'returns default device and display message' do
|
28
|
-
default_device = nil
|
29
|
-
message = capture(:stdout) { default_device = device.get(99) }
|
30
|
-
expect(default_device).to eq device.default
|
31
|
-
expect(message).to match('Device number 99 is not found')
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe '#exist?' do
|
37
|
-
context 'with exist device number' do
|
38
|
-
it 'returns true' do
|
39
|
-
expect(device.exist?(0)).to be true
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
context 'with not exist device number' do
|
44
|
-
it 'returns false' do
|
45
|
-
expect(device.exist?(99)).to be false
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
describe '#default' do
|
51
|
-
it 'returns first airplay device' do
|
52
|
-
expect(device.default).to eq Airplay.devices.first
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
require 'fakefs/spec_helpers'
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
module AirPlayer
|
6
|
-
describe Media do
|
7
|
-
include FakeFS::SpecHelpers
|
8
|
-
|
9
|
-
let (:media) do
|
10
|
-
AirPlayer::Media
|
11
|
-
end
|
12
|
-
|
13
|
-
describe '#playable?' do
|
14
|
-
context 'with supported mime types' do
|
15
|
-
it 'returns true' do
|
16
|
-
expect(media.playable?('007 SKYFALL.mp4')).to be true
|
17
|
-
expect(media.playable?('007 SKYFALL.ts')).to be true
|
18
|
-
expect(media.playable?('007 SKYFALL.m4v')).to be true
|
19
|
-
expect(media.playable?('007 SKYFALL.mov')).to be true
|
20
|
-
expect(media.playable?('007 SKYFALL.ts')).to be true
|
21
|
-
expect(media.playable?('マルチ☆バイト.mp4')).to be true
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'with unsupported mime types' do
|
26
|
-
it 'returns false' do
|
27
|
-
expect(media.playable?('007 SKYFALL.flv')).to be false
|
28
|
-
expect(media.playable?('007 SKYFALL.wmv')).to be false
|
29
|
-
expect(media.playable?('NOT_PLAYABLE_FILE')).to be false
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
describe '.file?' do
|
35
|
-
context 'with given local file' do
|
36
|
-
it 'returns true' do
|
37
|
-
FileUtils.touch('fake_movie.m4v')
|
38
|
-
expect(media.new('fake_movie.m4v').file?).to be true
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
describe '.url?' do
|
44
|
-
context 'with given URL' do
|
45
|
-
it 'returns true' do
|
46
|
-
expect(media.new('http://example.com/video.mp4').url?).to be true
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
require 'fakefs/spec_helpers'
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
module AirPlayer
|
5
|
-
describe Playlist do
|
6
|
-
let (:playlist) do
|
7
|
-
AirPlayer::Playlist.new
|
8
|
-
end
|
9
|
-
|
10
|
-
describe '.add' do
|
11
|
-
context 'with local directory' do
|
12
|
-
it 'returns media type is local file' do
|
13
|
-
FakeFS do
|
14
|
-
FileUtils.touch('01.m4v')
|
15
|
-
FileUtils.touch('02.m4v')
|
16
|
-
end
|
17
|
-
|
18
|
-
playlist.add('.')
|
19
|
-
playlist.entries do |media|
|
20
|
-
expect(media.file?).to be true
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'with URL' do
|
26
|
-
it 'returns media type is url' do
|
27
|
-
playlist.add('http://example.com/video.mp4')
|
28
|
-
expect(playlist.first.path).to match 'http'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context 'with multiple files' do
|
33
|
-
it 'have multiple files' do
|
34
|
-
expect(playlist.add('video.mp4').size).to eq 1
|
35
|
-
expect(playlist.add('video.m4v').size).to eq 2
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
context 'with podcast RSS' do
|
40
|
-
it 'returns media instances' do
|
41
|
-
playlist.add('http://rss.cnn.com/services/podcasting/cnnnewsroom/rss.xml')
|
42
|
-
playlist.entries do |media|
|
43
|
-
expect(media).to be_kind_of AirPlayer::Media
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
context 'with local file' do
|
49
|
-
it 'returns media instances' do
|
50
|
-
playlist.add('video.mp4')
|
51
|
-
playlist.entries do |media|
|
52
|
-
expect(media).to be_kind_of AirPlayer::Media
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'coveralls'
|
2
|
-
Coveralls.wear!
|
3
|
-
|
4
|
-
require 'stringio'
|
5
|
-
RSpec.configure do |c|
|
6
|
-
def capture(stream)
|
7
|
-
begin
|
8
|
-
stream = stream.to_s
|
9
|
-
eval "$#{stream} = StringIO.new"
|
10
|
-
yield
|
11
|
-
result = eval("$#{stream}").string
|
12
|
-
ensure
|
13
|
-
eval "$#{stream} = #{stream.upcase}"
|
14
|
-
end
|
15
|
-
result
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
|
20
|
-
require 'airplay'
|
21
|
-
Airplay.configure do |c|
|
22
|
-
c.autodiscover = false
|
23
|
-
end
|
24
|
-
|
25
|
-
# https://github.com/elcuervo/airplay/blob/master/lib/airplay/device.rb
|
26
|
-
# https://github.com/elcuervo/airplay/blob/master/lib/airplay/device/info.rb
|
27
|
-
def double_device
|
28
|
-
device = Airplay::Device.new(name: 'Double Device', address: 'double.appletv.local:7000')
|
29
|
-
allow(device).to receive('ip').and_return('127.0.0.1')
|
30
|
-
allow(device).to receive('server_info').and_return({
|
31
|
-
'model' => 'AppleTV2,1',
|
32
|
-
'os_version' => '11B554a',
|
33
|
-
'width' => '1280',
|
34
|
-
'height' => '720'
|
35
|
-
})
|
36
|
-
device
|
37
|
-
end
|
38
|
-
|
39
|
-
require 'airplayer'
|