[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