| 1 | #!/usr/bin/python
|
|---|
| 2 | # scriptlib.py by Ambrosa http://www.ambrosa.net
|
|---|
| 3 | # derived from E2_LOADEPG
|
|---|
| 4 | # 12-Jan-2011
|
|---|
| 5 |
|
|---|
| 6 | __author__ = "ambrosa http://www.ambrosa.net"
|
|---|
| 7 | __copyright__ = "Copyright (C) 2008-2011 Alessandro Ambrosini"
|
|---|
| 8 | __license__ = "CreativeCommons by-nc-sa http://creativecommons.org/licenses/by-nc-sa/3.0/"
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 | import os
|
|---|
| 12 | import sys
|
|---|
| 13 | import time
|
|---|
| 14 | import codecs
|
|---|
| 15 | import crossepg
|
|---|
| 16 |
|
|---|
| 17 | # escape some incorrect chars from filename
|
|---|
| 18 | def fn_escape(s):
|
|---|
| 19 | if type(s).__name__ == 'str':
|
|---|
| 20 | s = s.decode('utf-8')
|
|---|
| 21 |
|
|---|
| 22 | s = s.replace(' ','_')
|
|---|
| 23 | s = s.replace('/','_')
|
|---|
| 24 | s = s.replace(':','_')
|
|---|
| 25 | s = s.replace('.','_')
|
|---|
| 26 | s = s.replace('|','_')
|
|---|
| 27 | s = s.replace('!','_')
|
|---|
| 28 |
|
|---|
| 29 | return(s.encode('utf-8'))
|
|---|
| 30 |
|
|---|
| 31 | # logging class
|
|---|
| 32 | class logging_class:
|
|---|
| 33 |
|
|---|
| 34 | def __init__(self):
|
|---|
| 35 | # get where CrossEPG save data (dbroot) and use it for opening crossepg.log
|
|---|
| 36 | dbroot = crossepg.epgdb_get_dbroot()
|
|---|
| 37 | if dbroot == True:
|
|---|
| 38 | crossepg.log_open(dbroot)
|
|---|
| 39 |
|
|---|
| 40 | def log(self,s):
|
|---|
| 41 | crossepg.log_add(str(s))
|
|---|
| 42 |
|
|---|
| 43 | def log2video_status(self,s):
|
|---|
| 44 | print("LOGTEXT " + str(s))
|
|---|
| 45 |
|
|---|
| 46 | def log2video_scriptname(self,s):
|
|---|
| 47 | print("TYPE RUNNING CSCRIPT " + str(s))
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 | # decompress gzipped data
|
|---|
| 52 | class zlib_class:
|
|---|
| 53 | GZTMP_FILE = "gunzip_temp.gz"
|
|---|
| 54 | UNGZTMP_FILE = "gunzip_temp"
|
|---|
| 55 | BIN_GZUNZIP = "gunzip -c " + GZTMP_FILE
|
|---|
| 56 |
|
|---|
| 57 | def gzuncompress(self,data):
|
|---|
| 58 | fd = open(self.GZTMP_FILE,'w')
|
|---|
| 59 | fd.write(data)
|
|---|
| 60 | fd.close()
|
|---|
| 61 |
|
|---|
| 62 | fd = os.popen(self.BIN_GZUNZIP)
|
|---|
| 63 | data_ungz = fd.read()
|
|---|
| 64 | fd.close()
|
|---|
| 65 | os.unlink(self.GZTMP_FILE)
|
|---|
| 66 | return(data_ungz)
|
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 | # removing old cached epg files **
|
|---|
| 70 | def cleanup_oldcachedfiles(cachedir, field_separator):
|
|---|
| 71 | TODAY = time.strftime("%Y%m%d")
|
|---|
| 72 |
|
|---|
| 73 | for cachedfile in os.listdir(cachedir):
|
|---|
| 74 | # extract date from filename
|
|---|
| 75 | if cachedfile.split(field_separator)[-1] < TODAY :
|
|---|
| 76 | os.unlink(os.path.join(cachedir,cachedfile))
|
|---|
| 77 |
|
|---|
| 78 |
|
|---|
| 79 | # return LOCALTIME - GMTIME (with DST)
|
|---|
| 80 | # return negative number if timezone is east of GMT (like Italy)
|
|---|
| 81 | # return postive number if timezone is west of GMT (like USA)
|
|---|
| 82 | def delta_utc():
|
|---|
| 83 | if time.localtime().tm_isdst == 0 :
|
|---|
| 84 | # return localtime - gmtime (in seconds)
|
|---|
| 85 | return time.timezone
|
|---|
| 86 | else:
|
|---|
| 87 | # return (localtime - gmtime - DST)
|
|---|
| 88 | return time.altzone
|
|---|
| 89 |
|
|---|
| 90 |
|
|---|
| 91 | # return DST time difference (in seconds)
|
|---|
| 92 | def delta_dst():
|
|---|
| 93 | if time.localtime().tm_isdst == 0 :
|
|---|
| 94 | return 0
|
|---|
| 95 | else:
|
|---|
| 96 | # return DST difference
|
|---|
| 97 | return abs(time.altzone - time.timezone)
|
|---|
| 98 |
|
|---|
| 99 |
|
|---|
| 100 | # manage channel list from lamedb
|
|---|
| 101 | class lamedb_class:
|
|---|
| 102 |
|
|---|
| 103 | LAMEDB='/etc/enigma2/lamedb'
|
|---|
| 104 |
|
|---|
| 105 | # initialize an empty dictionary (Python array) indexed by channel name
|
|---|
| 106 | # format: { channel_name : [ (sid , provider) , (sid , provider) , .... ] }
|
|---|
| 107 | INDEXBYCHNAME = True
|
|---|
| 108 | lamedb_dict = {}
|
|---|
| 109 |
|
|---|
| 110 | # lamedb indexed by provider name
|
|---|
| 111 | # format: { provider_name : [ (sid , channel_name) , (sid , channel_name) , .... ] }
|
|---|
| 112 | INDEXBYPROVID = False # if True, also create the array lamedb_dict_prov, usually false for saving memory
|
|---|
| 113 | lamedb_provid_dict = {}
|
|---|
| 114 |
|
|---|
| 115 | def __init__(self, index_by_chname = True, index_by_provid = False):
|
|---|
| 116 | self.INDEXBYCHNAME = index_by_chname
|
|---|
| 117 | self.INDEXBYPROVID = index_by_provid
|
|---|
| 118 | self.read_lamedb()
|
|---|
| 119 |
|
|---|
| 120 | # first of all try to decode a string using UTF-8, if it fails then try with ISO-8859-1
|
|---|
| 121 | # always return an Unicode string
|
|---|
| 122 | def decode_charset(self,s):
|
|---|
| 123 | u = None
|
|---|
| 124 | charset_list = ('utf-8','iso-8859-1','iso-8859-2','iso-8859-15')
|
|---|
| 125 |
|
|---|
| 126 | for charset in charset_list:
|
|---|
| 127 | try:
|
|---|
| 128 | u = unicode(s,charset,"strict")
|
|---|
| 129 | except:
|
|---|
| 130 | pass
|
|---|
| 131 | else:
|
|---|
| 132 | break
|
|---|
| 133 |
|
|---|
| 134 | if u == None:
|
|---|
| 135 | print("CHARSET ERROR while decoding lamedb")
|
|---|
| 136 | sys.exit(1)
|
|---|
| 137 | else:
|
|---|
| 138 | return(u)
|
|---|
| 139 |
|
|---|
| 140 |
|
|---|
| 141 | def read_lamedb(self):
|
|---|
| 142 | if not os.path.exists(self.LAMEDB):
|
|---|
| 143 | print("ERROR ! \'%s\' NOT FOUND" % self.LAMEDB)
|
|---|
| 144 | sys.exit(1)
|
|---|
| 145 |
|
|---|
| 146 | # lamedb mix UTF-8 + iso-8859-* inside it
|
|---|
| 147 | # need charset decoding line by line
|
|---|
| 148 | fd = open(self.LAMEDB,"r")
|
|---|
| 149 |
|
|---|
| 150 | # skip transponder section
|
|---|
| 151 | # read lamedb until are found "end" and "services" lines
|
|---|
| 152 | while True:
|
|---|
| 153 | temp = self.decode_charset(fd.readline())
|
|---|
| 154 | if temp == '' :
|
|---|
| 155 | print("ERROR parsing lamedb, transponder section: end of file")
|
|---|
| 156 | sys.exit(1)
|
|---|
| 157 |
|
|---|
| 158 | temp = temp.strip(' \n\r')
|
|---|
| 159 | if temp == u"end":
|
|---|
| 160 | # next line should be "services"
|
|---|
| 161 | temp = self.decode_charset(fd.readline())
|
|---|
| 162 | temp = temp.strip(' \n\r')
|
|---|
| 163 | if temp == u'services':
|
|---|
| 164 | # reached end of transponder section, end loop and continue with parsing channel section
|
|---|
| 165 | break
|
|---|
| 166 | else:
|
|---|
| 167 | print("ERROR parsing lamedb, transponder section: not found \"end + services\" lines")
|
|---|
| 168 | sys.exit(1)
|
|---|
| 169 |
|
|---|
| 170 | # parsing lamedb channel section
|
|---|
| 171 | while True:
|
|---|
| 172 | sid = self.decode_charset(fd.readline()) # read SID , it's the first line
|
|---|
| 173 |
|
|---|
| 174 | if sid == '' :
|
|---|
| 175 | print("ERROR parsing lamedb, channel_name section: end of file")
|
|---|
| 176 | sys.exit(1)
|
|---|
| 177 |
|
|---|
| 178 | sid = sid.strip(' \n\r')
|
|---|
| 179 |
|
|---|
| 180 | if sid == u'end':
|
|---|
| 181 | # reached end of channel section, end loop
|
|---|
| 182 | break;
|
|---|
| 183 |
|
|---|
| 184 | channel_name = self.decode_charset(fd.readline()) # read channel name, this is the second line
|
|---|
| 185 |
|
|---|
| 186 | channel_name = channel_name.strip(' \n\r').lower() # force channel name lowercase
|
|---|
| 187 |
|
|---|
| 188 | temp = self.decode_charset(fd.readline()) # read provider , this is the third line
|
|---|
| 189 | temp = temp.strip(' \n\r').lower()
|
|---|
| 190 |
|
|---|
| 191 | temp_P = temp.find('p:')
|
|---|
| 192 | if temp_P == -1 :
|
|---|
| 193 | print("ERROR parsing lamedb, channel_name section: provider name \'p:\' not present")
|
|---|
| 194 | sys.exit(1)
|
|---|
| 195 | else:
|
|---|
| 196 | temp = temp[(temp_P + 2):]
|
|---|
| 197 | temp = temp.split(',')[0]
|
|---|
| 198 | temp = temp.strip(' \n\r')
|
|---|
| 199 | if temp == '':
|
|---|
| 200 | provider_name = u'noprovider'
|
|---|
| 201 | else:
|
|---|
| 202 | provider_name = temp.lower()
|
|---|
| 203 |
|
|---|
| 204 | #channel_name=channel_name.encode('utf-8')
|
|---|
| 205 | #provider_name=provider_name.encode('utf-8')
|
|---|
| 206 |
|
|---|
| 207 | if self.INDEXBYCHNAME == True:
|
|---|
| 208 | sp = (sid,provider_name)
|
|---|
| 209 | if channel_name != '':
|
|---|
| 210 | if self.lamedb_dict.has_key(channel_name):
|
|---|
| 211 | self.lamedb_dict[channel_name].append(sp)
|
|---|
| 212 | else:
|
|---|
| 213 | self.lamedb_dict[channel_name]=[sp]
|
|---|
| 214 |
|
|---|
| 215 | if self.INDEXBYPROVID == True:
|
|---|
| 216 | sp = (sid,channel_name)
|
|---|
| 217 | if self.lamedb_provid_dict.has_key(provider_name):
|
|---|
| 218 | self.lamedb_provid_dict[provider_name].append(sp)
|
|---|
| 219 | else:
|
|---|
| 220 | self.lamedb_provid_dict[provider_name]=[sp]
|
|---|
| 221 |
|
|---|
| 222 |
|
|---|
| 223 |
|
|---|
| 224 | fd.close()
|
|---|
| 225 |
|
|---|
| 226 | if len(self.lamedb_dict) == 0 :
|
|---|
| 227 | print("ERROR lamedb empty ?")
|
|---|
| 228 | sys.exit(1)
|
|---|
| 229 |
|
|---|
| 230 |
|
|---|
| 231 | def get_sid_byname(self,channel_name):
|
|---|
| 232 | sid_list = []
|
|---|
| 233 |
|
|---|
| 234 | if self.lamedb_dict.has_key(channel_name) :
|
|---|
| 235 | for v in self.lamedb_dict[channel_name]:
|
|---|
| 236 | # (sid,provider_name)
|
|---|
| 237 | sid_list.append(v[0])
|
|---|
| 238 |
|
|---|
| 239 | return(sid_list)
|
|---|
| 240 |
|
|---|
| 241 |
|
|---|
| 242 | def get_provid_byname(self,channel_name):
|
|---|
| 243 | provid_list = []
|
|---|
| 244 |
|
|---|
| 245 | if self.lamedb_dict.has_key(channel_name) :
|
|---|
| 246 | for v in self.lamedb_dict[channel_name]:
|
|---|
| 247 | # (sid,provider_name)
|
|---|
| 248 | provid_list.append(v[1])
|
|---|
| 249 |
|
|---|
| 250 | return(provid_list)
|
|---|
| 251 |
|
|---|
| 252 | def get_sidprovid_byname(self,channel_name):
|
|---|
| 253 | sidprov_list = []
|
|---|
| 254 | if self.lamedb_dict.has_key(channel_name) :
|
|---|
| 255 | # (sid,provider_name)
|
|---|
| 256 | sidprov_list = self.lamedb_dict[channel_name]
|
|---|
| 257 |
|
|---|
| 258 | return(sidprov_list)
|
|---|
| 259 |
|
|---|
| 260 |
|
|---|
| 261 | def get_chnames_byprov(self,provider_name):
|
|---|
| 262 | if self.INDEXBYPROVID == True:
|
|---|
| 263 | if self.lamedb_provid_dict.has_key(provider_name) :
|
|---|
| 264 | return self.lamedb_provid_dict[provider_name]
|
|---|
| 265 | else:
|
|---|
| 266 | return None
|
|---|
| 267 | return None
|
|---|
| 268 |
|
|---|
| 269 | def convert_sid(self,sid):
|
|---|
| 270 | s=[]
|
|---|
| 271 |
|
|---|
| 272 | # SID:ns:TSID:ONID:stype:unused
|
|---|
| 273 |
|
|---|
| 274 | try:
|
|---|
| 275 | tmp = sid.split(":")
|
|---|
| 276 | s.append(int(tmp[0],0x10)) # SID
|
|---|
| 277 | s.append(int(tmp[2],0X10)) # TSID
|
|---|
| 278 | s.append(int(tmp[3],0X10)) # ONID
|
|---|
| 279 | except:
|
|---|
| 280 | pass
|
|---|
| 281 |
|
|---|
| 282 | return(s)
|
|---|
| 283 |
|
|---|
| 284 |
|
|---|
| 285 | class crossepg_db_class:
|
|---|
| 286 |
|
|---|
| 287 | db_channel_ref = ''
|
|---|
| 288 | event_id = 1
|
|---|
| 289 |
|
|---|
| 290 | title_ref = ''
|
|---|
| 291 |
|
|---|
| 292 | def __init__(self):
|
|---|
| 293 | pass
|
|---|
| 294 |
|
|---|
| 295 | def open_db(self):
|
|---|
| 296 | # get where CrossEPG save data (dbroot)
|
|---|
| 297 | dbroot = crossepg.epgdb_get_dbroot()
|
|---|
| 298 | # open CrossEPG database
|
|---|
| 299 | if not crossepg.epgdb_open(dbroot):
|
|---|
| 300 | print("ERROR opening CrossEPG database")
|
|---|
| 301 | sys.exit(1)
|
|---|
| 302 |
|
|---|
| 303 | # load database structures (index, ....)
|
|---|
| 304 | crossepg.epgdb_load()
|
|---|
| 305 |
|
|---|
| 306 | def close_db(self):
|
|---|
| 307 | # save data
|
|---|
| 308 | if crossepg.epgdb_save(None):
|
|---|
| 309 | print("CrossEPG data saved")
|
|---|
| 310 | else:
|
|---|
| 311 | print("CrossEPG Error saving data")
|
|---|
| 312 |
|
|---|
| 313 | # close epgdb and clean memory
|
|---|
| 314 | crossepg.epgdb_close()
|
|---|
| 315 | crossepg.epgdb_clean()
|
|---|
| 316 |
|
|---|
| 317 |
|
|---|
| 318 | # add channel into db and get a reference to the structure
|
|---|
| 319 | # doesn't matter if the channel already exist... epgdb do all the work
|
|---|
| 320 | def add_channel(self,ch_sid):
|
|---|
| 321 | # epgdb_channels_add(onid, tsid, sid)
|
|---|
| 322 | self.db_channel_ref = crossepg.epgdb_channels_add(ch_sid[2], ch_sid[1], ch_sid[0])
|
|---|
| 323 | self.event_id = 1
|
|---|
| 324 |
|
|---|
| 325 | # add an EPG event
|
|---|
| 326 | def add_event(self, start_time, duration, title=' ', summarie=' ', language='eng', utf8=False):
|
|---|
| 327 | start_time = int(start_time)
|
|---|
| 328 | duration = int(duration)
|
|---|
| 329 |
|
|---|
| 330 | if (duration < 0) or (duration > 65535) :
|
|---|
| 331 | # duration must be >= 0 or < 65536 , skip this event (it's an error)
|
|---|
| 332 | print("DEBUG: length error %d" % duration)
|
|---|
| 333 | return
|
|---|
| 334 |
|
|---|
| 335 | event_ref = crossepg.epgdb_title_alloc() # alloc title structure in memory
|
|---|
| 336 | event_ref.event_id = self.event_id # event_id is unique inside a channel
|
|---|
| 337 | self.event_id += 1
|
|---|
| 338 |
|
|---|
| 339 | event_ref.start_time = start_time # Unix timestamp, always referred to gmt+0 without daylight saving
|
|---|
| 340 | event_ref.mjd = crossepg.epgdb_calculate_mjd(event_ref.start_time) # Modified Julian Date. if you don't know it you can calulate it with epgdb_calculate_mjd()
|
|---|
| 341 |
|
|---|
| 342 | # print(" title %s , starttime %s , duration %f" % (title, start_time, duration))
|
|---|
| 343 | event_ref.length = duration # event duration in seconds
|
|---|
| 344 |
|
|---|
| 345 | # ISO 639 language code. http://en.wikipedia.org/wiki/ISO_639
|
|---|
| 346 | event_ref.iso_639_1 = ord(language[0:1])
|
|---|
| 347 | event_ref.iso_639_2 = ord(language[1:2])
|
|---|
| 348 | event_ref.iso_639_3 = ord(language[2:3])
|
|---|
| 349 |
|
|---|
| 350 | # add event in epgdb and return back a reference to the structure
|
|---|
| 351 | # remember to use always the new structure reference
|
|---|
| 352 | # if the event already exist epgdb update it and automatically destroy the new structure
|
|---|
| 353 | event_ref = crossepg.epgdb_titles_add(self.db_channel_ref, event_ref)
|
|---|
| 354 |
|
|---|
| 355 |
|
|---|
| 356 | #print("DEBUG , title DATA TYPE: \'%s\'" % type(title).__name__ )
|
|---|
| 357 | #print("DEBUG , summarie DATA TYPE: \'%s\'" % type(summarie).__name__ )
|
|---|
| 358 |
|
|---|
| 359 | if utf8 == False :
|
|---|
| 360 | crossepg.epgdb_titles_set_description(event_ref, title);
|
|---|
| 361 | crossepg.epgdb_titles_set_long_description(event_ref, summarie);
|
|---|
| 362 | else:
|
|---|
| 363 | crossepg.epgdb_titles_set_description_utf8(event_ref, title);
|
|---|
| 364 | crossepg.epgdb_titles_set_long_description_utf8(event_ref, summarie);
|
|---|
| 365 |
|
|---|
| 366 |
|
|---|