Index: libs/libmythtv/siparser.cpp
===================================================================
--- libs/libmythtv/siparser.cpp	(revision 7164)
+++ libs/libmythtv/siparser.cpp	(working copy)
@@ -1433,9 +1433,8 @@
                     break;
 
                 case 0x54:
-                    e.ContentDescription =
-                            ProcessContentDescriptor(
-                                &buffer[des_pos],buffer[des_pos+1]+2);
+                    ProcessContentDescriptor(
+                        &buffer[des_pos],buffer[des_pos+1]+2,e);
                     break;
 
                 default:            
@@ -1952,13 +1951,24 @@
  *  \TODO Add all types, possibly just lookup from a big 
  *        array that is an include file?
  */
-QString SIParser::ProcessContentDescriptor(uint8_t *buf, int)
+void SIParser::ProcessContentDescriptor(uint8_t *buf, int, Event& e)
 {
     uint8_t content = buf[2];
     if (content)
-        return m_mapCategories[content];
-    else
-        return QString();
+    {
+        e.ContentDescription = m_mapCategories[content];
+        switch (content)
+        {
+        case 0x10 ... 0x1f:
+            e.CategoryType = "movie";
+            break;
+        case 0x40 ... 0x4f:
+            e.CategoryType = "sports";
+            break;
+        default:
+            e.CategoryType = "tvshow";
+        }     
+    }
 }
 
 /**
@@ -2922,12 +2932,19 @@
     event.Description = event.Description.replace("New Series","");
 
 
-    position = event.Description.find(':');
-    if (position != -1)
+    //This is trying to catch the case where the subtitle is in the main title
+    //but avoid cases where it isn't a subtitle e.g cd:uk
+    if (((position = event.Event_Name.find(":")) != -1) && 
+        (event.Description.find(":") == -1) && 
+        (event.Event_Name[position+1].upper()==event.Event_Name[position+1]))
     {
+        event.Event_Subtitle = event.Event_Name.mid(position+1);
+        event.Event_Name = event.Event_Name.left(position);
+    }
+    else if ((position = event.Description.find(":")) != -1)
+    {
         event.Event_Subtitle = event.Description.left(position);
-        event.Description = event.Description.right(
-            event.Description.length()-position-2);
+        event.Description = event.Description.mid(position+1);
         if ((event.Event_Subtitle.length() > 0) &&
             (event.Description.length() > 0) &&
             (event.Event_Subtitle.length() > event.Description.length()))
@@ -2939,22 +2956,136 @@
     }
 
     if (event.Event_Name.endsWith("...") && 
-        event.Event_Subtitle.startsWith("..."))
+        event.Event_Subtitle.startsWith(".."))
     {
         //try and make the subtitle
-        QString Full = event.Event_Name.left(event.Event_Name.length()-3)+" "+
-            event.Event_Subtitle.right(event.Event_Subtitle.length()-3);
-        if ((position = Full.find(":")) != -1)
+        QString Full = event.Event_Name.left(event.Event_Name.length()-3)+" ";
+
+        if (event.Event_Subtitle.startsWith("..."))
+            Full += event.Event_Subtitle.mid(3);
+        else
+            Full += event.Event_Subtitle.mid(2);
+        if (((position = Full.find(":")) != -1) ||
+            ((position = Full.find(".")) != -1))
         {
            event.Event_Name = Full.left(position);
-           event.Event_Subtitle = Full.right(Full.length()-position-2);
+           event.Event_Subtitle = Full.mid(position+1);
         }
-        else if ((position = Full.find(".")) != -1)
+        else
         {
+           event.Event_Name = Full;
+           event.Event_Subtitle="";
+        }
+    }
+    else if (event.Event_Subtitle.endsWith("...") && 
+        event.Description.startsWith("..."))
+    {
+         QString Full = event.Event_Subtitle.left(event.Event_Subtitle.length()
+                        -3)+" "+ event.Description.mid(3);
+        if (((position = Full.find(":")) != -1) ||
+            ((position = Full.find(".")) != -1))
+        {
+           event.Event_Subtitle = Full.left(position);
+           event.Description = Full.mid(position+1);
+        }
+    }
+    else if (event.Event_Name.endsWith("...") &&
+        event.Description.startsWith("...") && event.Event_Subtitle.isEmpty())
+    {
+        QString Full = event.Event_Name.left(event.Event_Name.length()
+                        -3)+" "+ event.Description.mid(3);
+        if (((position = Full.find(":")) != -1) ||
+            ((position = Full.find(".")) != -1))
+        {
            event.Event_Name = Full.left(position);
-           event.Event_Subtitle = Full.right(Full.length()-position-2);
+           event.Description = Full.mid(position+1);
         }
     }
+
+    //Work out the episode numbers (if any)
+    bool series = false;
+    QRegExp rx("^\\s*(\\d{1,2})/(\\d{1,2})\\.");
+    QRegExp rx1("\\((Part|Pt)\\s+(\\d{1,2})\\s+of\\s+(\\d{1,2})\\)");
+    if ((position = rx.search(event.Event_Name)) != -1)
+    {
+        event.PartNumber=rx.cap(1).toUInt();
+        event.PartTotal=rx.cap(2).toUInt();
+        //Remove from the title
+        event.Event_Name=event.Event_Name.mid(position+rx.cap(0).length());
+        //but add it to the description
+        event.Description+=rx.cap(0);
+        series=true;
+    }
+    else if ((position = rx.search(event.Event_Subtitle)) != -1)
+    {
+        event.PartNumber=rx.cap(1).toUInt();
+        event.PartTotal=rx.cap(2).toUInt();
+        //Remove from the sub title
+        event.Event_Subtitle=event.Event_Subtitle.mid(position+rx.cap(0).length());
+        //but add it to the description
+        event.Description+=rx.cap(0);
+        series=true;
+    }
+    else if ((position = rx.search(event.Description)) != -1)
+    {
+        event.PartNumber=rx.cap(1).toUInt();
+        event.PartTotal=rx.cap(2).toUInt();
+        //Don't cut it from the description
+        //event.Description=event.Description.left(position)+
+        //                  event.Description.mid(position+rx.cap(0).length());
+        series=true;
+    }
+    else if ((position = rx1.search(event.Description)) != -1)
+    {
+        event.PartNumber=rx1.cap(2).toUInt();
+        event.PartTotal=rx1.cap(3).toUInt();
+        //Don't cut it from the description
+        //event.Description=event.Description.left(position)+
+        //                  event.Description.mid(position+rx1.cap(0).length());
+        series=true;
+    }
+    if (series)
+        event.CategoryType="series";
+    //Work out the closed captions and Audio descriptions  (if any)
+    rx.setPattern("\\[(AD)(,(S)){,1}(,SL){,1}\\]|"
+               "\\[(S)(,AD){,1}(,SL){,1}\\]|"
+               "\\[(SL)(,AD){,1}(,(S)){,1}\\]");
+    if ((position = rx.search(event.Description)) != -1)
+    {
+        //Enumerate throught and see if we have subtitles, don't modify
+        //the description as we might destroy other useful information
+        QStringList captures = rx.capturedTexts();
+        QStringList::Iterator i = captures.begin();
+        QStringList::Iterator end = captures.end();
+        while (i!=end)
+            if (*(i++) == "S")
+                 event.SubTitled = true;
+    }
+    else if ((position = rx.search(event.Event_Subtitle)) != -1)
+    {
+        QStringList captures = rx.capturedTexts();
+        QStringList::Iterator i = captures.begin();
+        QStringList::Iterator end = captures.end();
+        while (i!=end)
+            if (*(i++) == "S")
+                 event.SubTitled = true;
+        //We do remove [AD,S] from the subtitle as it probably shouldn't be
+        //there.
+        QString Temp = event.Event_Subtitle;
+        event.Event_Subtitle = Temp.left(position)+Temp.mid(position+rx.cap(0).length());
+    }
+    //Work out the year (if any)
+    rx.setPattern("[\\[\\(]([\\d]{4})[\\)\\]]");
+    if ((position = rx.search(event.Description)) != -1)
+    {
+        event.Description=event.Description.left(position)+
+                          event.Description.mid(position+rx.cap(0).length());
+        event.Year=rx.cap(1);
+    }
+
+    event.Event_Name = event.Event_Name.stripWhiteSpace();
+    event.Event_Subtitle = event.Event_Subtitle.stripWhiteSpace();
+    event.Description = event.Description.stripWhiteSpace();
 }
 
 void SIParser::EITFixUpStyle3(Event &event)
Index: libs/libmythtv/sitypes.h
===================================================================
--- libs/libmythtv/sitypes.h	(revision 7164)
+++ libs/libmythtv/sitypes.h	(working copy)
@@ -353,6 +353,9 @@
     //bool PreviouslyShown;
     QDate OriginalAirDate;
     QValueList<Person> Credits;
+    unsigned PartNumber;
+    unsigned PartTotal;
+    QString  CategoryType;
 };
 
 // DVB TransportObject - Used with NIT Scanning
Index: libs/libmythtv/dbcheck.cpp
===================================================================
--- libs/libmythtv/dbcheck.cpp	(revision 7164)
+++ libs/libmythtv/dbcheck.cpp	(working copy)
@@ -192,6 +192,20 @@
 and must be selected when you compile %MythTV. Finally, the 'atscsrcid'
 field currently contains both the major and minor atsc channels, encoded
 in the form (majorChannel * 256 | minorChannel) when using DVB drivers.
+
+\section program_table Program Entry Table (program)
+'category_type' holds one of these exact four strings: "movie", "series", "sports" or "tvshow".
+
+'airdate' is a string representing the year of release for movies and may have no meaning for other types of shows.
+
+'stars' is a floating point number from 0.0 to 1.0. On a four star scale, 1.0 would be four stars, 0.75 would be three stars and so on.
+
+'originalairdate' if provided is the date when a show was, or will be, first televised. 
+
+'previouslyshown' is a field created by MythTV to try to determine if a showing is more than 14 days after its original air date or if the show was marked as a repeat and did not have a date for the first airing. If this is "0" it usually means that this is a brand new show or a rebroadcast within the first two weeks.
+
+'programid' is the Tribune Media Service database record identifier for each program description. In general, these start with a two letter prefix, MV, EP, SP or SH that equate to the 'category_type'. For most, the last four digits are "0000" except EP where the last four digits are the episode number in the series. Note that these are generated by TMS and not the show's producers but they are usually in the same order as the original air dates for the episodes. Detailed information can be found in the Data Direct documentation at http://labs.zap2it.com/ .
+
  */
 
 /** \fn UpdateDBVersionNumber(const QString&)
Index: libs/libmythtv/eithelper.cpp
===================================================================
--- libs/libmythtv/eithelper.cpp	(revision 7164)
+++ libs/libmythtv/eithelper.cpp	(working copy)
@@ -223,11 +223,13 @@
     query.prepare("REPLACE INTO program (chanid, starttime, "
                   "endtime, title, description, subtitle, "
                   "category, stereo, closecaptioned, hdtv, "
-                  "airdate, originalairdate) "
+                  "airdate, originalairdate, partnumber, parttotal, "
+                  "category_type) "
                   "VALUES (:CHANID, :STARTTIME, "
                   ":ENDTIME, :TITLE, :DESCRIPTION, :SUBTITLE, "
                   ":CATEGORY, :STEREO, :CLOSECAPTIONED, :HDTV, "
-                  ":AIRDATE, :ORIGINALAIRDATE);");
+                  ":AIRDATE, :ORIGINALAIRDATE, :PARTNUMBER, :PARTTOTAL,"
+                  ":CATTYPE);");
     query.bindValue(":CHANID", chanid);
     query.bindValue(":STARTTIME", event.StartTime.
                     toString(QString("yyyy-MM-dd hh:mm:00")));
@@ -243,6 +245,9 @@
     query.bindValue(":AIRDATE", event.Year);
     query.bindValue(":ORIGINALAIRDATE", event.OriginalAirDate.
                     toString(QString("yyyy-MM-dd")));
+    query.bindValue(":PARTNUMBER", event.PartNumber);
+    query.bindValue(":PARTTOTAL", event.PartTotal);
+    query.bindValue(":CATTYPE", event.CategoryType.utf8());
 
     if (!query.exec() || !query.isActive())
     {
Index: libs/libmythtv/sitypes.cpp
===================================================================
--- libs/libmythtv/sitypes.cpp	(revision 7164)
+++ libs/libmythtv/sitypes.cpp	(working copy)
@@ -251,6 +251,9 @@
     Stereo = false;
     HDTV = false;
     ATSC = false;
+    PartNumber = 0;
+    PartTotal = 0;
+    CategoryType="";
     //PreviouslyShown = false;
     OriginalAirDate = QDate();
     Credits.clear();
Index: libs/libmythtv/siparser.h
===================================================================
--- libs/libmythtv/siparser.h	(revision 7164)
+++ libs/libmythtv/siparser.h	(working copy)
@@ -162,7 +162,7 @@
     QString ProcessDescHuffmanTextLarge      (uint8_t *buf, uint sz);
 
     // DVB EIT Table Descriptor processors
-    QString ProcessContentDescriptor         (uint8_t *buf, int sz);
+    void ProcessContentDescriptor            (uint8_t *buf, int sz, Event &e);
     void ProcessShortEventDescriptor         (uint8_t *buf, int sz, Event &e);
     void ProcessExtendedEventDescriptor      (uint8_t *buf, int sz, Event &e);
     void ProcessComponentDescriptor          (uint8_t *buf, int sz, Event &e);
