eit.py

changeset 0
8b8cdafcc745
child 1
88b1fefb7c92
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eit.py	Sat Nov 04 20:12:11 2017 +0100
@@ -0,0 +1,377 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-2 -*-
+# encoding: utf-8
+#
+# EitSupport
+# Copyright (C) 2011 betonme
+# Copyright (C) 2016 Wolfgang Fahl
+#
+# This EITParser is based on:
+# https://github.com/betonme/e2openplugin-EnhancedMovieCenter/blob/master/src/EitSupport.py
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+#   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 3 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.
+#
+#   For more information on the GNU General Public License see:
+#   <http://www.gnu.org/licenses/>.
+#
+
+import os
+import struct
+import time
+
+from datetime import datetime
+
+from ISO639 import LanguageCodes
+
+#from Components.config import config
+#from Components.Language import language
+#from EMCTasker import emcDebugOut
+#from IsoFileSupport import IsoSupport
+#from MetaSupport import getInfoFile
+
+#def crc32(data):
+#   poly = 0x4c11db7
+#   crc = 0xffffffffL
+#   for byte in data:
+#       byte = ord(byte)
+#       for bit in range(7,-1,-1):  # MSB to LSB
+#           z32 = crc>>31    # top bit
+#           crc = crc << 1
+#           if ((byte>>bit)&1) ^ z32:
+#               crc = crc ^ poly
+#           crc = crc & 0xffffffffL
+#   return crc
+
+decoding_charSpecHR = {u'Ć': u'\u0106', u'æ': u'\u0107', u'®': u'\u017D', u'¾': u'\u017E', u'©': u'\u0160', u'¹': u'\u0161', u'Č': u'\u010C', u'è': u'\u010D', u'ð': u'\u0111'}
+
+decoding_charSpecCZSK = {u'Ï'+u'C': u'Č',u'Ï'+u'E': u'Ě',u'Ï'+u'L': u'Ľ',u'Ï'+u'N': u'Ň',u'Ï'+u'R': u'Ř',u'Ï'+u'S': u'Š',u'Ï'+u'T': u'Ť',u'Ï'+u'Z': u'Ž',u'Ï'+u'c': u'č',u'Ï'+u'd': u'ď',u'Ï'+u'e': u'ě',u'Ï'+u'l': u'ľ', u'Ï'+u'n': u'ň',
+u'Ï'+u'r': u'ř',u'Ï'+u's': u'š',u'Ï'+u't': u'ť',u'Ï'+u'z': u'ž',u'Ï'+u'D': u'Ď',u'Â'+u'A': u'Á',u'Â'+u'E': u'É',u'Â'+u'I': u'Í',u'Â'+u'O': u'Ó',u'Â'+u'U': u'Ú',u'Â'+u'a': u'á',u'Â'+u'e': u'é',u'Â'+u'i': u'í',u'Â'+u'o': u'ó',
+u'Â'+u'u': u'ú',u'Â'+u'y': u'ý',u'Ã'+u'o': u'ô',u'Ã'+u'O': u'Ô',u'Ê'+u'u': u'ů',u'Ê'+u'U': u'Ů',u'È'+u'A': u'Ä',u'È'+u'E': u'Ë',u'È'+u'I': u'Ï',u'È'+u'O': u'Ö',u'È'+u'U': u'Ü',u'È'+u'Y': u'Ÿ',u'È'+u'a': u'ä',u'È'+u'e': u'ë',
+u'È'+u'i': u'ï',u'È'+u'o': u'ö',u'È'+u'u': u'ü',u'È'+u'y': u'ÿ'}
+
+def convertCharSpecHR(text):
+    for i, j in decoding_charSpecHR.iteritems():
+        text = text.replace(i, j)
+    return text
+
+def convertCharSpecCZSK(text):
+    for i, j in decoding_charSpecCZSK.iteritems():
+        text = text.replace(i, j)
+    return text
+
+def parseMJD(MJD):
+    # Parse 16 bit unsigned int containing Modified Julian Date,
+    # as per DVB-SI spec
+    # returning year,month,day
+    YY = int( (MJD - 15078.2) / 365.25 )
+    MM = int( (MJD - 14956.1 - int(YY*365.25) ) / 30.6001 )
+    D  = MJD - 14956 - int(YY*365.25) - int(MM * 30.6001)
+    K=0
+    if MM == 14 or MM == 15: K=1
+
+    return (1900 + YY+K), (MM-1-K*12), D
+
+def unBCD(byte):
+    return (byte>>4)*10 + (byte & 0xf)
+
+def language_iso639_2to3(alpha2):
+    ret = alpha2
+    if alpha2 in LanguageCodes:
+        language = LanguageCodes[alpha2]
+        for alpha, name in LanguageCodes.items():
+            if name == language:
+                if len(alpha) == 3:
+                    return alpha
+    return ret
+
+
+class EitList():
+    """Eit File support class
+    Description
+    http://de.wikipedia.org/wiki/Event_Information_Table
+    """
+    EIT_SHORT_EVENT_DESCRIPTOR      = 0x4d
+    EIT_EXTENDED_EVENT_DESCRIPOR    =   0x4e
+
+    def __init__(self, path=None):
+        self.eit_file = None
+
+        self.eit = {}
+        self.iso = None
+
+        self.__newPath(path)
+        self.__readEitFile()
+
+    def __newPath(self, path):
+        name = None
+        if path:
+            if self.eit_file != path:
+                self.eit_file = path
+
+    def __mk_int(self, s):
+        return int(s) if s else 0
+
+    def __toDate(self, d, t):
+        if d and t:
+            try:
+                return datetime(int(d[0]), int(d[1]), int(d[2]), int(t[0]), int(t[1]))
+            except ValueError:
+                return None
+        else:
+            return None
+
+    def getEitsid(self):
+        return self.eit.get('service', "") #TODO
+
+    def getEitTsId(self):
+        return self.eit.get('transportstream', "") #TODO
+
+    def getEitWhen(self):
+        return self.eit.get('when', "")
+
+    def getEitStartDate(self):
+        return self.eit.get('startdate', "")
+
+    def getEitStartTime(self):
+        return self.eit.get('starttime', "")
+
+    def getEitDuration(self):
+        return self.eit.get('duration', "")
+
+    def getEitName(self):
+        return self.eit.get('name', "").strip()
+
+    def getEitDescription(self):
+        return self.eit.get('description', "").strip()
+
+    # Wrapper
+    def getEitShortDescription(self):
+        return self.getEitName()
+
+    def getEitExtendedDescription(self):
+        return self.getEitDescription()
+
+    def getEitLengthInSeconds(self):
+        length = self.eit.get('duration', "")
+        if len(length)>2:
+            return self.__mk_int((length[0]*60 + length[1])*60 + length[2])
+        elif len(length)>1:
+            return self.__mk_int(length[0]*60 + length[1])
+        else:
+            return self.__mk_int(length)
+
+    def getEitDate(self):
+        return self.__toDate(self.getEitStartDate(), self.getEitStartTime())
+
+    def dumpEit(self):
+        print self.eit
+
+    ##############################################################################
+    ## File IO Functions
+    def __readEitFile(self):
+        data = ""
+        path = self.eit_file
+
+        #lang = language.getLanguage()[:2]
+        lang = language_iso639_2to3( "de" )
+        #print lang + str(path)
+
+        if path and os.path.exists(path):
+            #print "Reading Event Information Table " + str(path)
+
+            # Read data from file
+            # OE1.6 with Pyton 2.6
+            #with open(self.eit_file, 'r') as file: lines = file.readlines()
+            f = None
+            try:
+                f = open(path, 'rb')
+                #lines = f.readlines()
+                data = f.read()
+            except Exception, e:
+                emcDebugOut("[META] Exception in readEitFile: " + str(e))
+            finally:
+                if f is not None:
+                    f.close()
+
+            # Parse the data
+            if data and 12 <= len(data):
+                # go through events
+                pos = 0
+                e = struct.unpack(">HHBBBBBBH", data[pos:pos+12])
+                event_id = e[0]
+                date     = parseMJD(e[1])                         # Y, M, D
+                time     = unBCD(e[2]), unBCD(e[3]), unBCD(e[4])  # HH, MM, SS
+                duration = unBCD(e[5]), unBCD(e[6]), unBCD(e[7])  # HH, MM, SS
+                running_status  = (e[8] & 0xe000) >> 13
+                free_CA_mode    = e[8] & 0x1000
+                descriptors_len = e[8] & 0x0fff
+
+                if running_status in [1,2]:
+                    self.eit['when'] = "NEXT"
+                elif running_status in [3,4]:
+                    self.eit['when'] = "NOW"
+
+                self.eit['startdate'] = date
+                self.eit['starttime'] = time
+                self.eit['duration'] = duration
+
+                pos = pos + 12
+                short_event_descriptor = []
+                short_event_descriptor_multi = []
+                extended_event_descriptor = []
+                extended_event_descriptor_multi = []
+                component_descriptor = []
+                content_descriptor = []
+                linkage_descriptor = []
+                parental_rating_descriptor = []
+                endpos = len(data) - 1
+                while pos < endpos:
+                    rec = ord(data[pos])
+                    length = ord(data[pos+1]) + 2
+                    if rec == 0x4D:
+                        descriptor_tag = ord(data[pos+1])
+                        descriptor_length = ord(data[pos+2])
+                        ISO_639_language_code = str(data[pos+3:pos+5])
+                        event_name_length = ord(data[pos+5])
+                        short_event_description = data[pos+6:pos+6+event_name_length]
+                        if ISO_639_language_code == lang:
+                            short_event_descriptor.append(short_event_description)
+                        short_event_descriptor_multi.append(short_event_description)
+                    elif rec == 0x4E:
+                        ISO_639_language_code = str(data[pos+3:pos+5])
+                        extended_event_description = ""
+                        extended_event_description_multi = ""
+                        for i in range (pos+8,pos+length):
+                            if str(ord(data[i]))=="138":
+                                extended_event_description += '\n'
+                                extended_event_description_multi += '\n'
+                            else:
+                                if data[i]== '\x10' or data[i]== '\x00' or  data[i]== '\x02':
+                                    pass
+                                else:
+                                    extended_event_description += data[i]
+                                    extended_event_description_multi += data[i]
+                        if ISO_639_language_code == lang:
+                            extended_event_descriptor.append(extended_event_description)
+                        extended_event_descriptor_multi.append(extended_event_description)
+                    elif rec == 0x50:
+                        component_descriptor.append(data[pos+8:pos+length])
+                    elif rec == 0x54:
+                        content_descriptor.append(data[pos+8:pos+length])
+                    elif rec == 0x4A:
+                        linkage_descriptor.append(data[pos+8:pos+length])
+                    elif rec == 0x55:
+                        parental_rating_descriptor.append(data[pos+2:pos+length])
+                    else:
+                        print "unsopported descriptor: %x %x" %(rec, pos + 12)
+                        #print data[pos:pos+length]
+                        pass
+                    pos += length
+
+                    ### TODO: right identication?!
+
+                    # Very bad but there can be both encodings
+                    # User files can be in cp1252
+                    # Is there no other way?
+                    if short_event_descriptor:
+                        short_event_descriptor = "".join(short_event_descriptor)
+                    else:
+                        short_event_descriptor = "".join(short_event_descriptor_multi)
+                    if short_event_descriptor:
+                        #try:
+                        #   short_event_descriptor = short_event_descriptor.decode("iso-8859-1").encode("utf-8")
+                        #except UnicodeDecodeError:
+                        #   pass
+                        try:
+                            short_event_descriptor.decode('utf-8')
+                        except UnicodeDecodeError:
+                            try:
+                                short_event_descriptor = short_event_descriptor.decode("cp1252").encode("utf-8")
+                            except UnicodeDecodeError:
+                                # do nothing, otherwise cyrillic wont properly displayed
+                                #short_event_descriptor = short_event_descriptor.decode("iso-8859-1").encode("utf-8")
+                                pass
+                            if (lang == "cs") or (lang == "sk"):
+                                short_event_descriptor = str(convertCharSpecCZSK(short_event_descriptor))
+                            if (lang == "hr"):
+                                short_event_descriptor = str(convertCharSpecHR(short_event_descriptor))
+                    self.eit['name'] = short_event_descriptor
+
+                    # Very bad but there can be both encodings
+                    # User files can be in cp1252
+                    # Is there no other way?
+                    if extended_event_descriptor:
+                        extended_event_descriptor = "".join(extended_event_descriptor)
+                    else:
+                        extended_event_descriptor = "".join(extended_event_descriptor_multi)
+                    if extended_event_descriptor:
+                        #try:
+                        #   extended_event_descriptor = extended_event_descriptor.decode("iso-8859-1").encode("utf-8")
+                        #except UnicodeDecodeError:
+                        #   pass
+                        try:
+                            extended_event_descriptor.decode('utf-8')
+                        except UnicodeDecodeError:
+                            try:
+                                extended_event_descriptor = extended_event_descriptor.decode("cp1252").encode("utf-8")
+                            except UnicodeDecodeError:
+                                # do nothing, otherwise cyrillic wont properly displayed
+                                #extended_event_descriptor = extended_event_descriptor.decode("iso-8859-1").encode("utf-8")
+                                pass
+                            if (lang == "cs") or (lang == "sk"):
+                                extended_event_descriptor = str(convertCharSpecCZSK(extended_event_descriptor))
+                            if (lang == "hr"):
+                                extended_event_descriptor = str(convertCharSpecHR(extended_event_descriptor))
+                    self.eit['description'] = extended_event_descriptor
+
+                else:
+                    # No date clear all
+                    self.eit = {}
+
+"""Module docstring.
+
+Read Eit File and show the information.
+"""
+import sys
+import getopt
+
+def readeit(eitfile):
+    eitlist=EitList(eitfile)
+    print "Name: ", eitlist.getEitName()
+    print "StartDate: ", eitlist.getEitStartDate()
+    print "Description: ", eitlist.getEitDescription()
+    print "Duration: ", eitlist.getEitDuration()
+    print "Seconds: ", eitlist.getEitLengthInSeconds()
+
+    #eitlist.dumpEit()
+
+def main():
+    # parse command line options
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
+    except getopt.error, msg:
+        print msg
+        print "for help use --help"
+        sys.exit(2)
+    # process options
+    for o, a in opts:
+        if o in ("-h", "--help"):
+            print __doc__
+            sys.exit(0)
+    # process arguments
+    for arg in args:
+        readeit(arg) # process() is defined elsewhere
+
+if __name__ == "__main__":
+    main()
+

mercurial