#!/usr/bin/env ruby
#
# IMDB Trivia & Goofs Growler
# By Henrik Nyh <http://henrik.nyh.se> 2008-01-27.
# Free to modify and redistribute with credit.
#
# USAGE
#
# Run this command like
# trivia http://www.imdb.com/title/tt0056937/
# before starting to watch a movie. Any IMDB URL containing a "tt..." type id is fine.
#
# If IMDB has any trivia and goofs listed for this movie, they will appear in pop-ups
# at even intervals for the duration of the movie. Does not work with Front Row. :(
#
# If you need to pause the movie, press ctrl+C once in the Terminal running the command.
# Press the Return key to resume.
# Press ctrl+C twice to quit the program. If quits itself when done.
#
# Run like
# DEBUG=true trivia http://www.imdb.com/title/tt0056937/
# to show things at short intervals instead of spread out over the duration of the movie.
#
#
# PREREQUISITES/INSTALLATION
#
# OS X Leopard has the prerequisite Ruby with all necessary libraries.
#
# You need to install Growl and growlnotify: http://growl.info/
#
# Configure (in the Growl preference pane) Growl to have a 10 second duration.
# I recommend the "Music Video" style at the "Huge" size.
# You can configure this style for the "IMDBTrivia" app only, once Growl's learned
# about that app -- i.e. once you've used this command or done this in a Terminal:
# growlnotify -n IMDBTrivia -m w00t
#
# To be able to run the command with just "trivia", this file needs to be executable
# (chmod +x trivia) and put inside a directory listed in your PATH environment variable.
start_padding = 3 # minutes -- give the user some time to get started
end_padding = 10 # minutes -- complete before credits
MAX_LENGTH = 30 # words -- if longer, split across multiple pop-ups
DURATION = 10 # seconds -- The Growl duration setting
require "open-uri"
require "enumerator" # for enum_slice
require "rubygems"
require "hpricot"
# Handle Ctrl+C quitting
handler = lambda {
trap(:SIGINT) { exit }
puts
puts "Paused. Press the Return key to resume or type Ctrl+C again to quit."
STDIN.readline # since _gets_ will pick the argument off ARGV
trap(:SIGINT, handler)
}
trap(:SIGINT, handler)
class Numeric
def minutes() self * 60 end
end
def bail(message)
puts message
exit
end
# Used to pluralize
def s_for(count)
"s" unless count == 1
end
def parse_items(url, header)
Hpricot(open(url)).
search('ul.trivia li').map {|li| [header, li.inner_text.strip] }
end
def parse_runtime(url)
Hpricot(open(url)).
search("h5").find {|h5| h5.inner_text == "Runtime:" }.parent.inner_text[/\d+/].to_i
rescue
90 # default to 90 minutes
end
def growl(title, message)
parts = message.split.enum_slice(MAX_LENGTH).to_a.map {|message| message.join(' ') }
parts.each_with_index do |part, index|
part_number = index + 1
last = part_number == parts.length
part_title = title
if parts.length > 1
part_title += " (part %s of %s)" % [part_number, parts.length]
part = [("... " if part_number > 1), part, (" ... (cont'd)" unless last)].join
end
system("growlnotify", "-m", part, "-t", part_title, "-a", "Front Row.app", "-n", "IMDBTrivia")
sleep DURATION unless last # This throws off the time count, but it's probably not a big deal
end
end
return unless $0 == __FILE__ # When run directly (and not included), do this:
puts "IMDB Trivia & Goofs Growler"
puts "By Henrik Nyh <http://henrik.nyh.se>"
puts
begin
id = ARGV.first[/tt\d+/]
rescue
bail "Please specify a valid IMDB URL, e.g.: http://www.imdb.com/title/tt0088323/"
end
puts "Retrieving data..."
puts
trivia = parse_items("http://www.imdb.com/title/#{id}/trivia", "Trivia")
goofs = parse_items("http://www.imdb.com/title/#{id}/goofs", "Goof")
items = (trivia + goofs).sort_by { rand }
bail("No trivia or goofs available! Sorry.") if items.empty?
runtime = parse_runtime("http://www.imdb.com/title/#{id}/")
showtime = runtime - start_padding - end_padding - DURATION/60.0*items.length
if showtime < 1
showtime = runtime
start_padding = end_padding = 0
end
inside_padding = showtime/items.length.to_f
if ENV['DEBUG'] == "true"
start_padding = 0
inside_padding = 0.1
end
popuptime = (DURATION/60.0 + inside_padding) * items.length - inside_padding
puts "Showing %s piece%s of trivia/goofs at %.2f minute intervals over %.2f minute%s. Starting in %s minute%s; ending %s minute%s before the movie ends." % [
items.length, s_for(items.length),
inside_padding,
popuptime, s_for(popuptime),
start_padding, s_for(start_padding),
end_padding, s_for(end_padding)
]
puts
puts "Ware spoilers!"
puts
sleep start_padding.minutes
puts "Starting."
items.each_with_index do |item, index|
growl(*item)
print "."
# can't just compare item == items.last, since there could be duplicates
sleep inside_padding.minutes unless (index + 1 == items.length)
end
puts
puts "Done. Exiting."