viddl-rb 0.65 → 0.66

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README.md +11 -2
  2. data/bin/viddl-rb +1 -0
  3. data/plugins/vimeo.rb +38 -24
  4. data/plugins/youtube.rb +27 -7
  5. metadata +22 -8
data/README.md CHANGED
@@ -18,16 +18,25 @@ Download a video and extract the audio:
18
18
 
19
19
  In both cases we'll name the output file according to the video title.
20
20
 
21
- Download all videos on a Youtube playlist:
21
+ __Youtube plugin specifics:__
22
+
23
+ Download all videos on a playlist:
22
24
  viddl-rb http://www.youtube.com/playlist?list=PL7E8DA0A515924126
23
25
 
24
- Download all videos from a Youtube user:
26
+ Download all videos from a user:
25
27
  viddl-rb http://www.youtube.com/user/tedtalksdirector
26
28
 
29
+ Filter videos to download from a user/playlist:
30
+ viddl-rb http://www.youtube.com/user/tedtalksdirector --filter=internet/i
31
+
32
+ The --filter argument accepts a regular expression and will only download videos where the title matches the regex.
33
+ The /i option does a case-insensitive search.
34
+
27
35
  __Requirements:__
28
36
 
29
37
  * curl/wget or the [progress bar](http://github.com/nex3/ruby-progressbar/) gem
30
38
  * [Nokogiri](http://nokogiri.org/)
39
+ * [Mechanize](http://mechanize.rubyforge.org/)
31
40
  * ffmpeg if you want to extract audio tracks from the videos
32
41
 
33
42
 
@@ -3,6 +3,7 @@ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'helper')
3
3
 
4
4
  require "rubygems"
5
5
  require "nokogiri"
6
+ require "mechanize"
6
7
  require "cgi"
7
8
  require "open-uri"
8
9
  require "open3"
@@ -1,25 +1,39 @@
1
+
1
2
  class Vimeo < PluginBase
2
- #this will be called by the main app to check whether this plugin is responsible for the url passed
3
- def self.matches_provider?(url)
4
- url.include?("vimeo.com")
5
- end
6
-
7
- def self.get_urls_and_filenames(url)
8
- #the vimeo ID consists of 7 decimal numbers in the URL
9
- vimeo_id = url[/\d{7,8}/]
10
- doc = Nokogiri::XML(open("http://www.vimeo.com/moogaloop/load/clip:#{vimeo_id}"))
11
- title = doc.at("//video/caption").inner_text
12
- puts "[VIMEO] Title: #{title}"
13
- request_signature = doc.at("//request_signature").inner_text
14
- request_signature_expires = doc.at("//request_signature_expires").inner_text
15
-
16
-
17
- puts "[VIMEO] Request Signature: #{request_signature} expires: #{request_signature_expires}"
18
-
19
- download_url = "http://www.vimeo.com/moogaloop/play/clip:#{vimeo_id}/#{request_signature}/#{request_signature_expires}/?q=hd"
20
- #todo: put the filename cleaning stuff into a seperate helper
21
- file_name = title.delete("\"'").gsub(/[^0-9A-Za-z]/, '_') + ".flv"
22
- puts "downloading to " + file_name
23
- [{:url => download_url, :name => file_name}]
24
- end
25
- end
3
+ #this will be called by the main app to check whether this plugin is responsible for the url passed
4
+ def self.matches_provider?(url)
5
+ url.include?("vimeo.com")
6
+ end
7
+
8
+ def self.get_urls_and_filenames(url)
9
+ #the vimeo ID consists of 7 decimal numbers in the URL
10
+ vimeo_id = url[/\d{7,8}/]
11
+
12
+ agent = Mechanize.new #use Mechanize for the automatic cookie handeling
13
+ agent.redirect_ok = false #don't follow redirects so we do not download the video when we get it's url
14
+
15
+ video_page = agent.get("http://vimeo.com/#{vimeo_id}")
16
+ page_html = video_page.root.inner_html
17
+
18
+ title = page_html[/<meta\s+property="og:title"\s+content="(.+?)"/, 1]
19
+ puts "[VIMEO] Title: #{title}"
20
+
21
+ #the timestamp and sig info is in the embedded player javascript in the video page
22
+ timestamp = page_html[/"timestamp":(\d+),/, 1]
23
+ signature = page_html[/"signature":"([\d\w]+)",/, 1]
24
+
25
+ # The quality and codecs are listed in order of preference in the url. If HD is not availabe SD will be download for example.
26
+ redirect_url = "http://player.vimeo.com/play_redirect?clip_id=#{vimeo_id}&sig=#{signature}&time=#{timestamp}&quality=hd,sd&codecs=H264,VP8,VP6"
27
+
28
+ #the download url is the value of the location (redirect) header
29
+ download_url = agent.get(redirect_url).header["location"]
30
+
31
+ file_name = make_filename(title)
32
+
33
+ [{:url => download_url, :name => file_name}]
34
+ end
35
+
36
+ def self.make_filename(title)
37
+ title.delete("\"'").gsub(/[^\d\w]/, '_') + ".mp4"
38
+ end
39
+ end
@@ -7,23 +7,43 @@ class Youtube < PluginBase
7
7
 
8
8
  #get all videos and return their urls in an array
9
9
  def self.get_video_urls(feed_url)
10
- urls = []
10
+ puts "[YOUTUBE] Retrieving videos..."
11
+ urls_titles = Hash.new
11
12
  result_feed = Nokogiri::HTML(open(feed_url))
12
- urls << grab_urls(result_feed)
13
+ urls_titles.merge!(grab_ut(result_feed))
13
14
 
14
15
  #as long as the feed has a next link we follow it and add the resulting video urls
15
16
  loop do
16
17
  next_link = result_feed.search("//feed/link[@rel='next']").first
17
18
  break if next_link.nil?
18
19
  result_feed = Nokogiri::HTML(open(next_link["href"]))
19
- urls << grab_urls(result_feed)
20
+ urls_titles.merge!(grab_ut(result_feed))
21
+ end
22
+
23
+ self.filter_urls(urls_titles)
24
+ end
25
+
26
+ #returns only the urls that match the --filter argument regex (if present)
27
+ def self.filter_urls(url_hash)
28
+ #get the --filter arg or "" if it is not present (because nil would break the next line)
29
+ filter = ARGV.find( proc {""} ) { |arg| arg =~ /--filter=/ }
30
+ regex = filter[/--filter=(.+?)(?:\/|$)/, 1]
31
+ if regex
32
+ puts "[YOUTUBE] Using filter: #{regex}"
33
+ ignore_case = filter.include?("/i")
34
+ filtered = url_hash.select { |url, title| title =~ Regexp.new(regex, ignore_case) }
35
+ filtered.keys
36
+ else
37
+ url_hash.keys
20
38
  end
21
- urls.flatten
22
39
  end
23
40
 
24
- #extract all video urls form a feed an return in an array
25
- def self.grab_urls(feed)
26
- feed.search("//entry/link[@rel='alternate']").map { |link| link["href"] }
41
+ #extract all video urls and their titles from a feed and return in a hash
42
+ def self.grab_ut(feed)
43
+ feed.remove_namespaces! #so that we can get to the titles easily
44
+ urls = feed.search("//entry/link[@rel='alternate']").map { |link| link["href"] }
45
+ titles = feed.search("//entry/group/title").map { |title| title.text }
46
+ Hash[urls.zip(titles)] #hash like this: url => title
27
47
  end
28
48
 
29
49
  def self.parse_playlist(url)
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: 137
4
+ hash: 143
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 65
9
- version: "0.65"
8
+ - 66
9
+ version: "0.66"
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: 2012-05-02 00:00:00 Z
17
+ date: 2012-06-03 00:00:00 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: nokogiri
@@ -31,7 +31,7 @@ dependencies:
31
31
  type: :runtime
32
32
  version_requirements: *id001
33
33
  - !ruby/object:Gem::Dependency
34
- name: rake
34
+ name: mechanize
35
35
  prerelease: false
36
36
  requirement: &id002 !ruby/object:Gem::Requirement
37
37
  none: false
@@ -42,10 +42,10 @@ dependencies:
42
42
  segments:
43
43
  - 0
44
44
  version: "0"
45
- type: :development
45
+ type: :runtime
46
46
  version_requirements: *id002
47
47
  - !ruby/object:Gem::Dependency
48
- name: minitest
48
+ name: rake
49
49
  prerelease: false
50
50
  requirement: &id003 !ruby/object:Gem::Requirement
51
51
  none: false
@@ -59,7 +59,7 @@ dependencies:
59
59
  type: :development
60
60
  version_requirements: *id003
61
61
  - !ruby/object:Gem::Dependency
62
- name: rest-client
62
+ name: minitest
63
63
  prerelease: false
64
64
  requirement: &id004 !ruby/object:Gem::Requirement
65
65
  none: false
@@ -72,6 +72,20 @@ dependencies:
72
72
  version: "0"
73
73
  type: :development
74
74
  version_requirements: *id004
75
+ - !ruby/object:Gem::Dependency
76
+ name: rest-client
77
+ prerelease: false
78
+ requirement: &id005 !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ hash: 3
84
+ segments:
85
+ - 0
86
+ version: "0"
87
+ type: :development
88
+ version_requirements: *id005
75
89
  description: An extendable commandline video downloader for flash video sites. Includes plugins for vimeo, youtube and megavideo
76
90
  email: mail@marc-seeger.de
77
91
  executables: