rutube 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rutube.rb +7 -0
  3. data/lib/util.rb +111 -0
  4. data/lib/video.rb +41 -0
  5. data/lib/youtube.rb +105 -0
  6. metadata +48 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dcbfc16dadfbb4964e347092236508a5678aeffb
4
+ data.tar.gz: 7db89d0aa31c0f1a2b31fa993ede058ce63662f7
5
+ SHA512:
6
+ metadata.gz: 355babb9207902c387b7f011ffd470e0b48f90da3abf3f8e7f25fc6a4558aad12c1009bbc0ecd9bf0638d657af251f990f2f95dc4c07334f4e75c413332b733d
7
+ data.tar.gz: 76c69493473b225d0717c5622b565a3234dcd9fdd18fb3ec6f82a101a3df9d4d3f6674d84fa3a012648cf9c174028c6539d51e2621e1c8819dcc6b47db0d20ab
@@ -0,0 +1,7 @@
1
+ module Rutube
2
+
3
+ require 'youtube'
4
+ require 'video'
5
+ require 'util'
6
+
7
+ end
@@ -0,0 +1,111 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'openssl'
4
+ require 'video'
5
+ require 'uri'
6
+
7
+ class Array
8
+ def iterate!(code)
9
+ self.each_with_index do |n, i|
10
+ self[i] = code.call(n)
11
+ end
12
+ end
13
+ end
14
+
15
+ module Util
16
+
17
+ YT_BASE_URL = 'http://www.youtube.com/get_video_info'
18
+ OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
19
+
20
+ # YouTube quality and codecs id map.
21
+ # source: http://en.wikipedia.org/wiki/YouTube#Quality_and_codecs
22
+ YT_ENCODING = {
23
+ # Flash Video
24
+ 5 => ["flv", "240p", "Sorenson H.263", "N/A", "0.25", "MP3", "64"],
25
+ 6 => ["flv", "270p", "Sorenson H.263", "N/A", "0.8", "MP3", "64"],
26
+ 34 => ["flv", "360p", "H.264", "Main", "0.5", "AAC", "128"],
27
+ 35 => ["flv", "480p", "H.264", "Main", "0.8-1", "AAC", "128"],
28
+
29
+ # 3GP
30
+ 36 => ["3gp", "240p", "MPEG-4 Visual", "Simple", "0.17", "AAC", "38"],
31
+ 13 => ["3gp", "N/A", "MPEG-4 Visual", "N/A", "0.5", "AAC", "N/A"],
32
+ 17 => ["3gp", "144p", "MPEG-4 Visual", "Simple", "0.05", "AAC", "24"],
33
+
34
+ # MPEG-4
35
+ 18 => ["mp4", "360p", "H.264", "Baseline", "0.5", "AAC", "96"],
36
+ 22 => ["mp4", "720p", "H.264", "High", "2-2.9", "AAC", "192"],
37
+ 37 => ["mp4", "1080p", "H.264", "High", "3-4.3", "AAC", "192"],
38
+ 38 => ["mp4", "3072p", "H.264", "High", "3.5-5", "AAC", "192"],
39
+ 82 => ["mp4", "360p", "H.264", "3D", "0.5", "AAC", "96"],
40
+ 83 => ["mp4", "240p", "H.264", "3D", "0.5", "AAC", "96"],
41
+ 84 => ["mp4", "720p", "H.264", "3D", "2-2.9", "AAC", "152"],
42
+ 85 => ["mp4", "1080p", "H.264", "3D", "2-2.9", "AAC", "152"],
43
+
44
+ # WebM
45
+ 43 => ["webm", "360p", "VP8", "N/A", "0.5", "Vorbis", "128"],
46
+ 44 => ["webm", "480p", "VP8", "N/A", "1", "Vorbis", "128"],
47
+ 45 => ["webm", "720p", "VP8", "N/A", "2", "Vorbis", "192"],
48
+ 46 => ["webm", "1080p", "VP8", "N/A", "N/A", "Vorbis", "192"],
49
+ 100 => ["webm", "360p", "VP8", "3D", "N/A", "Vorbis", "128"],
50
+ 101 => ["webm", "360p", "VP8", "3D", "N/A", "Vorbis", "192"],
51
+ 102 => ["webm", "720p", "VP8", "3D", "N/A", "Vorbis", "192"]
52
+ }
53
+
54
+ # The keys corresponding to the quality/codec map above.
55
+ YT_ENCODING_KEYS = [
56
+ 'extension',
57
+ 'resolution',
58
+ 'video_codec',
59
+ 'profile',
60
+ 'video_bitrate',
61
+ 'audio_codec',
62
+ 'audio_bitrate'
63
+ ]
64
+
65
+ def parse_stream_map(text)
66
+ videoinfo = {
67
+ "itag" => [],
68
+ "url" => [],
69
+ "quality" => [],
70
+ "fallback_host" => [],
71
+ "s" => [],
72
+ "type" => []
73
+ }
74
+
75
+ videos = text.split(",")
76
+ videos.iterate!(lambda { |video| video.split("&") })
77
+ for video in videos
78
+ for kv in video
79
+ key, value = kv.split("=")[0], URI.unescape(kv.split("=")[1])
80
+ videoinfo[key].index(value) == nil ? videoinfo[key].push(value) : ""
81
+ end
82
+ end
83
+ return videoinfo
84
+ end
85
+
86
+ module_function :parse_stream_map
87
+
88
+ def _extract_fmt(url)
89
+ fmt_data = Hash.new
90
+ if itag = url.match('itag=\d+').to_s.match('\d+').to_s.to_i
91
+ attr = YT_ENCODING[itag]
92
+ if attr == nil
93
+ return itag, fmt_data
94
+ end
95
+ lambda { |x, y| x.each_with_index { |i, j| fmt_data[i] = y[j] } }.call(YT_ENCODING_KEYS, attr)
96
+ return itag, fmt_data
97
+ end
98
+ end
99
+
100
+ module_function :_extract_fmt
101
+
102
+ def sanitize_filename(file_name)
103
+ file_name.gsub(/[^\w\.\-]/,"_")
104
+ end
105
+
106
+ module_function :sanitize_filename
107
+
108
+ def _cipher(s, url)
109
+ # TODO need to handle this by invoking js from ruby
110
+ end
111
+ end
@@ -0,0 +1,41 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
5
+
6
+ class Video
7
+ attr_accessor :url, :filename, :fmt_data
8
+
9
+ def initialize(url, filename, fmt_data)
10
+ @url, @filename, @fmt_data = url, filename, fmt_data
11
+ end
12
+
13
+ def download(dir=Dir.pwd)
14
+ Thread.new(dir){_download(dir)}
15
+ end
16
+
17
+ def _download(dir)
18
+ if !Dir.exist?(dir)
19
+ raise "Directory doesn't exist"
20
+ end
21
+
22
+ file_name = File.join(dir, @filename+"."+@fmt_data["extension"])
23
+
24
+ if (File.exist?(file_name))
25
+ raise "File Already Exists"
26
+ end
27
+
28
+ uri = URI(@url)
29
+ begin
30
+ Net::HTTP.get_response(uri) { |res|
31
+ if res != nil && res.is_a?(Net::HTTPSuccess)
32
+ f = File.new(file_name, "wb")
33
+ res.read_body do |chunk|
34
+ f.write(chunk)
35
+ end
36
+ f.close()
37
+ end
38
+ }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,105 @@
1
+ require 'util'
2
+
3
+ class Youtube
4
+ attr_accessor :file_name, :video_url, :video_id, :fmt_values, :videos, :title
5
+
6
+ def initialize(video_url)
7
+ @video_url = video_url
8
+ initialize_video_info(self)
9
+ end
10
+
11
+ def initialize_video_info(yt)
12
+ yt.title = ""
13
+ yt.videos = []
14
+ uri = URI(yt.video_url)
15
+ response = Net::HTTP.get_response(uri)
16
+
17
+ if response.is_a?(Net::HTTPMovedPermanently) && uri.scheme == "http"
18
+ puts "Changing the url scheme to https since redirections are not handled by ruby"
19
+ uri.scheme = "https"
20
+ uri.port = 443
21
+ response = Net::HTTP.get_response(uri)
22
+ end
23
+
24
+ if response != nil && response.is_a?(Net::HTTPSuccess)
25
+ content = response.body
26
+ player_conf = content[17 + content.index("ytplayer.config = ")..content.length]
27
+ bracket_count = 0
28
+ index = 0
29
+ player_conf.each_char.with_index { |char, i|
30
+ index = i
31
+ if char == "{"
32
+ bracket_count += 1
33
+ elsif char == "}"
34
+ bracket_count -= 1
35
+ if bracket_count == 0
36
+ break
37
+ end
38
+ end
39
+ }
40
+
41
+ index = index+1
42
+ data = JSON.parse(player_conf[0, index])
43
+
44
+ is_vevo = false
45
+ if ['vevo', 'dashmpd'].include?(data["args"]["pk"])
46
+ is_vevo = true
47
+ end
48
+
49
+ stream_map = Util.parse_stream_map(data["args"]["url_encoded_fmt_stream_map"])
50
+ yt.title = data["args"]["title"]
51
+ js_url = "http:" + data["assets"]["js"]
52
+ video_urls = stream_map["url"]
53
+ self.file_name = Util.sanitize_filename(yt.title)
54
+
55
+ yt.videos, yt.fmt_values = [], []
56
+
57
+ video_urls.each_with_index { |url, index|
58
+ begin
59
+ fmt, fmt_data = Util._extract_fmt(url)
60
+ rescue StandardError
61
+ puts $!, $@
62
+ end
63
+
64
+ if url.index('signature=') == nil
65
+ if is_vevo
66
+ ## TODO : need to handle it differently
67
+ else
68
+ signature = _cipher(stream_map["s"][i], js_url)
69
+ url = "%url&signature=%sig".gsub('url', url).gsub('sig', signature)
70
+ end
71
+ end
72
+
73
+ yt.videos.push(Video.new(url, yt.file_name, fmt_data))
74
+ yt.fmt_values.push(fmt)
75
+ }
76
+ end
77
+ return yt
78
+ rescue JSON::JSONError
79
+ puts "cannot decode json"
80
+ end
81
+
82
+
83
+ def get(type, resoln=nil)
84
+ return_videos = []
85
+ for video in @videos
86
+ if video.fmt_data["extension"] == type && (resoln == nil || (resoln != nil && resoln == video.fmt_data["resolution"]))
87
+ return_videos.push(video)
88
+ break
89
+ end
90
+ end
91
+ if return_videos.length == 1
92
+ return return_videos[0]
93
+ else
94
+ puts "no video present for the given format #{type}"
95
+ return nil
96
+ end
97
+ end
98
+
99
+ def list_formats
100
+ for video in @videos
101
+ puts "Video : #{video.fmt_data["video_codec"]} (.#{video.fmt_data["extension"]}) - #{video.fmt_data["resolution"]}"
102
+ end
103
+ return nil
104
+ end
105
+ end
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rutube
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.3'
5
+ platform: ruby
6
+ authors:
7
+ - Vidyuth Dandu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-30 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Ruby port of pytube for downloading youtube videos
14
+ email:
15
+ - vidyuth.bitsgoa@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/rutube.rb
21
+ - lib/util.rb
22
+ - lib/video.rb
23
+ - lib/youtube.rb
24
+ homepage: http://github.com
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 2.0.0
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 1.3.6
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 2.4.5
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: Download youtube videos using rutube
48
+ test_files: []