Created
April 30, 2010 12:00
-
-
Save ion1/385097 to your computer and use it in GitHub Desktop.
Overengineer much?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'rubygems' | |
require 'cgi' | |
require 'erubis' | |
require 'iconv' | |
require 'logger' | |
require 'mechanize' | |
require 'ostruct' | |
require 'v8' | |
require 'yaml' | |
class DelayedMechanize < Mechanize | |
def fetch_page *args | |
sleep 1 + rand(5) | |
super | |
end | |
end | |
module InteractiveZooCameras | |
DATA = OpenStruct.new YAML.load ::DATA | |
class Error < RuntimeError; end | |
class TreeEntry < Struct.new(:title, :child); end | |
class RTMPStream < Struct.new(:title, :stream_uri, :page_uri, :swf_uri) | |
def uri | |
params = [page_uri, swf_uri, stream_uri].map {|s| CGI.escape s } | |
'http://0.0.0.0:8001/?p=%s&s=%s&r=%s' % params | |
end | |
end | |
class WMVStream < Struct.new(:title, :uri, :page_uri); end | |
def self.html | |
er = Erubis::EscapedEruby.new DATA.html_template | |
er.result :streams => Scrape.new.scrape | |
end | |
def self.item_html value | |
template = if value.is_a? Array | |
DATA.array_template | |
elsif [:title, :child].all? {|s| value.respond_to? s } | |
DATA.tree_template | |
elsif [:title, :uri, :page_uri ].all? {|s| value.respond_to? s } | |
DATA.stream_template | |
else | |
raise Error, "Can't render #{value.inspect}" | |
end | |
er = Erubis::EscapedEruby.new template | |
er.result :value => value | |
end | |
class Scrape < DelayedMechanize | |
def initialize | |
super | |
self.log = Logger.new $stderr | |
self.log.level = Logger::INFO | |
get 'http://interactivezoo.eu/' | |
click page.link_with(:text => /\bkaamerate\b/) | |
end | |
def scrape | |
streams = [] | |
page.search('.image_map a[href]').each do |a| | |
transact do | |
click a | |
streams << TreeEntry.new(to_utf8(a[:title]), scrape_level_b) | |
end | |
end | |
streams | |
end | |
private | |
def scrape_level_b | |
streams = [] | |
page.search('a[href][rel*="prettyPhoto"]').each do |a| | |
transact do | |
click a | |
streams.concat scrape_single_camera(to_utf8(a[:title])) | |
end | |
end | |
streams | |
end | |
def scrape_single_camera title | |
scripts = page.search('script[type="text/javascript"]').map do |script| | |
"#{script.text}\n" | |
end.join | |
page_streams = JavaScript.eval title, page, scripts | |
if page_streams.empty? | |
raise Error, "No streams found in #{page.uri}" | |
end | |
page_streams | |
end | |
def to_utf8 string | |
string = string.to_s | |
begin | |
Iconv.new('UTF-8', 'UTF-8').iconv string | |
rescue Iconv::IllegalSequence | |
Iconv.new('UTF-8//translit', 'ISO-8859-1').iconv string | |
end | |
end | |
end | |
module JavaScript | |
def self.eval title, page, script | |
streams = [] | |
page_uri = page.uri.to_s | |
V8::Context.open do |c| | |
c['add_rtmp_stream'] = lambda do |uri, swf_uri| | |
streams << RTMPStream.new(title, uri, page_uri, swf_uri) | |
end | |
c['add_wmv_stream'] = lambda do |uri| | |
streams << WMVStream.new(title, uri, page_uri) | |
end | |
c.eval [DATA.javascript_header, script].join | |
end | |
streams | |
end | |
end | |
end | |
if $0 == __FILE__ | |
puts InteractiveZooCameras.html | |
end | |
# vi:set et sw=2 sts=2: | |
__END__ | |
javascript_header: | | |
// Mock SWFObject 1.5 | |
function SWFObject (swf_uri, id, width, height, version, bgcolor, quality, | |
xi_redirect_url, redirect_url, detect_key) | |
{ | |
this.swf_uri = swf_uri; | |
this.streamer = null; | |
this.file = null; | |
} | |
SWFObject.prototype.useExpressInstall = function (key, value) { }; | |
SWFObject.prototype.addParam = function (key, value) { }; | |
SWFObject.prototype.addVariable = function (key, value) { | |
if (key === 'streamer') { | |
this.streamer = value; | |
} else if (key === 'file') { | |
this.file = value; | |
} | |
}; | |
SWFObject.prototype.setAttribute = function (key, value) { }; | |
SWFObject.prototype.write = function (id) { | |
if (this.swf_uri && this.streamer && this.file) { | |
add_rtmp_stream (this.streamer+'/'+this.file, this.swf_uri); | |
} | |
}; | |
// Mock document | |
document = {}; | |
document.getElementById = function (id) { return {}; } | |
// Mock wmvplayer | |
jeroenwijering = {}; | |
jeroenwijering.Player = function (container, source, config) { | |
if (config && config.file) { | |
add_wmv_stream (config.file); | |
} | |
}; | |
html_template: | | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>interactivezoo.eu cameras</title> | |
</head> | |
<body> | |
<h1><a href="http://interactivezoo.eu/">interactivezoo.eu</a> cameras</h1> | |
<p class="note"> | |
Hint: the “Amuuri Leopardid – Pesa kaamera” one is popular. | |
</p> | |
<div id="streams"> | |
<%== InteractiveZooCameras.item_html streams %> | |
</div> | |
<p> | |
Install <strong>flvstreamer</strong> and run <code>streams -g 8001 -f | |
LNX.10,0,45,2</code> first. | |
</p> | |
<p> | |
Users of certain systems may alternatively add the following desktop | |
entry e.g. to their panel and drag and drop the stream links to it. | |
Make sure <strong>vlc</strong> and <strong>flvstreamer</strong> are | |
installed and name the file as e.g. | |
<strong>vlc-flvstreamer.desktop</strong>. | |
</p> | |
<pre id="desktop-entry"><%= InteractiveZooCameras::DATA.desktop_entry %></pre> | |
</body> | |
</html> | |
array_template: | | |
<ul> | |
<% value.each do |item| %> | |
<li><%== InteractiveZooCameras.item_html item %></li> | |
<% end %> | |
</ul> | |
tree_template: | | |
<span class="title"><%= value.title %></span> | |
<%== InteractiveZooCameras.item_html value.child %> | |
stream_template: | | |
<a href="<%= value.uri %>"><%= value.title %></a> | |
(<a href="<%= value.page_uri %>">source</a>) | |
desktop_entry: | | |
[Desktop Entry] | |
Version=1.0 | |
Name=Play with VLC and flvstreamer | |
Exec=sh -ec '((vlc -- "$@" || :; printf q >&4;) 4>&1 >&3 | streams -g 8001 -f LNX.10,0,45,2;) 3>&1' -- %U | |
Icon=vlc | |
Terminal=false | |
Type=Application | |
Categories=AudioVideo;Player; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment