#!/usr/bin/ruby -w # With Ruby 1.8.3, yaml gives a lot of warnings... :-(( Fixed with 1.8.4 #!/usr/bin/env ruby # # xmltv2html.rb - A Ruby script to transform the XMLTV output into HTML. # # Version : 0.8.0 # Author : Kurt V. Hindenburg # # Copyright (C) 2003, 2004, 2005 Kurt V. Hindenburg # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # =begin = NAME = SYNOPSIS xmltv2html.rb [arg1 ... argN] < xmltv.xml > listing.html xmltv2html.rb -h|--help|-v|--version = DESCRIPTION ((*xmltv2html*)) is a script that transforms the output from xmltv into HTML. The HTML output has the times horizontally and the shows vertically. The shows' information is displayed via a DHTML popup window. Virtually every aspect of the HTML output can be customized using a configuration file and a CSS file. = OPTIONS : -c, --configfile=FILE Configuration file to use. : --urlprev=URL URL for previous link. : --urlnext=URL URL for next link. : -h or --help Display usage information. : -v or --version Display version information. = AUTHOR Written by Kurt V. Hindenburg = COPYRIGHT Copyright (C) 2003-2005 Kurt V. Hindenburg This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. = FILES * popup.js - The show's information is displayed using this JavaScript DHTML popup window script. * xmltv2html.css - An optional CSS file used in the HTML output. * xmltv2htmlrc - An optional configuration file. = SEE ALSO * ((<"xmltv2html.rb home page - http://xmltv2html.rubyforge.org/"|URL:http://xmltv2html.rubyforge.org/>)) * ((<"xmltv home page - http://sourceforge.net/projects/xmltv"|URL:http://sourceforge.net/projects/xmltv>)) = BUGS * Please process the xmltv.xml file through tv_sort ((*before*)) using xmltv2html.rb =end require 'optparse' require 'yaml' require 'rexml/document' require 'singleton' require 'time' require 'set' require 'ostruct' XMLTV2HTML_VERSION="0.8.0" XMLTV2HTML_DATE="Feb 26, 2006" class Time class << self ### Time. # time string in format 'yyyymmddhhmmss (+/-xxxx)' def parse_xmltv_time(str) begin Time::parse(str.split(' ')[0]) rescue ArgumentError die "Unable to parse #{str}\n\n" end end end ### object. def round_to_interval self - (self.min % @@options[:time_divisor]) * 60 end end module XMLTV2HTML @@options = nil def info(*args) $stderr.print args, "\n" end def die(*args) info args exit(1) end class ConfReader def initialize @@options = self # set Module variable @cmd_line_options = nil clear set_defaults parse_command_line_options_for_config_file read_conf if @@options[:use_config_file] parse_command_line_options # Downcase description search list for ease @@options[:desc_search_list].each { |l| l.downcase! } @@options[:favorites_list].each { |l| l.downcase! } # convert favorites_list to Set for faster lookup @@options[:favorites] = Set.new @@options[:favorites_list] end # Don't edit these, use an external xmltv2htmlrc file. def set_defaults @tree = { :verbose => false, :times_interval => 4, :channels_interval => 4, :time_format_12 => true, :use_favorites => false, :output_favorites => false, :favorites_list => [], :favorites_sort_by_date => true, :desc_search_list => [], :desc_search_sort_by_date => true, :css_filename => 'xmltv2html.css', :categories => {}, :output_date_in_time => false, :date_format => '%a %d', :use_programme_popup => true, :programme_popup_method => "DHTML", :popup_title_format => "%T", :popup_body_format => "%S %P
%D
Rating: %R", :popup_title_color => "#FFFFFF", :popup_title_font => "", :popup_title_font_size => "2", :popup_title_background_color => "#000099", :popup_body_color => "#000000", :popup_body_font => "", :popup_body_font_size => "1", :popup_body_background_color => "#E8E8FF", :popup_body_width => "200", :output_links => false, :start_time => "", :stop_time => "", :time_divisor => 10, # Divide programmes' times in X minute slots :total_hours => 0, :config_file => "", :use_config_file => true, } end def [](key) @tree[key] end def []=(key, value) @tree[key] = value end def clear @tree = Hash.new end def read_conf return if @@options[:config_file].empty? f=File.expand_path(@@options[:config_file]) if not File.exists?(f) or not File.stat(f).readable_real? info "* Unable to read #{f}\n" return end new_xmltv2htmlrc_format = false File.open(f, "r") do |aFile| first_line = aFile.gets new_xmltv2htmlrc_format = true if first_line =~ /^#config_version: 2/ end if new_xmltv2htmlrc_format == false info "\nWhoa! You are using an old xmltv2htmlrc format file (< v0.7.x)." info "Please update your xmltv2htmlrc using the sample file provided" info "with xmltv2html-0.7.0+." die "\n" end info "^ Reading #{f}\n" @tree.merge!(YAML::load(File.open(f))) end # Check to see if --configfile= is present. # TODO: Should be a way to do with with OptionParser def parse_command_line_options_for_config_file ARGV.each do |opts| if opts =~ /--configfile=/ @@options[:config_file] = opts.split(/=/)[1] end end end def parse_command_line_options ARGV.options do |opts| opts.banner = "Usage: #{File.basename($0)} < xmltv.xml > tv.html\n" # separater opts.on_tail opts.on_tail("common options:") opts.on("--configfile=FILE", String, "config file to use") { |@@options[:config_file]|} opts.on("--starttime=YYYYMMDDHHMM", String, "Start time") { |@@options[:start_time]|} opts.on("--stoptime=YYYYMMDDHHMM", String, "Stop time") { |@@options[:stop_time]|} opts.on("--urlprev=URL", String, "URL for previous link") { |@@options[:url_prev]|} opts.on("--urlnext=URL", String, "URL for next link") { |@@options[:url_next]|} # no argument, shows at tail opts.on_tail("-h", "--help", "show this message") {puts opts; exit} opts.on_head("specific options:") # no argument opts.on_tail("-v", "--version", "show version") do print < [show1, show2, ...] class Programmes < Hash def []=(id, programme) #info "Adding #{programme} to #{id}" case programme when Programme begin if empty? or not has_key?(id) super id, Array.new << programme else super id, (fetch(id) << programme) end end when Array begin delete id super id, programme end else $stderr.print "^^^ Programmes []= Unknown class=#{programme.class}\n" end end end class Channel attr_reader(:name, :id, :fullname) attr_accessor(:programmes) attr_accessor(:totalSpan) def initialize(id, fn) @id = id @fullname = fn @number = "" @name = @fullname @totalSpan = 0 # 24 hours * 4 = 96 @programmes = Programmes.new end def <=>(o) fullname.to_i <=> o.fullname.to_i end def <<(p) # info "#{@id} : Adding #{p}\n" @programmes[@id] = p end def number_of_programmes return @programmes[@id].size if @programmes[@id] 0 end def programme_at(i) @programmes[@id][i] if @programmes[@id] end def addDummyProgramme(index, start, stop) info "\n * Adding dummy programme: #{start}, #{stop}\n" + " This could indicate a problem with your xmltv grabber.\n\n" @programmes[@id][index,0] = Programme.new("No Data", "", @id, start, stop, "", "", "", false) @programmes[@id][index].calculateTimeData @programmes[@id][index].calculateProgrammeSlots end # Verify that each show's STOP date is the next show's START date # tv_sort doesn't fix all the start/stop issues... def verifyStopDate dummy_record = OpenStruct.new dummy_progs = [] @programmes[@id].each_index { |si| s = @programmes[@id][si] next_show = @programmes[@id][si.succ] next if next_show == nil next if s.stopTime == next_show.startTime if s.stopTime > next_show.startTime die "\n * Whoa...this is bad!\n" + " * Previous programme's stop time #{s.stopTime}, next\n" + " * programme's start time #{next_show.startTime}\n\n" end dummy_record.index = si + 1 dummy_record.start = s.stopTime dummy_record.stop = next_show.startTime dummy_progs << dummy_record addDummyProgramme(si + 1, s.stopTime, next_show.startTime) # info " * Using tv_sort from the xmltv may correct this!\n" + # " * Currently xmltv2html is unable to continue...\n\n" + # " * Previous programme's stop time #{s.stopTime}, next\n" + # " * programme's start time #{next_show.startTime}\n\n" + # " * Previous programme's stop time #{s.stopTimeStr}, next\n" + # " * programme's start time #{next_show.startTimeStr}\n\n" } end def calc_programme_slots # $stderr.print "\n# programmes=#{number_of_programmes}\n\n" # @programmes[@id].each { |p| # p.calculateSlots # } end def calculateSlots(slist) tinterval = @@options[:channels_interval] * 60 / @@options[:time_divisor] left = [] right = slist.dup index = 0 total = 0 slist.each { |e| cmd = e.slice(0..0).to_s span = e.slice(1..-1).to_i if total + span > tinterval # $stderr.print "removing #{e} ::: " prev = tinterval - total # $stderr.print "adding prev #{prev}; " nxt = total + span - tinterval # $stderr.print "adding next #{nxt}\n" # exit if nxt + prev != span if cmd == "D" # Dummy data left.push "D"+prev.to_s left.push "D"+nxt.to_s else if cmd =="Q" # The removed was a Q left.push "Q"+prev.to_s else left.push "P"+prev.to_s end left.push "Q"+nxt.to_s end right.shift break elsif total + span == tinterval total = 0 left.push e right.shift else left.push e right.shift total += span end index += 1 } left = left.concat(right) # left.each { |i| $stderr.print "#{i}*" }; $stderr.print "\n" left end # Create a slot list to account for displaying channel info # channel_interval = # of hours to display channel info # Handle empty programmes at start/end of channel. def createSlotList l = Array.new span_counter = 0 times_counter = 1 sindex = 0 total = 0 if @@options[:channels_interval] > 0 tinterval = @@options[:channels_interval] * 60 / @@options[:time_divisor] else tinterval = 9999 end ci = tinterval slist = Array.new s = @programmes[@id].first # 1st programme for this channel # $stderr.print "### programmes=#{@programmes[@id].size}\n" # $stderr.print "#{s.class}, startSlot=#{s.startSlot}\n" # $stderr.print "startSlot=#{s.startTime},#{s.startTimeStr}\n" if s.startSlot > 0 # Missing programme at start dummy_span = s.startSlot - span_counter slist.push "D"+dummy_span.to_s end @programmes[@id].each { |s| # if s.title == "No Data" # Where we had to add dummy in middle # slist.push "D"+s.spanSlots.to_s # $stderr.print "push dummy...slots=#{s.spanSlots.to_s}\n" # else slist.push "P"+s.spanSlots.to_s # if s.spanSlots # end } slist.each { |entry| span = entry.slice(1..-1).to_i total += span } dinterval = @@options[:total_hours] * 60 / @@options[:time_divisor] #$stderr.print "totalhours=#{@@options[:total_hours]}\n" #$stderr.print "total=#{total}, dinterval=#{dinterval}\n" if total < dinterval # Not enough programmes' data at end slist.push("D"+(dinterval - total).to_s) end #$stderr.print slist,"\n" total = 0 if @@options[:channels_interval] > 0 sl = calculateSlots(slist) while sl != slist slist = sl.dup sl = calculateSlots(sl) end end slist.each { |entry| span = entry.slice(1..-1).to_i l.push entry total += span l.push("C0") if (total % tinterval) == 0 and @@options[:channels_interval] > 0 } slist = l.unshift("C0") # Add left-most channel #$stderr.print slist,"\n" slist end end # [channel id] -> Channel class Channels < Hash include Singleton attr_accessor(:output_total_hours) def initialize @output_total_hours = 0 # Set to hours displayed @output_start_hour = 0 # Start of hours displayed @output_stop_hour = 0 # Stop hours displayed end def calc_programmes_slots each { |id, c| c.calc_programme_slots } end end class XmlTV attr_reader :srcInfoName attr_reader :genInfoName attr_reader :HTML_title # Title of HTML page attr_reader :top_title # Title of HTML page attr_accessor(:firstShowStartDate, :lastShowStartDate, :lastShowStopDate) attr_reader :desc_match_list def initialize @firstShowStartDate = "99999999999999" @lastShowStartDate = "0" @lastShowStopDate = "0" file = $stdin @doc = REXML::Document.new file @doc.elements.each("tv") { |e| # Should only be one # The date here is the date/time that the user obtained the tv # listings, NOT the date/time of the actual shows. # @date = e.attributes["date"] @srcInfoName = e.attributes["source-info-name"] @genInfoName = e.attributes["generator-info-name"] @HTML_title = "" } end def setTitle(dates) @top_title = "" @HTML_title = "" @top_title += @srcInfoName + " :: " if @srcInfoName t1 = Time.parse(@@options[:start_time]) t2 = Time.parse(@@options[:stop_time]) # t2 = Time.parse(@lastShowStopDate) @time_first = t1.strftime("%a %b %d %I:%M %p") @time_last = t2.strftime("%a %b %d %I:%M %p %Z") if ( (t1.hour == 0) or (t1.hour > 19) ) and (t2.hour < 05) t3 = t1 + (5 * 3600) @top_title += t3.strftime("%A %b %d") @HTML_title = @top_title @top_title += "
" @top_title += "" @top_title += @time_first + " - " + @time_last + "" else @top_title += @time_first + " - " + @time_last @HTML_title = @top_title end end def parseChannels channels = Channels.instance @doc.elements.each("tv/channel") { |element| id = element.attributes["id"] fn = "" element.each_element { |e| if e.name == "display-name" # Use 1st entry fn = e.text() break end } channels[id] = Channel.new(id, fn) } if channels.size < 1 die "No channels in the input file!" end end def parseProgrammes channels = Channels.instance @doc.elements.each("tv/programme") { |element| title="" subtitle="" desc="" rating="" cats = nil rerun = false start = element.attributes["start"] stop = element.attributes["stop"] dstart = "" dstop= "" dstart = start[0..13] dstop = stop[0..13] if stop @firstShowStartDate = dstart if @firstShowStartDate > dstart @lastShowStartDate = dstart if @lastShowStartDate < dstart @lastShowStopDate = dstop if @lastShowStopDate < dstop channel = element.attributes["channel"] element.each_element {|e| title = e.text() if e.name == "title" subtitle = e.text() if e.name == "sub-title" desc=e.text() if e.name == "desc" rerun=true if e.name == "previously-shown" if e.name == "rating" rv = e.elements[1] rating = rv.text() if rv.name == "value" end # Check to see if user want to use special CSS class for category # FIXME: What happens when more than 1 category is triggered? if e.name == "category" if @@options[:categories].has_key?(e.text()) cats = @@options[:categories][e.text()]; end end } channels[channel] << Programme.new(title, subtitle, channel, start, stop, desc, rating, cats, rerun) } channels.each { |id, c| if c.number_of_programmes < 1 die "No programmes for channel #{id} in the input file!" end } end # Set :start_time and :stop_time if needed. # Adjust start/stop_time to round to next hour # Add stop time, should be very last programme stop time. # Correct start, stop times due to --starttime, --stoptime # Remove programmes that are too short # Remove programmes which stop before --starttime # Remove programmes which start after --stoptime def filterProgrammes # $stderr.print "@firstShowStartDate=#{@firstShowStartDate}\n" # $stderr.print "@lastShowStartDate =#{@lastShowStartDate}\n" # $stderr.print "@lastShowStopDate =#{@lastShowStopDate}\n" # Set if --starttime= nor --stoptime= not given. if (@@options[:start_time].empty?) @@options[:start_time] = @firstShowStartDate end if (@@options[:stop_time].empty?) @@options[:stop_time] = @lastShowStopDate end # Force start/stop time on hour @@options[:start_time][10,4] = "0000" if @@options[:stop_time][10,2] != "00" hour = (@@options[:stop_time][8,2]).to_i + 1 if hour > 23 # Next day pstop = Time.parse(@@options[:stop_time]) + (3600) @@options[:stop_time] = pstop.strftime("%Y%m%d%H%M%S") @@options[:stop_time][12,2] = "00" end # FIXME: Below still needed? @@options[:stop_time][8,2] = hour.to_s.rjust(2).sub(/\s/,'0') if hour < 10 end @@options[:stop_time][10,4] = "0000" channels = Channels.instance channels.each { |id, c| programmes = c.programmes # $stderr.print "\n# programmes=#{c.number_of_programmes}\n\n" programmes.each { | id, p| delete_indicies = [] p.each_with_index { |programme, index| if !programme.stopTimeStr programme.setStopTime @@options[:stop_time] end dstart = programme.startTimeStr dstop = programme.stopTimeStr # If programme ends before the desired start time...drop it. nstart = @@options[:start_time].clone nstart[10..11] = @@options[:time_divisor].to_s if dstop < nstart # $stderr.print "#{index}, programme ends #{dstop} before start #{nstart}\n" # p.delete programme # p = p.compact delete_indicies << index next end # If programme starts after the desired stop time...drop it. if (dstart >= @@options[:stop_time]) # $stderr.print "#{index}, programme starts #{dstart} after stoptime #{@@options[:stop_time]}\n" # p.delete programme # p = p.compact delete_indicies << index next end # If programme starts before the desired start time, change it. if (dstart < @@options[:start_time]) programme.setStartTime @@options[:start_time] end # If programme stops after the desired stop time, change it. if (dstop > @@options[:stop_time]) programme.setStopTime @@options[:stop_time] end programme.calculateTimeData programme.calculateProgrammeSlots if programme.spanSlots < 1 # $stderr.print "span slot=#{programme.spanSlots}\n" delete_indicies << index next end } # Delete programme's in reverse order delete_indicies.reverse_each { |i| c.programmes[id].delete_at i c.programmes[id].compact! } c.verifyStopDate # $stderr.print "# programmes=#{c.number_of_programmes}\n" } } end def searchProgrammesDesc @desc_match_list = [] channels = Channels.instance channels.each { |id, c| programmes = c.programmes programmes.each { | id, p| p.each { |programme| @@options[:desc_search_list].each { |s| @desc_match_list << programme if programme.desc.downcase.include?(s) } } } } end end class XMLTV2HTML2 attr_accessor(:dates) def initialize @xml = XmlTV.new @out = Html.new @dates = Array.new end def parseXML @xml.parseChannels @xml.parseProgrammes @xml.filterProgrammes #$stderr.print "start_time=#{@@options[:start_time]}\n" #$stderr.print "stop_time= #{@@options[:stop_time]}\n" pstart = Time.parse(@@options[:start_time]) pstop = Time.parse(@@options[:stop_time]) @@options[:total_hours] = ((pstop - pstart) / 3600).to_i #$stderr.print " hours #{@@options[:total_hours]}\n" channels = Channels.instance channels.calc_programmes_slots fdate = @@options[:start_time][0,8] ldate = @@options[:stop_time][0,8] @xml.setTitle(@dates) @xml.searchProgrammesDesc end def generatePopups i = 0 @out.outputPopupHTMLBegin channels = Channels.instance channels.each { |id, c| i = @out.outputPopupDescs(c, i) } @out.outputPopupHTMLEnd end def generateHTML sindex =-1 times_counter = 1 favorites_list = [] @out.dates = @dates @out.doctype @out.header(@xml.HTML_title) generatePopups if @@options[:use_programme_popup] @out.text_before_table(@xml.top_title) @out.table_start @out.table_times channels = Channels.instance sorted_channels = channels.sort { |a, b| a[1]<=>b[1] } sorted_channels.each { |id, c| @out.outputChannelBegin slot_list = c.createSlotList next unless slot_list i = 0 pi = -1 slot_list.each { |entry| cmd = entry.slice(0..0).to_s span = entry.slice(1..-1).to_i case cmd when "D" @out.outputDummyProgramme(span) when "P" # Programme sindex += 1 pi += 1 prog = c.programme_at(pi) # Where we had to add dummy in middle if prog.title == "No Data" @out.outputDummyProgramme(span) next end @out.outputProgramme(prog, sindex, span) if @@options[:use_favorites] and @@options[:output_favorites] and @@options[:favorites].include?(prog.title.downcase) favorites_list << prog end when "Q" # Use previous Programme's info prog = c.programme_at(pi) # Where we had to add dummy in middle if prog.title == "No Data" @out.outputDummyProgramme(span) next end @out.outputProgramme(prog, sindex, span) when "C" @out.outputChannel(c) else $stderr.print "Unknown slot entry #{entry}\n" ; exit end i += 1 } @out.outputChannelEnd if @@options[:times_interval] > 0 and ((times_counter % @@options[:times_interval]) == 0) @out.table_times end times_counter += 1 } @out.table_end @out.output_favorites_list(favorites_list) if not favorites_list.empty? @out.output_desc_matches(@xml.desc_match_list) if not @xml.desc_match_list.empty? @out.text_after_table @out.footer end end class Html attr_accessor(:hours, :dates) def initialize @hours = 0 # hours displayed end def nl print "\n" end def nb print " " end def dots print "../" end def doctype print '' nl end def header(title) print ''; nl print ''; nl print ''; nl print '' print title if title print ''; nl print ''; nl if @@options[:use_programme_popup] if @@options[:programme_popup_method] == "DHTML" print ''; nl else print ''; nl end end print ''; nl; print ''; nl end def outputPopupHTMLBegin print ''; nl print ''; nl nl end def outputPopupDescs(c, cindex) # The descriptions go here...Text[#]=["title","text"] c.programmes[c.id()].each { |s| title = @@options[:popup_title_format].sub(/\%T/, s.title) title.sub!(/\%R/, s.rating) desc = @@options[:popup_body_format].gsub(/%T/, s.title) desc.sub!(/\%S/, s.subtitle) if (s.previouslyShown) # rerun desc.sub!(/\%P/, "(R)") else desc.sub!(/\%P/, "") end desc.sub!(/%D/, s.desc) desc.sub!(/%R/, s.rating) if s.desc != "" or s.rating != "" print 'Text[',cindex,']=["',title,'","',desc,'"]'; nl s.popupIndex = cindex cindex += 1 end } nl cindex end def footer print '' nl end def text_before_table(text) if text print '

' print text print '

'; nl end if @@options[:url_prev] print '<<< Previous | ' end if @@options[:url_next] print 'Next >>>' end end def text_after_table if @@options[:url_prev] print '<<< Previous | ' end if @@options[:url_next] print 'Next >>>' end print '
' outputInfo outputLinks if @@options[:output_links] end def table_start print ''; nl end def table_end print '
'; nl end # Space for channel names def output_channel_space print ''; nb; print ''; nl end def outputDate days = @hours/24 colspan = 96 colspan += 96 / @@options[:channels_interval] if @@options[:channels_interval] > 0 print ''; nl (0 .. days-1).each { |d| print '','date here',''; nl } print ''; nl end def table_times intervals = 60 / @@options[:time_divisor] # Need hour to start.... we'll force it to be on an hour later starting_day = @@options[:start_time][6,2].to_i starting_hour = @@options[:start_time][8,2].to_i # $stderr.print "Day to start: #{starting_day}\n" # $stderr.print "Hour to start: #{starting_hour}\n" # $stderr.print "Total hours: #{$params.total_hours}\n" print ''; nl output_channel_space cs = 0 (starting_hour .. starting_hour + @@options[:total_hours] - 1).each { |h| (0 .. intervals-1).each { |hh| print ''; printf "%02d", hh * @@options[:time_divisor]; print ''; } cs += 1 output_channel_space if @@options[:channels_interval] > 0 and cs % @@options[:channels_interval] == 0 } print ''; nl print ''; nl output_channel_space days = @hours/24 cs = 0 cdate = Time.parse(@@options[:start_time]) (starting_hour .. starting_hour + @@options[:total_hours] - 1).each { |hi| h = hi % 24 print ''; if @@options[:output_date_in_time] nl; print ''; nl print ''; nl; print '
'; end if @@options[:time_format_12] if h < 12 out = "#{h} am" else out = (h - 12).to_s + " pm" end out.gsub!(/^0/, '12') else out = sprintf "%02d", h end print out if @@options[:output_date_in_time] print ''; print cdate.strftime(@@options[:date_format]) print '
'; nl else print ''; end cs += 1 cdate += 3600 # Add 1 hour output_channel_space if @@options[:channels_interval] > 0 and cs % @@options[:channels_interval] == 0 } print ''; nl end def outputChannelBegin print ''; nl end def outputChannelEnd print ''; nl end def outputChannel(c) print ''; print c.fullname; print ''; nl end def outputDummyProgramme(span) print 'No Data' end def outputProgramme(s, index, slots) return if !s or s.spanSlots == 0 # Invalid programme - too short if slots > 0 print '' elsif s.category print s.category,'">' else print 'programme">' end # Styles : 12=right; 1=center; 2=left; 3=float if @@options[:use_programme_popup] if s.popupIndex >= 0 if @@options[:programme_popup_method] == "DHTML" print ''; nl else print ''; nl end print s.title ; print ''; nl else print s.title ; nl end else print s.title ; nl end print ''; nl end def output_favorites_list(fav_list) if @@options[:favorites_sort_by_date] fav_list = fav_list.sort! { |a, b| a.startTime<=>b.startTime } else fav_list = fav_list.sort! { |a, b| a.title<=>b.title } end print '

'; nl print ''; nl print ''; nl print ''; print ''; print ''; print ''; print ''; nl channels = Channels.instance fav_list.each { |p| print ''; nl print ''; nl outputProgramme(p, p.popupIndex, 1) print ''; nl print ''; nl print ''; nl } print '
Favorites
ChannelTitleStartStop
'; nl print channels[p.channelid].fullname print ''; nl print p.startTime.strftime("%a %b %d %I:%M %p") print ''; nl print p.stopTime.strftime("%a %b %d %I:%M %p") print '
'; nl end def output_desc_matches(list) if @@options[:desc_search_sort_by_date] list = list.sort! { |a, b| a.startTime<=>b.startTime } else list = list.sort! { |a, b| a.title<=>b.title } end print '

'; nl print ''; nl print ''; nl print ''; print ''; print ''; print ''; print ''; nl channels = Channels.instance list.each { |p| print ''; nl print ''; nl outputProgramme(p, p.popupIndex, 1) print ''; nl print ''; nl print ''; nl } print '
Description Search Matches
ChannelTitleStartStop
'; nl print channels[p.channelid].fullname print ''; nl print p.startTime.strftime("%a %b %d %I:%M %p") print ''; nl print p.stopTime.strftime("%a %b %d %I:%M %p") print '
'; nl end def outputInfo print '
' print '' nl print "Generated by xmltv2html.rb v#{XMLTV2HTML_VERSION} (#{XMLTV2HTML_DATE})" print " on #{Time.now}" nl print '' print '
' end def outputLinks nl # print 'Links: ' print 'xmltv' nl print 'xmltv2html' nl end end end include XMLTV2HTML trap("INT") do info "\n Received user interrupt...exiting.\n" exit end confreader = ConfReader.new xmltv2html = XMLTV2HTML2.new time1 = Time.new xmltv2html.parseXML time2 = Time.new $stderr.print "^ Parsing XML took #{time2-time1} seconds.\n" time3 = Time.new xmltv2html.generateHTML time4 = Time.new $stderr.print "^ Generating HTML took #{time4-time3} seconds.\n" exit # vim:set ts=3 sw=3 expandtab: