Report abuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/usr/bin/env ruby

# SweDB XMLTV Grabber for EyeTV
# by Henrik Nyh <http://henrik.nyh.se> 2008-03-09.
# Free to modify and redistribute with credit.

# 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).
# 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.

# Use ids as listed in http://tv.swedb.se/xmltv/channels.xml.gz.
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.0 <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 -a EyeTV] + updates
system(*command)