viddl-rb 0.84 → 0.85

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.
@@ -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
-