2 # Copyright Michael Orlitzky
4 # http://michael.orlitzky.com/
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.
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.
16 # http://www.fsf.org/licensing/licenses/gpl.html
21 class Megaporn
< Website
23 VALID_MEGAPORN_URL_REGEX
= /^(http:\/\
/)?(www\.)?(megaporn|megavideo)\.com\/video\
/\?v=[[:alnum:]]+$/
25 def self.owns_url
?(url
)
26 return url
=~ VALID_MEGAPORN_URL_REGEX
30 def get_video_filename
31 # Just use the video id.
32 id_regex
= /v=([[:alnum:]]+)/
33 matches
= id_regex
.match(@url)
35 if (matches
.nil? || matches
.length
< 2)
38 return (matches
[1] +
'.flv')
44 data = get_page_data(@url)
46 # Megaporn attaches a numeric suffix to their 'www'
47 # hostname that (I suppose) is necessary to find the
48 # video file. The suffix is simply provided as a flash
50 host_suffix
= parse_flash_variable(data, 's')
52 # There are also two or three keys, sent as Flash variables,
53 # that are used in the "decryption" routine. We get integers
54 # to be on the safe side.
55 key1
= parse_flash_variable(data, 'k1').to_i
56 key2
= parse_flash_variable(data, 'k2').to_i
58 # This is a magic hex string, passed to the decryption
59 # routine along with key1 and key2.
60 seed
= parse_flash_variable(data, 'un')
62 secret_path
= self.decrypt(seed
, key1
, key2
)
64 # Note that the path to the video file looks like a folder.
65 # We need to remember to save it as a file.
66 return "http://www#{host_suffix}.#{self.domain}/videos/#{secret_path}/"
73 # I'm guessing they serve videos from both of these domains.
74 if (@url.include?('megavideo.com'))
75 return 'megavideo.com'
82 def parse_flash_variable(data, variable_name
)
83 # The Flash variables differ only in name, so this method can
85 var_regex
= /flashvars\.#{variable_name} = \"(.+?)\"/
86 matches
= var_regex
.match(data)
88 if (matches
.nil? || matches
.length
< 2)
89 raise StandardError
.new("Could not parse Flash variable: #{variable_name}.")
96 def string_to_binary_array(target_string
)
99 target_string
.each_char
do |c
|
100 binary_int
= c
.to_i(16).to_s(2).to_i
101 binary_char
= sprintf('%04d', binary_int
)
102 binary_char
.each_char
do |bc
|
111 def binary_words_to_hex(target_array
)
114 target_array
.each
do |word
|
115 hex
<< word
.to_i(2).to_s(16)
122 def decrypt(seed
, key1
, key2
)
123 # I reverse-engineered this one myself. Suck it, megaporn.
125 # Round 1. Convert the seed to its binary reprentation,
126 # storing each bit as an element of an array.
127 loc1
= string_to_binary_array(seed
)
133 0.upto(383) do |loc3
|
134 key1
= (key1
* 11 +
77213) % 81371
135 key2
= (key2
* 17 +
92717) % 192811
136 loc6
[loc3
] = (key1 + key2
) % 128
141 256.downto(0) do |loc3
|
145 loc1
[loc5
] = loc1
[loc4
]
151 0.upto(127) do |loc3
|
152 loc1
[loc3
] = (loc1
[loc3
].to_i ^ loc6
[loc3 +
256]) & 1
160 while (offset
< loc12
.length
)
161 loc7
<< loc12
[offset
, 4];
167 return binary_words_to_hex(loc7
)