]> gitweb.michael.orlitzky.com - dead/whatever-dl.git/blob - src/websites/redtube.rb
3b1a4fa5005641817a2f0cc4f87902e8d20f263f
[dead/whatever-dl.git] / src / websites / redtube.rb
1 #
2 # Copyright Michael Orlitzky
3 #
4 # http://michael.orlitzky.com/
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # http://www.fsf.org/licensing/licenses/gpl.html
17 #
18 #
19 # NOTE:
20 #
21 # All credit belongs to whomever reverse-engineered the Redtube
22 # Flash applet in the first place. I took the algorithm from this
23 # script:
24 #
25 # http://userscripts.org/scripts/review/8691
26 #
27 # and merely cleaned it up a bit while porting it to Ruby.
28 #
29
30 # The Redtube class needs the extra string methods..
31 require 'src/string'
32 require 'src/website'
33
34 # This class handles the algorithm magic needed to get
35 # the URL from a Redtube video id.
36 class Redtube < Website
37
38 VALID_REDTUBE_URL_REGEX = /^(http:\/\/)?(www\.)?redtube\.com\/(\d+)$/
39
40 def self.owns_url?(url)
41 return url =~ VALID_REDTUBE_URL_REGEX
42 end
43
44
45 # The only public method. This calls the other parts
46 # of the algorithm and, with any luck, we wind up with
47 # the URL to the video.
48 def get_video_url()
49 # First, parse the video ID out of the URL.
50 video_id = parse_video_id()
51
52 padded_id = video_id.to_s.pad_left('0', 7)
53
54 video_dir = self.get_video_dir(video_id)
55 file_name = self.get_file_name(padded_id)
56
57 page_data = self.get_page_data(@url)
58
59 # As far as I know, the MP4s don't work yet.
60 # So just default to the FLV.
61 top_secret_hash = parse_hash_flv(page_data)
62
63 return 'http://dl.redtube.com/' +
64 self.get_root_server_dir() +
65 "/#{video_dir}/#{file_name}" +
66 top_secret_hash
67 end
68
69
70 def get_video_filename()
71 # Mildly redundant.
72 video_id = parse_video_id()
73 padded_id = video_id.to_s.pad_left('0', 7)
74
75 return self.get_file_name(padded_id)
76 end
77
78
79 protected;
80
81 VIDEO_FILE_EXTENSION = '.flv'
82
83 def parse_video_id()
84 return /\d+/.match(@url)[0]
85 end
86
87
88
89 def parse_hash_flv(page_data)
90 # Hashes are generated for both flv/mp4 and are delivered
91 # along with the video page. We need to stick them back on
92 # the end of the final video URL, or else it doesn't work.
93 hash_flv_regex = /&hash_flv=(\/.*?)&/
94
95 matches = hash_flv_regex.match(page_data)
96
97 if matches.nil?
98 raise StandardError.new("Couldn't parse the hash_flv variable.")
99 end
100
101 return matches[1]
102 end
103
104
105 def get_root_server_dir()
106 # They hard code this shit into the SWF file.
107 return '467f9bca32b1989277b48582944f325afa3374'
108 end
109
110 # Not sure what they're thinking with this one.
111 def get_video_dir(video_id)
112 return (video_id.to_f / 1000.0).floor.to_s.pad_left('0', 7)
113 end
114
115
116 # The first part of the algorithmic magic. Multiply each
117 # digit of the padded video id by the index of the
118 # following digit, and sum them up.
119 def int_magic(padded_video_id)
120 ret = 0
121
122 0.upto(6) do |a|
123 ret += padded_video_id[a,1].to_i * (a+1)
124 end
125
126 return ret
127 end
128
129
130 # Part 2 of the magic. Sum the digits of the result
131 # of the first magic.
132 def more_magic(file_string)
133 magic = self.int_magic(file_string).to_s
134
135 ret = 0
136
137 0.upto(magic.length - 1) do |a|
138 ret += magic[a,1].to_i
139 end
140
141 return ret
142 end
143
144
145 # Complete fricking mystery
146 def get_file_name(file_string)
147 map = ['R', '1', '5', '3', '4', '2', 'O', '7', 'K', '9', 'H', 'B', 'C', 'D', 'X', 'F', 'G', 'A', 'I', 'J', '8', 'L', 'M', 'Z', '6', 'P', 'Q', '0', 'S', 'T', 'U', 'V', 'W', 'E', 'Y', 'N']
148
149 # The stupid variable names I copied from the
150 # source script. Considering myself disclaimed.
151 my_int = self.more_magic(file_string)
152 new_char = '0' + my_int.to_s
153
154 if my_int >= 10 then
155 new_char = my_int.to_s
156 end
157
158 file_name = map[file_string[3] - 48 + my_int + 3]
159 file_name += new_char[1,1]
160 file_name += map[file_string[0] - 48 + my_int + 2]
161 file_name += map[file_string[2] - 48 + my_int + 1]
162 file_name += map[file_string[5] - 48 + my_int + 6]
163 file_name += map[file_string[1] - 48 + my_int + 5]
164 file_name += new_char[0,1]
165 file_name += map[file_string[4] - 48 + my_int + 7]
166 file_name += map[file_string[6] - 48 + my_int + 4]
167 file_name += VIDEO_FILE_EXTENSION
168
169 return file_name
170 end
171
172
173 end