Skip to content

Instantly share code, notes, and snippets.

@ion1
Created April 30, 2010 12:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ion1/385097 to your computer and use it in GitHub Desktop.
Save ion1/385097 to your computer and use it in GitHub Desktop.
Overengineer much?
#!/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