Sat, 04 Nov 2017 20:56:03 +0100
pylint the crap
0 | 1 | #!/usr/bin/python |
1 | 2 | # -*- coding: utf-8 -*- |
3 | # iso-8859-2 | |
0 | 4 | # |
5 | # EitSupport | |
6 | # Copyright (C) 2011 betonme | |
7 | # Copyright (C) 2016 Wolfgang Fahl | |
8 | # | |
9 | # This EITParser is based on: | |
10 | # https://github.com/betonme/e2openplugin-EnhancedMovieCenter/blob/master/src/EitSupport.py | |
11 | # | |
12 | # In case of reuse of this source code please do not remove this copyright. | |
13 | # | |
14 | # This program is free software: you can redistribute it and/or modify | |
15 | # it under the terms of the GNU General Public License as published by | |
16 | # the Free Software Foundation, either version 3 of the License, or | |
17 | # (at your option) any later version. | |
18 | # | |
19 | # This program is distributed in the hope that it will be useful, | |
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | # GNU General Public License for more details. | |
23 | # | |
24 | # For more information on the GNU General Public License see: | |
25 | # <http://www.gnu.org/licenses/>. | |
26 | # | |
27 | ||
28 | import os | |
29 | import struct | |
1 | 30 | import sys |
31 | import getopt | |
0 | 32 | |
33 | from datetime import datetime | |
34 | ||
35 | from ISO639 import LanguageCodes | |
36 | ||
37 | #def crc32(data): | |
38 | # poly = 0x4c11db7 | |
39 | # crc = 0xffffffffL | |
40 | # for byte in data: | |
41 | # byte = ord(byte) | |
42 | # for bit in range(7,-1,-1): # MSB to LSB | |
43 | # z32 = crc>>31 # top bit | |
44 | # crc = crc << 1 | |
45 | # if ((byte>>bit)&1) ^ z32: | |
46 | # crc = crc ^ poly | |
47 | # crc = crc & 0xffffffffL | |
48 | # return crc | |
49 | ||
1 | 50 | decoding_charSpecHR = { |
51 | 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' | |
53 | } | |
0 | 54 | |
1 | 55 | decoding_charSpecCZSK = { |
56 | 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'ď', | |
58 | 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'É', | |
60 | u'Â'+u'I': u'Í', u'Â'+u'O': u'Ó', u'Â'+u'U': u'Ú', u'Â'+u'a': u'á', u'Â'+u'e': u'é', | |
61 | u'Â'+u'i': u'í', u'Â'+u'o': u'ó', u'Â'+u'u': u'ú', u'Â'+u'y': u'ý', u'Ã'+u'o': u'ô', | |
62 | 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'ä', | |
64 | u'È'+u'e': u'ë', u'È'+u'i': u'ï', u'È'+u'o': u'ö', u'È'+u'u': u'ü', u'È'+u'y': u'ÿ' | |
65 | } | |
66 | ||
67 | def emcDebugOut(msg): | |
68 | print msg | |
0 | 69 | |
70 | def convertCharSpecHR(text): | |
71 | for i, j in decoding_charSpecHR.iteritems(): | |
72 | text = text.replace(i, j) | |
73 | return text | |
74 | ||
75 | def convertCharSpecCZSK(text): | |
76 | for i, j in decoding_charSpecCZSK.iteritems(): | |
77 | text = text.replace(i, j) | |
78 | return text | |
79 | ||
1 | 80 | def parseMJD(mjd): |
81 | """Parse 16 bit unsigned int containing Modified Julian Date, | |
82 | as per DVB-SI spec | |
83 | returning year,month,day""" | |
84 | year = int((mjd - 15078.2) / 365.25) | |
85 | month = int((mjd - 14956.1 - int(year * 365.25)) / 30.6001) | |
86 | day = mjd - 14956 - int(year * 365.25) - int(month * 30.6001) | |
87 | correction = 0 | |
88 | if month == 14 or month == 15: | |
89 | correction = 1 | |
90 | return (1900 + year + correction), (month - 1 - correction * 12), day | |
0 | 91 | |
1 | 92 | def bcd2dec(byte): |
93 | return (byte >> 4) * 10 + (byte & 0xf) | |
0 | 94 | |
95 | def language_iso639_2to3(alpha2): | |
96 | ret = alpha2 | |
97 | if alpha2 in LanguageCodes: | |
98 | language = LanguageCodes[alpha2] | |
99 | for alpha, name in LanguageCodes.items(): | |
100 | if name == language: | |
101 | if len(alpha) == 3: | |
102 | return alpha | |
103 | return ret | |
104 | ||
105 | class EitList(): | |
106 | """Eit File support class | |
107 | Description | |
108 | http://de.wikipedia.org/wiki/Event_Information_Table | |
109 | """ | |
1 | 110 | EIT_SHORT_EVENT_DESCRIPTOR = 0x4d |
111 | EIT_EXTENDED_EVENT_DESCRIPOR = 0x4e | |
0 | 112 | |
113 | def __init__(self, path=None): | |
114 | self.eit_file = None | |
115 | ||
116 | self.eit = {} | |
117 | self.iso = None | |
118 | ||
119 | self.__newPath(path) | |
120 | self.__readEitFile() | |
121 | ||
122 | def __newPath(self, path): | |
123 | name = None | |
124 | if path: | |
125 | if self.eit_file != path: | |
126 | self.eit_file = path | |
127 | ||
128 | def __mk_int(self, s): | |
129 | return int(s) if s else 0 | |
130 | ||
131 | def __toDate(self, d, t): | |
132 | if d and t: | |
133 | try: | |
1 | 134 | return datetime( |
135 | int(d[0]), int(d[1]), int(d[2]), | |
136 | int(t[0]), int(t[1])) | |
0 | 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', "") | |
153 | ||
154 | def getEitStartTime(self): | |
155 | return self.eit.get('starttime', "") | |
156 | ||
157 | def getEitDuration(self): | |
158 | return self.eit.get('duration', "") | |
159 | ||
160 | def getEitName(self): | |
161 | return self.eit.get('name', "").strip() | |
162 | ||
163 | def getEitDescription(self): | |
164 | return self.eit.get('description', "").strip() | |
165 | ||
166 | # Wrapper | |
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', "") | |
1 | 175 | if len(length) > 2: |
176 | return self.__mk_int((length[0] * 60 + length[1]) * 60 + length[2]) | |
177 | elif len(length) > 1: | |
178 | return self.__mk_int(length[0] * 60 + length[1]) | |
0 | 179 | else: |
180 | return self.__mk_int(length) | |
181 | ||
182 | def getEitDate(self): | |
183 | return self.__toDate(self.getEitStartDate(), self.getEitStartTime()) | |
184 | ||
185 | def dumpEit(self): | |
186 | print self.eit | |
187 | ||
188 | ############################################################################## | |
189 | ## File IO Functions | |
190 | def __readEitFile(self): | |
191 | data = "" | |
192 | path = self.eit_file | |
193 | ||
194 | lang = language_iso639_2to3( "de" ) | |
195 | ||
196 | if path and os.path.exists(path): | |
1 | 197 | print "Reading Event Information Table " + str(path) |
0 | 198 | |
199 | # Read data from file | |
1 | 200 | fd = None |
0 | 201 | try: |
1 | 202 | fd = open(path, 'rb') |
0 | 203 | #lines = f.readlines() |
1 | 204 | data = fd.read() |
205 | except Exception, err: | |
206 | print "[META] Exception in readEitFile: " + str(err) | |
0 | 207 | finally: |
1 | 208 | if fd is not None: |
209 | fd.close() | |
0 | 210 | |
211 | # Parse the data | |
212 | if data and 12 <= len(data): | |
213 | # go through events | |
214 | pos = 0 | |
1 | 215 | e = struct.unpack(">HHBBBBBBH", data[pos:pos + 12]) |
0 | 216 | event_id = e[0] |
1 | 217 | date = parseMJD(e[1]) # Y, M, D |
218 | 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 | |
220 | running_status = (e[8] & 0xe000) >> 13 | |
221 | free_CA_mode = e[8] & 0x1000 | |
0 | 222 | descriptors_len = e[8] & 0x0fff |
223 | ||
1 | 224 | if running_status in [1, 2]: |
0 | 225 | self.eit['when'] = "NEXT" |
1 | 226 | elif running_status in [3, 4]: |
0 | 227 | self.eit['when'] = "NOW" |
228 | ||
229 | self.eit['startdate'] = date | |
230 | self.eit['starttime'] = time | |
231 | self.eit['duration'] = duration | |
232 | ||
233 | pos = pos + 12 | |
234 | short_event_descriptor = [] | |
235 | short_event_descriptor_multi = [] | |
236 | extended_event_descriptor = [] | |
237 | extended_event_descriptor_multi = [] | |
238 | component_descriptor = [] | |
239 | content_descriptor = [] | |
240 | linkage_descriptor = [] | |
241 | parental_rating_descriptor = [] | |
242 | endpos = len(data) - 1 | |
243 | while pos < endpos: | |
244 | rec = ord(data[pos]) | |
1 | 245 | length = ord(data[pos + 1]) + 2 |
0 | 246 | if rec == 0x4D: |
1 | 247 | descriptor_tag = ord(data[pos + 1]) |
248 | descriptor_length = ord(data[pos + 2]) | |
249 | ISO_639_language_code = str(data[pos + 3:pos + 5]) | |
250 | event_name_length = ord(data[pos + 5]) | |
251 | short_event_description = data[pos + 6:pos + 6 + event_name_length] | |
0 | 252 | if ISO_639_language_code == lang: |
253 | short_event_descriptor.append(short_event_description) | |
254 | short_event_descriptor_multi.append(short_event_description) | |
255 | elif rec == 0x4E: | |
1 | 256 | ISO_639_language_code = str(data[pos + 3:pos + 5]) |
0 | 257 | extended_event_description = "" |
258 | extended_event_description_multi = "" | |
259 | for i in range (pos+8,pos+length): | |
260 | if str(ord(data[i]))=="138": | |
261 | extended_event_description += '\n' | |
262 | extended_event_description_multi += '\n' | |
263 | else: | |
1 | 264 | if data[i]=='\x10' or data[i]=='\x00' or data[i]=='\x02': |
0 | 265 | pass |
266 | else: | |
267 | extended_event_description += data[i] | |
268 | extended_event_description_multi += data[i] | |
269 | if ISO_639_language_code == lang: | |
270 | extended_event_descriptor.append(extended_event_description) | |
271 | extended_event_descriptor_multi.append(extended_event_description) | |
272 | elif rec == 0x50: | |
1 | 273 | component_descriptor.append(data[pos + 8:pos + length]) |
0 | 274 | elif rec == 0x54: |
1 | 275 | content_descriptor.append(data[pos + 8:pos + length]) |
0 | 276 | elif rec == 0x4A: |
1 | 277 | linkage_descriptor.append(data[pos + 8:pos + length]) |
0 | 278 | elif rec == 0x55: |
1 | 279 | parental_rating_descriptor.append(data[pos + 2:pos + length]) |
0 | 280 | else: |
1 | 281 | print "unsupported descriptor: %x %x" % (rec, pos + 12) |
0 | 282 | #print data[pos:pos+length] |
283 | pass | |
284 | pos += length | |
285 | ||
1 | 286 | # Very bad but there can be both encodings |
287 | # User files can be in cp1252 | |
288 | # Is there no other way? | |
289 | if short_event_descriptor: | |
290 | short_event_descriptor = "".join(short_event_descriptor) | |
291 | else: | |
292 | short_event_descriptor = "".join(short_event_descriptor_multi) | |
293 | if short_event_descriptor: | |
294 | #try: | |
295 | # short_event_descriptor = short_event_descriptor.decode("iso-8859-1").encode("utf-8") | |
296 | #except UnicodeDecodeError: | |
297 | # pass | |
298 | try: | |
299 | short_event_descriptor.decode('utf-8') | |
300 | except UnicodeDecodeError: | |
0 | 301 | try: |
1 | 302 | short_event_descriptor = short_event_descriptor.decode("cp1252").encode("utf-8") |
0 | 303 | except UnicodeDecodeError: |
1 | 304 | # do nothing, otherwise cyrillic wont properly displayed |
305 | #short_event_descriptor = short_event_descriptor.decode("iso-8859-1").encode("utf-8") | |
306 | pass | |
307 | if (lang == "cs") or (lang == "sk"): | |
308 | short_event_descriptor = str(convertCharSpecCZSK(short_event_descriptor)) | |
309 | if lang == "hr": | |
310 | short_event_descriptor = str(convertCharSpecHR(short_event_descriptor)) | |
311 | self.eit['name'] = short_event_descriptor | |
0 | 312 | |
1 | 313 | # Very bad but there can be both encodings |
314 | # User files can be in cp1252 | |
315 | # Is there no other way? | |
316 | if extended_event_descriptor: | |
317 | extended_event_descriptor = "".join(extended_event_descriptor) | |
318 | else: | |
319 | extended_event_descriptor = "".join(extended_event_descriptor_multi) | |
320 | if extended_event_descriptor: | |
321 | #try: | |
322 | # extended_event_descriptor = extended_event_descriptor.decode("iso-8859-1").encode("utf-8") | |
323 | #except UnicodeDecodeError: | |
324 | # pass | |
325 | try: | |
326 | extended_event_descriptor.decode('utf-8') | |
327 | except UnicodeDecodeError: | |
0 | 328 | try: |
1 | 329 | extended_event_descriptor = extended_event_descriptor.decode("cp1252").encode("utf-8") |
0 | 330 | except UnicodeDecodeError: |
1 | 331 | # do nothing, otherwise cyrillic wont properly displayed |
332 | #extended_event_descriptor = extended_event_descriptor.decode("iso-8859-1").encode("utf-8") | |
333 | pass | |
334 | if (lang == "cs") or (lang == "sk"): | |
335 | extended_event_descriptor = str(convertCharSpecCZSK(extended_event_descriptor)) | |
336 | if lang == "hr": | |
337 | extended_event_descriptor = str(convertCharSpecHR(extended_event_descriptor)) | |
338 | self.eit['description'] = extended_event_descriptor | |
0 | 339 | |
1 | 340 | else: |
341 | # No data clear all | |
342 | self.eit = {} | |
0 | 343 | |
344 | ||
345 | def readeit(eitfile): | |
1 | 346 | """Module docstring. |
347 | Read Eit File and show the information. | |
348 | """ | |
349 | eitlist = EitList(eitfile) | |
0 | 350 | print "Name: ", eitlist.getEitName() |
351 | print "StartDate: ", eitlist.getEitStartDate() | |
352 | print "Description: ", eitlist.getEitDescription() | |
353 | print "Duration: ", eitlist.getEitDuration() | |
354 | print "Seconds: ", eitlist.getEitLengthInSeconds() | |
355 | ||
356 | #eitlist.dumpEit() | |
357 | ||
358 | def main(): | |
359 | # parse command line options | |
360 | try: | |
361 | opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) | |
362 | except getopt.error, msg: | |
363 | print msg | |
364 | print "for help use --help" | |
365 | sys.exit(2) | |
366 | # process options | |
367 | for o, a in opts: | |
368 | if o in ("-h", "--help"): | |
369 | print __doc__ | |
370 | sys.exit(0) | |
371 | # process arguments | |
372 | for arg in args: | |
373 | readeit(arg) # process() is defined elsewhere | |
374 | ||
375 | if __name__ == "__main__": | |
376 | main() | |
377 |