viddl-rb 0.4.1

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.
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: []