--- /dev/null
+#
+# 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