youtube_utils 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/bin/youtube_utils +57 -0
  2. data/lib/youtube_utils.rb +151 -0
  3. metadata +82 -0
data/bin/youtube_utils ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'youtube_utils'
5
+
6
+ def usage
7
+ puts 'Usage:'
8
+ puts 'youtube_utils <youtube_url>'
9
+ puts 'Example:'
10
+ puts 'youtube_utils http://www.youtube.com/watch?v=cRdxXPV9GNQ'
11
+ end
12
+
13
+ def download url, output
14
+ if `/usr/bin/env curl --version` =~ /libcurl/
15
+ run_command "/usr/bin/env curl -L -C - -o #{output} '#{url}'"
16
+ elsif `/usr/bin/env wget --version` =~ /Wget/
17
+ run_command "/usr/bin/env wget '#{url}' -c -O #{output}"
18
+ else
19
+ puts "Can not find curl or wget!"
20
+ end
21
+ end
22
+
23
+ def ask_user_which_one_to_download videos
24
+ i = 0
25
+ videos.each {|x|
26
+ puts "#{i}\t#{x['quality']}\t#{YoutubeUtils.type2suffix(x['type'])}"
27
+ i += 1
28
+ }
29
+ print "Please select a video to download by enter its number:"
30
+ return $stdin.gets
31
+ end
32
+
33
+ def build_filename url, type
34
+ vid = YoutubeUtils.get_vid(url)
35
+ suffix = YoutubeUtils.type2suffix(type)
36
+ return vid + "." + suffix
37
+ end
38
+
39
+ def run_command command
40
+ puts command
41
+ result = `#{command}`
42
+ puts result
43
+ return result
44
+ end
45
+
46
+ def run url
47
+ videos = YoutubeUtils.new.get_videos url
48
+ index = ask_user_which_one_to_download(videos).to_i
49
+ download videos[index]['url'], build_filename(url, videos[index]['type'])
50
+ end
51
+
52
+ if ARGV.empty?
53
+ usage
54
+ else
55
+ run ARGV[0]
56
+ end
57
+
@@ -0,0 +1,151 @@
1
+ require 'net/http'
2
+ require 'yajl'
3
+ require 'uri'
4
+
5
+ #http://unlockforus.blogspot.com/2010/04/downloading-youtube-videos-not-working.html
6
+ #http://board.jdownloader.org/showthread.php?t=18520
7
+ class YoutubeUtils
8
+ def get_videos youtube_watch_url
9
+ res = get_webpage(youtube_watch_url);
10
+ unless res.code == '200'
11
+ puts res.code
12
+ return []
13
+ end
14
+
15
+ hash = json_to_hash(get_PLAYER_CONFIG(res.body))
16
+ unless hash
17
+ puts "no PLAYER_CONFIG"
18
+ return []
19
+ end
20
+
21
+ args = hash['args']
22
+ unless args
23
+ puts "no args"
24
+ return []
25
+ end
26
+
27
+ result = []
28
+
29
+ html5_fmt_map = args['html5_fmt_map']
30
+ result.concat(convert_html5_fmt_map(html5_fmt_map)) if html5_fmt_map
31
+
32
+ fmt_stream_map = args['fmt_stream_map']
33
+ fmt_list = args['fmt_list']
34
+ result.concat(convert_fmt_stream_map(fmt_stream_map, fmt_list)) if fmt_list&&fmt_stream_map
35
+
36
+ return result
37
+ end
38
+
39
+ #input: http://www.youtube.com/watch?v=cRdxXPV9GNQ
40
+ #output: cRdxXPV9GNQ
41
+ def self.get_vid url
42
+ querys = URI.parse(url).query.split('&')
43
+ querys.each {|x|
44
+ a = x.split('=')
45
+ if a[0] == 'v'
46
+ return a[1]
47
+ end
48
+ }
49
+ return nil
50
+ end
51
+
52
+ #input: video/webm; codecs="vp8.0, v
53
+ #outut: webm
54
+ def self.type2suffix type
55
+ return type.split(';')[0].split('/')[1]
56
+ end
57
+
58
+ private
59
+
60
+ def convert_itag_to_type itag
61
+ case itag.to_i
62
+ when 0, 6
63
+ return "video/flv; codecs=\" h263, mp3 mono\""
64
+ when 5
65
+ return "video/flv; codecs=\"h263, mp3 stereo\""
66
+ when 34, 35
67
+ return "video/flv; codecs=\"h264, aac stereo\""
68
+ when 13
69
+ return "video/3gp; codecs=\"h263, amr mono\""
70
+ when 17
71
+ return "video/3gp; codecs=\"h264 aac stereo\""
72
+ when 18, 22, 37, 38, 78
73
+ return "video/mp4; codecs=\"h264, aac stereo\""
74
+ when 43, 45
75
+ return "video/webm; codecs=\"vp8.0, vorbis stereo\""
76
+ else
77
+ return "unknown #{itag}"
78
+ end
79
+ end
80
+
81
+ #{"url"=>"http://v5.lscache8.c.youtube.com/videoplayback?...", "type"=>"video/webm; codecs=\"vp8.0, vorbis\"", "itag"=>43, "quality"=>"medium"}
82
+ def convert_html5_fmt_map a
83
+ result = []
84
+ a.each {|x|
85
+ result << {'url' => x['url'], 'type' => x['type'], 'quality' => x['quality']}
86
+ }
87
+ return result
88
+ end
89
+
90
+ #22/1280x720/9/0/115
91
+ def convert_fmt_stream_map fmt_stream_map, fmt_list
92
+ result = []
93
+ a1 = fmt_stream_map.split(',')
94
+ a2 = fmt_list.split(',')
95
+ i = 0
96
+ a1.each {|x|
97
+ a11 = x.split('|')
98
+ itag = a11[0]
99
+ url = a11[1]
100
+
101
+ a21 = a2[i].split('/')
102
+ resolution = a21[1]
103
+ result << {'url' => url, 'type' => convert_itag_to_type(itag), 'quality' => resolution2quality(resolution)}
104
+ i += 1;
105
+ }
106
+ return result
107
+ end
108
+
109
+ def resolution2quality resolution
110
+ height = resolution.split('x')[1].to_i
111
+ if height > 1080
112
+ return "Original"
113
+ elsif height > 720
114
+ return "1080p"
115
+ elsif height > 576
116
+ return "720p"
117
+ elsif height > 360
118
+ return "480p"
119
+ elsif height > 240
120
+ return "360p"
121
+ else
122
+ return "240p"
123
+ end
124
+ end
125
+
126
+ def get_webpage youtube_watch_url
127
+ uri = URI.parse(youtube_watch_url)
128
+ res = Net::HTTP.start(uri.host, uri.port) { |http|
129
+ http.get(uri.path + "?" + uri.query, {'Cookie' => 'PREF=f2=40000000'})
130
+ }
131
+
132
+ return res
133
+ end
134
+
135
+ def json_to_hash js
136
+ json = StringIO.new(js)
137
+ parser = Yajl::Parser.new
138
+ begin
139
+ return parser.parse(json)
140
+ rescue
141
+ puts "Unable to parse as json"
142
+ return nil
143
+ end
144
+ end
145
+
146
+ def get_PLAYER_CONFIG body
147
+ body[/\'PLAYER_CONFIG\':(.*)\}\)\;\n/]
148
+ return $1
149
+ end
150
+ end
151
+
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: youtube_utils
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Xue Yong Zhi
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-28 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: yajl-ruby
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description:
36
+ email:
37
+ - yong@intridea.com
38
+ executables:
39
+ - youtube_utils
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - lib/youtube_utils.rb
46
+ - bin/youtube_utils
47
+ has_rdoc: true
48
+ homepage: http://github.com/yong/youtube_utils
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.7
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: youtube downloader
81
+ test_files: []
82
+