# # Copyright Michael Orlitzky # # http://michael.orlitzky.com/ # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # http://www.fsf.org/licensing/licenses/gpl.html # require 'src/website' class Megaporn < Website VALID_MEGAPORN_URL_REGEX = /^(http:\/\/)?(www\.)?(megaporn|megavideo)\.com\/video\/\?v=[[:alnum:]]+$/ def self.owns_url?(url) return url =~ VALID_MEGAPORN_URL_REGEX end def get_video_filename # Just use the video id. id_regex = /v=([[:alnum:]]+)/ matches = id_regex.match(@url) if (matches.nil? || matches.length < 2) return 'default.flv' else return (matches[1] + '.flv') end end def get_video_url() data = get_page_data(@url) # Megaporn attaches a numeric suffix to their 'www' # hostname that (I suppose) is necessary to find the # video file. The suffix is simply provided as a flash # variable, though. host_suffix = parse_flash_variable(data, 's') # There are also two or three keys, sent as Flash variables, # that are used in the "decryption" routine. We get integers # to be on the safe side. key1 = parse_flash_variable(data, 'k1').to_i key2 = parse_flash_variable(data, 'k2').to_i # This is a magic hex string, passed to the decryption # routine along with key1 and key2. seed = parse_flash_variable(data, 'un') secret_path = self.decrypt(seed, key1, key2) # Note that the path to the video file looks like a folder. # We need to remember to save it as a file. return "http://www#{host_suffix}.#{self.domain}/videos/#{secret_path}/" end protected def domain # I'm guessing they serve videos from both of these domains. if (@url.include?('megavideo.com')) return 'megavideo.com' else return 'megaporn.com' end end def parse_flash_variable(data, variable_name) # The Flash variables differ only in name, so this method can # parse them all. var_regex = /flashvars\.#{variable_name} = \"(.+?)\"/ matches = var_regex.match(data) if (matches.nil? || matches.length < 2) raise StandardError.new("Could not parse Flash variable: #{variable_name}.") end return matches[1] end def string_to_binary_array(target_string) binary_array = [] target_string.each_char do |c| binary_int = c.to_i(16).to_s(2).to_i binary_char = sprintf('%04d', binary_int) binary_char.each_char do |bc| binary_array << bc end end return binary_array end def binary_words_to_hex(target_array) hex = '' target_array.each do |word| hex << word.to_i(2).to_s(16) end return hex end def decrypt(seed, key1, key2) # I reverse-engineered this one myself. Suck it, megaporn. # # Round 1. Convert the seed to its binary reprentation, # storing each bit as an element of an array. loc1 = string_to_binary_array(seed) # Round 2 loc6 = [] key1 = key1.to_i key2 = key2.to_i 0.upto(383) do |loc3| key1 = (key1 * 11 + 77213) % 81371 key2 = (key2 * 17 + 92717) % 192811 loc6[loc3] = (key1 + key2) % 128 end # Round 3 256.downto(0) do |loc3| loc5 = loc6[loc3] loc4 = loc3 % 128 loc8 = loc1[loc5] loc1[loc5] = loc1[loc4] loc1[loc4] = loc8 end # Round 4 0.upto(127) do |loc3| loc1[loc3] = (loc1[loc3].to_i ^ loc6[loc3 + 256]) & 1 end # Round 5 loc12 = loc1.to_s offset = 0 loc7 = [] while (offset < loc12.length) loc7 << loc12[offset, 4]; offset += 4 end # Round 6 return binary_words_to_hex(loc7) end end