Index: libs/libmythtv/videosource.h
===================================================================
--- libs/libmythtv/videosource.h	(revision 14205)
+++ libs/libmythtv/videosource.h	(working copy)
@@ -11,8 +11,26 @@
 class SignalTimeout;
 class ChannelTimeout;
 class UseEIT;
+class VideoSource;
 
-class VideoSource;
+static inline bool is_grabber_external(const QString &grabber)
+{
+    return !(grabber == "datadirect" ||
+             grabber == "eitonly" ||
+             grabber == "schedulesdirect1" ||
+             grabber == "/bin/true");
+}
+
+static inline bool is_grabber_datadirect(const QString &grabber)
+{
+    return (grabber == "datadirect") || (grabber == "schedulesdirect1");
+}
+
+static inline bool is_grabber_labs(const QString &grabber)
+{
+    return grabber == "datadirect";
+}
+
 class VSSetting: public SimpleDBStorage {
 protected:
     VSSetting(const VideoSource& _parent, QString name):
@@ -62,7 +80,7 @@
 {
     Q_OBJECT
   public:
-    DataDirect_config(const VideoSource& _parent, int _source = DD_ZAP2IT); 
+    DataDirect_config(const VideoSource& _parent, int _ddsource); 
 
     virtual void load(void);
 
Index: libs/libmythtv/dbcheck.cpp
===================================================================
--- libs/libmythtv/dbcheck.cpp	(revision 14205)
+++ libs/libmythtv/dbcheck.cpp	(working copy)
@@ -8,6 +8,7 @@
 
 #include "mythcontext.h"
 #include "mythdbcon.h"
+#include "datadirect.h" // for DataDirectProcessor::FixProgramIDs
 
 /// This is the DB schema version expected by the running MythTV instance.
 const QString currentDatabaseVersion = "1160";
@@ -464,6 +465,9 @@
 
     bool ret = doUpgradeTVDatabaseSchema();
 
+    if (!gContext->GetNumSetting("MythFillFixProgramIDsHasRunOnce", 0))
+        DataDirectProcessor::FixProgramIDs();
+
     if (ret)
         VERBOSE(VB_IMPORTANT, "Database Schema upgrade complete, unlocking.");
     else
Index: libs/libmythtv/datadirect.h
===================================================================
--- libs/libmythtv/datadirect.h	(revision 14205)
+++ libs/libmythtv/datadirect.h	(working copy)
@@ -11,8 +11,9 @@
 
 enum DD_PROVIDERS
 {
-    DD_ZAP2IT = 0,
-    DD_PROVIDER_COUNT,
+    DD_ZAP2IT           = 0,
+    DD_SCHEDULES_DIRECT = 1,
+    DD_PROVIDER_COUNT   = 2,
 };
 
 class DataDirectURLs
@@ -315,6 +316,9 @@
     static int  UpdateChannelsSafe(uint sourceid, bool insert_channels);
     static bool UpdateChannelsUnsafe(uint sourceid);
 
+    // static command, makes Labs and Schedules Direct ProgramIDs compatible.
+    static void FixProgramIDs(void);
+
   private:
     void CreateTempTables(void);
     void CreateATempTable(const QString &ptablename,
Index: libs/libmythtv/datadirect.cpp
===================================================================
--- libs/libmythtv/datadirect.cpp	(revision 14205)
+++ libs/libmythtv/datadirect.cpp	(working copy)
@@ -542,7 +542,14 @@
         "http://datadirect.webservices.zap2it.com/tvlistings/xtvdService",
         "http://labs.zap2it.com",
         "/ztvws/ztvws_login/1,1059,TMS01-1,00.html");
+    DataDirectURLs urls1(
+        "Schedules Direct",
+        "http://webservices.schedulesdirect.tmsdatadirect.com"
+        "/schedulesdirect/tvlistings/xtvdService",
+        "http://schedulesdirect.org",
+        "/login/index.php");
     providers.push_back(urls0);
+    providers.push_back(urls1);
 
     QString tmpDir = "/tmp";
     tmpPostFile   = makeTempFile(tmpDir + "/mythtv_post_XXXXXX");
@@ -723,6 +730,40 @@
     return true;
 }
 
+void DataDirectProcessor::FixProgramIDs(void)
+{
+    VERBOSE(VB_GENERAL, "DataDirectProcessor::FixProgramIDs() -- begin");
+
+    MSqlQuery query(MSqlQuery::DDCon());
+    query.prepare(
+        "UPDATE recorded "
+        "SET programid=CONCAT(SUBSTRING(programid, 1, 2), "
+        "                     '00', SUBSTRING(programid, 3)) "
+        "WHERE length(programid) = 12");
+
+    if (!query.exec())
+    {
+        MythContext::DBError("Fixing program ids in recorded", query);
+        return;
+    }
+
+    query.prepare(
+        "UPDATE oldrecorded "
+        "SET programid=CONCAT(SUBSTRING(programid, 1, 2), "
+        "                     '00', SUBSTRING(programid, 3)) "
+        "WHERE length(programid) = 12");
+
+    if (!query.exec())
+    {
+        MythContext::DBError("Fixing program ids in oldrecorded", query);
+        return;
+    }
+
+    gContext->SetSetting("MythFillFixProgramIDsHasRunOnce", "1");
+
+    VERBOSE(VB_GENERAL, "DataDirectProcessor::FixProgramIDs() -- end");
+}
+
 FILE *DataDirectProcessor::DDPost(
     QString    ddurl,
     QString    postFilename, QString    inputFile,
@@ -1018,7 +1059,7 @@
         "  channelMinor char(3) )";
 
     dd_tables["dd_schedule"] =
-        "( programid char(12),           stationid char(12), "
+        "( programid char(40),           stationid char(12), "
         "  scheduletime datetime,        duration time,      "
         "  isrepeat bool,                stereo bool,        "
         "  subtitled bool,               hdtv bool,          "
@@ -1028,7 +1069,7 @@
         "INDEX progidx (programid) )";
 
     dd_tables["dd_program"] =
-        "( programid char(12) NOT NULL,  seriesid char(12),     "
+        "( programid char(40) NOT NULL,  seriesid char(12),     "
         "  title varchar(120),           subtitle varchar(150), "
         "  description text,             mpaarating char(5),    "
         "  starrating char(5),           runtime time,          "
@@ -1050,19 +1091,19 @@
         "  partnumber int,               parttotal int,               "
         "  seriesid char(12),            originalairdate date,        "
         "  showtype varchar(30),         colorcode varchar(20),       "
-        "  syndicatedepisodenumber varchar(20), programid char(12),   "
+        "  syndicatedepisodenumber varchar(20), programid char(40),   "
         "  tvrating char(5),             mpaarating char(5),          "
         "INDEX progidx (programid))";
 
     dd_tables["dd_productioncrew"] =
-        "( programid char(12),           role char(30),    "
+        "( programid char(40),           role char(30),    "
         "  givenname char(20),           surname char(20), "
         "  fullname char(41), "
         "INDEX progidx (programid), "
         "INDEX nameidx (fullname))";
 
     dd_tables["dd_genre"] =
-        "( programid char(12) NOT NULL,  class char(30), "
+        "( programid char(40) NOT NULL,  class char(30), "
         "  relevance char(1), "
         "INDEX progidx (programid))";
 
Index: libs/libmythtv/videosource.cpp
===================================================================
--- libs/libmythtv/videosource.cpp	(revision 14205)
+++ libs/libmythtv/videosource.cpp	(working copy)
@@ -19,6 +19,7 @@
 #include <qmap.h>
 #include <qdir.h>
 #include <qprocess.h>
+#include <qdatetime.h>
 
 // MythTV headers
 #include "mythconfig.h"
@@ -148,7 +149,9 @@
 class DataDirectPassword: public LineEditSetting, public VSSetting {
   public:
     DataDirectPassword(const VideoSource& parent):
-        VSSetting(parent, "password") {
+        VSSetting(parent, "password")
+    {
+        SetPasswordEcho(true);
         setLabel(QObject::tr("Password"));
     };
 };
@@ -159,7 +162,6 @@
 {
     (void) uid;
     (void) pwd;
-    (void) _source;
 #ifdef USING_BACKEND
     if (uid.isEmpty() || pwd.isEmpty())
         return;
@@ -200,8 +202,11 @@
 void DataDirect_config::load() 
 {
     VerticalConfigurationGroup::load();
-    if ((userid->getValue() != lastloadeduserid) || 
-        (password->getValue() != lastloadedpassword)) 
+    bool is_sd_userid = userid->getValue().contains("@") > 0;
+    bool match = ((is_sd_userid  && (source == DD_SCHEDULES_DIRECT)) ||
+                  (!is_sd_userid && (source == DD_ZAP2IT)));
+    if (((userid->getValue() != lastloadeduserid) ||
+         (password->getValue() != lastloadedpassword)) && match)
     {
         lineupselector->fillSelections(userid->getValue(), 
                                        password->getValue(),
@@ -296,8 +301,7 @@
         "instead of just 'mythfilldatabase'.\nYour grabber does not provide "
         "channel numbers, so you have to set them manually.");
 
-    if (grabber != "datadirect" && grabber != "eitonly" && 
-        grabber != "/bin/true")
+    if (is_grabber_external(grabber))
     {
         VERBOSE(VB_IMPORTANT, "\n" << err_msg);
         MythPopupBox::showOkPopup(
@@ -365,9 +369,17 @@
     // only save settings for the selected grabber
     setSaveAll(false);
 
-    addTarget("datadirect", new DataDirect_config(parent));
-    grabber->addSelection("North America (DataDirect) (Internal)", "datadirect");
+    addTarget("schedulesdirect1",
+              new DataDirect_config(parent, DD_SCHEDULES_DIRECT));
+    grabber->addSelection("North America (SchedulesDirect.org) "
+                          "(Internal)", "schedulesdirect1");
 
+#if 1
+    addTarget("datadirect", new DataDirect_config(parent, DD_ZAP2IT));
+    grabber->addSelection(
+        "North America (TMS Labs) (Internal)", "datadirect");
+#endif
+
     addTarget("eitonly", new EITOnly_config(parent));
     grabber->addSelection("Transmitted guide only (EIT)", "eitonly");
 
Index: libs/libmyth/settings.cpp
===================================================================
--- libs/libmyth/settings.cpp	(revision 14205)
+++ libs/libmyth/settings.cpp	(working copy)
@@ -494,7 +494,8 @@
         connect(edit, SIGNAL(changeHelpText(QString)), cg, 
                 SIGNAL(changeHelpText(QString)));
 
-    edit->setRW(rw);
+    setRW(rw);
+    SetPasswordEcho(password_echo);
 
     return widget;
 }
@@ -519,6 +520,13 @@
     }
 }
 
+void LineEditSetting::SetPasswordEcho(bool b)
+{
+    password_echo = b;
+    if (edit)
+        edit->setEchoMode(b ? QLineEdit::Password : QLineEdit::Normal);
+}
+
 QWidget* SliderSetting::configWidget(ConfigurationGroup *cg, QWidget* parent,
                                      const char* widgetName) {
     QHBox* widget;
Index: libs/libmyth/mythcontext.h
===================================================================
--- libs/libmyth/mythcontext.h	(revision 14205)
+++ libs/libmyth/mythcontext.h	(working copy)
@@ -224,7 +224,7 @@
 
 /// Update this whenever the plug-in API changes.
 /// Including changes in the libmythtv class methods used by plug-ins.
-#define MYTH_BINARY_VERSION "0.20.20060828-4"
+#define MYTH_BINARY_VERSION "0.20.20070818-1"
 
 /** \brief Increment this whenever the MythTV network protocol changes.
  *
Index: libs/libmyth/settings.h
===================================================================
--- libs/libmyth/settings.h	(revision 14205)
+++ libs/libmyth/settings.h	(working copy)
@@ -276,7 +276,8 @@
 
 class LineEditSetting: virtual public Setting {
 protected:
-    LineEditSetting(bool readwrite = true) : edit(NULL) { rw = readwrite; };
+    LineEditSetting(bool readwrite = true) :
+        edit(NULL), rw(readwrite),  password_echo(false) { }
 public:
     virtual QWidget* configWidget(ConfigurationGroup *cg, QWidget* parent, 
                                   const char* widgetName = 0);
@@ -292,10 +293,12 @@
 
     virtual void setEnabled(bool b);
     virtual void setVisible(bool b);
+    virtual void SetPasswordEcho(bool b);
 
 private:
     MythLineEdit* edit;
     bool rw;
+    bool password_echo;
 };
 
 // TODO: set things up so that setting the value as a string emits
Index: programs/mythfilldatabase/filldata.cpp
===================================================================
--- programs/mythfilldatabase/filldata.cpp	(revision 14205)
+++ programs/mythfilldatabase/filldata.cpp	(working copy)
@@ -40,6 +40,7 @@
 #include "cardutil.h"
 #include "sourceutil.h"
 #include "remoteutil.h"
+#include "videosource.h" // for is_grabber..
 
 using namespace std;
 
@@ -852,7 +853,8 @@
         UpdateSourceIcons(source.id);
 
     // Unselect channels not in users lineup for DVB, HDTV
-    if (!insert_channels && (new_channels > 0))
+    if (!insert_channels && (new_channels > 0) &&
+        is_grabber_labs(source.xmltvgrabber))
     {
         bool ok0 = (logged_in == source.userid);
         bool ok1 = (raw_lineup == source.id);
@@ -877,6 +879,13 @@
 
 bool DataDirectUpdateChannels(Source source)
 {
+    if (!is_grabber_labs(source.xmltvgrabber))
+    {
+        VERBOSE(VB_IMPORTANT, "FillData: We only support "
+                "DataDirectUpdateChannels with TMS Labs channel editor");
+        return false;
+    }
+
     ddprocessor.SetListingsProvider(DD_ZAP2IT);
     ddprocessor.SetUserID(source.userid);
     ddprocessor.SetPassword(source.password);
@@ -2531,6 +2540,8 @@
 
     if (xmltv_grabber == "datadirect")
         return grabDDData(source, offset, *qCurrentDate, DD_ZAP2IT);
+    else if (xmltv_grabber == "schedulesdirect1")
+        return grabDDData(source, offset, *qCurrentDate, DD_SCHEDULES_DIRECT);
     else if (xmltv_grabber == "technovera")
     {
         VERBOSE(VB_ALL, "This grabber is no longer supported");
@@ -2857,7 +2868,7 @@
                                   .arg((*it).xmltvgrabber));
 
         QString xmltv_grabber = (*it).xmltvgrabber;
-        need_post_grab_proc |= (xmltv_grabber != "datadirect");
+        need_post_grab_proc |= !is_grabber_datadirect(xmltv_grabber);
 
         if (xmltv_grabber == "tv_grab_uk" || xmltv_grabber == "tv_grab_uk_rt" ||
             xmltv_grabber == "tv_grab_fi" || xmltv_grabber == "tv_grab_es" ||
@@ -2872,7 +2883,7 @@
             if (!grabData(*it, 0))
                 ++failures;
         }
-        else if ((xmltv_grabber == "datadirect") && dd_grab_all)
+        else if (is_grabber_labs(xmltv_grabber) && dd_grab_all)
         {
             if (only_update_channels)
                 DataDirectUpdateChannels(*it);
@@ -2882,7 +2893,7 @@
                 grabData(*it, 0, &qCurrentDate);
             }
         }
-        else if (xmltv_grabber == "datadirect" ||
+        else if (is_grabber_datadirect(xmltv_grabber) ||
                  xmltv_grabber == "tv_grab_se_swedb" ||
                  xmltv_grabber == "tv_grab_no" ||
                  xmltv_grabber == "tv_grab_de_tvtoday" ||
@@ -2906,7 +2917,7 @@
             // often decided by the person maintaining the grabbers.
             if (maxDays > 0) // passed with --max-days
                 grabdays = maxDays;
-            else if (xmltv_grabber == "datadirect")
+            else if (is_grabber_datadirect(xmltv_grabber))
                 grabdays = 14;
             else if (xmltv_grabber == "tv_grab_se_swedb")
                 grabdays = 10;
@@ -2922,7 +2933,7 @@
             if (grabdays == 1)
                 refresh_today = true;
 
-            if ((xmltv_grabber == "datadirect") && only_update_channels)
+            if (is_grabber_labs(xmltv_grabber) && only_update_channels)
             {
                 DataDirectUpdateChannels(*it);
                 grabdays = 0;
@@ -3330,7 +3341,7 @@
     int fromxawfile_id = 1;
     QString fromxawfile_name;
 
-    bool usingDataDirect = false;
+    bool usingDataDirect = false, usingDataDirectLabs = false;
     bool grab_data = true;
 
     bool export_iconmap = false;
@@ -3860,8 +3871,10 @@
                        newsource.lineupid = sourcequery.value(5).toString();
 
                        sourcelist.append(newsource);
-                       if (newsource.xmltvgrabber == "datadirect")
-                           usingDataDirect = true;
+                       usingDataDirect |=
+                           is_grabber_datadirect(newsource.xmltvgrabber);
+                       usingDataDirectLabs |=
+                           is_grabber_labs(newsource.xmltvgrabber);
                   }
              }
              else
@@ -4095,6 +4108,12 @@
         ddprocessor.GrabNextSuggestedTime();
     }
 
+    if (usingDataDirectLabs ||
+        !gContext->GetNumSetting("MythFillFixProgramIDsHasRunOnce", 0))
+    {
+        DataDirectProcessor::FixProgramIDs();
+    }
+
     VERBOSE(VB_IMPORTANT, "\n"
             "===============================================================\n"
             "| Attempting to contact the master backend for rescheduling.  |\n"
