From fada57a307d66d696d9286fc943c579a3fd22de6 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 13 Aug 2018 09:17:28 -0500 Subject: [PATCH] Add geo-bypass for video info --- src/invidious/helpers/proxy.cr | 143 +++++++++++++++++++++++++++++++++ src/invidious/videos.cr | 46 +++++++++++ src/invidious/views/watch.ecr | 9 +++ 3 files changed, 198 insertions(+) create mode 100644 src/invidious/helpers/proxy.cr diff --git a/src/invidious/helpers/proxy.cr b/src/invidious/helpers/proxy.cr new file mode 100644 index 00000000..3e94042d --- /dev/null +++ b/src/invidious/helpers/proxy.cr @@ -0,0 +1,143 @@ +# See https://github.com/crystal-lang/crystal/issues/2963 +class HTTPProxy + getter proxy_host : String + getter proxy_port : Int32 + getter options : Hash(Symbol, String) + getter tls : OpenSSL::SSL::Context::Client? + + def initialize(@proxy_host, @proxy_port = 80, @options = {} of Symbol => String) + end + + def open(host, port, tls = nil, connection_options = {} of Symbol => Float64 | Nil) + dns_timeout = connection_options.fetch(:dns_timeout, nil) + connect_timeout = connection_options.fetch(:connect_timeout, nil) + read_timeout = connection_options.fetch(:read_timeout, nil) + + socket = TCPSocket.new @proxy_host, @proxy_port, dns_timeout, connect_timeout + socket.read_timeout = read_timeout if read_timeout + socket.sync = true + + socket << "CONNECT #{host}:#{port} HTTP/1.1\r\n" + + if options[:user]? + credentials = Base64.strict_encode("#{options[:user]}:#{options[:password]}") + credentials = "#{credentials}\n".gsub(/\s/, "") + socket << "Proxy-Authorization: Basic #{credentials}\r\n" + end + + socket << "\r\n" + + resp = parse_response(socket) + + if resp[:code]? == 200 + {% if !flag?(:without_openssl) %} + if tls + tls_socket = OpenSSL::SSL::Socket::Client.new(socket, context: tls, sync_close: true, hostname: host) + socket = tls_socket + end + {% end %} + + return socket + else + socket.close + raise IO::Error.new(resp.inspect) + end + end + + private def parse_response(socket) + resp = {} of Symbol => Int32 | String | Hash(String, String) + + begin + version, code, reason = socket.gets.as(String).chomp.split(/ /, 3) + + headers = {} of String => String + + while (line = socket.gets.as(String)) && (line.chomp != "") + name, value = line.split(/:/, 2) + headers[name.strip] = value.strip + end + + resp[:version] = version + resp[:code] = code.to_i + resp[:reason] = reason + resp[:headers] = headers + rescue + end + + return resp + end +end + +class HTTPClient < HTTP::Client + def set_proxy(proxy : HTTPProxy) + begin + @socket = proxy.open(host: @host, port: @port, tls: @tls, connection_options: proxy_connection_options) + rescue IO::Error + @socket = nil + end + end + + def proxy_connection_options + opts = {} of Symbol => Float64 | Nil + + opts[:dns_timeout] = @dns_timeout + opts[:connect_timeout] = @connect_timeout + opts[:read_timeout] = @read_timeout + + return opts + end +end + +def get_proxies(country_code = "US") + client = HTTP::Client.new(URI.parse("http://spys.one")) + client.read_timeout = 10.seconds + client.connect_timeout = 10.seconds + + headers = HTTP::Headers.new + headers["User-Agent"] = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" + headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8" + headers["Accept-Language"] = "Accept-Language: en-US,en;q=0.9" + headers["Host"] = "spys.one" + headers["Content-Type"] = "application/x-www-form-urlencoded" + body = { + "xpp" => "2", + "xf1" => "0", + "xf2" => "2", + "xf4" => "1", + "xf5" => "1", + } + response = client.post("/free-proxy-list/#{country_code}/", headers, form: body) + response = XML.parse_html(response.body) + + proxies = [] of {ip: String, port: Int32, score: Float64} + response = response.xpath_nodes(%q(//table))[1] + response.xpath_nodes(%q(.//tr)).each do |node| + if !node["onmouseover"]? + next + end + + ip = node.xpath_node(%q(.//td[1]/font[2])).to_s.match(/(?
[^<]+)Wilson Score : <%= video.wilson_score.round(4) %>

Rating : <%= rating.round(4) %> / 5

Engagement : <%= engagement.round(2) %>%

+ <% if video.allowed_regions.size != REGIONS.size %> +

+ <% if video.allowed_regions.size < REGIONS.size / 2 %> + Whitelisted regions: <%= video.allowed_regions.join(", ") %> + <% else %> + Blacklisted regions: <%= (REGIONS.to_a - video.allowed_regions).join(", ") %> + <% end %> +

+ <% end %> <% if ad_slots %>

Ad Slots : <%= ad_slots %>

<% end %>