viddl-rb 0.84 → 0.85

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- viddl-rb (0.82)
4
+ viddl-rb (0.84)
5
5
  mechanize
6
+ multi_json
6
7
  nokogiri (~> 1.5.0)
7
8
  progressbar
8
9
 
@@ -24,6 +25,7 @@ GEM
24
25
  webrobots (>= 0.0.9, < 0.2)
25
26
  mime-types (1.24)
26
27
  minitest (5.0.6)
28
+ multi_json (1.8.0)
27
29
  net-http-digest_auth (1.4)
28
30
  net-http-persistent (2.9)
29
31
  nokogiri (1.5.10)
data/README.md CHANGED
@@ -115,12 +115,12 @@ __Requirements:__
115
115
  * [Mechanize](http://mechanize.rubyforge.org/)
116
116
  * ffmpeg if you want to extract audio tracks from the videos
117
117
 
118
- __Co Maintainer:__
118
+ __Co Maintainers:__
119
119
  * [kl](https://github.com/kl): Windows support (who knew!), bug fixes, veoh plugin, metacafe plugin, refactoring it into a library, ...
120
+ * [farleyknight](https://github.com/farleyknight): Various small fixes and improvements
120
121
 
121
122
  __Contributors:__
122
123
  * [divout](https://github.com/divout) aka Ivan K: blip.tv plugin, bugfixes
123
124
  * Sniper: bugfixes
124
125
  * [Serabe](https://github.com/Serabe) aka Sergio Arbeo: packaging viddl as a binary
125
126
  * [laserlemon](https://github.com/laserlemon): Adding gemnasium images to readme
126
- * [farleyknight](https://github.com/farleyknight): Various small fixes and improvements
@@ -4,6 +4,7 @@ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'helper')
4
4
  require "rubygems"
5
5
  require "net/http"
6
6
  require "nokogiri"
7
+ require "multi_json"
7
8
  require "mechanize"
8
9
  require "cgi"
9
10
  require "open-uri"
@@ -0,0 +1,37 @@
1
+ require 'multi_json'
2
+
3
+ class ArtePlusSeven < PluginBase
4
+ # this will be called by the main app to check whether this plugin is responsible for the url passed
5
+ def self.matches_provider?(url)
6
+ url.include?("arte.tv")
7
+ end
8
+
9
+ # return the url for original video file and title
10
+ def self.get_urls_and_filenames(url, options = {})
11
+ id = self.to_id(url)
12
+ country = self.extract_country(url)
13
+ json_url = "http://arte.tv/papi/tvguide/videos/stream/player/#{country}/#{id}_PLUS7-#{country.upcase}/ALL/ALL.json"
14
+ doc = MultiJson.load(open(json_url))['videoJsonPlayer']
15
+ # This can be improved a lot,
16
+ # check the results on http://floriancrouzat.net/arte/
17
+ first_http_key = doc['VSR'].keys.find{|k| k.start_with?('HTTP')}
18
+ download_url = doc['VSR'][first_http_key]['url']
19
+ title = doc['VTI']
20
+ file_name = PluginBase.make_filename_safe(title) + ".mp4"
21
+ [{:url => download_url, :name => file_name}]
22
+ end
23
+
24
+ def self.to_id(url)
25
+ url[/([\d-]+)/,1]
26
+ end
27
+
28
+ def self.extract_country(url)
29
+ url_country = url[/\/guide\/(..)\//,1]
30
+ mapping = {
31
+ 'de' => 'D',
32
+ 'fr' => 'F'
33
+ }
34
+ mapping[url_country]
35
+
36
+ end
37
+ end
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
 
2
3
  class Youtube < PluginBase
3
4
 
@@ -34,9 +35,9 @@ class Youtube < PluginBase
34
35
  return_vals = []
35
36
 
36
37
  if playlist_urls = parser.get_playlist_urls(url, filter)
37
- playlist_urls.each { |url| return_vals << grab_single_url_filename(url) }
38
+ playlist_urls.each { |url| return_vals << grab_single_url_filename(url, options) }
38
39
  else
39
- return_vals << grab_single_url_filename(url)
40
+ return_vals << grab_single_url_filename(url, options)
40
41
  end
41
42
 
42
43
  clean_return_values(return_vals)
@@ -52,128 +53,150 @@ class Youtube < PluginBase
52
53
  end
53
54
  end
54
55
 
55
- def self.grab_single_url_filename(url)
56
- grab_url_embeddable(url) || grab_url_non_embeddable(url)
56
+ def self.grab_single_url_filename(url, options)
57
+ UrlGrabber.new(url, self, options).process
57
58
  end
58
59
 
59
- def self.grab_url_embeddable(url)
60
- video_info = get_video_info(url)
61
- video_params = extract_video_parameters(video_info)
62
- unless video_params[:embeddable]
63
- notify("VIDEO IS NOT EMBEDDABLE")
64
- return false
60
+ class UrlGrabber
61
+ attr_accessor :url, :options, :plugin, :quality
62
+
63
+ def initialize(url, plugin, options)
64
+ @url = url
65
+ @plugin = plugin
66
+ @options = options
67
+ @quality = options[:quality]
65
68
  end
66
69
 
67
- urls_formats = extract_urls_formats(video_info)
68
- selected_format = choose_format(urls_formats)
69
- title = video_params[:title]
70
- file_name = PluginBase.make_filename_safe(title) + "." + VIDEO_FORMATS[selected_format][:extension]
70
+ def process
71
+ grab_url_embeddable(url) || grab_url_non_embeddable(url)
72
+ end
71
73
 
72
- {:url => urls_formats[selected_format], :name => file_name}
73
- end
74
+ # VEVO video: http://www.youtube.com/watch?v=A_J7kEhY9sM
75
+ # Non-VEVO video: http://www.youtube.com/watch?v=WkkC9cK8Hz0
74
76
 
75
- def self.grab_url_non_embeddable(url)
76
- video_info = open(url).read
77
- stream_map = video_info[/url_encoded_fmt_stream_map\" *: *\"([^\"]+)\"/,1]
78
- urls_formats = parse_stream_map(url_decode(stream_map))
79
- selected_format = choose_format(urls_formats)
80
- title = video_info[/<meta name="title" content="([^"]*)">/, 1]
81
- file_name = PluginBase.make_filename_safe(title) + "." + VIDEO_FORMATS[selected_format][:extension]
82
-
83
- # cleaning
84
- clean_url = urls_formats[selected_format].gsub(/\\u0026[^&]*/, "").split(',type=video').first
85
- {:url => clean_url, :name => file_name}
86
- end
77
+ def grab_url_embeddable(url)
78
+ video_info = get_video_info(url)
79
+ video_params = extract_video_parameters(video_info)
87
80
 
88
- def self.get_video_info(url)
89
- id = extract_video_id(url)
90
- request_url = VIDEO_INFO_URL + id + VIDEO_INFO_PARMS
91
- open(request_url).read
92
- end
81
+ unless video_params[:embeddable]
82
+ Youtube.notify("VIDEO IS NOT EMBEDDABLE")
83
+ return false
84
+ end
93
85
 
94
- def self.extract_video_id(url)
95
- # the youtube video ID looks like this: [...]v=abc5a5_afe5agae6g&[...], we only want the ID (the \w in the brackets)
96
- # addition: might also look like this /v/abc5-a5afe5agae6g
97
- # alternative: video_id = url[/v[\/=]([\w-]*)&?/, 1]
98
- url = open(url).base_uri.to_s if url.include?("youtu.be")
99
- video_id = url[/(v|embed)[=\/]([^\/\?\&]*)/, 2]
86
+ urls_formats = extract_urls_formats(video_info)
87
+ selected_format = choose_format(urls_formats)
88
+ title = video_params[:title]
89
+ file_name = PluginBase.make_filename_safe(title) + "." + VIDEO_FORMATS[selected_format][:extension]
100
90
 
101
- if video_id
102
- notify("ID FOUND: #{video_id}")
103
- video_id
104
- else
105
- download_error("No video id found.")
91
+ {:url => urls_formats[selected_format], :name => file_name}
92
+ end
93
+
94
+ def grab_url_non_embeddable(url)
95
+ video_info = open(url).read
96
+ stream_map = video_info[/url_encoded_fmt_stream_map\" *: *\"([^\"]+)\"/,1]
97
+ urls_formats = parse_stream_map(url_decode(stream_map))
98
+ selected_format = choose_format(urls_formats)
99
+ title = video_info[/<meta name="title" content="([^"]*)">/, 1]
100
+ file_name = PluginBase.make_filename_safe(title) + "." + VIDEO_FORMATS[selected_format][:extension]
101
+
102
+ # cleaning
103
+ clean_url = urls_formats[selected_format].gsub(/\\u0026[^&]*/, "").split(',type=video').first
104
+ {:url => clean_url, :name => file_name}
106
105
  end
107
- end
108
106
 
109
- def self.extract_video_parameters(video_info)
110
- decoded = url_decode(video_info)
107
+ def get_video_info(url)
108
+ id = extract_video_id(url)
109
+ request_url = VIDEO_INFO_URL + id + VIDEO_INFO_PARMS
110
+ open(request_url).read
111
+ end
111
112
 
112
- {:title => decoded[/title=(.+?)(?:&|$)/, 1],
113
- :length_sec => decoded[/length_seconds=(.+?)(?:&|$)/, 1],
114
- :author => decoded[/author=(.+?)(?:&|$)/, 1],
115
- :embeddable => !decoded.include?("status=fail")}
116
- end
113
+ def extract_video_parameters(video_info)
114
+ video_params = CGI.parse(url_decode(video_info))
117
115
 
118
- def self.extract_urls_formats(video_info)
119
- stream_map = video_info[/url_encoded_fmt_stream_map=(.+?)(?:&|$)/, 1]
120
- parse_stream_map(stream_map)
121
- end
116
+ {
117
+ :title => video_params["title"].first,
118
+ :length_sec => video_params["length_seconds"].first,
119
+ :author => video_params["author"].first,
120
+ :embeddable => (video_params["status"].first != "fail")
121
+ }
122
+ end
122
123
 
123
- def self.parse_stream_map(stream_map)
124
- urls = extract_download_urls(stream_map)
125
- formats_urls = {}
124
+ def extract_video_id(url)
125
+ # the youtube video ID looks like this: [...]v=abc5a5_afe5agae6g&[...], we only want the ID (the \w in the brackets)
126
+ # addition: might also look like this /v/abc5-a5afe5agae6g
127
+ # alternative: video_id = url[/v[\/=]([\w-]*)&?/, 1]
128
+ url = open(url).base_uri.to_s if url.include?("youtu.be")
129
+ video_id = url[/(v|embed)[=\/]([^\/\?\&]*)/, 2]
126
130
 
127
- urls.each do |url|
128
- format = url[/itag=(\d+)/, 1]
129
- formats_urls[format] = url
131
+ if video_id
132
+ Youtube.notify("ID FOUND: #{video_id}")
133
+ video_id
134
+ else
135
+ Youtube.download_error("No video id found.")
136
+ end
130
137
  end
131
138
 
132
- formats_urls
133
- end
139
+ def extract_urls_formats(video_info)
140
+ stream_map = video_info[/url_encoded_fmt_stream_map=(.+?)(?:&|$)/, 1]
141
+ parse_stream_map(stream_map)
142
+ end
134
143
 
135
- def self.extract_download_urls(stream_map)
136
- entries = stream_map.split("%2C")
137
- decoded = entries.map { |entry| url_decode(entry) }
144
+ def choose_format(urls_formats)
145
+ available_formats = urls_formats.keys
146
+
147
+ if @quality #if the user specified a format
148
+ ext = @quality[:extension]
149
+ res = @quality[:resolution]
150
+ #gets a nested array with all the formats of the same res as the user wanted
151
+ requested = VIDEO_FORMATS.select { |id, format| format[:name].include?(res) }.to_a
152
+
153
+ if requested.empty?
154
+ Youtube.notify "Requested format \"#{res}:#{ext}\" not found. Downloading default format."
155
+ get_default_format(available_formats)
156
+ else
157
+ pick = requested.find { |format| format[1][:extension] == ext } # get requsted extension if possible
158
+ pick ? pick.first : get_default_format(requested.map { |req| req.first }) # else return the default format
159
+ end
160
+ else
161
+ get_default_format(available_formats)
162
+ end
163
+ end
138
164
 
139
- decoded.map do |entry|
140
- url = entry[/url=(.*?itag=.+?)(?:itag=|;|$)/, 1]
141
- sig = entry[/sig=(.+?)(?:&|$)/, 1]
165
+ def parse_stream_map(stream_map)
166
+ urls = extract_download_urls(stream_map)
167
+ formats_urls = {}
142
168
 
143
- url + "&signature=#{sig}"
169
+ urls.each do |url|
170
+ format = url[/itag=(\d+)/, 1]
171
+ formats_urls[format] = url
172
+ end
173
+
174
+ formats_urls
144
175
  end
145
- end
146
176
 
147
- def self.choose_format(urls_formats)
148
- available_formats = urls_formats.keys
177
+ def extract_download_urls(stream_map)
178
+ entries = stream_map.split("%2C")
179
+ decoded = entries.map { |entry| url_decode(entry) }
149
180
 
150
- if @quality #if the user specified a format
151
- ext = @quality[:extension]
152
- res = @quality[:resolution]
153
- #gets a nested array with all the formats of the same res as the user wanted
154
- requested = VIDEO_FORMATS.select { |id, format| format[:name].include?(res) }.to_a
181
+ decoded.map do |entry|
182
+ url = entry[/url=(.*?itag=.+?)(?:itag=|;|$)/, 1]
183
+ sig = entry[/sig=(.+?)(?:&|$)/, 1]
155
184
 
156
- if requested.empty?
157
- notify "Requested format \"#{res}:#{ext}\" not found. Downloading default format."
158
- get_default_format(available_formats)
159
- else
160
- pick = requested.find { |format| format[1][:extension] == ext } # get requsted extension if possible
161
- pick ? pick.first : get_default_format(requested.map { |req| req.first }) # else return the default format
185
+ url + "&signature=#{sig}"
162
186
  end
163
- else
164
- get_default_format(available_formats)
165
187
  end
166
- end
167
188
 
168
- def self.get_default_format(available)
169
- DEFAULT_FORMAT_ORDER.find { |default| available.include?(default) }
170
- end
189
+ def get_default_format(available)
190
+ DEFAULT_FORMAT_ORDER.find { |default| available.include?(default) }
191
+ end
171
192
 
172
- def self.url_decode(text)
173
- while text != (decoded = CGI::unescape(text)) do
174
- text = decoded
193
+ def url_decode(text)
194
+ while text != (decoded = CGI::unescape(text)) do
195
+ text = decoded
196
+ end
197
+ text
175
198
  end
176
- text
199
+
177
200
  end
178
201
 
179
202
  def self.notify(message)
@@ -211,24 +234,24 @@ class Youtube < PluginBase
211
234
  #http://www.youtube.com/watch?v=Tk78sr5JMIU&videos=jKY836_WMhE
212
235
 
213
236
  playlist_ID = url[/(?:list=PL|p=)(.+?)(?:&|\/|$)/, 1]
214
- notify "Playlist ID: #{playlist_ID}"
237
+ Youtube.notify "Playlist ID: #{playlist_ID}"
215
238
  feed_url = PLAYLIST_FEED % playlist_ID
216
239
  url_array = get_video_urls(feed_url)
217
- notify "#{url_array.size} links found!"
240
+ Youtube.notify "#{url_array.size} links found!"
218
241
  url_array
219
242
  end
220
243
 
221
244
  def parse_user(username)
222
- notify "User: #{username}"
245
+ Youtube.notify "User: #{username}"
223
246
  feed_url = USER_FEED % username
224
247
  url_array = get_video_urls(feed_url)
225
- notify "#{url_array.size} links found!"
248
+ Youtube.notify "#{url_array.size} links found!"
226
249
  url_array
227
250
  end
228
251
 
229
252
  #get all videos and return their urls in an array
230
253
  def get_video_urls(feed_url)
231
- notify "Retrieving videos..."
254
+ Youtube.notify "Retrieving videos..."
232
255
  urls_titles = {}
233
256
  result_feed = Nokogiri::XML(open(feed_url))
234
257
  urls_titles.merge!(grab_urls_and_titles(result_feed))
@@ -255,16 +278,12 @@ class Youtube < PluginBase
255
278
  #returns only the urls that match the --filter argument regex (if present)
256
279
  def filter_urls(url_hash)
257
280
  if @filter
258
- notify "Using filter: #{@filter}"
281
+ Youtube.notify "Using filter: #{@filter}"
259
282
  filtered = url_hash.select { |url, title| title =~ @filter }
260
283
  filtered.keys
261
284
  else
262
285
  url_hash.keys
263
286
  end
264
287
  end
265
-
266
- def notify(message)
267
- Youtube.notify(message)
268
- end
269
288
  end
270
289
  end
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: viddl-rb
3
3
  version: !ruby/object:Gem::Version
4
- hash: 163
4
+ hash: 161
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 84
9
- version: "0.84"
8
+ - 85
9
+ version: "0.85"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Marc Seeger
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2013-09-02 00:00:00 Z
17
+ date: 2013-10-05 00:00:00 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: nokogiri
@@ -61,7 +61,7 @@ dependencies:
61
61
  type: :runtime
62
62
  version_requirements: *id003
63
63
  - !ruby/object:Gem::Dependency
64
- name: rake
64
+ name: multi_json
65
65
  prerelease: false
66
66
  requirement: &id004 !ruby/object:Gem::Requirement
67
67
  none: false
@@ -72,10 +72,10 @@ dependencies:
72
72
  segments:
73
73
  - 0
74
74
  version: "0"
75
- type: :development
75
+ type: :runtime
76
76
  version_requirements: *id004
77
77
  - !ruby/object:Gem::Dependency
78
- name: rest-client
78
+ name: rake
79
79
  prerelease: false
80
80
  requirement: &id005 !ruby/object:Gem::Requirement
81
81
  none: false
@@ -89,7 +89,7 @@ dependencies:
89
89
  type: :development
90
90
  version_requirements: *id005
91
91
  - !ruby/object:Gem::Dependency
92
- name: minitest
92
+ name: rest-client
93
93
  prerelease: false
94
94
  requirement: &id006 !ruby/object:Gem::Requirement
95
95
  none: false
@@ -102,6 +102,20 @@ dependencies:
102
102
  version: "0"
103
103
  type: :development
104
104
  version_requirements: *id006
105
+ - !ruby/object:Gem::Dependency
106
+ name: minitest
107
+ prerelease: false
108
+ requirement: &id007 !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ type: :development
118
+ version_requirements: *id007
105
119
  description: An extendable commandline video downloader for flash video sites. Includes plugins for vimeo, youtube, dailymotion and more
106
120
  email: mail@marc-seeger.de
107
121
  executables:
@@ -120,6 +134,7 @@ files:
120
134
  - helper/download-helper.rb
121
135
  - helper/plugin-helper.rb
122
136
  - helper/utility-helper.rb
137
+ - plugins/arte_plus_seven.rb
123
138
  - plugins/blip.rb
124
139
  - plugins/dailymotion.rb
125
140
  - plugins/metacafe.rb
@@ -131,7 +146,6 @@ files:
131
146
  - Gemfile.lock
132
147
  - Rakefile
133
148
  - README.md
134
- - TODO.txt
135
149
  homepage: https://github.com/rb2k/viddl-rb
136
150
  licenses: []
137
151
 
data/TODO.txt DELETED
@@ -1,3 +0,0 @@
1
- * wrap all classes used by the lib in a module (for namespace reasons)
2
- * add save_file method to library
3
-