45 # if ((byte>>bit)&1) ^ z32: |
49 # if ((byte>>bit)&1) ^ z32: |
46 # crc = crc ^ poly |
50 # crc = crc ^ poly |
47 # crc = crc & 0xffffffffL |
51 # crc = crc & 0xffffffffL |
48 # return crc |
52 # return crc |
49 |
53 |
50 decoding_charSpecHR = { |
54 EIT_SHORT_EVENT_DESCRIPTOR = 0x4d |
|
55 EIT_EXTENDED_EVENT_DESCRIPOR = 0x4e |
|
56 |
|
57 CHARSPEC_HR = { |
51 u'Ć': u'\u0106', u'æ': u'\u0107', u'®': u'\u017D', u'¾': u'\u017E', |
58 u'Ć': u'\u0106', u'æ': u'\u0107', u'®': u'\u017D', u'¾': u'\u017E', |
52 u'©': u'\u0160', u'¹': u'\u0161', u'Č': u'\u010C', u'è': u'\u010D', u'ð': u'\u0111' |
59 u'©': u'\u0160', u'¹': u'\u0161', u'Č': u'\u010C', u'è': u'\u010D', u'ð': u'\u0111' |
53 } |
60 } |
54 |
61 |
55 decoding_charSpecCZSK = { |
62 CHARSPEC_CZSK = { |
56 u'Ï'+u'C': u'Č', u'Ï'+u'E': u'Ě', u'Ï'+u'L': u'Ľ', u'Ï'+u'N': u'Ň', u'Ï'+u'R': u'Ř', |
63 u'Ï'+u'C': u'Č', u'Ï'+u'E': u'Ě', u'Ï'+u'L': u'Ľ', u'Ï'+u'N': u'Ň', u'Ï'+u'R': u'Ř', |
57 u'Ï'+u'S': u'Š', u'Ï'+u'T': u'Ť', u'Ï'+u'Z': u'Ž', u'Ï'+u'c': u'č', u'Ï'+u'd': u'ď', |
64 u'Ï'+u'S': u'Š', u'Ï'+u'T': u'Ť', u'Ï'+u'Z': u'Ž', u'Ï'+u'c': u'č', u'Ï'+u'd': u'ď', |
58 u'Ï'+u'e': u'ě', u'Ï'+u'l': u'ľ', u'Ï'+u'n': u'ň', u'Ï'+u'r': u'ř', u'Ï'+u's': u'š', |
65 u'Ï'+u'e': u'ě', u'Ï'+u'l': u'ľ', u'Ï'+u'n': u'ň', u'Ï'+u'r': u'ř', u'Ï'+u's': u'š', |
59 u'Ï'+u't': u'ť', u'Ï'+u'z': u'ž', u'Ï'+u'D': u'Ď', u'Â'+u'A': u'Á', u'Â'+u'E': u'É', |
66 u'Ï'+u't': u'ť', u'Ï'+u'z': u'ž', u'Ï'+u'D': u'Ď', u'Â'+u'A': u'Á', u'Â'+u'E': u'É', |
60 u'Â'+u'I': u'Í', u'Â'+u'O': u'Ó', u'Â'+u'U': u'Ú', u'Â'+u'a': u'á', u'Â'+u'e': u'é', |
67 u'Â'+u'I': u'Í', u'Â'+u'O': u'Ó', u'Â'+u'U': u'Ú', u'Â'+u'a': u'á', u'Â'+u'e': u'é', |
62 u'Ã'+u'O': u'Ô', u'Ê'+u'u': u'ů', u'Ê'+u'U': u'Ů', u'È'+u'A': u'Ä', u'È'+u'E': u'Ë', |
69 u'Ã'+u'O': u'Ô', u'Ê'+u'u': u'ů', u'Ê'+u'U': u'Ů', u'È'+u'A': u'Ä', u'È'+u'E': u'Ë', |
63 u'È'+u'I': u'Ï', u'È'+u'O': u'Ö', u'È'+u'U': u'Ü', u'È'+u'Y': u'Ÿ', u'È'+u'a': u'ä', |
70 u'È'+u'I': u'Ï', u'È'+u'O': u'Ö', u'È'+u'U': u'Ü', u'È'+u'Y': u'Ÿ', u'È'+u'a': u'ä', |
64 u'È'+u'e': u'ë', u'È'+u'i': u'ï', u'È'+u'o': u'ö', u'È'+u'u': u'ü', u'È'+u'y': u'ÿ' |
71 u'È'+u'e': u'ë', u'È'+u'i': u'ï', u'È'+u'o': u'ö', u'È'+u'u': u'ü', u'È'+u'y': u'ÿ' |
65 } |
72 } |
66 |
73 |
67 def emcDebugOut(msg): |
74 def convert_charspec_hr(text): |
68 print msg |
75 for i, j in CHARSPEC_HR.iteritems(): |
69 |
|
70 def convertCharSpecHR(text): |
|
71 for i, j in decoding_charSpecHR.iteritems(): |
|
72 text = text.replace(i, j) |
76 text = text.replace(i, j) |
73 return text |
77 return text |
74 |
78 |
75 def convertCharSpecCZSK(text): |
79 def convert_charspec_czsk(text): |
76 for i, j in decoding_charSpecCZSK.iteritems(): |
80 for i, j in CHARSPEC_CZSK.iteritems(): |
77 text = text.replace(i, j) |
81 text = text.replace(i, j) |
78 return text |
82 return text |
79 |
83 |
80 def parseMJD(mjd): |
84 def parse_mjd(mjd): |
81 """Parse 16 bit unsigned int containing Modified Julian Date, |
85 """Parse 16 bit unsigned int containing Modified Julian Date, |
82 as per DVB-SI spec |
86 as per DVB-SI spec |
83 returning year,month,day""" |
87 returning year,month,day""" |
84 year = int((mjd - 15078.2) / 365.25) |
88 year = int((mjd - 15078.2) / 365.25) |
85 month = int((mjd - 14956.1 - int(year * 365.25)) / 30.6001) |
89 month = int((mjd - 14956.1 - int(year * 365.25)) / 30.6001) |
86 day = mjd - 14956 - int(year * 365.25) - int(month * 30.6001) |
90 day = mjd - 14956 - int(year * 365.25) - int(month * 30.6001) |
87 correction = 0 |
91 correction = 0 |
88 if month == 14 or month == 15: |
92 if month == 14 or month == 15: |
89 correction = 1 |
93 correction = 1 |
90 return (1900 + year + correction), (month - 1 - correction * 12), day |
94 return (1900 + year + correction), (month - 1 - correction * 12), day |
91 |
95 |
92 def bcd2dec(byte): |
96 def bcd2dec(byte): |
93 return (byte >> 4) * 10 + (byte & 0xf) |
97 return (byte >> 4) * 10 + (byte & 0xf) |
|
98 |
|
99 |
|
100 def mkint(data): |
|
101 """ |
|
102 Convert string to Integer |
|
103 """ |
|
104 return int(data) if data else 0 |
|
105 |
|
106 def todate(sdate, stime): |
|
107 """ |
|
108 Convert date and time to datetime tuple |
|
109 """ |
|
110 if sdate and stime: |
|
111 try: |
|
112 return datetime( |
|
113 int(sdate[0]), int(sdate[1]), int(sdate[2]), |
|
114 int(stime[0]), int(stime[1])) |
|
115 except ValueError: |
|
116 return None |
|
117 else: |
|
118 return None |
|
119 |
|
120 def cleanstring(data): |
|
121 """remove nonprintable chars from short desc |
|
122 """ |
|
123 for char in ['\x10', '\x00', '\x02', '\x15']: |
|
124 data = data.replace(char, '') |
|
125 return data |
94 |
126 |
95 def language_iso639_2to3(alpha2): |
127 def language_iso639_2to3(alpha2): |
96 ret = alpha2 |
128 ret = alpha2 |
97 if alpha2 in LanguageCodes: |
129 if alpha2 in LanguageCodes: |
98 language = LanguageCodes[alpha2] |
130 language = LanguageCodes[alpha2] |
100 if name == language: |
132 if name == language: |
101 if len(alpha) == 3: |
133 if len(alpha) == 3: |
102 return alpha |
134 return alpha |
103 return ret |
135 return ret |
104 |
136 |
105 class EitList(): |
137 class EitList(object): |
106 """Eit File support class |
138 """Eit File support class |
107 Description |
139 Description |
108 http://de.wikipedia.org/wiki/Event_Information_Table |
140 http://de.wikipedia.org/wiki/Event_Information_Table |
109 """ |
141 """ |
110 EIT_SHORT_EVENT_DESCRIPTOR = 0x4d |
|
111 EIT_EXTENDED_EVENT_DESCRIPOR = 0x4e |
|
112 |
|
113 def __init__(self, path=None): |
142 def __init__(self, path=None): |
114 self.eit_file = None |
143 self.eit_file = None |
115 |
144 |
116 self.eit = {} |
145 self.eit = {} |
117 self.iso = None |
146 self.iso = None |
118 |
147 |
119 self.__newPath(path) |
148 self.load(path) |
120 self.__readEitFile() |
149 |
121 |
150 def load(self, path): |
122 def __newPath(self, path): |
|
123 name = None |
|
124 if path: |
151 if path: |
125 if self.eit_file != path: |
152 self.eit_file = path |
126 self.eit_file = path |
153 self._read_file() |
127 |
154 |
128 def __mk_int(self, s): |
155 def get_genre(self): |
129 return int(s) if s else 0 |
156 return self.eit.get('genre', "") |
130 |
157 |
131 def __toDate(self, d, t): |
158 def get_components(self): |
132 if d and t: |
159 return self.eit.get('components', "") |
133 try: |
160 |
134 return datetime( |
161 def get_startdate(self): |
135 int(d[0]), int(d[1]), int(d[2]), |
|
136 int(t[0]), int(t[1])) |
|
137 except ValueError: |
|
138 return None |
|
139 else: |
|
140 return None |
|
141 |
|
142 def getEitsid(self): |
|
143 return self.eit.get('service', "") #TODO |
|
144 |
|
145 def getEitTsId(self): |
|
146 return self.eit.get('transportstream', "") #TODO |
|
147 |
|
148 def getEitWhen(self): |
|
149 return self.eit.get('when', "") |
|
150 |
|
151 def getEitStartDate(self): |
|
152 return self.eit.get('startdate', "") |
162 return self.eit.get('startdate', "") |
153 |
163 |
154 def getEitStartTime(self): |
164 def get_starttime(self): |
155 return self.eit.get('starttime', "") |
165 return self.eit.get('starttime', "") |
156 |
166 |
157 def getEitDuration(self): |
167 def get_duration(self): |
158 return self.eit.get('duration', "") |
168 return self.eit.get('duration', "") |
159 |
169 |
160 def getEitName(self): |
170 def get_name(self): |
161 return self.eit.get('name', "").strip() |
171 return self.eit.get('name', "").strip() |
162 |
172 |
163 def getEitDescription(self): |
173 def get_description(self): |
164 return self.eit.get('description', "").strip() |
174 return self.eit.get('description', "").strip() |
165 |
175 |
166 # Wrapper |
176 def get_duration_seconds(self): |
167 def getEitShortDescription(self): |
|
168 return self.getEitName() |
|
169 |
|
170 def getEitExtendedDescription(self): |
|
171 return self.getEitDescription() |
|
172 |
|
173 def getEitLengthInSeconds(self): |
|
174 length = self.eit.get('duration', "") |
177 length = self.eit.get('duration', "") |
175 if len(length) > 2: |
178 if len(length) > 2: |
176 return self.__mk_int((length[0] * 60 + length[1]) * 60 + length[2]) |
179 return mkint((length[0] * 60 + length[1]) * 60 + length[2]) |
177 elif len(length) > 1: |
180 elif len(length) > 1: |
178 return self.__mk_int(length[0] * 60 + length[1]) |
181 return mkint(length[0] * 60 + length[1]) |
179 else: |
182 else: |
180 return self.__mk_int(length) |
183 return mkint(length) |
181 |
184 |
182 def getEitDate(self): |
185 def get_date(self): |
183 return self.__toDate(self.getEitStartDate(), self.getEitStartTime()) |
186 return todate(self.get_startdate(), self.get_starttime()) |
184 |
187 |
185 def dumpEit(self): |
188 def dumpEit(self): |
186 print self.eit |
189 print self.eit |
187 |
190 |
188 ############################################################################## |
191 ############################################################################## |
189 ## File IO Functions |
192 ## File IO Functions |
190 def __readEitFile(self): |
193 def _read_file(self): |
191 data = "" |
194 data = "" |
192 path = self.eit_file |
195 path = self.eit_file |
193 |
196 |
194 lang = language_iso639_2to3( "de" ) |
197 lang = language_iso639_2to3("de") |
195 |
198 |
196 if path and os.path.exists(path): |
199 if path and os.path.exists(path): |
197 print "Reading Event Information Table " + str(path) |
200 print "Reading Event Information Table " + str(path) |
198 |
201 |
199 # Read data from file |
202 # Read data from file |
212 if data and 12 <= len(data): |
215 if data and 12 <= len(data): |
213 # go through events |
216 # go through events |
214 pos = 0 |
217 pos = 0 |
215 e = struct.unpack(">HHBBBBBBH", data[pos:pos + 12]) |
218 e = struct.unpack(">HHBBBBBBH", data[pos:pos + 12]) |
216 event_id = e[0] |
219 event_id = e[0] |
217 date = parseMJD(e[1]) # Y, M, D |
220 date = parse_mjd(e[1]) # Y, M, D |
218 time = bcd2dec(e[2]), bcd2dec(e[3]), bcd2dec(e[4]) # HH, MM, SS |
221 time = bcd2dec(e[2]), bcd2dec(e[3]), bcd2dec(e[4]) # HH, MM, SS |
219 duration = bcd2dec(e[5]), bcd2dec(e[6]), bcd2dec(e[7]) # HH, MM, SS |
222 duration = bcd2dec(e[5]), bcd2dec(e[6]), bcd2dec(e[7]) # HH, MM, SS |
220 running_status = (e[8] & 0xe000) >> 13 |
223 #running_status = (e[8] & 0xe000) >> 13 |
221 free_CA_mode = e[8] & 0x1000 |
224 #free_CA_mode = e[8] & 0x1000 |
222 descriptors_len = e[8] & 0x0fff |
225 descriptors_len = e[8] & 0x0fff |
223 |
226 |
224 if running_status in [1, 2]: |
227 #if running_status in [1, 2]: |
225 self.eit['when'] = "NEXT" |
228 # self.eit['when'] = "NEXT" |
226 elif running_status in [3, 4]: |
229 #elif running_status in [3, 4]: |
227 self.eit['when'] = "NOW" |
230 # self.eit['when'] = "NOW" |
228 |
231 |
229 self.eit['startdate'] = date |
232 self.eit['startdate'] = date |
230 self.eit['starttime'] = time |
233 self.eit['starttime'] = time |
231 self.eit['duration'] = duration |
234 self.eit['duration'] = duration |
232 |
235 |
246 if rec == 0x4D: |
249 if rec == 0x4D: |
247 descriptor_tag = ord(data[pos + 1]) |
250 descriptor_tag = ord(data[pos + 1]) |
248 descriptor_length = ord(data[pos + 2]) |
251 descriptor_length = ord(data[pos + 2]) |
249 ISO_639_language_code = str(data[pos + 3:pos + 5]) |
252 ISO_639_language_code = str(data[pos + 3:pos + 5]) |
250 event_name_length = ord(data[pos + 5]) |
253 event_name_length = ord(data[pos + 5]) |
251 short_event_description = data[pos + 6:pos + 6 + event_name_length] |
254 short_event_description = cleanstring(data[pos + 6:pos + 6 + event_name_length]) |
|
255 |
|
256 tmp_length = ord(data[pos + 6 + event_name_length]) |
|
257 self.eit['genre'] = cleanstring(data[pos + 7 + event_name_length:pos + 7 + tmp_length + event_name_length]) |
|
258 |
252 if ISO_639_language_code == lang: |
259 if ISO_639_language_code == lang: |
253 short_event_descriptor.append(short_event_description) |
260 short_event_descriptor.append(short_event_description) |
254 short_event_descriptor_multi.append(short_event_description) |
261 short_event_descriptor_multi.append(short_event_description) |
255 elif rec == 0x4E: |
262 elif rec == 0x4E: |
256 ISO_639_language_code = str(data[pos + 3:pos + 5]) |
263 ISO_639_language_code = str(data[pos + 3:pos + 5]) |
257 extended_event_description = "" |
264 extended_event_description = "" |
258 extended_event_description_multi = "" |
265 extended_event_description_multi = "" |
259 for i in range (pos+8,pos+length): |
266 for i in range(pos + 8, pos + length): |
260 if str(ord(data[i]))=="138": |
267 if str(ord(data[i])) == "138": |
261 extended_event_description += '\n' |
268 extended_event_description += '\n' |
262 extended_event_description_multi += '\n' |
269 extended_event_description_multi += '\n' |
263 else: |
270 elif data[i] not in ['\x10', '\x00', '\x02', '\x15']: |
264 if data[i]=='\x10' or data[i]=='\x00' or data[i]=='\x02': |
271 extended_event_description += data[i] |
265 pass |
272 extended_event_description_multi += data[i] |
266 else: |
|
267 extended_event_description += data[i] |
|
268 extended_event_description_multi += data[i] |
|
269 if ISO_639_language_code == lang: |
273 if ISO_639_language_code == lang: |
270 extended_event_descriptor.append(extended_event_description) |
274 extended_event_descriptor.append(extended_event_description) |
271 extended_event_descriptor_multi.append(extended_event_description) |
275 extended_event_descriptor_multi.append(extended_event_description) |
272 elif rec == 0x50: |
276 elif rec == 0x50: |
273 component_descriptor.append(data[pos + 8:pos + length]) |
277 #tmp_type = ord(data[pos + 3:pos + 4]) |
|
278 #print "type: %x" % tmp_type |
|
279 component_descriptor.append(cleanstring(data[pos + 8:pos + length])) |
274 elif rec == 0x54: |
280 elif rec == 0x54: |
275 content_descriptor.append(data[pos + 8:pos + length]) |
281 content_descriptor.append(cleanstring(data[pos + 8:pos + length])) |
276 elif rec == 0x4A: |
282 elif rec == 0x4A: |
277 linkage_descriptor.append(data[pos + 8:pos + length]) |
283 linkage_descriptor.append(cleanstring(data[pos + 8:pos + length])) |
278 elif rec == 0x55: |
284 elif rec == 0x55: |
279 parental_rating_descriptor.append(data[pos + 2:pos + length]) |
285 parental_rating_descriptor.append(cleanstring(data[pos + 2:pos + length])) |
280 else: |
286 else: |
281 print "unsupported descriptor: %x %x" % (rec, pos + 12) |
287 print "unsupported descriptor: %x %x" % (rec, pos + 12) |
282 #print data[pos:pos+length] |
288 print data[pos:pos + length] |
283 pass |
289 |
284 pos += length |
290 pos += length |
|
291 |
|
292 self.eit['components'] = ", ".join(component_descriptor) |
|
293 |
|
294 |
285 |
295 |
286 # Very bad but there can be both encodings |
296 # Very bad but there can be both encodings |
287 # User files can be in cp1252 |
297 # User files can be in cp1252 |
288 # Is there no other way? |
298 # Is there no other way? |
289 if short_event_descriptor: |
299 if short_event_descriptor: |
303 except UnicodeDecodeError: |
313 except UnicodeDecodeError: |
304 # do nothing, otherwise cyrillic wont properly displayed |
314 # do nothing, otherwise cyrillic wont properly displayed |
305 #short_event_descriptor = short_event_descriptor.decode("iso-8859-1").encode("utf-8") |
315 #short_event_descriptor = short_event_descriptor.decode("iso-8859-1").encode("utf-8") |
306 pass |
316 pass |
307 if (lang == "cs") or (lang == "sk"): |
317 if (lang == "cs") or (lang == "sk"): |
308 short_event_descriptor = str(convertCharSpecCZSK(short_event_descriptor)) |
318 short_event_descriptor = str(convert_charspec_czsk(short_event_descriptor)) |
309 if lang == "hr": |
319 if lang == "hr": |
310 short_event_descriptor = str(convertCharSpecHR(short_event_descriptor)) |
320 short_event_descriptor = str(convert_charspec_hr(short_event_descriptor)) |
311 self.eit['name'] = short_event_descriptor |
321 self.eit['name'] = short_event_descriptor |
312 |
322 |
313 # Very bad but there can be both encodings |
323 # Very bad but there can be both encodings |
314 # User files can be in cp1252 |
324 # User files can be in cp1252 |
315 # Is there no other way? |
325 # Is there no other way? |
345 def readeit(eitfile): |
355 def readeit(eitfile): |
346 """Module docstring. |
356 """Module docstring. |
347 Read Eit File and show the information. |
357 Read Eit File and show the information. |
348 """ |
358 """ |
349 eitlist = EitList(eitfile) |
359 eitlist = EitList(eitfile) |
350 print "Name: ", eitlist.getEitName() |
360 print "Name: ", eitlist.get_name() |
351 print "StartDate: ", eitlist.getEitStartDate() |
361 print "Genre: ", eitlist.get_genre() |
352 print "Description: ", eitlist.getEitDescription() |
362 print "Components: ", eitlist.get_components() |
353 print "Duration: ", eitlist.getEitDuration() |
363 print "StartDate: ", eitlist.get_date() |
354 print "Seconds: ", eitlist.getEitLengthInSeconds() |
364 print "Description: ", eitlist.get_description() |
|
365 print "Duration: ", eitlist.get_duration() |
|
366 print "Minutes: ", eitlist.get_duration_seconds() / 60 |
355 |
367 |
356 #eitlist.dumpEit() |
368 #eitlist.dumpEit() |
357 |
369 |
358 def main(): |
370 def main(): |
359 # parse command line options |
371 # parse command line options |