Change install dir in default config
[recordtv.git] / src / rtv_tvguide.py
blob8fc3ee3e26123879e1ad4f042be7f8f273f4dc06
1 #!/usr/bin/python
3 import datetime, os
4 import rtv_config, rtv_propertiesfile, rtv_utils, rtv_selection
5 from rtv_orderedpropertiesfile import OrderedPropertiesFile
7 FILENAME_DATE_FORMAT = "%Y-%m-%d"
8 HTML_TIME_FORMAT = "%H:%M"
10 LEFT_OFFSET = 20
11 TOP_OFFSET = 5
12 HOR_PIXELS_PER_MIN = 6.0
13 VER_PIXELS_PER_CHAN = 35
14 HOR_GAP_PIXELS = 10
15 VER_GAP_PIXELS = 12
16 SCREEN_WIDTH_PIXELS = rtv_utils.MINS_IN_DAY * HOR_PIXELS_PER_MIN
18 DAY_START = datetime.time( 6, 0 )
20 class TVGuideGenerator( object ):
22 def __init__( self, config ):
23 self.config = config
24 self.startTime = datetime.datetime.today()
26 def timesHoursToMinsDiff( self, laterTime, earlierTime ):
27 if laterTime.hour > earlierTime.hour:
28 return 60 * (laterTime.hour - earlierTime.hour) + (laterTime.minute - earlierTime.minute)
29 elif laterTime.hour < earlierTime.hour:
30 return 60 * ( laterTime.hour + 24 - earlierTime.hour ) + (laterTime.minute - earlierTime.minute)
31 elif laterTime.minute >= earlierTime.minute:
32 return laterTime.minute - earlierTime.minute
33 else:
34 return rtv_utils.MINS_IN_DAY + (laterTime.minute - earlierTime.minute)
38 def get_times( self ):
40 time_tmpl = rtv_utils.read_file_into_str( self.config.html_time_file )
42 ret = ""
43 tm = DAY_START
44 for i in range( 24 ):
46 startMins = self.timesHoursToMinsDiff( tm, DAY_START )
47 lengthMins = 60
49 x = LEFT_OFFSET + startMins*HOR_PIXELS_PER_MIN
50 width = lengthMins*HOR_PIXELS_PER_MIN - HOR_GAP_PIXELS
52 repl = time_tmpl.replace( "$time", tm.strftime( HTML_TIME_FORMAT ) ).replace(
53 "$left", str( x ) ).replace(
54 "$width", str( width ) )
55 ret += repl
57 if tm.hour == 23:
58 tm = tm.replace( hour=0 )
59 else:
60 tm = tm.replace( hour=tm.hour+1 )
62 return ret
64 def generate( self ):
66 self.favs_and_sels = rtv_selection.read_favs_and_selections(
67 self.config )
69 self.channel_xmltv2tzap = rtv_propertiesfile.PropertiesFile()
70 self.channel_xmltv2tzap.load( self.config.channel_xmltv2tzap_file
73 self.channels_order = OrderedPropertiesFile()
74 self.channels_order.load( self.config.channels_order_file )
76 rtv_utils.ensure_dir_exists( self.config.tvguide_dir )
78 # Hash date -> open file
79 self.html_files = {}
80 self.times = self.get_times()
81 self.channels = ""
83 self.single_day_foot = None
84 self.single_day_head = None
85 ( self.single_day_head_tmpl, self.single_day_foot ) = rtv_utils.read_file_into_str(
86 self.config.html_single_day_file ).split( "$programmes" )
88 self.prog_small_normal = rtv_utils.read_file_into_str(
89 self.config.html_programme_small_normal_file )
90 self.prog_small_highlight = rtv_utils.read_file_into_str(
91 self.config.html_programme_small_highlight_file )
92 self.prog_small_remind = rtv_utils.read_file_into_str(
93 self.config.html_programme_small_remind_file )
94 self.prog_small_record = rtv_utils.read_file_into_str(
95 self.config.html_programme_small_record_file )
97 self.prog_full_normal = rtv_utils.read_file_into_str(
98 self.config.html_programme_full_normal_file )
99 self.prog_full_highlight = rtv_utils.read_file_into_str(
100 self.config.html_programme_full_highlight_file )
101 self.prog_full_remind = rtv_utils.read_file_into_str(
102 self.config.html_programme_full_remind_file )
103 self.prog_full_record = rtv_utils.read_file_into_str(
104 self.config.html_programme_full_record_file )
106 self.cur_id = 0
107 rtv_utils.parse_xmltv_files( self.config, self.sax_callback )
109 for dt in self.html_files.keys():
110 fl = self.html_files[dt]
111 fl.write( rtv_utils.encode_text( self.single_day_foot ) )
112 fl.close()
114 def timedeltaToMins( self, timedelta ):
115 return ( ( timedelta.days * rtv_utils.SECS_IN_DAY ) + timedelta.seconds ) / 60
117 def get_file_for_date( self, date ):
118 if date not in self.html_files:
119 fn = os.path.join( self.config.tvguide_dir,
120 "tv-%s.html" % date.strftime( FILENAME_DATE_FORMAT ) )
121 print "Creating file '%s'" % fn
122 fl = file( fn, "w" )
123 self.html_files[date] = fl
125 # We assume that by the time we get to any programmes, we have been given all the channels
126 if self.single_day_head == None:
127 self.single_day_head = self.single_day_head_tmpl.replace( "$height",
128 str( VER_PIXELS_PER_CHAN - VER_GAP_PIXELS ) )
129 self.single_day_head = self.single_day_head.replace( "$times", self.times )
130 self.single_day_head = self.single_day_head.replace( "$channels", self.channels )
132 fl.write( rtv_utils.encode_text( self.single_day_head ) )
133 else:
134 fl = self.html_files[date]
136 return fl
139 def get_adjusted_date( self, dt ):
140 rdt = dt.date()
141 startTimeTime = dt.time()
142 if startTimeTime < DAY_START:
143 adjStartDate = rdt + datetime.timedelta( - 1 )
144 else:
145 adjStartDate = rdt
146 return adjStartDate
148 def sax_callback( self, pi ):
150 adjStartDate = self.get_adjusted_date( pi.startTime )
151 adjEndDate = self.get_adjusted_date( pi.endTime )
153 fl = self.get_file_for_date( adjStartDate )
154 self.add_prog_to_file( pi, fl, adjStartDate )
156 if adjEndDate != adjStartDate:
157 fl2 = self.get_file_for_date( adjEndDate )
158 self.add_prog_to_file( pi, fl2, adjEndDate )
160 self.cur_id += 1
162 def add_prog_to_file( self, pi, fl, dt ):
164 dayStart = datetime.datetime.combine( dt, DAY_START )
166 extra_style = ""
168 startMins = self.timedeltaToMins( pi.startTime - dayStart )
169 lengthMins = self.timedeltaToMins( pi.endTime - pi.startTime )
170 tmfmt = pi.startTime.time().strftime( HTML_TIME_FORMAT )
172 if startMins + lengthMins < 0:
173 print "Programme appearing too early?"
175 if startMins > rtv_utils.MINS_IN_DAY:
176 print "Programme appearing too late?"
178 if startMins < 0:
179 lengthMins += startMins
180 startMins = 0
181 extra_style += "border-left-style: none"
183 if startMins + lengthMins >= rtv_utils.MINS_IN_DAY:
184 lengthMins = rtv_utils.MINS_IN_DAY - startMins
185 extra_style += "border-right-style: none"
187 if not self.channels_order.has_key( pi.channel ):
188 self.channels_order.set_value( pi.channel, pi.channel )
189 channelNum = self.channels_order.get_position( pi.channel )
191 x = LEFT_OFFSET + startMins*HOR_PIXELS_PER_MIN
193 y = TOP_OFFSET + channelNum*VER_PIXELS_PER_CHAN
195 width = lengthMins*HOR_PIXELS_PER_MIN - HOR_GAP_PIXELS
197 if width > 0:
198 matched = False
199 for fav in self.favs_and_sels:
200 if fav.matches( pi ):
201 matched = True
202 prog_small_html = self.prog_small_record
203 prog_full_html = self.prog_full_record
205 if not matched:
206 prog_small_html = self.prog_small_normal
207 prog_full_html = self.prog_full_normal
209 prog_small_html = prog_small_html.replace( "$top", "%d" % y )
210 prog_small_html = prog_small_html.replace( "$left", "%d" % x )
211 prog_small_html = prog_small_html.replace( "$width", "%d" % width )
212 prog_small_html = prog_small_html.replace( "$time", tmfmt )
213 prog_small_html = prog_small_html.replace( "$title", pi.title )
214 prog_small_html = prog_small_html.replace( "$style", extra_style )
215 prog_small_html = prog_small_html.replace( "$id",
216 "%d" % self.cur_id )
218 fl.write( rtv_utils.encode_text( prog_small_html ) )
220 prog_full_html = prog_full_html.replace( "$title", pi.title )
221 prog_full_html = prog_full_html.replace( "$time", tmfmt )
222 prog_full_html = prog_full_html.replace( "$channel",
223 pi.channel )
224 prog_full_html = prog_full_html.replace( "$desc",
225 pi.description )
227 full_fl = file( os.path.join( self.config.tvguide_dir,
228 "prog-%d.inc" % self.cur_id ), "w" )
229 full_fl.write( rtv_utils.encode_text( prog_full_html ) )
230 full_fl.close()
234 def generate( config ):
235 generator = TVGuideGenerator( config )
236 generator.generate()