viddl-rb 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.txt ADDED
@@ -0,0 +1,12 @@
1
+ === 0.4
2
+ * New Features
3
+ * Use wget or curl (if present)
4
+
5
+ * Bugfixes
6
+ * Fixed Vimeo Plugin
7
+
8
+ === 0.3
9
+ * New Features
10
+
11
+ * added megavideo plugin
12
+
data/README.txt ADDED
@@ -0,0 +1,13 @@
1
+ = viddl-rb
2
+
3
+ * http://github.com/rb2k/viddl-rb
4
+
5
+ == Gems needed so far:
6
+
7
+ Progressbar
8
+ gem install progressbar
9
+ homepage: http://github.com/nex3/ruby-progressbar/
10
+
11
+ Nokogiri
12
+ gem install nokogiri
13
+ homepage: http://nokogiri.org/
data/bin/viddl-rb ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'helper')
3
+
4
+ require "download-helper.rb"
5
+ require "plugin-helper.rb"
6
+
7
+
8
+ if ARGV.first.nil?
9
+ puts "Usage: viddl-rb [URL]!"
10
+ exit
11
+ end
12
+
13
+ puts "Loading Plugins"
14
+ Dir[File.join(File.dirname(__FILE__),"../plugins/*.rb")].each do |plugin|
15
+ load plugin
16
+ end
17
+
18
+ puts "Plugins loaded: #{PluginBase.registered_plugins.inspect}"
19
+
20
+
21
+ url = ARGV.first
22
+ puts "Analyzing URL: #{url}"
23
+ PluginBase.registered_plugins.each do |plugin|
24
+ if plugin.matches_provider?(url)
25
+ puts "#{plugin}: true"
26
+ begin
27
+ download_queue = plugin.get_urls_and_filenames(url)
28
+ rescue StandardError => e
29
+ puts "Error while running the #{plugin.name.inspect} plugin. Maybe it has to be updated?"
30
+ exit
31
+ end
32
+ download_queue.each do |url_name|
33
+ result = DownloadHelper.save_file(url_name[:url], url_name[:name])
34
+ if result
35
+ puts "Download for #{url_name[:name]} successful."
36
+ else
37
+ puts "Download for #{url_name[:name]} failed."
38
+ end
39
+ end
40
+ exit
41
+ else
42
+ puts "#{plugin}: false"
43
+ end
44
+ end
45
+
46
+ puts "No plugin seems to feel responsible for this URL."
@@ -0,0 +1,44 @@
1
+ class DownloadHelper
2
+ #usually not called directly
3
+ def self.fetch_file(uri)
4
+
5
+ begin
6
+ require "progressbar" #http://github.com/nex3/ruby-progressbar
7
+ rescue LoadError
8
+ puts "ERROR: You don't seem to have curl or wget on your system. In this case you'll need to install the 'progressbar' gem."
9
+ exit
10
+ end
11
+ progress_bar = nil
12
+ open(uri, :proxy => nil,
13
+ :content_length_proc => lambda { |length|
14
+ if length && 0 < length
15
+ progress_bar = ProgressBar.new(uri.to_s, length)
16
+ end
17
+ },
18
+ :progress_proc => lambda { |progress|
19
+ progress_bar.set(progress) if progress_bar
20
+ }) {|file| return file.read}
21
+ end
22
+
23
+ #simple helper that will save a file from the web and save it with a progress bar
24
+ def self.save_file(file_uri, file_name)
25
+ unescaped_uri = CGI::unescape(file_uri)
26
+ result = false
27
+ if `which wget`.include?("wget")
28
+ puts "using wget"
29
+ IO.popen("wget \"#{unescaped_uri}\" -O #{file_name}", "r") { |pipe| pipe.each {|line| print line}}
30
+ result = ($?.exitstatus == 0)
31
+ elsif `which curl`.include?("curl")
32
+ puts "using curl"
33
+ #-L means: follow redirects, We set an agent because Vimeo seems to want one
34
+ IO.popen("curl -A 'Mozilla/2.02 (OS/2; U)' -L \"#{unescaped_uri}\" -o #{file_name}", "r") { |pipe| pipe.each {|line| print line }}
35
+ result = ($?.exitstatus == 0)
36
+ else
37
+ open(file_name, 'wb') { |file|
38
+ file.write(fetch_file(unescaped_uri)); puts
39
+ }
40
+ result = true
41
+ end
42
+ result
43
+ end
44
+ end
@@ -0,0 +1,10 @@
1
+ class PluginBase
2
+ #some static stuff
3
+ class << self; attr_reader :registered_plugins end
4
+ @registered_plugins = []
5
+
6
+ #if you inherit from this class, the child gets added to the "registered plugins" array
7
+ def self.inherited(child)
8
+ PluginBase.registered_plugins << child
9
+ end
10
+ end
@@ -0,0 +1,100 @@
1
+ class Megavideo < PluginBase
2
+ require "cgi"
3
+ require "open-uri"
4
+ require "nokogiri"
5
+
6
+ #this will be called by the main app to check weather this plugin is responsible for the url passed
7
+ def self.matches_provider?(url)
8
+ url.include?("megavideo.com")
9
+ end
10
+
11
+ def self.decrypt(un,k1,k2)
12
+ #thanks to http://userscripts.org/scripts/review/42944
13
+ k1 = k1.to_i
14
+ k2 = k2.to_i
15
+
16
+ #convert the hex "un" to binary
17
+ location1 = Array.new
18
+ un.each_char do |char|
19
+ #puts "#{char} => #{char.to_i(16).to_s(2)}"
20
+ location1 << ("000" + char.to_i(16).to_s(2))[-4,4]
21
+ end
22
+
23
+ location1 = location1.join("").split("")
24
+
25
+ location6 = Array.new
26
+ 0.upto(383) do |n|
27
+ k1 = (k1 * 11 + 77213) % 81371
28
+ k2 = (k2 * 17 + 92717) % 192811
29
+ location6[n] = (k1 + k2) % 128
30
+ end
31
+
32
+
33
+
34
+ location3 = Array.new
35
+ location4 = Array.new
36
+ location5 = Array.new
37
+ location8 = Array.new
38
+ 256.downto(0) do |n|
39
+ location5 = location6[n]
40
+ location4 = n % 128
41
+ location8 = location1[location5]
42
+ location1[location5] = location1[location4]
43
+ location1[location4] = location8
44
+
45
+ end
46
+
47
+ 0.upto(127) do |n|
48
+ location1[n] = location1[n].to_i ^ location6[n+256] & 1
49
+ end
50
+
51
+ location12 = location1.join("")
52
+ location7 = Array.new
53
+
54
+ n = 0
55
+ while (n < location12.length) do
56
+ location9 = location12[n,4]
57
+ location7 << location9
58
+ n+=4
59
+ end
60
+
61
+ result = ""
62
+ location7.each do |bin|
63
+ result = result + bin.to_i(2).to_s(16)
64
+ end
65
+ result
66
+ end
67
+
68
+
69
+ def self.get_urls_and_filenames(url)
70
+ #the megavideo video ID looks like this: http://www.megavideo.com/?v=ABCDEF72 , we only want the ID (the \w in the brackets)
71
+ video_id = url[/v[\/=](\w*)&?/, 1]
72
+ puts "[MEGAVIDEO] ID FOUND: " + video_id
73
+ video_page = Nokogiri::XML(open("http://www.megavideo.com/xml/videolink.php?v=#{video_id}"))
74
+ info = video_page.at("//ROWS/ROW")
75
+ title = info["title"]
76
+ puts "[MEGAVIDEO] title: #{title}"
77
+ runtime = info["runtimehms"]
78
+ puts "[MEGAVIDEO] runtime: #{runtime}"
79
+ size = info["size"].to_i / 1024 / 1024
80
+ puts "[MEGAVIDEO] size: #{size} MB"
81
+ #lame crypto stuff
82
+ key_s = info["s"]
83
+ key_un = info["un"]
84
+ key_k1 = info["k1"]
85
+ key_k2 = info["k2"]
86
+ puts "[MEGAVIDEO] lame pseudo crypto keys:"
87
+ puts "[MEGAVIDEO] s=#{key_s}"
88
+ puts "[MEGAVIDEO] un=#{key_un}"
89
+ puts "[MEGAVIDEO] k1=#{key_k1}"
90
+ puts "[MEGAVIDEO] k2=#{key_k2}"
91
+ puts "decrypting"
92
+ download_url = "http://www#{key_s}.megavideo.com/files/#{decrypt(key_un,key_k1,key_k2)}/#{title}.flv"
93
+ puts download_url
94
+ puts "done decrypting"
95
+ file_name = title + ".flv"
96
+ puts "downloading to " + file_name
97
+ [{:url => download_url, :name => file_name}]
98
+ end
99
+
100
+ end
data/plugins/vimeo.rb ADDED
@@ -0,0 +1,28 @@
1
+ class Vimeo < PluginBase
2
+ require "nokogiri"
3
+ require "open-uri"
4
+
5
+ #this will be called by the main app to check weather this plugin is responsible for the url passed
6
+ def self.matches_provider?(url)
7
+ url.include?("vimeo.com")
8
+ end
9
+
10
+ def self.get_urls_and_filenames(url)
11
+ #the vimeo ID consists of 7 decimal numbers in the URL
12
+ vimeo_id = url[/\d{7,8}/]
13
+ doc = Nokogiri::XML(open("http://www.vimeo.com/moogaloop/load/clip:#{vimeo_id}"))
14
+ title = doc.at("//video/caption").inner_text
15
+ puts "[VIMEO] Title: #{title}"
16
+ request_signature = doc.at("//request_signature").inner_text
17
+ request_signature_expires = doc.at("//request_signature_expires").inner_text
18
+
19
+
20
+ puts "[VIMEO] Request Signature: #{request_signature} expires: #{request_signature_expires}"
21
+
22
+ download_url = "http://www.vimeo.com/moogaloop/play/clip:#{vimeo_id}/#{request_signature}/#{request_signature_expires}/?q=hd"
23
+ #todo: put the filename cleaning stuff into a seperate helper
24
+ file_name = title.delete("\"'").gsub(/[^0-9A-Za-z]/, '_') + ".flv"
25
+ puts "downloading to " + file_name
26
+ [{:url => download_url, :name => file_name}]
27
+ end
28
+ end
@@ -0,0 +1,118 @@
1
+ class Youtube < PluginBase
2
+ require "cgi"
3
+ require "open-uri"
4
+ require "nokogiri"
5
+
6
+ #this will be called by the main app to check weather this plugin is responsible for the url passed
7
+ def self.matches_provider?(url)
8
+ url.include?("youtube.com")
9
+ end
10
+
11
+ def self.parse_playlist(url)
12
+ #http://www.youtube.com/view_play_list?p=F96B063007B44E1E&search_query=welt+auf+schwäbisch
13
+ #http://www.youtube.com/watch?v=9WEP5nCxkEY&videos=jKY836_WMhE&playnext_from=TL&playnext=1
14
+ #http://www.youtube.com/watch?v=Tk78sr5JMIU&videos=jKY836_WMhE
15
+
16
+ playlist_ID = url[/p=(\w{16})&?/,1]
17
+ puts "[YOUTUBE] Playlist ID: #{playlist_ID}"
18
+ url_array = Array.new
19
+ video_info = Nokogiri::HTML(open("http://gdata.youtube.com/feeds/api/playlists/#{playlist_ID}?v=2"))
20
+ video_info.search("//content").each do |video|
21
+ url_array << video["url"] if video["url"].include?("http://www.youtube.com/v/") #filters out rtsp links
22
+ end
23
+
24
+ puts "[YOUTUBE] #{url_array.size} links found!"
25
+ url_array
26
+ end
27
+
28
+
29
+ def self.get_urls_and_filenames(url)
30
+ return_values = []
31
+ if url.include?("view_play_list")
32
+ puts "[YOUTUBE] playlist found! analyzing..."
33
+ files = self.parse_playlist(url)
34
+ puts "[YOUTUBE] Starting playlist download"
35
+ files.each do |file|
36
+ puts "[YOUTUBE] Downloading next movie on the playlist (#{file})"
37
+ return_values << self.grab_single_url_filename(url)
38
+ end
39
+ else
40
+ return_values << self.grab_single_url_filename(url)
41
+ end
42
+ return_values
43
+ end
44
+
45
+ def self.grab_single_url_filename(url)
46
+ #the youtube video ID looks like this: [...]v=abc5a5_afe5agae6g&[...], we only want the ID (the \w in the brackets)
47
+ #addition: might also look like this /v/abc5-a5afe5agae6g
48
+ # alternative: video_id = url[/v[\/=]([\w-]*)&?/, 1]
49
+ video_id = url[/(v|embed)[\/=]([^\/\?\&]*)/,2]
50
+ if video_id.nil?
51
+ puts "no video id found."
52
+ exit
53
+ else
54
+ puts "[YOUTUBE] ID FOUND: #{video_id}"
55
+ end
56
+ #let's get some infos about the video. data is urlencoded
57
+ video_info = open("http://youtube.com/get_video_info?video_id=#{video_id}").read
58
+
59
+ #converting the huge infostring into a hash. simply by splitting it at the & and then splitting it into key and value arround the =
60
+ #[...]blabla=blubb&narf=poit&marc=awesome[...]
61
+ video_info_hash = Hash[*video_info.split("&").collect { |v|
62
+ key, value = v.split "="
63
+ value = CGI::unescape(value) if value
64
+ if key =~ /_map/
65
+ value = value.split(",")
66
+ value = if key == "fmt_map"
67
+ Hash[*value.collect{ |v|
68
+ k2, *v2 = v.split("/")
69
+ [k2, v2]
70
+ }.flatten(1)]
71
+ elsif key == "fmt_url_map" || key == "fmt_stream_map"
72
+ Hash[*value.collect { |v| v.split("|")}.flatten]
73
+ end
74
+ end
75
+ [key, value]
76
+ }.flatten]
77
+
78
+ if video_info_hash["status"] == "fail"
79
+ puts "Error: embedding disabled, no video info found"
80
+ return false
81
+ end
82
+
83
+ title = video_info_hash["title"]
84
+ length_s = video_info_hash["length_seconds"]
85
+ token = video_info_hash["token"]
86
+
87
+
88
+ #Standard = 34 <- flv
89
+ #Medium = 18 <- mp4
90
+ #High = 35 <- flv
91
+ #720p = 22 <- mp4
92
+ #1080p = 37 <- mp4
93
+ #mobile = 17 <- 3gp
94
+ # --> 37 > 22 > 35 > 18 > 34 > 17
95
+ formats = video_info_hash["fmt_map"].keys
96
+
97
+ format_ext = {}
98
+ format_ext["37"] = ["mp4", "MP4 Highest Quality 1920x1080"]
99
+ format_ext["22"] = ["mp4", "MP4 1280x720"]
100
+ format_ext["35"] = ["flv", "FLV 854x480"]
101
+ format_ext["34"] = ["flv", "FLV 640x360"]
102
+ format_ext["18"] = ["mp4", "MP4 480x270"]
103
+ format_ext["17"] = ["3gp", "3gp"]
104
+ format_ext["5"] = ["flv", "old default?"]
105
+
106
+ puts "[YOUTUBE] Title: #{title}"
107
+ puts "[YOUTUBE] Length: #{length_s} s"
108
+ puts "[YOUTUBE] t-parameter: #{token}"
109
+ #best quality seems always to be firsts
110
+ puts "[YOUTUBE] formats available: #{formats} (downloading ##{formats.first} -> #{format_ext[formats.first].last})"
111
+
112
+
113
+ download_url = video_info_hash["fmt_url_map"][formats.first]
114
+ file_name = title.delete("\"'").gsub(/[^0-9A-Za-z]/, '_') + "." + format_ext[formats.first].first
115
+ puts "downloading to " + file_name
116
+ {:url => download_url, :name => file_name}
117
+ end
118
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: viddl-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Marc Seeger
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-07-16 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: &70145693005120 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70145693005120
25
+ description: An extendable commandline video downloader for flash video sites. Includes
26
+ plugins for vimeo, youtube and megavideo
27
+ email: mail@marc-seeger.de
28
+ executables:
29
+ - viddl-rb
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - bin/viddl-rb
34
+ - helper/download-helper.rb
35
+ - helper/plugin-helper.rb
36
+ - plugins/megavideo.rb
37
+ - plugins/vimeo.rb
38
+ - plugins/youtube.rb
39
+ - CHANGELOG.txt
40
+ - README.txt
41
+ homepage: https://github.com/rb2k/viddl-rb
42
+ licenses: []
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: 1.3.4
59
+ requirements: []
60
+ rubyforge_project: viddl-rb
61
+ rubygems_version: 1.8.5
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: An extendable commandline video downloader for flash video sites.
65
+ test_files: []