[mythtv] Program Info protocol

Ben Bucksch linux.news at bucksch.org
Mon Jun 30 04:42:43 EDT 2003


As I understand, the frontend and backend exchange information about 
programs (shows, both recorded and in the guide) via the MythTV 
protocol, and it's ProgramInfo::FromStringList() / ToStringList which 
encodes/decodes the on-the-wire protocol.

Unfortunately, these functions just shuffle the values into a stringlist 
and rely on their order. This causes the frontend to break *horribly*, 
if it doesn't match the backend version, and it's not necessarily 
obivous what is the cause for that.

IMHO, if MythTV should be something to rely upon, which can be installed 
at users without maintainance, then a stable protocol is needed, with 
both forwards- and backwards compatibility, so that older frontends work 
with newer backends and newer frontends with older backends. The goal is 
that you can install a frontend and have it running for years without 
changes, while the backend is being continuously upgraded. It's hard to 
achieve, but possible, and I think we should aim for it, to allow 
acceptance of MythTV in environments where users don't want to worry 
about their TV at all, it should "just work".

Anyways, I tried to make a small step in that direction by making the 
functions mentioned above much more tolerant to changes. I used a scheme 
similar to email headers, so that the order is not important and that we 
can easily add new values without breaking older stuff. Older frontends 
wouldn't notice a difference in that case (unless the backend requires 
that the new values are being used by the frontend, which it shouldn't). 
Newer frontends wouldn't get some values filled, but they should deal 
with that the same way older frontends did (which didn't need the new 
values either). Maybe you don't need to be that strict right away, as 
MythTV is still very much in development, but once it hits MythTV, at 
least the 1.0 frontend should work with all future backends IMHO.

I created a preliminary patch. It compiles, but I couldn't test it at 
all yet, I don't have time for that atm, so it's not meant for checkin. 
I just wanted to get that idea out of my head :-) and to you.
-------------- next part --------------
Index: libs/libmythtv/programinfo.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/programinfo.cpp,v
retrieving revision 1.55
diff -u -r1.55 programinfo.cpp
--- libs/libmythtv/programinfo.cpp	18 Jun 2003 23:43:41 -0000	1.55
+++ libs/libmythtv/programinfo.cpp	30 Jun 2003 01:28:49 -0000
@@ -74,31 +74,44 @@
         delete record;
 }
 
+#define HEADERS
+
+const char *const myversion = "1.0";
+
 void ProgramInfo::ToStringList(QStringList &list)
 {
-    if (title == "")
-        title = " ";
-    if (subtitle == "")
-        subtitle = " ";
-    if (description == "")
-        description = " ";
-    if (category == "")
-        category = " ";
-    if (pathname == "")
-        pathname = " ";
-    if (hostname == "")
-        hostname = " ";
-    if (chanid == "")
-        chanid = " ";
-    if (chanstr == "")
-        chanstr = " ";
-    if (chansign == "")
-        chansign = " ";
-    if (channame == "")
-        channame = " ";
-    if (pathname == "")
-        pathname = " ";
+#ifdef HEADERS
+    /* This idea here is forward- and backward-compatibility, i.e. that
+       older frontends work with newer backends and newer frontends with
+       older backends.
+       Warning: Do *not* change existing names, unless you know that nobody
+       needs them, because that would not allow older versions to use them,
+       potentially breaking them. */
 
+    /* I18N: Do not translate these string literals. They are for the
+       on-the-wire protocol and never shown to users and must be stable across
+       versions and installations. */
+    list << "Program Info; version=" + QString(myversion);
+    list << "Title: " + title;
+    list << "Subtitle: " + subtitle;
+    list << "Description: " + description;
+    list << "Category: " + category;
+    list << "Channel ID: " + chanid;
+    list << "Channel String: " + chanstr;
+    list << "Channel Short name: " + chansign;
+    list << "Channel Long name: " + channame;
+    list << "File Path: " + pathname;
+    // XXX encodeLongLong(list, filesize);
+    list << "Time Start: " + startts.toString(Qt::ISODate);
+    list << "Time End: " + endts.toString(Qt::ISODate);
+    list << "Conflicting: " + QString(conflicting ? "YES" : "NO");
+    list << "Recording: " + QString(recording ? "YES" : "NO");
+    list << "Duplicate: " + QString(duplicate ? "YES" : "NO");
+    list << "Hostname: " + hostname;
+    list << "Source ID: " + QString::number(sourceid);
+    list << "Card ID: " + QString::number(cardid);
+    list << "Input ID: " + QString::number(inputid);
+#else
     list << title;
     list << subtitle;
     list << description;
@@ -118,10 +131,86 @@
     list << QString::number(sourceid);
     list << QString::number(cardid);
     list << QString::number(inputid);
+#endif
 }
 
 void ProgramInfo::FromStringList(QStringList &list, int offset)
 {
+#ifdef HEADERS
+    /* I18N: Do not translate the string literals here (apart from cout/cerr
+       mabye). They are for the on-the-wire protocol and never shown to users
+       and must be stable across versions and installations. */
+    QString line;
+
+    // check version
+    /* Syntax of first line is roughly similar to mimetypes with their
+       parameters of the form "; name=value" */
+    line = list[0];
+    cout << line << endl; // XXX
+    const int startlen = 22; // length of "Program Info; version="
+    if (!(line.startsWith("Program Info") &&
+          line.contains("; version=")))
+    {
+      cerr << "Program Info stringlist protocol unrecognized" << endl;
+      return; // XXX throw to inform user
+    }
+    else
+    {
+      int end = line.find("; ", startlen); // start of next param, if any
+      if (end == -1) // not found
+        end = 0xffffffff; // rest of string
+      QString gotversion = line.mid(startlen, end);
+      cout << "Version " << gotversion << endl; // XXX
+      if (gotversion != myversion)
+      {
+        cout << "Warning: Program Info stringlist protocol mismatch: "
+                "expected version " << myversion
+             << ", but got version " << gotversion
+             << ". I will try to continue anyways." << endl;
+        // XXX inform user?
+      }
+    }
+
+    // Get values
+    /* Syntax is similar to email headers, but spaces in header names are
+       allowed. E.g. "Some name: Some value". */
+    QString entryname;
+    QString entryvalue;
+    for (QStringList::const_iterator i = list.begin(); i != list.end(); i++)
+    {
+      line = *i;
+      int sep = line.find(": ");
+      entryname = line.left(sep);
+      entryvalue = line.mid(sep + 2);
+      cout << "name: " << entryname << ", value: " << entryvalue << endl;// XXX
+
+      if (entryname == "Title") title = entryvalue;
+      else if (entryname == "Subtitle") subtitle = entryvalue;
+      else if (entryname == "Description") description = entryvalue;
+      else if (entryname == "Category") category = entryvalue;
+      else if (entryname == "Channel ID") chanid = entryvalue;
+      else if (entryname == "Channel String") chanstr = entryvalue;
+      else if (entryname == "Channel Short name") chansign = entryvalue;
+      else if (entryname == "Channel Long name") channame = entryvalue;
+      else if (entryname == "File Path") pathname = entryvalue;
+      // XXX else if (entryname == "File Size") decodeLongLong(list, 9);
+      else if (entryname == "Time Start")
+          startts = QDateTime::fromString(entryvalue, Qt::ISODate);
+      else if (entryname == "Time End")
+          endts = QDateTime::fromString(entryvalue, Qt::ISODate);
+      else if (entryname == "Conflicting") conflicting = entryvalue == "YES";
+      else if (entryname == "Recording") recording = entryvalue == "YES";
+      else if (entryname == "Duplicate") duplicate = entryvalue == "YES";
+      else if (entryname == "Hostname") hostname = entryvalue;
+      else if (entryname == "Source ID") sourceid = entryvalue.toInt();
+      else if (entryname == "Card ID") cardid = entryvalue.toInt();
+      else if (entryname == "Input ID") inputid = entryvalue.toInt();
+
+      else
+          cout << "Unrecognized program info: " << line << endl;
+    }
+
+#else
     if (offset + NUMPROGRAMLINES > (int)list.size())
     {
         cerr << "offset is: " << offset << " but size is " << list.size() 
@@ -173,6 +262,7 @@
         chansign = "";
     if (channame == " ")
         channame = "";
+#endif
 }
 
 void ProgramInfo::ToMap(QSqlDatabase *db, QMap<QString, QString> &progMap)


More information about the mythtv-dev mailing list