youtube_utils 0.1.0
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/bin/youtube_utils +57 -0
- data/lib/youtube_utils.rb +151 -0
- 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
|
+
|