Skip to content

Instantly share code, notes, and snippets.

@tors
Forked from henrik/eyetv_xmltv_grabber.rb
Created October 28, 2010 15:00
Show Gist options
  • Save tors/651512 to your computer and use it in GitHub Desktop.
Save tors/651512 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# SweDB XMLTV Grabber for EyeTV
# by Henrik Nyh <http://henrik.nyh.se> under the MIT License.
# Configure the list of channels below and run this script as a cron job, e.g.
# * */12 * * * ruby /path/to/this/script.rb
# to run it every 12 hours (suggested since cron jobs don't run if the computer is off).
# For more detailed instructions (in Swedish), see
# http://forums.elgato.com/viewtopic.php?f=86&t=1627&p=16279#p16420.
# Every time it runs it will get schedules for those channels and import them into EyeTV 3.
# The script uses conditional GET to only update files if they've changed.
# CHANGELOG
# 2009-05-05 1.3: MIT License.
# 2008-10-30 1.2: EyeTV no longer steals focus on import.
# 2008-09-15 1.1: Always update channel list; not just if updated on server. Don't break on 404.
# 2008-03-09 1.0: First version.
# Use ids as listed in http://tv.swedb.se/xmltv/channels.xml.gz ("View Source" to see the XML with ids).
CHANNELS = %w[
kanal9.se
hd.svt.se
history.viasat.se
world.discovery.com
nordic.travel.discovery.com
ztv.se
nature.viasat.se
showtime.nonstop.tv
silver.nonstop.tv
]
%w[zlib open-uri date fileutils rubygems hpricot].each {|lib| require lib }
DAYS = 15 # A value from 1 to 15. Fetches yesterday + n days.
# We need yesterday for today's early morning shows.
ROOT_URL = "http://tv.swedb.se/xmltv/channels.xml.gz"
USER_AGENT = "SweDB XMLTV Grabber for EyeTV/1.3 <henrik@nyh.se>"
DIRECTORY = "/Library/Application Support/EyeTV/XMLTV"
DB = File.join(DIRECTORY, "condget.db")
SCHEDULE_RE = /_(\d{4}-\d{2}-\d{2})\.xml$/
YESTERDAY = Date.today - 1
# Load or create DB hash mapping file paths to Conditional GET modification dates.
# Using file paths instead of URLs since this makes removing out-of-date entries easier.
$db = File.open(DB) {|file| Marshal.load(file) } rescue Hash.new("")
# This method does conditional GET based on Last-Modified/If-Modified-Since only (no etag)
# and assumes gzipped content.
# Returns local file path if new content was retrieved, false otherwise.
def condget(url)
filename = File.basename(url, ".gz")
path = path_from_url(url)
open(url, "If-Modified-Since" => $db[path], "User-Agent" => USER_AGENT) do |response|
# Store last-modified
$db[path] = response.meta["last-modified"]
# Gunzip
response_body = Zlib::GzipReader.new(response).read
File.open(path, 'w') {|file| file.write response_body }
path
end
rescue OpenURI::HTTPError => e
raise unless e.message =~ /^(304|404)/
false
end
def path_from_url(url)
filename = File.basename(url, ".gz")
File.join(DIRECTORY, filename)
end
# Remove expired schedules
glob = File.join(DIRECTORY, "*.xml")
expired_files = Dir[glob].select {|file| file =~ SCHEDULE_RE && Date.parse($1) < YESTERDAY }
expired_files.each do |file|
File.delete(file)
$db.delete(file)
end
# Update scheduling
updates = []
# Create directory structure if it doesn't exist already
FileUtils.mkdir_p DIRECTORY
# Get and parse root file
new_root_file = condget(ROOT_URL)
doc = Hpricot(open(path_from_url(ROOT_URL)))
# Create active_channels.xml file containing only the requested channels
excluded_channels = doc.search('channel').reject {|channel| CHANNELS.include?(channel[:id]) }
Hpricot::Elements[*excluded_channels].remove
active_channels_file = File.join(DIRECTORY, "active_channels.xml")
active_channels_xml = doc.to_s.gsub(/\n\s+\n/, "\n") # get rid of excess whitespace
File.open(active_channels_file, "w") {|file| file.write active_channels_xml }
updates << active_channels_file
# Loop over channels and get schedules
channels = doc.search('channel').map {|channel| [channel[:id], channel.at('base-url').inner_text] }
channels.each do |channel|
id, base_url = channel
(YESTERDAY).upto(YESTERDAY + DAYS) do |day|
filename = "#{id}_#{day}.xml.gz"
url = File.join(base_url, filename)
if path = condget(url)
updates << path
end
end
end
# Persist the conditional GET DB hash to disk
File.open(DB, "w") {|file| Marshal.dump($db, file) }
# Do the actual EyeTV import
command = %w[open -ga EyeTV] + updates
system(*command)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment