Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 87947 | Differences between
and this patch

Collapse All | Expand All

(-)mythtv-0.17/libs/libmyth/util.cpp (+17 lines)
Lines 742-747 Link Here
742
    return text;
742
    return text;
743
}
743
}
744
744
745
int MythSecsTo(const QDateTime &from, const QDateTime &to)
746
{
747
  return (from.time().secsTo(to.time()) +
748
	  from.date().daysTo(to.date()) * 60 * 60 * 24);
749
}
750
      
751
QDateTime MythUTCToLocal(const QDateTime &utc)
752
{
753
  QDateTime local = QDateTime(QDate(1970, 1, 1));
754
      
755
  int timesecs = MythSecsTo(local, utc);
756
  QDateTime localdt;
757
  localdt.setTime_t(timesecs);
758
  
759
  return localdt;
760
}
761
745
long long stringToLongLong(const QString &str)
762
long long stringToLongLong(const QString &str)
746
{
763
{
747
    long long retval = 0;
764
    long long retval = 0;
(-)mythtv-0.17/libs/libmyth/util.h (-1 / +4 lines)
Lines 3-8 Link Here
3
3
4
#include <qsocket.h>
4
#include <qsocket.h>
5
#include <qstringlist.h>
5
#include <qstringlist.h>
6
#include <qdatetime.h>
6
#include <qcolor.h>
7
#include <qcolor.h>
7
8
8
#include <time.h>
9
#include <time.h>
Lines 46-52 Link Here
46
int myth_system(const QString &command, int flags = 0);
47
int myth_system(const QString &command, int flags = 0);
47
48
48
QString cutDownString(QString text, QFont *testFont, int maxwidth);
49
QString cutDownString(QString text, QFont *testFont, int maxwidth);
49
50
QDateTime MythUTCToLocal(const QDateTime &utc);
51
int MythSecsTo(const QDateTime &from, const QDateTime &to);
52
      
50
long long stringToLongLong(const QString &str);
53
long long stringToLongLong(const QString &str);
51
QString longLongToString(long long ll);
54
QString longLongToString(long long ll);
52
55
(-)mythtv-0.17/libs/libmyth/util.h~ (+57 lines)
Line 0 Link Here
1
#ifndef UTIL_H_
2
#define UTIL_H_
3
4
#include <qsocket.h>
5
#include <qstringlist.h>
6
#include <qdatetime.h>
7
#include <qcolor.h>
8
9
#include <time.h>
10
11
class QPixmap;
12
class QImage;
13
class QPainter;
14
class QFont;
15
16
QString SocDevErrStr(int error);
17
18
// QSockectDevice (frontend)
19
bool connectSocket(QSocketDevice *socket, const QString &host, int port);
20
21
bool WriteStringList(QSocketDevice *socket, QStringList &list);
22
bool ReadStringList(QSocketDevice *socket, QStringList &list,
23
                    bool quickTimeout = false);
24
25
bool WriteBlock(QSocketDevice *socket, void *data, int len);
26
27
// QSockect (backend)
28
bool WriteStringList(QSocket *socket, QStringList &list);
29
bool ReadStringList(QSocket *socket, QStringList &list);
30
31
bool WriteBlock(QSocket *socket, void *data, int len);
32
int ReadBlock(QSocket *socket, void *data, int maxlen);
33
34
void encodeLongLong(QStringList &list, long long num);
35
long long decodeLongLong(QStringList &list, int offset);
36
long long decodeLongLong(QStringList &list, QStringList::iterator &it);
37
38
#if defined(Q_WS_X11)
39
void GetMythTVGeometry(Display *dpy, int screen_num, int *x, int *y, 
40
                       int *w, int *h);
41
#endif
42
43
QRgb blendColors(QRgb source, QRgb add, int alpha);
44
45
#define MYTH_SYSTEM_DONT_BLOCK_LIRC (1)
46
#define MYTH_SYSTEM_DONT_BLOCK_JOYSTICK_MENU (2)
47
int myth_system(const QString &command, int flags = 0);
48
49
QString cutDownString(QString text, QFont *testFont, int maxwidth);
50
51
long long stringToLongLong(const QString &str);
52
QString longLongToString(long long ll);
53
54
bool diskUsage(const char *path, double &total, double &used, double &free);
55
bool getUptime(time_t &uptime);
56
bool getMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM);
57
#endif
(-)mythtv-0.17/libs/libmythtv/datadirect.cpp (-9 / +3 lines)
Lines 1-6 Link Here
1
#include "datadirect.h"
1
#include "datadirect.h"
2
#include "../libmyth/mythwidgets.h"
2
#include "../libmyth/mythwidgets.h"
3
#include "../libmyth/mythcontext.h"
3
#include "../libmyth/mythcontext.h"
4
#include "../libmyth/util.h"
4
5
5
#include <qfile.h>
6
#include <qfile.h>
6
#include <qstring.h>
7
#include <qstring.h>
Lines 68-83 Link Here
68
        curr_schedule.stationid = pxmlatts.value("station");
69
        curr_schedule.stationid = pxmlatts.value("station");
69
70
70
        QString timestr = pxmlatts.value("time");
71
        QString timestr = pxmlatts.value("time");
71
        QDateTime basedt = QDateTime(QDate(1970, 1, 1));
72
        QDateTime UTCdt = QDateTime::fromString(timestr, Qt::ISODate);
73
        curr_schedule.time = MythUTCToLocal(UTCdt);
72
74
73
        QDateTime UTCdt;
74
        QDateTime localdt;
75
        UTCdt = QDateTime::fromString(pxmlatts.value("time"), Qt::ISODate);
76
77
        int timesecs = basedt.secsTo(UTCdt);
78
        localdt.setTime_t(timesecs);
79
80
        curr_schedule.time = localdt;
81
        QString durstr;
75
        QString durstr;
82
76
83
        durstr = pxmlatts.value("duration");
77
        durstr = pxmlatts.value("duration");
(-)mythtv-0.17/libs/libmythtv/siparser.cpp (-19 / +5 lines)
Lines 1-4 Link Here
1
#include "../libmyth/mythcontext.h"
1
#include "../libmyth/mythcontext.h"
2
#include "../libmyth/util.h"
3
2
#include "siparser.h"
4
#include "siparser.h"
3
#include <qdatetime.h>
5
#include <qdatetime.h>
4
#include <qtextcodec.h>
6
#include <qtextcodec.h>
Lines 931-947 Link Here
931
    QDateTime UTCTime = QDateTime(QDate(year,month,day),QTime(hour,min,sec));
933
    QDateTime UTCTime = QDateTime(QDate(year,month,day),QTime(hour,min,sec));
932
934
933
    // Convert to localtime
935
    // Convert to localtime
934
    QDateTime EPOCTime = QDateTime(QDate(1970, 1, 1));
936
    return MythUTCToLocal(UTCTime);
935
    int timesecs = EPOCTime.secsTo(UTCTime);
936
937
    QDateTime LocalTime;
938
939
    LocalTime.setTime_t(timesecs);
940
941
    QString UTCText = UTCTime.toString();
942
    QString LocalText = LocalTime.toString();
943
944
    return LocalTime;
945
937
946
}
938
}
947
939
Lines 1993-2006 Link Here
1993
    // Get event time and add on GPS Second Difference
1985
    // Get event time and add on GPS Second Difference
1994
//    QDateTime UTCTime = ATSCEPOC.addSecs(offset - (((STTHandler*) Table[STT])->GPSOffset) );
1986
//    QDateTime UTCTime = ATSCEPOC.addSecs(offset - (((STTHandler*) Table[STT])->GPSOffset) );
1995
    QDateTime UTCTime = ATSCEPOC.addSecs(offset - 13 );
1987
    QDateTime UTCTime = ATSCEPOC.addSecs(offset - 13 );
1996
    // Get UTC
1988
    // Convert to localtime
1997
    QDateTime UTCEPOC = QDateTime(QDate(1970,1,1));
1989
    return MythUTCToLocal(UTCTime);
1998
    // Now sloppily convert it to localtime
1999
2000
    int timesecs = UTCEPOC.secsTo(UTCTime);
2001
    QDateTime LocalTime;
2002
    LocalTime.setTime_t(timesecs);
2003
    return LocalTime;
2004
}
1990
}
2005
1991
2006
/*
1992
/*
(-)mythtv-0.17/programs/mythfilldatabase/filldata.cpp (-7 / +2 lines)
Lines 1013-1025 Link Here
1013
1013
1014
    if (!quiet)
1014
    if (!quiet)
1015
        cout << "Clearing data for source...\n";
1015
        cout << "Clearing data for source...\n";
1016
    QDateTime basedt = QDateTime(QDate(1970, 1, 1));
1016
    QDateTime fromlocaldt = MythUTCToLocal(ddprocessor.getActualListingsFrom());
1017
    QDateTime fromlocaldt;
1017
    QDateTime tolocaldt = MythUTCToLocal(ddprocessor.getActualListingsTo());
1018
    int timesecs = basedt.secsTo(ddprocessor.getActualListingsFrom());
1019
    fromlocaldt.setTime_t(timesecs);
1020
    QDateTime tolocaldt;
1021
    timesecs = basedt.secsTo(ddprocessor.getActualListingsTo());
1022
    tolocaldt.setTime_t(timesecs);
1023
1018
1024
    if (!quiet)
1019
    if (!quiet)
1025
        cout << "Clearing from " << fromlocaldt.toString() 
1020
        cout << "Clearing from " << fromlocaldt.toString() 
(-)mythtv-0.17/programs/mythfilldatabase/filldata.cpp~ (+3678 lines)
Line 0 Link Here
1
#include <qapplication.h>
2
#include <qdom.h>
3
#include <qfile.h>
4
#include <qstring.h>
5
#include <qregexp.h>
6
#include <qstringlist.h>
7
#include <qvaluelist.h>
8
#include <qmap.h>
9
#include <qdatetime.h>
10
#include <qdir.h>
11
#include <qfile.h>
12
#include <qsqldatabase.h>
13
#include <qsqlquery.h>
14
#include <qurl.h>
15
16
#include <unistd.h>
17
#include <signal.h>
18
#include <sys/wait.h>
19
20
#include <iostream>
21
#include <fstream>
22
#include <string>
23
#include <cstdlib>
24
#include <cstdio>
25
#include <ctime>
26
27
28
#include "libmyth/mythcontext.h"
29
#include "libmythtv/scheduledrecording.h"
30
#include "libmythtv/datadirect.h"
31
#include "libmyth/mythdbcon.h"
32
33
using namespace std;
34
35
static QString SetupIconCacheDirectory();
36
37
bool interactive = false;
38
bool channel_preset = false;
39
bool non_us_updating = false;
40
bool from_file = false;
41
bool quiet = false;
42
bool no_delete = false;
43
bool isNorthAmerica = false;
44
bool isJapan = false;
45
bool interrupted = false;
46
bool refresh_today = false;
47
bool refresh_tomorrow = true;
48
bool refresh_second = false;
49
bool refresh_tba = true;
50
int listing_wrap_offset = 0;
51
bool dd_grab_all = false;
52
bool dddataretrieved = false;
53
bool mark_repeats = true;
54
bool channel_updates = false;
55
56
QString lastdduserid;
57
DataDirectProcessor ddprocessor;
58
QString graboptions = "";
59
60
class ChanInfo
61
{
62
  public:
63
    ChanInfo() { }
64
    ChanInfo(const ChanInfo &other) { callsign = other.callsign; 
65
                                      iconpath = other.iconpath;
66
                                      chanstr = other.chanstr;
67
                                      xmltvid = other.xmltvid;
68
                                      old_xmltvid = other.old_xmltvid;
69
                                      name = other.name;
70
                                      freqid = other.freqid;
71
                                      finetune = other.finetune;
72
                                      tvformat = other.tvformat;
73
                                    }
74
75
    QString callsign;
76
    QString iconpath;
77
    QString chanstr;
78
    QString xmltvid;
79
    QString old_xmltvid;
80
    QString name;
81
    QString freqid;
82
    QString finetune;
83
    QString tvformat;
84
};
85
86
struct ProgRating
87
{
88
    QString system;
89
    QString rating;
90
};
91
92
struct ProgCredit
93
{
94
    QString role;
95
    QString name;
96
};
97
98
class ProgInfo
99
{
100
  public:
101
    ProgInfo() { }
102
    ProgInfo(const ProgInfo &other) { channel = other.channel;
103
                                      startts = other.startts;
104
                                      endts = other.endts;
105
                                      start = other.start;
106
                                      end = other.end;
107
                                      title = other.title;
108
                                      subtitle = other.subtitle;
109
                                      desc = other.desc;
110
                                      category = other.category;
111
                                      catType = other.catType;
112
                                      airdate = other.airdate;
113
                                      stars = other.stars;
114
                                      previouslyshown = other.previouslyshown;
115
                                      title_pronounce = other.title_pronounce;
116
                                      stereo = other.stereo;
117
                                      subtitled = other.subtitled;
118
                                      hdtv = other.hdtv;
119
                                      closecaptioned = other.closecaptioned;
120
                                      partnumber = other.partnumber;
121
                                      parttotal = other.parttotal;
122
                                      seriesid = other.seriesid;
123
                                      originalairdate = other.originalairdate;
124
                                      showtype = other.showtype;
125
                                      colorcode = other.colorcode;
126
                                      syndicatedepisodenumber = other.syndicatedepisodenumber;
127
                                      programid = other.programid;
128
        
129
                                      clumpidx = other.clumpidx;
130
                                      clumpmax = other.clumpmax;
131
                                      ratings = other.ratings;
132
                                      credits = other.credits;
133
                                      content = other.content;
134
                                    }
135
136
137
    QString channel;
138
    QString startts;
139
    QString endts;
140
    QDateTime start;
141
    QDateTime end;
142
    QString title;
143
    QString subtitle;
144
    QString desc;
145
    QString category;
146
    QString catType;
147
    QString airdate;
148
    QString stars;
149
    bool previouslyshown;
150
    QString title_pronounce;
151
    bool stereo;
152
    bool subtitled;
153
    bool hdtv;
154
    bool closecaptioned;
155
    QString partnumber;
156
    QString parttotal;
157
    QString seriesid;                                
158
    QString originalairdate;
159
    QString showtype;
160
    QString colorcode;
161
    QString syndicatedepisodenumber;
162
    QString programid;
163
164
    QString clumpidx;
165
    QString clumpmax;
166
    QValueList<ProgRating> ratings;
167
    QValueList<ProgCredit> credits;
168
    QString content;
169
};
170
171
bool operator<(const ProgInfo &a, const ProgInfo &b)
172
{
173
    return (a.start < b.start);
174
}
175
176
bool operator>(const ProgInfo &a, const ProgInfo &b)
177
{
178
    return (a.start > b.start);
179
}
180
181
bool operator<=(const ProgInfo &a, const ProgInfo &b)
182
{
183
    return (a.start <= b.start);
184
}
185
186
struct Source
187
{
188
    int id;
189
    QString name;
190
    QString xmltvgrabber;
191
    QString userid;
192
    QString password;
193
    QString lineupid;
194
};
195
196
197
unsigned int ELFHash(const char *s)
198
{
199
    /* ELF hash uses unsigned chars and unsigned arithmetic for portability */
200
    const unsigned char *name = (const unsigned char *)s;
201
    unsigned long h = 0, g;
202
203
    while (*name)
204
    { /* do some fancy bitwanking on the string */
205
        h = (h << 4) + (unsigned long)(*name++);
206
        if ((g = (h & 0xF0000000UL))!=0)
207
            h ^= (g >> 24);
208
        h &= ~g;
209
210
    }
211
212
    return (int)h;
213
}
214
215
void clearDataByChannel(int chanid, QDateTime from, QDateTime to) 
216
{
217
    MSqlQuery query;
218
    QString querystr;
219
220
    querystr.sprintf("DELETE FROM program "
221
                     "WHERE starttime >= '%s' AND starttime < '%s' "
222
                     "AND chanid = %d;",
223
                     from.toString("yyyyMMddhhmmss").ascii(),
224
                     to.toString("yyyyMMddhhmmss").ascii(),
225
                     chanid);
226
    query.exec(querystr);
227
228
    querystr.sprintf("DELETE FROM programrating WHERE starttime >= '%s' "
229
                     " AND starttime < '%s' AND chanid = %d;",
230
                     from.toString("yyyyMMddhhmmss").ascii(),
231
                     to.toString("yyyyMMddhhmmss").ascii(),
232
                     chanid);
233
    query.exec(querystr);
234
235
    querystr.sprintf("DELETE FROM credits WHERE starttime >= '%s' "
236
                     "AND starttime < '%s' AND chanid = %d;",
237
                     from.toString("yyyyMMddhhmmss").ascii(),
238
                     to.toString("yyyyMMddhhmmss").ascii(),
239
                     chanid);
240
    query.exec(querystr);
241
242
    querystr.sprintf("DELETE FROM programgenres WHERE starttime >= '%s' "
243
                     "AND starttime < '%s' AND chanid = %d;",
244
                     from.toString("yyyyMMddhhmmss").ascii(),
245
                     to.toString("yyyyMMddhhmmss").ascii(),
246
                     chanid);
247
    query.exec(querystr);
248
}
249
250
void clearDataBySource(int sourceid, QDateTime from, QDateTime to) 
251
{
252
    QString querystr= QString("SELECT chanid FROM channel WHERE "
253
                              "sourceid = \"%0\";").arg(sourceid);
254
255
    MSqlQuery query;
256
257
    if (!query.exec(querystr))
258
        MythContext::DBError("Selecting channels per source", query);
259
        
260
    if (query.isActive() && query.numRowsAffected() > 0)
261
    {
262
        while (query.next())
263
        {
264
            int chanid = query.value(0).toInt();
265
            clearDataByChannel(chanid, from, to);
266
        }
267
    }
268
}
269
270
// icon mapping stuff
271
namespace {
272
const char * const IM_DOC_TAG = "iconmappings";
273
274
const char * const IM_CS_TO_NET_TAG = "callsigntonetwork";
275
const char * const IM_CS_TAG = "callsign";
276
277
const char * const IM_NET_TAG = "network";
278
279
const char * const IM_NET_TO_URL_TAG = "networktourl";
280
const char * const IM_NET_URL_TAG = "url";
281
282
const char * const BASEURLMAP_START = "mythfilldatabase.urlmap.";
283
284
const char * const IM_BASEURL_TAG = "baseurl";
285
const char * const IM_BASE_STUB_TAG = "stub";
286
287
QString expandURLString(const QString &url)
288
{
289
    QRegExp expandtarget("\\[([^\\]]+)\\]");
290
    QString retval = url;
291
292
    int found_at = 0;
293
    int start_index = 0;
294
    while (found_at != -1)
295
    {
296
        found_at = expandtarget.search(retval, start_index);
297
        if (found_at != -1)
298
        {
299
            QString no_mapping("no_URL_mapping");
300
            QString search_string = expandtarget.cap(1);
301
            QString expanded_text = gContext->GetSetting(
302
                    QString(BASEURLMAP_START) + search_string, no_mapping);
303
            if (expanded_text != no_mapping)
304
            {
305
                retval.replace(found_at, expandtarget.matchedLength(),
306
                        expanded_text);
307
            }
308
            else
309
            {
310
                start_index = found_at + expandtarget.matchedLength();
311
            }
312
        }
313
    }
314
315
    return retval;
316
}
317
318
void UpdateSourceIcons(int sourceid)
319
{
320
    if (!quiet)
321
        cout << QString("Updating icons for sourceid: %1").arg(sourceid) << endl;
322
323
    QString fileprefix = SetupIconCacheDirectory();
324
325
    MSqlQuery query;
326
    query.prepare("SELECT ch.chanid, nim.url "
327
            "FROM channel ch, callsignnetworkmap csm "
328
            "RIGHT JOIN networkiconmap nim ON csm.network = nim.network "
329
            "WHERE ch.callsign = csm.callsign AND "
330
            "(icon = :NOICON OR icon = '') AND ch.sourceid = :SOURCEID");
331
    query.bindValue(":SOURCEID", sourceid);
332
    query.bindValue(":NOICON", "none");
333
334
    if (!query.exec())
335
        MythContext::DBError("Looking for icons to fetch", query);
336
337
    if (query.isActive() && query.numRowsAffected() > 0)
338
    {
339
        while (query.next())
340
        {
341
            QString icon_url = expandURLString(query.value(1).toString());
342
            QFileInfo qfi(icon_url);
343
            QFile localfile(fileprefix + "/" + qfi.fileName());
344
            if (!localfile.exists())
345
            {
346
                QString icon_get_command = QString("wget --timestamping "
347
                        "--directory-prefix=") + fileprefix + " " + icon_url;
348
                if (!quiet)
349
                    cout << QString("Attempting to fetch icon with: %1").arg(icon_get_command) << endl;
350
351
                system(icon_get_command);
352
            }
353
354
            if (localfile.exists())
355
            {
356
                int chanid = query.value(0).toInt();
357
                if (!quiet)
358
                {
359
                    QString m = QString("Updating channel icon for chanid: %1")
360
                        .arg(chanid);
361
                    cout << m << endl;
362
                }
363
                MSqlQuery icon_update_query;
364
                icon_update_query.prepare("UPDATE channel SET icon = :ICON "
365
                        "WHERE chanid = :CHANID AND sourceid = :SOURCEID");
366
                icon_update_query.bindValue(":ICON", localfile.name());
367
                icon_update_query.bindValue(":CHANID", query.value(0).toInt());
368
                icon_update_query.bindValue(":SOURCEID", sourceid);
369
370
                if (!icon_update_query.exec())
371
                    MythContext::DBError("Setting the icon file name",
372
                            icon_update_query);
373
            }
374
            else
375
            {
376
                cerr << QString(
377
                        "Error retrieving icon from '%1' to file '%2'")
378
                        .arg(icon_url)
379
                        .arg(localfile.name())
380
                     << endl;
381
            }
382
        }
383
    }
384
}
385
386
bool dash_open(QFile &file, const QString &filename, int m, FILE *handle = NULL)
387
{
388
    bool retval = false;
389
    if (filename == "-")
390
    {
391
        if (handle == NULL)
392
        {
393
            handle = stdout;
394
            if (m & IO_ReadOnly)
395
            {
396
                handle = stdin;
397
            }
398
        }
399
        retval = file.open(m, handle);
400
    }
401
    else
402
    {
403
        file.setName(filename);
404
        retval = file.open(m);
405
    }
406
407
    return retval;
408
}
409
410
class DOMException
411
{
412
  private:
413
    QString message;
414
415
  protected:
416
    void setMessage(const QString &mes)
417
    {
418
        message = mes;
419
    }
420
421
  public:
422
    DOMException() : message("Unknown DOMException") {}
423
    virtual ~DOMException() {}
424
    DOMException(const QString &mes) : message(mes) {}
425
    QString getMessage()
426
    {
427
        return message;
428
    }
429
};
430
431
class DOMBadElementConversion : public DOMException
432
{
433
  public:
434
    DOMBadElementConversion()
435
    {
436
        setMessage("Unknown DOMBadElementConversion");
437
    }
438
    DOMBadElementConversion(const QString &mes) : DOMException(mes) {}
439
    DOMBadElementConversion(const QDomNode &node)
440
    {
441
        setMessage(QString("Unable to convert node: '%1' to QDomElement.")
442
                .arg(node.nodeName()));
443
    }
444
};
445
446
class DOMUnknownChildElement : public DOMException
447
{
448
  public:
449
    DOMUnknownChildElement()
450
    {
451
        setMessage("Unknown DOMUnknownChildElement");
452
    }
453
    DOMUnknownChildElement(const QString &mes) : DOMException(mes) {}
454
    DOMUnknownChildElement(const QDomElement &e, QString child_name)
455
    {
456
        setMessage(QString("Unknown child element '%1' of: '%2'")
457
                .arg(child_name)
458
                .arg(e.tagName()));
459
    }
460
};
461
462
QDomElement nodeToElement(QDomNode &node)
463
{
464
    QDomElement retval = node.toElement();
465
    if (retval.isNull())
466
    {
467
        throw DOMBadElementConversion(node);
468
    }
469
    return retval;
470
}
471
472
QString getNamedElementText(const QDomElement &e,
473
        const QString &child_element_name)
474
{
475
    QDomNode child_node = e.namedItem(child_element_name);
476
    if (child_node.isNull())
477
    {
478
        throw DOMUnknownChildElement(e, child_element_name);
479
    }
480
    QDomElement element = nodeToElement(child_node);
481
    return element.text();
482
}
483
484
void ImportIconMap(const QString &filename)
485
{
486
    if (!quiet)
487
    {
488
        QString msg = QString("Importing icon mapping from %1...")
489
                .arg(filename);
490
        cout << msg << endl;
491
    }
492
    QFile xml_file;
493
494
    if (dash_open(xml_file, filename, IO_ReadOnly))
495
    {
496
        QDomDocument doc;
497
        QString de_msg;
498
        int de_ln = 0;
499
        int de_column = 0;
500
        if (doc.setContent(&xml_file, false, &de_msg, &de_ln, &de_column))
501
        {
502
            MSqlQuery nm_query;
503
            nm_query.prepare("REPLACE INTO networkiconmap(network, url) "
504
                    "VALUES(:NETWORK, :URL)");
505
            MSqlQuery cm_query;
506
            cm_query.prepare("REPLACE INTO callsignnetworkmap(callsign, "
507
                    "network) VALUES(:CALLSIGN, :NETWORK)");
508
            MSqlQuery su_query;
509
            su_query.prepare("UPDATE settings SET data = :URL "
510
                    "WHERE value = :STUBNAME");
511
            MSqlQuery si_query;
512
            si_query.prepare("INSERT INTO settings(value, data) "
513
                    "VALUES(:STUBNAME, :URL)");
514
515
            QDomElement element = doc.documentElement();
516
517
            QDomNode node = element.firstChild();
518
            while (!node.isNull())
519
            {
520
                try
521
                {
522
                    QDomElement e = nodeToElement(node);
523
                    if (e.tagName() == IM_NET_TO_URL_TAG)
524
                    {
525
                        QString net = getNamedElementText(e, IM_NET_TAG);
526
                        QString u = getNamedElementText(e, IM_NET_URL_TAG);
527
528
                        nm_query.bindValue(":NETWORK", net.stripWhiteSpace());
529
                        nm_query.bindValue(":URL", u.stripWhiteSpace());
530
                        if (!nm_query.exec())
531
                            MythContext::DBError(
532
                                    "Inserting network->url mapping", nm_query);
533
                    }
534
                    else if (e.tagName() == IM_CS_TO_NET_TAG)
535
                    {
536
                        QString cs = getNamedElementText(e, IM_CS_TAG);
537
                        QString net = getNamedElementText(e, IM_NET_TAG);
538
539
                        cm_query.bindValue(":CALLSIGN", cs.stripWhiteSpace());
540
                        cm_query.bindValue(":NETWORK", net.stripWhiteSpace());
541
                        if (!cm_query.exec())
542
                            MythContext::DBError("Inserting callsign->network "
543
                                    "mapping", cm_query);
544
                    }
545
                    else if (e.tagName() == IM_BASEURL_TAG)
546
                    {
547
                        MSqlQuery *qr = &si_query;
548
549
                        QString st(BASEURLMAP_START);
550
                        st += getNamedElementText(e, IM_BASE_STUB_TAG);
551
                        QString u = getNamedElementText(e, IM_NET_URL_TAG);
552
553
                        MSqlQuery qc;
554
                        qc.prepare("SELECT COUNT(*) FROM settings "
555
                                "WHERE value = :STUBNAME");
556
                        qc.bindValue(":STUBNAME", st);
557
                        qc.exec();
558
                        if (qc.isActive() && qc.numRowsAffected() > 0)
559
                        {
560
                            qc.first();
561
                            if (qc.value(0).toInt() != 0)
562
                            {
563
                                qr = &su_query;
564
                            }
565
                        }
566
567
                        qr->bindValue(":STUBNAME", st);
568
                        qr->bindValue(":URL", u);
569
570
                        if (!qr->exec())
571
                            MythContext::DBError(
572
                                    "Inserting callsign->network mapping", *qr);
573
                    }
574
                }
575
                catch (DOMException &e)
576
                {
577
                    cerr << QString("Error while processing %1: %2")
578
                            .arg(node.nodeName())
579
                            .arg(e.getMessage())
580
                         << endl;
581
                }
582
                node = node.nextSibling();
583
            }
584
        }
585
        else
586
        {
587
            cerr << QString(
588
                    "Error unable to set document content: %1:%2c%3 %4")
589
                    .arg(filename)
590
                    .arg(de_ln)
591
                    .arg(de_column)
592
                    .arg(de_msg)
593
                 << endl;
594
        }
595
    }
596
    else
597
    {
598
        cerr << QString("Error unable to open '%1' for reading.")
599
                .arg(filename)
600
             << endl;
601
    }
602
}
603
604
void ExportIconMap(const QString &filename)
605
{
606
    if (!quiet)
607
    {
608
        cout << QString("Exporting icon mapping to %1...").arg(filename)
609
             << endl;
610
    }
611
    QFile xml_file(filename);
612
    if (dash_open(xml_file, filename, IO_WriteOnly))
613
    {
614
        QTextStream os(&xml_file);
615
        os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
616
        os << "<!-- generated by mythfilldatabase -->\n";
617
618
        QDomDocument iconmap;
619
        QDomElement roote = iconmap.createElement(IM_DOC_TAG);
620
621
        MSqlQuery query;
622
        query.exec("SELECT * FROM callsignnetworkmap ORDER BY callsign");
623
624
        if (query.isActive() && query.numRowsAffected() > 0)
625
        {
626
            while (query.next())
627
            {
628
                QDomElement cs2nettag = iconmap.createElement(IM_CS_TO_NET_TAG);
629
                QDomElement cstag = iconmap.createElement(IM_CS_TAG);
630
                QDomElement nettag = iconmap.createElement(IM_NET_TAG);
631
                QDomText cs_text = iconmap.createTextNode(
632
                        query.value(1).toString());
633
                QDomText net_text = iconmap.createTextNode(
634
                        query.value(2).toString());
635
636
                cstag.appendChild(cs_text);
637
                nettag.appendChild(net_text);
638
639
                cs2nettag.appendChild(cstag);
640
                cs2nettag.appendChild(nettag);
641
642
                roote.appendChild(cs2nettag);
643
            }
644
        }
645
646
        query.exec("SELECT * FROM networkiconmap ORDER BY network");
647
        if (query.isActive() && query.numRowsAffected() > 0)
648
        {
649
            while (query.next())
650
            {
651
                QDomElement net2urltag = iconmap.createElement(
652
                        IM_NET_TO_URL_TAG);
653
                QDomElement nettag = iconmap.createElement(IM_NET_TAG);
654
                QDomElement urltag = iconmap.createElement(IM_NET_URL_TAG);
655
                QDomText net_text = iconmap.createTextNode(
656
                        query.value(1).toString());
657
                QDomText url_text = iconmap.createTextNode(
658
                        query.value(2).toString());
659
660
                nettag.appendChild(net_text);
661
                urltag.appendChild(url_text);
662
663
                net2urltag.appendChild(nettag);
664
                net2urltag.appendChild(urltag);
665
666
                roote.appendChild(net2urltag);
667
            }
668
        }
669
670
        query.prepare("SELECT value,data FROM settings WHERE value "
671
                "LIKE :URLMAP");
672
        query.bindValue(":URLMAP", QString(BASEURLMAP_START) + "%");
673
        query.exec();
674
        if (query.isActive() && query.numRowsAffected() > 0)
675
        {
676
            QRegExp baseax("\\.([^\\.]+)$");
677
            while (query.next())
678
            {
679
                QString base_stub = query.value(0).toString();
680
                if (baseax.search(base_stub) != -1)
681
                {
682
                    base_stub = baseax.cap(1);
683
                }
684
685
                QDomElement baseurltag = iconmap.createElement(IM_BASEURL_TAG);
686
                QDomElement stubtag = iconmap.createElement(
687
                        IM_BASE_STUB_TAG);
688
                QDomElement urltag = iconmap.createElement(IM_NET_URL_TAG);
689
                QDomText base_text = iconmap.createTextNode(base_stub);
690
                QDomText url_text = iconmap.createTextNode(
691
                        query.value(1).toString());
692
693
                stubtag.appendChild(base_text);
694
                urltag.appendChild(url_text);
695
696
                baseurltag.appendChild(stubtag);
697
                baseurltag.appendChild(urltag);
698
699
                roote.appendChild(baseurltag);
700
            }
701
        }
702
703
        iconmap.appendChild(roote);
704
        iconmap.save(os, 4);
705
    }
706
    else
707
    {
708
        cerr << QString("Error unable to open '%1' for writing.") << endl;
709
    }
710
}
711
712
void RunSimpleQuery(const QString &query)
713
{
714
    MSqlQuery q;
715
    if (!q.exec(query))
716
        MythContext::DBError("RunSimpleQuery ", q);
717
}
718
719
void ResetIconMap(bool reset_icons)
720
{
721
    MSqlQuery query;
722
    query.prepare("DELETE FROM settings WHERE value LIKE :URLMAPLIKE");
723
    query.bindValue(":URLMAPLIKE", QString(BASEURLMAP_START) + '%');
724
    if (!query.exec())
725
        MythContext::DBError("ResetIconMap", query);
726
727
    RunSimpleQuery("TRUNCATE TABLE callsignnetworkmap;");
728
    RunSimpleQuery("TRUNCATE TABLE networkiconmap");
729
730
    if (reset_icons)
731
    {
732
        RunSimpleQuery("UPDATE channel SET icon = 'none'");
733
    }
734
}
735
736
} // namespace
737
738
// DataDirect stuff
739
void DataDirectStationUpdate(Source source)
740
{
741
    MSqlQuery query;
742
    QString querystr;
743
    int chanid;
744
745
    ddprocessor.updateStationViewTable();
746
747
    MSqlQuery query1;
748
    query1.prepare("SELECT dd_v_station.stationid,dd_v_station.callsign,"
749
               "dd_v_station.stationname,dd_v_station.channel,"
750
               "dd_v_station.fccchannelnumber,dd_v_station.channelMinor "
751
               "FROM dd_v_station LEFT JOIN channel ON "
752
               "dd_v_station.stationid = channel.xmltvid "
753
               "AND channel.sourceid = :SOURCEID "
754
               "WHERE channel.chanid IS NULL;");
755
    query1.bindValue(":SOURCEID", source.id);
756
757
    if (!query1.exec())
758
        MythContext::DBError("Selecting new channels", query1);
759
760
    if (query1.isActive() && query1.numRowsAffected() > 0)
761
    {
762
        while (query1.next())
763
        {
764
            chanid = source.id * 1000 + query1.value(3).toString().toInt();
765
766
            while(1)
767
            {
768
                querystr.sprintf("SELECT channum FROM channel WHERE "
769
                                 "chanid = %d;", chanid);
770
                if (!query.exec(querystr))
771
                    break;
772
773
                if (query.isActive() && query.numRowsAffected() > 0)
774
                    chanid++;
775
                else
776
                    break;
777
            }
778
779
            QString xmltvid = query1.value(0).toString();
780
            QString callsign = query1.value(1).toString();
781
            if (callsign == "")
782
                callsign = QString::number(chanid);
783
            QString name = query1.value(2).toString();
784
            QString channel = query1.value(3).toString();
785
            QString freqid = query1.value(4).toString();
786
            QString minor = query1.value(5).toString();
787
788
            if (minor != "") 
789
            {
790
                freqid += "-" + minor;
791
                channel += "_" + minor; // default channel number
792
            }
793
            else
794
                freqid = channel;
795
796
            query.prepare("INSERT INTO channel (chanid,channum,sourceid,"
797
                          "callsign, name, xmltvid, freqid, tvformat) "
798
                          "VALUES (:CHANID,:CHANNUM,:SOURCEID,:CALLSIGN,"
799
                          ":NAME,:XMLTVID,:FREQID,:TVFORMAT);");
800
801
            query.bindValue(":CHANID", chanid);
802
            query.bindValue(":CHANNUM", channel);
803
            query.bindValue(":SOURCEID", source.id);
804
            query.bindValue(":CALLSIGN", callsign);
805
            query.bindValue(":NAME", name);
806
            query.bindValue(":XMLTVID", xmltvid);
807
            query.bindValue(":FREQID", freqid);
808
            query.bindValue(":TVFORMAT", "Default");
809
        
810
            if (!query.exec())
811
                MythContext::DBError("Inserting new channel", query);
812
        }
813
    }
814
815
816
    if(channel_updates)
817
    {
818
819
        //
820
        //  User must pass "--do_channel_update" for this block of code to
821
        //  execute
822
        //
823
824
        MSqlQuery dd_station_info("SELECT callsign, stationname, stationid,"
825
                "channel, fccchannelnumber, channelMinor FROM dd_v_station;");
826
827
        if (dd_station_info.first())
828
        {
829
            MSqlQuery dd_update;
830
            dd_update.prepare("UPDATE channel SET callsign = :CALLSIGN,"
831
                    " name = :NAME, channum = :CHANNEL, freqid = :FREQID "
832
                    " WHERE xmltvid = :STATIONID AND sourceid = :SOURCEID;");
833
            do
834
            {
835
                QString channel = dd_station_info.value(3).toString();
836
                QString freqid = dd_station_info.value(4).toString();
837
                QString minor = dd_station_info.value(5).toString();
838
839
                if (minor != "")
840
                {
841
                    freqid += "-" + minor;
842
                    channel += "_" + minor; // default channel number
843
                }
844
                else
845
                {
846
                    freqid = channel;
847
                }
848
849
                dd_update.bindValue(":CALLSIGN", dd_station_info.value(0));
850
                dd_update.bindValue(":NAME", dd_station_info.value(1));
851
                dd_update.bindValue(":STATIONID", dd_station_info.value(2));
852
                dd_update.bindValue(":CHANNEL", channel);
853
                dd_update.bindValue(":SOURCEID", source.id);
854
                dd_update.bindValue(":FREQID", freqid);
855
                if (!dd_update.exec())
856
                {
857
                    MythContext::DBError("Updating channel table",
858
                            dd_update.lastQuery());
859
                }
860
            } 
861
            while (dd_station_info.next());
862
        }
863
    }
864
865
    UpdateSourceIcons(source.id);
866
867
    // Now, delete any channels which no longer exist
868
    // (Not currently done in standard program -- need to verify if required)
869
}
870
871
void DataDirectProgramUpdate(Source source) 
872
{
873
    MSqlQuery query;
874
   
875
    //cerr << "Creating program view table...\n";
876
    ddprocessor.updateProgramViewTable(source.id);
877
    //cerr <<  "Finished creating program view table...\n";
878
879
    //cerr << "Adding rows to main program table from view table..\n";
880
    if (!query.exec("INSERT IGNORE INTO program (chanid, starttime, endtime, "
881
                    "title, subtitle, description, "
882
                    "showtype, category, category_type, "
883
                    "airdate, stars, previouslyshown, stereo, subtitled, "
884
                    "hdtv, closecaptioned, partnumber, parttotal, seriesid, "
885
                    "originalairdate, colorcode, syndicatedepisodenumber, "
886
                    "programid) "
887
                    "SELECT chanid, starttime, endtime, "
888
                    "title, subtitle, description, "
889
                    "showtype, dd_genre.class, category_type, "
890
                    "airdate, stars, previouslyshown, stereo, subtitled, "
891
                    "hdtv, closecaptioned, partnumber, parttotal, seriesid, "
892
                    "originalairdate, colorcode, syndicatedepisodenumber, "
893
                    "dd_v_program.programid FROM dd_v_program "
894
                    "LEFT JOIN dd_genre ON ("
895
                    "dd_v_program.programid = dd_genre.programid AND "
896
                    "dd_genre.relevance = '0');"))
897
        MythContext::DBError("Inserting into program table", query);
898
899
    //cerr << "Finished adding rows to main program table...\n";
900
    //cerr << "Adding program ratings...\n";
901
902
    if (!query.exec("INSERT IGNORE INTO programrating (chanid, starttime, "
903
                    "system, rating) SELECT chanid, starttime, 'MPAA', "
904
                    "mpaarating FROM dd_v_program WHERE mpaarating != '';"))
905
        MythContext::DBError("Inserting into programrating table", query);
906
907
    if (!query.exec("INSERT IGNORE INTO programrating (chanid, starttime, "
908
                    "system, rating) SELECT chanid, starttime, 'VCHIP', "
909
                    "tvrating FROM dd_v_program WHERE tvrating != '';"))
910
        MythContext::DBError("Inserting into programrating table", query);
911
912
    //cerr << "Finished adding program ratings...\n";
913
    //cerr << "Populating people table from production crew list...\n";
914
915
    if (!query.exec("INSERT IGNORE INTO people (name) SELECT fullname "
916
                    "FROM dd_productioncrew;"))
917
        MythContext::DBError("Inserting into people table", query);
918
919
    //cerr << "Finished adding people...\n";
920
    //cerr << "Adding credits entries from production crew list...\n";
921
922
    if (!query.exec("INSERT IGNORE INTO credits (chanid, starttime, person, "
923
                    "role) SELECT chanid, starttime, person, role "
924
                    "FROM dd_productioncrew, dd_v_program, people "
925
                    "WHERE "
926
                    "((dd_productioncrew.programid = dd_v_program.programid) "
927
                    "AND (dd_productioncrew.fullname = people.name));"))
928
        MythContext::DBError("Inserting into credits table", query);
929
930
    //cerr << "Finished inserting credits...\n";
931
    //cerr << "Adding genres...\n";
932
933
    if (!query.exec("INSERT IGNORE INTO programgenres (chanid, starttime, "
934
                    "relevance, genre) SELECT chanid, starttime, "
935
                    "relevance, class FROM dd_v_program, dd_genre "
936
                    "WHERE (dd_v_program.programid = dd_genre.programid);"))
937
        MythContext::DBError("Inserting into programgenres table",query);
938
939
    //cerr << "Done...\n";
940
}
941
942
bool grabDDData(Source source, int poffset, QDate pdate) 
943
{
944
    ddprocessor.setLineup(source.lineupid);
945
    ddprocessor.setUserID(source.userid);
946
    ddprocessor.setPassword(source.password);
947
948
    bool needtoretrieve = true;
949
950
    if (source.userid != lastdduserid)
951
        dddataretrieved = false;
952
953
    if (dd_grab_all && dddataretrieved)
954
        needtoretrieve = false;
955
956
    QDateTime qdtNow = QDateTime::currentDateTime();
957
    MSqlQuery query;
958
    QString status = "currently running.";
959
960
    query.exec(QString("UPDATE settings SET data ='%1' "
961
                       "WHERE value='mythfilldatabaseLastRunStart'")
962
                       .arg(qdtNow.toString("yyyy-MM-dd hh:mm")));
963
964
    if (needtoretrieve)
965
    {
966
        if (!quiet)
967
            cout << "Retrieving datadirect data... \n";
968
        if (dd_grab_all) 
969
        {
970
            if (!quiet)
971
                cout << "Grabbing ALL available data...\n";
972
            if (!ddprocessor.grabAllData())
973
            {
974
                cerr << "Encountered error in grabbing data...\n";
975
                return false;
976
            }
977
        }
978
        else
979
        {
980
            if (!quiet)
981
                cout << "Grabbing data for " << pdate.toString() 
982
                     << " offset " << poffset << "\n";
983
            QDateTime fromdatetime = QDateTime(pdate);
984
            QDateTime todatetime;
985
            fromdatetime.setTime_t(QDateTime(pdate).toTime_t(),Qt::UTC);
986
            fromdatetime = fromdatetime.addDays(poffset);
987
            todatetime = fromdatetime.addDays(1);
988
            if (!quiet)
989
                cout << "From : " << fromdatetime.toString() 
990
                     << " To : " << todatetime.toString() << " (UTC)\n";
991
            ddprocessor.grabData(false, fromdatetime, todatetime);
992
        }
993
994
        dddataretrieved = true;
995
        lastdduserid = source.userid;
996
    }
997
    else
998
    {
999
        if (!quiet)
1000
            cout << "Using existing grabbed data in temp tables..\n";
1001
    }
1002
1003
    if (!quiet)
1004
        cout << "Grab complete.  Actual data from " 
1005
             << ddprocessor.getActualListingsFrom().toString() << " "
1006
             << "to " << ddprocessor.getActualListingsTo().toString() 
1007
             << " (UTC) \n";
1008
1009
    qdtNow = QDateTime::currentDateTime();
1010
    query.exec(QString("UPDATE settings SET data ='%1' "
1011
                       "WHERE value='mythfilldatabaseLastRunEnd'")
1012
                       .arg(qdtNow.toString("yyyy-MM-dd hh:mm")));
1013
1014
    if (!quiet)
1015
        cout << "Clearing data for source...\n";
1016
    QDateTime fromlocaldt = MythUTCToLocal(ddprocessor.getActualListingFrom());
1017
    QDateTime tolocaldt = MythUTCToLocal(ddprocessor.getActualListingsTo());
1018
1019
    if (!quiet)
1020
        cout << "Clearing from " << fromlocaldt.toString() 
1021
             << " to " << tolocaldt.toString() << " (localtime)\n";
1022
1023
    clearDataBySource(source.id, fromlocaldt,tolocaldt);
1024
    if (!quiet)
1025
        cout << "Data for source cleared...\n";
1026
1027
    if (!quiet)
1028
        cout << "Main temp tables populated.  Updating myth channels...\n";
1029
    DataDirectStationUpdate(source);
1030
1031
    if (!quiet)
1032
        cout << "Channels updated..  Updating programs...\n";
1033
    DataDirectProgramUpdate(source);
1034
1035
    return true;
1036
}
1037
1038
// XMLTV stuff
1039
1040
QDateTime fromXMLTVDate(QString &text)
1041
{
1042
    int year, month, day, hour, min, sec;
1043
    QDate ldate;
1044
    QTime ltime;
1045
    QDateTime dt;
1046
1047
    if (text == QString::null)
1048
        return dt;
1049
1050
    if (text.find(QRegExp("^\\d{12}")) != 0)
1051
        return dt;
1052
1053
    year = atoi(text.mid(0, 4).ascii());
1054
    month = atoi(text.mid(4, 2).ascii());
1055
    day = atoi(text.mid(6, 2).ascii());
1056
    hour = atoi(text.mid(8, 2).ascii());
1057
    min = atoi(text.mid(10, 2).ascii());
1058
    if (text.find(QRegExp("^\\d\\d"), 12) == 0)
1059
        sec = atoi(text.mid(12, 2).ascii());
1060
    else
1061
        sec = 0;
1062
1063
    ldate = QDate(year, month, day);
1064
    ltime = QTime(hour, min, sec);
1065
1066
    dt = QDateTime(ldate, ltime);
1067
1068
    return dt; 
1069
}
1070
1071
QString getFirstText(QDomElement element)
1072
{
1073
    for (QDomNode dname = element.firstChild(); !dname.isNull();
1074
         dname = dname.nextSibling())
1075
    {
1076
        QDomText t = dname.toText();
1077
        if (!t.isNull())
1078
            return t.data();
1079
    }
1080
    return "";
1081
}
1082
1083
ChanInfo *parseChannel(QDomElement &element, QUrl baseUrl) 
1084
{
1085
    ChanInfo *chaninfo = new ChanInfo;
1086
1087
    QString xmltvid = element.attribute("id", "");
1088
    QStringList split = QStringList::split(" ", xmltvid);
1089
1090
    bool xmltvisjunk = false;
1091
1092
    if (isNorthAmerica)
1093
    {
1094
        if (xmltvid.contains("zap2it"))
1095
        {
1096
            xmltvisjunk = true;
1097
            chaninfo->chanstr = "";
1098
            chaninfo->xmltvid = xmltvid;
1099
            chaninfo->callsign = "";
1100
        }
1101
        else
1102
        {
1103
            chaninfo->xmltvid = split[0];
1104
            chaninfo->chanstr = split[0];
1105
            if (split.size() > 1)
1106
                chaninfo->callsign = split[1];
1107
            else
1108
                chaninfo->callsign = "";
1109
        }
1110
    }
1111
    else
1112
    {
1113
        chaninfo->callsign = "";
1114
        chaninfo->chanstr = "";
1115
        chaninfo->xmltvid = xmltvid;
1116
    }
1117
1118
    chaninfo->iconpath = "";
1119
    chaninfo->name = "";
1120
    chaninfo->finetune = "";
1121
    chaninfo->tvformat = "Default";
1122
1123
    for (QDomNode child = element.firstChild(); !child.isNull();
1124
         child = child.nextSibling())
1125
    {
1126
        QDomElement info = child.toElement();
1127
        if (!info.isNull())
1128
        {
1129
            if (info.tagName() == "icon")
1130
            {
1131
                QUrl iconUrl(baseUrl, info.attribute("src", ""), true);
1132
                chaninfo->iconpath = iconUrl.toString();
1133
            }
1134
            else if (info.tagName() == "display-name")
1135
            {
1136
                if (chaninfo->name.length() == 0)
1137
                {
1138
                    chaninfo->name = info.text();
1139
                    if (xmltvisjunk)
1140
                    {
1141
                        QStringList split = QStringList::split(" ", 
1142
                                                               chaninfo->name);
1143
          
1144
                        if (split[0] == "Channel")
1145
                        { 
1146
                            chaninfo->old_xmltvid = split[1];
1147
                            chaninfo->chanstr = split[1];
1148
                            if (split.size() > 2)
1149
                                chaninfo->callsign = split[2];
1150
                        }
1151
                        else
1152
                        {
1153
                            chaninfo->old_xmltvid = split[0];
1154
                            chaninfo->chanstr = split[0];
1155
                            if (split.size() > 1)
1156
                                chaninfo->callsign = split[1];
1157
                        }
1158
                    }
1159
                }
1160
                else if (isJapan && chaninfo->callsign.length() == 0)
1161
                {
1162
                    chaninfo->callsign = info.text();
1163
                }
1164
                else if (chaninfo->chanstr.length() == 0)
1165
                {
1166
                    chaninfo->chanstr = info.text();
1167
                }
1168
            }
1169
        }
1170
    }
1171
1172
    chaninfo->freqid = chaninfo->chanstr;
1173
    return chaninfo;
1174
}
1175
1176
int TimezoneToInt (QString timezone)
1177
{
1178
    // we signal an error by setting it invalid (> 840min = 14hr)
1179
    int result = 841;
1180
1181
    if (timezone.length() == 5)
1182
    {
1183
        bool ok;
1184
1185
        result = timezone.mid(1,2).toInt(&ok, 10);
1186
1187
        if (!ok)
1188
            result = 841;
1189
        else
1190
        {
1191
            result *= 60;
1192
1193
            int min = timezone.right(2).toInt(&ok, 10);
1194
1195
            if (!ok)
1196
                result = 841;
1197
            else
1198
            {
1199
                result += min;
1200
                if (timezone.left(1) == "-")
1201
                    result *= -1;
1202
            }
1203
        }
1204
    }
1205
    return result;
1206
}
1207
1208
void addTimeOffset(QString &timestr, int localTimezoneOffset)
1209
{
1210
    if (timestr.isEmpty() || abs(localTimezoneOffset) > 840)
1211
        return;
1212
1213
    QStringList split = QStringList::split(" ", timestr);
1214
    QString ts = split[0];
1215
    int ts_offset = localTimezoneOffset;
1216
1217
    if (split.size() > 1)
1218
    {
1219
        QString tmp = split[1];
1220
        tmp.stripWhiteSpace();
1221
1222
        ts_offset = TimezoneToInt(tmp);
1223
        if (abs(ts_offset) > 840)
1224
            ts_offset = localTimezoneOffset;
1225
    }
1226
1227
    int diff = localTimezoneOffset - ts_offset;
1228
    int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0;
1229
1230
    if (diff != 0)
1231
    {
1232
        bool ok;
1233
                    
1234
            if (ts.length() == 14)
1235
            {
1236
                year  = ts.left(4).toInt(&ok, 10);
1237
                month = ts.mid(4,2).toInt(&ok, 10);
1238
                day   = ts.mid(6,2).toInt(&ok, 10);
1239
                hour  = ts.mid(8,2).toInt(&ok, 10);
1240
                min   = ts.mid(10,2).toInt(&ok, 10);
1241
                sec   = ts.mid(12,2).toInt(&ok, 10);
1242
            }
1243
            else if (ts.length() == 12)
1244
            {
1245
                year  = ts.left(4).toInt(&ok, 10);
1246
                month = ts.mid(4,2).toInt(&ok, 10);
1247
                day   = ts.mid(6,2).toInt(&ok, 10);
1248
                hour  = ts.mid(8,2).toInt(&ok, 10);
1249
                min   = ts.mid(10,2).toInt(&ok, 10);
1250
                sec   = 0;
1251
            }
1252
            else
1253
            {
1254
                diff = 0;
1255
                cerr << "Ignoring unknown timestamp format: " << ts << endl;
1256
            }
1257
    }
1258
1259
    if (diff != 0)
1260
    {
1261
        QDateTime dt = QDateTime(QDate(year, month, day),QTime(hour, min, sec));
1262
        dt = dt.addSecs(diff * 60 );
1263
        timestr = dt.toString("yyyyMMddhhmmss");
1264
    }
1265
}
1266
1267
void parseCredits(QDomElement &element, ProgInfo *pginfo)
1268
{
1269
    for (QDomNode child = element.firstChild(); !child.isNull();
1270
         child = child.nextSibling())
1271
    {
1272
        QDomElement info = child.toElement();
1273
        if (!info.isNull())
1274
        {
1275
            ProgCredit credit;
1276
            credit.role = info.tagName();
1277
            credit.name = getFirstText(info);
1278
            pginfo->credits.append(credit);
1279
        }
1280
    }
1281
}
1282
1283
ProgInfo *parseProgram(QDomElement &element, int localTimezoneOffset)
1284
{
1285
    QString uniqueid, seriesid, season, episode;
1286
    ProgInfo *pginfo = new ProgInfo;
1287
 
1288
    pginfo->previouslyshown = pginfo->stereo = pginfo->subtitled =
1289
    pginfo->hdtv = pginfo->closecaptioned = false;
1290
1291
    pginfo->subtitle = pginfo->title = pginfo->desc =
1292
    pginfo->category = pginfo->content = pginfo->catType =
1293
    pginfo->syndicatedepisodenumber =  pginfo->partnumber =
1294
    pginfo->parttotal = pginfo->showtype = pginfo->colorcode =
1295
    pginfo->stars = "";
1296
1297
    QString text = element.attribute("start", "");
1298
    addTimeOffset(text, localTimezoneOffset);
1299
    pginfo->startts = text;
1300
1301
    text = element.attribute("stop", "");
1302
    addTimeOffset(text, localTimezoneOffset);
1303
    pginfo->endts = text;
1304
1305
    text = element.attribute("channel", "");
1306
    QStringList split = QStringList::split(" ", text);   
1307
 
1308
    pginfo->channel = split[0];
1309
1310
    text = element.attribute("clumpidx", "");
1311
    if (!text.isEmpty()) 
1312
    {
1313
        split = QStringList::split("/", text);
1314
        pginfo->clumpidx = split[0];
1315
        pginfo->clumpmax = split[1];
1316
    }
1317
1318
    pginfo->start = fromXMLTVDate(pginfo->startts);
1319
    pginfo->end = fromXMLTVDate(pginfo->endts);
1320
1321
    for (QDomNode child = element.firstChild(); !child.isNull();
1322
         child = child.nextSibling())
1323
    {
1324
        QDomElement info = child.toElement();
1325
        if (!info.isNull())
1326
        {
1327
            if (info.tagName() == "title")
1328
            {
1329
                if (isJapan)
1330
                {
1331
                    if (info.attribute("lang") == "ja_JP")
1332
                    {
1333
                        pginfo->title = getFirstText(info);
1334
                    }
1335
                    else if (info.attribute("lang") == "ja_JP@kana")
1336
                    {
1337
                        pginfo->title_pronounce = getFirstText(info);
1338
                    }
1339
                }
1340
                else if (pginfo->title == "")
1341
                {
1342
                    pginfo->title = getFirstText(info);
1343
                }
1344
            }
1345
            else if (info.tagName() == "sub-title" && pginfo->subtitle == "")
1346
            {
1347
                pginfo->subtitle = getFirstText(info);
1348
            }
1349
            else if (info.tagName() == "content")
1350
            {
1351
                pginfo->content = getFirstText(info);
1352
            }
1353
            else if (info.tagName() == "desc" && pginfo->desc == "")
1354
            {
1355
                pginfo->desc = getFirstText(info);
1356
            }
1357
            else if (info.tagName() == "category")
1358
            {
1359
                QString cat = getFirstText(info);
1360
                
1361
                if (cat == "movie" || cat == "series" || 
1362
                    cat == "sports" || cat == "tvshow")
1363
                {
1364
                    if (pginfo->catType.isEmpty())
1365
                        pginfo->catType = cat;
1366
                }
1367
                else if (pginfo->category.isEmpty())
1368
                {
1369
                    pginfo->category = cat;
1370
                }
1371
1372
                if ((cat == "Film" || cat == "film") && !isNorthAmerica)
1373
                {
1374
                    // Hack for tv_grab_uk_rt
1375
                    pginfo->catType = "movie";
1376
                }
1377
            }
1378
            else if (info.tagName() == "date" && pginfo->airdate == "")
1379
            {
1380
                pginfo->airdate = getFirstText(info);
1381
1382
                if (4 != pginfo->airdate.length())
1383
                    pginfo->airdate = "";
1384
            }
1385
            else if (info.tagName() == "star-rating")
1386
            {
1387
                QDomNodeList values = info.elementsByTagName("value");
1388
                QDomElement item;
1389
                QString stars, num, den;
1390
                float avg = 0.0;
1391
                // not sure why the XML suggests multiple ratings,
1392
                // but the following will average them anyway.
1393
                for (unsigned int i = 0; i < values.length(); i++)
1394
                {
1395
                    item = values.item(i).toElement();
1396
                    if (item.isNull())
1397
                        continue;
1398
                    stars = getFirstText(item);
1399
                    num = stars.section('/', 0, 0);
1400
                    den = stars.section('/', 1, 1);
1401
                    if (0.0 >= den.toFloat())
1402
                        continue;
1403
                    avg *= i/(i+1);
1404
                    avg += (num.toFloat()/den.toFloat()) / (i+1);
1405
                }
1406
                pginfo->stars.setNum(avg);
1407
            }
1408
            else if (info.tagName() == "rating")
1409
            {
1410
                // again, the structure of ratings seems poorly represented
1411
                // in the XML.  no idea what we'd do with multiple values.
1412
                QDomNodeList values = info.elementsByTagName("value");
1413
                QDomElement item = values.item(0).toElement();
1414
                if (item.isNull())
1415
                    continue;
1416
                ProgRating rating;
1417
                rating.system = info.attribute("system", "");
1418
                rating.rating = getFirstText(item);
1419
                if ("" != rating.system)
1420
                    pginfo->ratings.append(rating);
1421
            }
1422
            else if (info.tagName() == "previously-shown")
1423
            {
1424
                pginfo->previouslyshown = true;
1425
1426
                QString prevdate = getFirstText(info);
1427
                pginfo->originalairdate = prevdate;
1428
            } 
1429
            else if (info.tagName() == "credits")
1430
            {
1431
                parseCredits(info, pginfo);
1432
            }
1433
            else if (info.tagName() == "episode-num" &&
1434
                     info.attribute("system") == "xmltv_ns")
1435
            {
1436
                int tmp;
1437
                QString episodenum(getFirstText(info));
1438
                episode = episodenum.section('.',1,1);
1439
                episode = episode.section('/',0,0).stripWhiteSpace();
1440
                season = episodenum.section('.',0,0).stripWhiteSpace();
1441
                QString part(episodenum.section('.',2,2));
1442
                QString partnumber(part.section('/',0,0).stripWhiteSpace());
1443
                QString parttotal(part.section('/',1,1).stripWhiteSpace());
1444
1445
                if (!episode.isEmpty())
1446
                {
1447
                    tmp = episode.toInt() + 1;
1448
                    episode = QString::number(tmp);
1449
                    pginfo->syndicatedepisodenumber = QString("E" + episode);
1450
                }
1451
1452
                if (!season.isEmpty())
1453
                {
1454
                    tmp = season.toInt() + 1;
1455
                    season = QString::number(tmp);
1456
                    pginfo->syndicatedepisodenumber.append(QString("S" + season));
1457
                }
1458
1459
                if (!partnumber.isEmpty())
1460
                {                
1461
                    tmp = partnumber.toInt() + 1;
1462
                    partnumber = QString::number(tmp);
1463
                }
1464
                
1465
                if (partnumber != 0 && parttotal >= partnumber && !parttotal.isEmpty())
1466
                {
1467
                    pginfo->parttotal = parttotal;
1468
                    pginfo->partnumber = partnumber;
1469
                }
1470
            }
1471
            else if (info.tagName() == "episode-num" &&
1472
                     info.attribute("system") == "onscreen" &&
1473
                     pginfo->subtitle.isEmpty())
1474
            {
1475
                 pginfo->subtitle = getFirstText(info);
1476
            }
1477
        }
1478
    }
1479
1480
    if (pginfo->category.isEmpty() && !pginfo->catType.isEmpty())
1481
        pginfo->category = pginfo->catType;
1482
1483
    /* Do what MythWeb does and assume that programmes with
1484
       star-rating in America are movies. This allows us to
1485
       unify app code with grabbers which explicitly deliver that
1486
       info. */
1487
    if (isNorthAmerica && pginfo->catType == "" &&
1488
        pginfo->stars != "" && pginfo->airdate != "")
1489
        pginfo->catType = "movie";
1490
    
1491
    /* Hack for teveblad grabber to do something with the content tag*/
1492
    if (pginfo->content != "")
1493
    {
1494
        if (pginfo->category == "film")
1495
        {
1496
            pginfo->subtitle = pginfo->desc;
1497
            pginfo->desc = pginfo->content;
1498
        }
1499
        else if (pginfo->desc != "") 
1500
        {
1501
            pginfo->desc = pginfo->desc + " - " + pginfo->content;
1502
        }
1503
        else if (pginfo->desc == "")
1504
        {
1505
            pginfo->desc = pginfo->content;
1506
        }
1507
    }
1508
    
1509
    if (pginfo->airdate.isEmpty())
1510
        pginfo->airdate = QDate::currentDate().toString("yyyy");
1511
1512
    /* Let's build ourself a programid */
1513
    QString programid;
1514
    
1515
    if (pginfo->catType == "movie")
1516
        programid = "MV";
1517
    else if (pginfo->catType == "series")
1518
        programid = "EP";
1519
    else if (pginfo->catType == "sports")
1520
        programid = "SP";
1521
    else
1522
        programid = "SH";
1523
    
1524
    if (!uniqueid.isEmpty()) // we already have a unique id ready for use
1525
        programid.append(uniqueid);
1526
    else
1527
    {
1528
        if (seriesid.isEmpty()) //need to hash ourself a seriesid from the title
1529
        {
1530
            seriesid = QString::number(ELFHash(pginfo->title));
1531
        }
1532
        pginfo->seriesid = seriesid;
1533
        programid.append(seriesid);
1534
1535
        if (!episode.isEmpty() && !season.isEmpty())
1536
        {
1537
            programid.append(episode);
1538
            programid.append(season);
1539
            if (!pginfo->partnumber.isEmpty() && !pginfo->parttotal.isEmpty())
1540
            {
1541
                programid.append(pginfo->partnumber);
1542
                programid.append(pginfo->parttotal);
1543
            }
1544
        }
1545
        else
1546
        {
1547
            /* No ep/season info? Well then remove the programid and rely on
1548
               normal dupchecking methods instead. */
1549
            if (pginfo->catType != "movie")
1550
                programid = "";
1551
        }
1552
    }
1553
    
1554
    pginfo->programid = programid;
1555
1556
    return pginfo;
1557
}
1558
                  
1559
void parseFile(QString filename, QValueList<ChanInfo> *chanlist,
1560
               QMap<QString, QValueList<ProgInfo> > *proglist)
1561
{
1562
    QDomDocument doc;
1563
    QFile f;
1564
1565
    if (!dash_open(f, filename, IO_ReadOnly))
1566
    {
1567
        return;
1568
    }
1569
1570
    QString errorMsg = "unknown";
1571
    int errorLine = 0;
1572
    int errorColumn = 0;
1573
1574
    if (!doc.setContent(&f, &errorMsg, &errorLine, &errorColumn))
1575
    {
1576
        cerr << "Error in " << errorLine << ":" << errorColumn << ": "
1577
             << errorMsg << endl;
1578
1579
        f.close();
1580
        return;
1581
    }
1582
1583
    f.close();
1584
1585
    // now we calculate the localTimezoneOffset, so that we can fix
1586
    // the programdata if needed
1587
    QString config_offset = gContext->GetSetting("TimeOffset", "None");
1588
    // we disable this feature by setting it invalid (> 840min = 14hr)
1589
    int localTimezoneOffset = 841;
1590
1591
    if (config_offset == "Auto")
1592
    {
1593
        time_t now = time(NULL);
1594
        struct tm local_tm;
1595
        localtime_r(&now, &local_tm);
1596
        localTimezoneOffset = local_tm.tm_gmtoff / 60;
1597
    }
1598
    else if (config_offset != "None")
1599
    {
1600
        localTimezoneOffset = TimezoneToInt(config_offset);
1601
        if (abs(localTimezoneOffset) > 840)
1602
            cerr << "Ignoring invalid TimeOffset " << config_offset << endl;
1603
    }
1604
1605
    QDomElement docElem = doc.documentElement();
1606
1607
    QUrl baseUrl(docElem.attribute("source-data-url", ""));
1608
1609
    QUrl sourceUrl(docElem.attribute("source-info-url", ""));
1610
    if (sourceUrl.toString() == "http://labs.zap2it.com/")
1611
    {
1612
        cerr << "Don't use tv_grab_na_dd, use the internal datadirect grabber."
1613
             << endl;
1614
        exit(14);
1615
    }
1616
1617
    QString aggregatedTitle;
1618
    QString aggregatedDesc;
1619
    QString groupingTitle;
1620
    QString groupingDesc;
1621
1622
    QDomNode n = docElem.firstChild();
1623
    while (!n.isNull())
1624
    {
1625
        QDomElement e = n.toElement();
1626
        if (!e.isNull()) 
1627
        {
1628
            if (e.tagName() == "channel")
1629
            {
1630
                ChanInfo *chinfo = parseChannel(e, baseUrl);
1631
                chanlist->push_back(*chinfo);
1632
                delete chinfo;
1633
            }
1634
            else if (e.tagName() == "programme")
1635
            {
1636
                ProgInfo *pginfo = parseProgram(e, localTimezoneOffset);
1637
1638
                if (pginfo->startts == pginfo->endts)
1639
                {
1640
                    /* Not a real program : just a grouping marker */
1641
                    if (!pginfo->title.isEmpty())
1642
                        groupingTitle = pginfo->title + " : ";
1643
1644
                    if (!pginfo->desc.isEmpty())
1645
                        groupingDesc = pginfo->desc + " : ";
1646
                }
1647
                else
1648
                {
1649
                    if (pginfo->clumpidx.isEmpty())
1650
                    {
1651
                        if (!groupingTitle.isEmpty())
1652
                        {
1653
                            pginfo->title.prepend(groupingTitle);
1654
                            groupingTitle = "";
1655
                        }
1656
1657
                        if (!groupingDesc.isEmpty())
1658
                        {
1659
                            pginfo->desc.prepend(groupingDesc);
1660
                            groupingDesc = "";
1661
                        }
1662
1663
                        (*proglist)[pginfo->channel].push_back(*pginfo);
1664
                    }
1665
                    else
1666
                    {
1667
                        /* append all titles/descriptions from one clump */
1668
                        if (pginfo->clumpidx.toInt() == 0)
1669
                        {
1670
                            aggregatedTitle = "";
1671
                            aggregatedDesc = "";
1672
                        }
1673
1674
                        if (!pginfo->title.isEmpty())
1675
                        {
1676
                            if (!aggregatedTitle.isEmpty())
1677
                                aggregatedTitle.append(" | ");
1678
                            aggregatedTitle.append(pginfo->title);
1679
                        }
1680
1681
                        if (!pginfo->desc.isEmpty())
1682
                        {
1683
                            if (!aggregatedDesc.isEmpty())
1684
                                aggregatedDesc.append(" | ");
1685
                            aggregatedDesc.append(pginfo->desc);
1686
                        }    
1687
                        if (pginfo->clumpidx.toInt() == 
1688
                            pginfo->clumpmax.toInt() - 1)
1689
                        {
1690
                            pginfo->title = aggregatedTitle;
1691
                            pginfo->desc = aggregatedDesc;
1692
                            (*proglist)[pginfo->channel].push_back(*pginfo);
1693
                        }
1694
                    }
1695
                }
1696
                delete pginfo;
1697
            }
1698
        }
1699
        n = n.nextSibling();
1700
    }
1701
1702
    return;
1703
}
1704
1705
bool conflict(ProgInfo &a, ProgInfo &b)
1706
{
1707
    if ((a.start <= b.start && b.start < a.end) ||
1708
        (b.end <= a.end && a.start < b.end))
1709
        return true;
1710
    return false;
1711
}
1712
1713
void fixProgramList(QValueList<ProgInfo> *fixlist)
1714
{
1715
    qHeapSort(*fixlist);
1716
1717
    QValueList<ProgInfo>::iterator i = fixlist->begin();
1718
    QValueList<ProgInfo>::iterator cur;
1719
    while (1)    
1720
    {
1721
        cur = i;
1722
        i++;
1723
        // fill in miss stop times
1724
        if ((*cur).endts == "" || (*cur).startts > (*cur).endts)
1725
        {
1726
            if (i != fixlist->end())
1727
            {
1728
                (*cur).endts = (*i).startts;
1729
                (*cur).end = (*i).start;
1730
            }
1731
            else
1732
            {
1733
                (*cur).end = (*cur).start;
1734
                if ((*cur).end < QDateTime((*cur).end.date(), QTime(6, 0)))
1735
                {
1736
                    (*cur).end.setTime(QTime(6, 0));
1737
                }
1738
                else
1739
                {
1740
                   (*cur).end.setTime(QTime(0, 0));
1741
                   (*cur).end.setDate((*cur).end.date().addDays(1));
1742
                }
1743
1744
                (*cur).endts = (*cur).end.toString("yyyyMMddhhmmss").ascii();
1745
            }
1746
        }
1747
        if (i == fixlist->end())
1748
            break;
1749
        // remove overlapping programs
1750
        if (conflict(*cur, *i))
1751
        {
1752
            QValueList<ProgInfo>::iterator tokeep, todelete;
1753
1754
            if ((*cur).end <= (*cur).start)
1755
                tokeep = i, todelete = cur;
1756
            else if ((*i).end <= (*i).start)
1757
                tokeep = cur, todelete = i;
1758
            else if ((*cur).subtitle != "" && (*i).subtitle == "")
1759
                tokeep = cur, todelete = i;
1760
            else if ((*i).subtitle != "" && (*cur).subtitle == "")
1761
                tokeep = i, todelete = cur;
1762
            else if ((*cur).desc != "" && (*i).desc == "")
1763
                tokeep = cur, todelete = i;
1764
            else if ((*i).desc != "" && (*cur).desc == "")
1765
                tokeep = i, todelete = cur;
1766
            else
1767
                tokeep = i, todelete = cur;
1768
1769
            if (!quiet)
1770
            {
1771
                cerr << "removing conflicting program: "
1772
                     << (*todelete).channel << " "
1773
                     << (*todelete).title.local8Bit() << " "
1774
                     << (*todelete).startts << "-" << (*todelete).endts << endl;
1775
                cerr << "conflicted with             : "
1776
                     << (*tokeep).channel << " "
1777
                     << (*tokeep).title.local8Bit() << " "
1778
                     << (*tokeep).startts << "-" <<   (*tokeep).endts << endl;
1779
                cerr << endl;
1780
            }
1781
1782
            if (todelete == i)
1783
                i = cur;
1784
            fixlist->erase(todelete);
1785
        }
1786
    }
1787
}
1788
1789
QString getResponse(const QString &query, const QString &def)
1790
{
1791
    cout << query;
1792
1793
    if (def != "")
1794
    {
1795
        cout << " [" << (const char *)def.local8Bit() << "]  ";
1796
    }
1797
    
1798
    char response[80];
1799
    cin.getline(response, 80);
1800
1801
    QString qresponse = QString::fromLocal8Bit(response);
1802
1803
    if (qresponse == "")
1804
        qresponse = def;
1805
1806
    return qresponse;
1807
}
1808
1809
unsigned int promptForChannelUpdates(QValueList<ChanInfo>::iterator chaninfo, 
1810
                                     unsigned int chanid)
1811
{
1812
    if (chanid == 0)
1813
    {
1814
        // Default is 0 to allow rapid skipping of many channels,
1815
        // in some xmltv outputs there may be over 100 channel, but
1816
        // only 10 or so that are available in each area.
1817
        chanid = atoi(getResponse("Choose a channel ID (positive integer) ",
1818
                                  "0"));
1819
1820
        // If we wish to skip this channel, use the default 0 and return.
1821
        if (chanid == 0)
1822
            return(0);
1823
    }
1824
1825
    (*chaninfo).name = getResponse("Choose a channel name (any string, "
1826
                                   "long version) ",(*chaninfo).name);
1827
    (*chaninfo).callsign = getResponse("Choose a channel callsign (any string, "
1828
                                       "short version) ",(*chaninfo).callsign);
1829
1830
    if (channel_preset)
1831
    {
1832
        (*chaninfo).chanstr = getResponse("Choose a channel preset (0..999) ",
1833
                                         (*chaninfo).chanstr);
1834
        (*chaninfo).freqid  = getResponse("Choose a frequency id (just like "
1835
                                          "xawtv) ",(*chaninfo).freqid);
1836
    }
1837
    else
1838
    {
1839
        (*chaninfo).chanstr  = getResponse("Choose a channel number (just like "
1840
                                           "xawtv) ",(*chaninfo).chanstr);
1841
        (*chaninfo).freqid = (*chaninfo).chanstr;
1842
    }
1843
1844
    (*chaninfo).finetune = getResponse("Choose a channel fine tune offset (just"
1845
                                       " like xawtv) ",(*chaninfo).finetune);
1846
1847
    (*chaninfo).tvformat = getResponse("Choose a TV format "
1848
                                       "(PAL/SECAM/NTSC/ATSC/Default) ",
1849
                                       (*chaninfo).tvformat);
1850
1851
    (*chaninfo).iconpath = getResponse("Choose a channel icon image (any path "
1852
                                       "name) ",(*chaninfo).iconpath);
1853
1854
    return(chanid);
1855
}
1856
1857
static QString SetupIconCacheDirectory()
1858
{
1859
    QString fileprefix = QDir::homeDirPath() + "/.mythtv";
1860
1861
    QDir dir(fileprefix);
1862
    if (!dir.exists())
1863
        dir.mkdir(fileprefix);
1864
1865
    fileprefix += "/channels";
1866
1867
    dir = QDir(fileprefix);
1868
    if (!dir.exists())
1869
        dir.mkdir(fileprefix);
1870
1871
    return fileprefix;
1872
}
1873
1874
void handleChannels(int id, QValueList<ChanInfo> *chanlist)
1875
{
1876
    QString fileprefix = SetupIconCacheDirectory();
1877
1878
    QDir::setCurrent(fileprefix);
1879
1880
    fileprefix += "/";
1881
1882
    QValueList<ChanInfo>::iterator i = chanlist->begin();
1883
    for (; i != chanlist->end(); i++)
1884
    {
1885
        QString localfile = "";
1886
1887
        if ((*i).iconpath != "")
1888
        {
1889
            QDir remotefile = QDir((*i).iconpath);
1890
            QString filename = remotefile.dirName();
1891
1892
            localfile = fileprefix + filename;
1893
            QFile actualfile(localfile);
1894
            if (!actualfile.exists())
1895
            {
1896
                QString command = QString("wget ") + (*i).iconpath;
1897
                system(command);
1898
            }
1899
        }
1900
1901
        MSqlQuery query;
1902
1903
        QString querystr;
1904
1905
        if ((*i).old_xmltvid != "")
1906
        {
1907
            querystr.sprintf("SELECT xmltvid FROM channel WHERE xmltvid = \"%s\"",
1908
                             (*i).old_xmltvid.ascii());
1909
            query.exec(querystr);
1910
1911
            if (query.isActive() && query.numRowsAffected() > 0)
1912
            {
1913
                if (!quiet)
1914
                    cout << "Converting old xmltvid (" << (*i).old_xmltvid << ") to new ("
1915
                         << (*i).xmltvid << ")\n";
1916
1917
                query.exec(QString("UPDATE channel SET xmltvid = '%1' WHERE xmltvid = '%2'")
1918
                            .arg((*i).xmltvid)
1919
                            .arg((*i).old_xmltvid));
1920
1921
                if (!query.numRowsAffected())
1922
                    MythContext::DBError("xmltvid conversion",query);
1923
            }
1924
        }
1925
1926
        querystr.sprintf("SELECT chanid,name,callsign,channum,finetune,"
1927
                         "icon,freqid,tvformat FROM channel WHERE "
1928
                         "xmltvid = \"%s\" AND sourceid = %d;", 
1929
                         (*i).xmltvid.ascii(), id); 
1930
1931
        query.exec(querystr);
1932
        if (query.isActive() && query.numRowsAffected() > 0)
1933
        {
1934
            query.next();
1935
1936
            QString chanid = query.value(0).toString();
1937
            if (interactive)
1938
            {
1939
                QString name     = QString::fromUtf8(query.value(1).toString());
1940
                QString callsign = QString::fromUtf8(query.value(2).toString());
1941
                QString chanstr  = QString::fromUtf8(query.value(3).toString());
1942
                QString finetune = QString::fromUtf8(query.value(4).toString());
1943
                QString icon     = QString::fromUtf8(query.value(5).toString());
1944
                QString freqid   = QString::fromUtf8(query.value(6).toString());
1945
                QString tvformat = QString::fromUtf8(query.value(7).toString());
1946
1947
                cout << "### " << endl;
1948
                cout << "### Existing channel found" << endl;
1949
                cout << "### " << endl;
1950
                cout << "### xmltvid  = " << (*i).xmltvid.local8Bit() << endl;
1951
                cout << "### chanid   = " << chanid.local8Bit()       << endl;
1952
                cout << "### name     = " << name.local8Bit()         << endl;
1953
                cout << "### callsign = " << callsign.local8Bit()     << endl;
1954
                cout << "### channum  = " << chanstr.local8Bit()      << endl;
1955
                if (channel_preset)
1956
                    cout << "### freqid   = " << freqid.local8Bit()   << endl;
1957
                cout << "### finetune = " << finetune.local8Bit()     << endl;
1958
                cout << "### tvformat = " << tvformat.local8Bit()     << endl;
1959
                cout << "### icon     = " << icon.local8Bit()         << endl;
1960
                cout << "### " << endl;
1961
1962
                (*i).name = name;
1963
                (*i).callsign = callsign;
1964
                (*i).chanstr  = chanstr;
1965
                (*i).finetune = finetune;
1966
                (*i).freqid = freqid;
1967
                (*i).tvformat = tvformat;
1968
1969
                promptForChannelUpdates(i, atoi(chanid.ascii()));
1970
1971
                if ((*i).callsign == "")
1972
                    (*i).callsign = chanid;
1973
1974
                if (name     != (*i).name ||
1975
                    callsign != (*i).callsign ||
1976
                    chanstr  != (*i).chanstr ||
1977
                    finetune != (*i).finetune ||
1978
                    freqid   != (*i).freqid ||
1979
                    icon     != localfile ||
1980
                    tvformat != (*i).tvformat)
1981
                {
1982
                    MSqlQuery subquery;
1983
                    subquery.prepare("UPDATE channel SET chanid = :CHANID, "
1984
                                     "name = :NAME, callsign = :CALLSIGN, "
1985
                                     "channum = :CHANNUM, finetune = :FINE, "
1986
                                     "icon = :ICON, freqid = :FREQID, "
1987
                                     "tvformat = :TVFORMAT "
1988
                                     " WHERE xmltvid = :XMLTVID "
1989
                                     "AND sourceid = :SOURCEID;");
1990
                    subquery.bindValue(":CHANID", chanid);
1991
                    subquery.bindValue(":NAME", (*i).name.utf8());
1992
                    subquery.bindValue(":CALLSIGN", (*i).callsign.utf8());
1993
                    subquery.bindValue(":CHANNUM", (*i).chanstr);
1994
                    subquery.bindValue(":FINE", (*i).finetune.toInt());
1995
                    subquery.bindValue(":ICON", localfile);
1996
                    subquery.bindValue(":FREQID", (*i).freqid);
1997
                    subquery.bindValue(":TVFORMAT", (*i).tvformat);
1998
                    subquery.bindValue(":XMLTVID", (*i).xmltvid);
1999
                    subquery.bindValue(":SOURCEID", id);
2000
2001
                    if (!subquery.exec())
2002
                    {
2003
                        cerr << "DB Error: Channel update failed, SQL query "
2004
                             << "was:" << endl;
2005
                        cerr << querystr << endl;
2006
                    }
2007
                    else
2008
                    {
2009
                        cout << "### " << endl;
2010
                        cout << "### Change performed" << endl;
2011
                        cout << "### " << endl;
2012
                    }
2013
                }
2014
                else
2015
                {
2016
                    cout << "### " << endl;
2017
                    cout << "### Nothing changed" << endl;
2018
                    cout << "### " << endl;
2019
                }
2020
            }
2021
            else
2022
            {
2023
                if (!non_us_updating && localfile != "")
2024
                {
2025
                    MSqlQuery subquery;
2026
                    subquery.prepare("UPDATE channel SET icon = :ICON WHERE "
2027
                                     "chanid = :CHANID;");
2028
                    subquery.bindValue(":ICON", localfile);
2029
                    subquery.bindValue(":CHANID", chanid);
2030
2031
                    if (!subquery.exec())
2032
                        MythContext::DBError("Channel icon change", subquery);
2033
                }
2034
            }
2035
        }
2036
        else
2037
        {
2038
            if (interactive)
2039
            {
2040
                cout << "### " << endl;
2041
                cout << "### New channel found" << endl;
2042
                cout << "### " << endl;
2043
                cout << "### name     = " << (*i).name.local8Bit()     << endl;
2044
                cout << "### callsign = " << (*i).callsign.local8Bit() << endl;
2045
                cout << "### channum  = " << (*i).chanstr.local8Bit()  << endl;
2046
                if (channel_preset)
2047
                    cout << "### freqid   = " << (*i).freqid.local8Bit() << endl;
2048
                cout << "### finetune = " << (*i).finetune.local8Bit() << endl;
2049
                cout << "### tvformat = " << (*i).tvformat.local8Bit() << endl;
2050
                cout << "### icon     = " << localfile.local8Bit()     << endl;
2051
                cout << "### " << endl;
2052
2053
                unsigned int chanid = promptForChannelUpdates(i,0);
2054
2055
                if ((*i).callsign == "")
2056
                    (*i).callsign = QString::number(chanid);
2057
2058
                if (chanid > 0)
2059
                {
2060
                    MSqlQuery subquery;
2061
                    subquery.prepare("INSERT INTO channel (chanid,name"
2062
                                     ",callsign,channum,finetune,icon"
2063
                                     ",xmltvid,sourceid,freqid,tvformat) "
2064
                                     "VALUES(:CHANID,:NAME,:CALLSIGN,:CHANNUM,"
2065
                                     ":FINE,:ICON,:XMLTVID,:SOURCEID,:FREQID,"
2066
                                     ":TVFORMAT);");
2067
                    subquery.bindValue(":CHANID", chanid);
2068
                    subquery.bindValue(":NAME", (*i).name.utf8());
2069
                    subquery.bindValue(":CALLSIGN", (*i).callsign.utf8());
2070
                    subquery.bindValue(":CHANNUM", (*i).chanstr);
2071
                    subquery.bindValue(":FINE", (*i).finetune.toInt());
2072
                    subquery.bindValue(":ICON", localfile);
2073
                    subquery.bindValue(":XMLTVID", (*i).xmltvid);
2074
                    subquery.bindValue(":SOURCEID", id);
2075
                    subquery.bindValue(":FREQID", (*i).freqid);
2076
                    subquery.bindValue(":TVFORMAT", (*i).tvformat);
2077
2078
                    if (!subquery.exec())
2079
                    {
2080
                        MythContext::DBError("Channel insert", subquery);
2081
                    }
2082
                    else
2083
                    {
2084
                        cout << "### " << endl;
2085
                        cout << "### Channel inserted" << endl;
2086
                        cout << "### " << endl;
2087
                    }
2088
                }
2089
                else
2090
                {
2091
                    cout << "### " << endl;
2092
                    cout << "### Channel skipped" << endl;
2093
                    cout << "### " << endl;
2094
                }
2095
            }
2096
            else if (!non_us_updating)
2097
            {
2098
                // Make up a chanid if automatically adding one.
2099
                // Must be unique, nothing else matters, nobody else knows.
2100
                // We only do this if we are not asked to skip it with the
2101
                // --updating flag.
2102
2103
                int chanid = id * 1000 + atoi((*i).chanstr.ascii());
2104
2105
                while(1)
2106
                {
2107
                    querystr.sprintf("SELECT channum FROM channel WHERE "
2108
                                     "chanid = %d;", chanid);
2109
                    if (!query.exec(querystr))
2110
                        break;
2111
2112
                    if (query.isActive() && query.numRowsAffected() > 0)
2113
                        chanid++;
2114
                    else
2115
                        break;
2116
                }
2117
2118
                if ((*i).callsign == "")
2119
                    (*i).callsign = QString::number(chanid);
2120
2121
                MSqlQuery subquery;
2122
                subquery.prepare("INSERT INTO channel (chanid,name"
2123
                                 ",callsign,channum,finetune,icon"
2124
                                 ",xmltvid,sourceid,freqid,tvformat) "
2125
                                 "VALUES(:CHANID,:NAME,:CALLSIGN,:CHANNUM,"
2126
                                 ":FINE,:ICON,:XMLTVID,:SOURCEID,:FREQID,"
2127
                                 ":TVFORMAT);");
2128
                subquery.bindValue(":CHANID", chanid);
2129
                subquery.bindValue(":NAME", (*i).name.utf8());
2130
                subquery.bindValue(":CALLSIGN", (*i).callsign.utf8());
2131
                subquery.bindValue(":CHANNUM", (*i).chanstr);
2132
                subquery.bindValue(":FINE", (*i).finetune.toInt());
2133
                subquery.bindValue(":ICON", localfile);
2134
                subquery.bindValue(":XMLTVID", (*i).xmltvid);
2135
                subquery.bindValue(":SOURCEID", id);
2136
                subquery.bindValue(":FREQID", (*i).freqid);
2137
                subquery.bindValue(":TVFORMAT", (*i).tvformat);
2138
2139
                if (!subquery.exec())
2140
                    MythContext::DBError("channel insert", subquery);
2141
2142
            }
2143
        }
2144
    }
2145
2146
    UpdateSourceIcons(id);
2147
}
2148
2149
void clearDBAtOffset(int offset, int chanid, QDate *qCurrentDate)
2150
{
2151
    if (no_delete)
2152
        return;
2153
2154
    QDate newDate; 
2155
    if (qCurrentDate == 0)
2156
    {
2157
        newDate = QDate::currentDate();
2158
        qCurrentDate = &newDate;
2159
    }
2160
2161
    int nextoffset = 1;
2162
2163
    if (offset == -1)
2164
    {
2165
        offset = 0;
2166
        nextoffset = 10;
2167
    }
2168
2169
    QDateTime from, to;
2170
    from.setDate(*qCurrentDate);
2171
    from = from.addDays(offset);
2172
    from = from.addSecs(listing_wrap_offset);
2173
    to = from.addDays(nextoffset);
2174
2175
    clearDataByChannel(chanid, from, to);
2176
}
2177
2178
void handlePrograms(int id, QMap<QString, QValueList<ProgInfo> > *proglist)
2179
{
2180
    int unchanged = 0, updated = 0;
2181
    QMap<QString, QValueList<ProgInfo> >::Iterator mapiter;
2182
2183
    for (mapiter = proglist->begin(); mapiter != proglist->end(); ++mapiter)
2184
    {
2185
        MSqlQuery query;
2186
2187
        if (mapiter.key() == "")
2188
            continue;
2189
2190
        int chanid = 0;
2191
2192
        query.prepare("SELECT chanid FROM channel WHERE sourceid = :ID AND "
2193
                      "xmltvid = :XMLTVID;"); 
2194
        query.bindValue(":ID", id);
2195
        query.bindValue(":XMLTVID", mapiter.key());
2196
2197
        query.exec();
2198
2199
        if (query.isActive() && query.numRowsAffected() > 0)
2200
        {
2201
            query.next();
2202
            chanid = query.value(0).toInt();
2203
        }
2204
2205
        if (chanid == 0)
2206
        {
2207
            cerr << "Unknown xmltv channel identifier: " << mapiter.key()
2208
                 << endl << "Skipping channel.\n";
2209
            continue;
2210
        }
2211
2212
        QValueList<ProgInfo> *sortlist = &((*proglist)[mapiter.key()]);
2213
2214
        fixProgramList(sortlist);
2215
2216
        QValueList<ProgInfo>::iterator i = sortlist->begin();
2217
        for (; i != sortlist->end(); i++)
2218
        {
2219
            QString startstr = (*i).start.toString(Qt::ISODate);
2220
            QString endstr = (*i).end.toString(Qt::ISODate);
2221
2222
            query.prepare("SELECT * FROM program WHERE "
2223
                          "chanid=:CHANID AND "
2224
                          "starttime=:START AND "
2225
                          "endtime=:END AND "
2226
                          "title=:TITLE AND "
2227
                          "subtitle=:SUBTITLE AND "
2228
                          "description=:DESC AND "
2229
                          "category=:CATEGORY AND "
2230
                          "category_type=:CATEGORY_TYPE AND "
2231
                          "airdate=:AIRDATE AND "
2232
                          "stars=:STARS AND "
2233
                          "previouslyshown=:PREVIOUSLYSHOWN AND "
2234
                          "title_pronounce=:TITLE_PRONOUNCE AND "
2235
                          "stereo=:STEREO AND "
2236
                          "subtitled=:SUBTITLED AND "
2237
                          "hdtv=:HDTV AND "
2238
                          "closecaptioned=:CLOSECAPTIONED AND "
2239
                          "partnumber=:PARTNUMBER AND "
2240
                          "parttotal=:PARTTOTAL AND "
2241
                          "seriesid=:SERIESID AND "
2242
                          "showtype=:SHOWTYPE AND "
2243
                          "colorcode=:COLORCODE AND "
2244
                          "syndicatedepisodenumber=:SYNDICATEDEPISODENUMBER AND "
2245
                          "programid=:PROGRAMID;");
2246
            query.bindValue(":CHANID", chanid);
2247
            query.bindValue(":START", startstr);
2248
            query.bindValue(":END", endstr);
2249
            query.bindValue(":TITLE", (*i).title.utf8());
2250
            query.bindValue(":SUBTITLE", (*i).subtitle.utf8());
2251
            query.bindValue(":DESC", (*i).desc.utf8());
2252
            query.bindValue(":CATEGORY", (*i).category.utf8());
2253
            query.bindValue(":CATEGORY_TYPE", (*i).catType.utf8());
2254
            query.bindValue(":AIRDATE", (*i).airdate);
2255
            query.bindValue(":STARS", (*i).stars);
2256
            query.bindValue(":PREVIOUSLYSHOWN", (*i).previouslyshown);
2257
            query.bindValue(":TITLE_PRONOUNCE", (*i).title_pronounce.utf8());
2258
            query.bindValue(":STEREO", (*i).stereo);
2259
            query.bindValue(":SUBTITLED", (*i).subtitled);
2260
            query.bindValue(":HDTV", (*i).hdtv);
2261
            query.bindValue(":CLOSECAPTIONED", (*i).closecaptioned);
2262
            query.bindValue(":PARTNUMBER", (*i).partnumber);
2263
            query.bindValue(":PARTTOTAL", (*i).parttotal);
2264
            query.bindValue(":SERIESID", (*i).seriesid);
2265
            query.bindValue(":SHOWTYPE", (*i).showtype);
2266
            query.bindValue(":COLORCODE", (*i).colorcode);
2267
            query.bindValue(":SYNDICATEDEPISODENUMBER", (*i).syndicatedepisodenumber);
2268
            query.bindValue(":PROGRAMID", (*i).programid);
2269
            query.exec();
2270
2271
            if (query.isActive() && query.numRowsAffected() > 0)
2272
            {
2273
                unchanged++;
2274
                continue;
2275
            }
2276
2277
            if (!no_delete)
2278
            {
2279
                query.prepare("SELECT title,starttime,endtime FROM program WHERE "
2280
                              "chanid=:CHANID AND starttime>=:START AND "
2281
                              "starttime<:END;");
2282
                query.bindValue(":CHANID", chanid);
2283
                query.bindValue(":START", startstr);
2284
                query.bindValue(":END", endstr);
2285
                query.exec();
2286
2287
                if (query.isActive() && query.numRowsAffected() > 0)
2288
                {
2289
                    if (!quiet)
2290
                    {
2291
                        while(query.next())
2292
                        {
2293
                            cerr << "removing existing program: "
2294
                                 << (*i).channel.local8Bit() << " "
2295
                                 << query.value(0).toString().local8Bit() << " "
2296
                                 << query.value(1).toDateTime().toString(Qt::ISODate) << " - "
2297
                                 << query.value(2).toDateTime().toString(Qt::ISODate) << endl;
2298
                        }
2299
2300
                        cerr << "inserting new program    : "
2301
                             << (*i).channel.local8Bit() << " "
2302
                             << (*i).title.local8Bit() << " "
2303
                             << startstr << " - " << endstr << endl << endl;
2304
                    }
2305
2306
                    MSqlQuery subquery;
2307
                    subquery.prepare("DELETE FROM program WHERE "
2308
                                     "chanid=:CHANID AND starttime>=:START "
2309
                                     "AND starttime<:END;");
2310
                    subquery.bindValue(":CHANID", chanid);
2311
                    subquery.bindValue(":START", startstr);
2312
                    subquery.bindValue(":END", endstr);
2313
2314
                    subquery.exec();
2315
2316
                    subquery.prepare("DELETE FROM programrating WHERE "
2317
                                     "chanid=:CHANID AND starttime>=:START "
2318
                                     "AND starttime<:END;");
2319
                    subquery.bindValue(":CHANID", chanid);
2320
                    subquery.bindValue(":START", startstr);
2321
                    subquery.bindValue(":END", endstr);
2322
2323
                    subquery.exec();
2324
2325
                    subquery.prepare("DELETE FROM credits WHERE "
2326
                                     "chanid=:CHANID AND starttime>=:START "
2327
                                     "AND starttime<:END;");
2328
                    subquery.bindValue(":CHANID", chanid);
2329
                    subquery.bindValue(":START", startstr);
2330
                    subquery.bindValue(":END", endstr);
2331
2332
                    subquery.exec();
2333
                }
2334
            }
2335
2336
            query.prepare("INSERT INTO program (chanid,starttime,endtime,"
2337
                          "title,subtitle,description,category,category_type,"
2338
                          "airdate,stars,previouslyshown,title_pronounce,stereo,"
2339
                          "subtitled,hdtv,closecaptioned,partnumber,parttotal,"
2340
                          "seriesid,originalairdate,showtype,colorcode,"
2341
                          "syndicatedepisodenumber,programid) "
2342
                          "VALUES(:CHANID,:STARTTIME,:ENDTIME,:TITLE,"
2343
                          ":SUBTITLE,:DESCRIPTION,:CATEGORY,:CATEGORY_TYPE,:AIRDATE,:STARS,"
2344
                          ":PREVIOUSLYSHOWN,:TITLE_PRONOUNCE,:STEREO,:SUBTITLED,"
2345
                          ":HDTV,:CLOSECAPTIONED,:PARTNUMBER,:PARTTOTAL,:SERIESID,"
2346
                          ":ORIGINALAIRDATE,:SHOWTYPE,:COLORCODE,:SYNDICATEDEPISODENUMBER,"
2347
                          ":PROGRAMID);");
2348
            query.bindValue(":CHANID", chanid);
2349
            query.bindValue(":STARTTIME", startstr);
2350
            query.bindValue(":ENDTIME", endstr);
2351
            query.bindValue(":TITLE", (*i).title.utf8());
2352
            query.bindValue(":SUBTITLE", (*i).subtitle.utf8());
2353
            query.bindValue(":DESCRIPTION", (*i).desc.utf8());
2354
            query.bindValue(":CATEGORY", (*i).category.utf8());
2355
            query.bindValue(":CATEGORY_TYPE", (*i).catType.utf8());
2356
            query.bindValue(":AIRDATE", (*i).airdate.utf8());
2357
            query.bindValue(":STARS", (*i).stars.utf8());
2358
            query.bindValue(":PREVIOUSLYSHOWN", (*i).previouslyshown);
2359
            query.bindValue(":TITLE_PRONOUNCE", (*i).title_pronounce.utf8());
2360
            query.bindValue(":STEREO", (*i).stereo);
2361
            query.bindValue(":SUBTITLED", (*i).subtitled);
2362
            query.bindValue(":HDTV", (*i).hdtv);
2363
            query.bindValue(":CLOSECAPTIONED", (*i).closecaptioned);
2364
            query.bindValue(":PARTNUMBER", (*i).partnumber);
2365
            query.bindValue(":PARTTOTAL", (*i).parttotal);
2366
            query.bindValue(":SERIESID", (*i).seriesid);
2367
            query.bindValue(":ORIGINALAIRDATE", (*i).originalairdate);
2368
            query.bindValue(":SHOWTYPE", (*i).showtype);
2369
            query.bindValue(":COLORCODE", (*i).colorcode);
2370
            query.bindValue(":SYNDICATEDEPISODENUMBER", (*i).syndicatedepisodenumber);
2371
            query.bindValue(":PROGRAMID", (*i).programid);
2372
            if (!query.exec())
2373
            {
2374
                MythContext::DBError("program insert", query);
2375
            }
2376
2377
            updated++;
2378
2379
            QValueList<ProgRating>::iterator j = (*i).ratings.begin();
2380
            for (; j != (*i).ratings.end(); j++)
2381
            {
2382
                query.prepare("INSERT INTO programrating (chanid,starttime,"
2383
                              "system,rating) VALUES (:CHANID, :START, :SYS, "
2384
                              ":RATING);");
2385
                query.bindValue(":CHANID", chanid);
2386
                query.bindValue(":START", startstr);
2387
                query.bindValue(":SYS", (*j).system.utf8());
2388
                query.bindValue(":RATING", (*j).rating.utf8());
2389
2390
                if (!query.exec())
2391
                {
2392
                    MythContext::DBError("programrating insert", query);
2393
                }
2394
            }
2395
2396
            QValueList<ProgCredit>::iterator k = (*i).credits.begin();
2397
            for (; k != (*i).credits.end(); k++)
2398
            {
2399
                query.prepare("SELECT person FROM people WHERE "
2400
                              "name = :NAME;");
2401
                query.bindValue(":NAME", (*k).name.utf8());
2402
                if (!query.exec())
2403
                    MythContext::DBError("person lookup", query);
2404
2405
                int personid = -1;
2406
                if (query.isActive() && query.numRowsAffected() > 0)
2407
                {
2408
                    query.next();
2409
                    personid = query.value(0).toInt();
2410
                }
2411
2412
                if (personid < 0)
2413
                {
2414
                    query.prepare("INSERT INTO people (name) VALUES "
2415
                                  "(:NAME);");
2416
                    query.bindValue(":NAME", (*k).name.utf8());
2417
                    if (!query.exec())
2418
                        MythContext::DBError("person insert", query);
2419
2420
                    query.prepare("SELECT person FROM people WHERE "
2421
                                  "name = :NAME;");
2422
                    query.bindValue(":NAME", (*k).name.utf8());
2423
                    if (!query.exec())
2424
                       MythContext::DBError("person lookup", query);
2425
2426
                    if (query.isActive() && query.numRowsAffected() > 0)
2427
                    {
2428
                        query.next();
2429
                        personid = query.value(0).toInt();
2430
                    }
2431
                }
2432
2433
                if (personid < 0)
2434
                {
2435
                    cerr << "Error inserting person\n";
2436
                    continue;
2437
                }
2438
2439
                query.prepare("INSERT INTO credits (chanid,starttime,"
2440
                              "role,person) VALUES "
2441
                              "(:CHANID, :START, :ROLE, :PERSON);");
2442
                query.bindValue(":CHANID", chanid);
2443
                query.bindValue(":START", startstr);
2444
                query.bindValue(":ROLE", (*k).role.utf8());
2445
                query.bindValue(":PERSON", personid);
2446
                if (!query.exec())
2447
                {
2448
                    // be careful of the startime/timestamp "feature"!
2449
                    query.prepare("UPDATE credits SET "
2450
                                  "role = concat(role,',:ROLE'), "
2451
                                  "starttime = :START "
2452
                                  "WHERE chanid = :CHANID AND "
2453
                                  "starttime = :START2 and person = :PERSON");
2454
                    query.bindValue(":ROLE", (*k).role.utf8());
2455
                    query.bindValue(":START", startstr);
2456
                    query.bindValue(":CHANID", chanid);
2457
                    query.bindValue(":START2", startstr);
2458
                    query.bindValue(":PERSON", personid);
2459
2460
                    if (!query.exec())
2461
                        MythContext::DBError("credits update", query);
2462
                }
2463
            }
2464
        }
2465
    }
2466
    if (!quiet)
2467
    {
2468
        cerr << "Updated programs: " << updated
2469
             << "  Unchanged programs: " << unchanged << endl;
2470
    }
2471
}
2472
2473
void grabDataFromFile(int id, QString &filename)
2474
{
2475
    QValueList<ChanInfo> chanlist;
2476
    QMap<QString, QValueList<ProgInfo> > proglist;
2477
2478
    parseFile(filename, &chanlist, &proglist);
2479
2480
    handleChannels(id, &chanlist);
2481
    handlePrograms(id, &proglist);
2482
}
2483
2484
time_t toTime_t(QDateTime &dt)
2485
{
2486
    tm brokenDown;
2487
    brokenDown.tm_sec = dt.time().second();
2488
    brokenDown.tm_min = dt.time().minute();
2489
    brokenDown.tm_hour = dt.time().hour();
2490
    brokenDown.tm_mday = dt.date().day();
2491
    brokenDown.tm_mon = dt.date().month() - 1;
2492
    brokenDown.tm_year = dt.date().year() - 1900;
2493
    brokenDown.tm_isdst = -1;
2494
    int secsSince1Jan1970UTC = (int) mktime( &brokenDown );
2495
    if ( secsSince1Jan1970UTC < -1 )
2496
        secsSince1Jan1970UTC = -1;
2497
    return secsSince1Jan1970UTC;
2498
}
2499
2500
bool grabData(Source source, int offset, QDate *qCurrentDate = 0)
2501
{
2502
    QString xmltv_grabber = source.xmltvgrabber;
2503
2504
    if (xmltv_grabber == "datadirect")
2505
        return grabDDData(source, offset, *qCurrentDate);
2506
2507
    char tempfilename[] = "/tmp/mythXXXXXX";
2508
    if (mkstemp(tempfilename) == -1) {
2509
         perror("mkstemp");
2510
         exit(15);
2511
    }
2512
2513
    QString filename = QString(tempfilename);
2514
2515
    QString home = QDir::homeDirPath();
2516
    QString configfile = QString("%1/.mythtv/%2.xmltv").arg(home)
2517
                                                       .arg(source.name);
2518
    QString command;
2519
2520
    if (xmltv_grabber == "tv_grab_uk")
2521
        command.sprintf("nice %s --days 7 --config-file '%s' --output %s",
2522
                        xmltv_grabber.ascii(), configfile.ascii(), 
2523
                        filename.ascii());
2524
    else if (xmltv_grabber == "tv_grab_uk_rt")
2525
        command.sprintf("nice %s --days 14 --config-file '%s' --output %s",
2526
                        xmltv_grabber.ascii(),
2527
                        configfile.ascii(), filename.ascii());
2528
    else if (xmltv_grabber == "tv_grab_au")
2529
        command.sprintf("nice %s --days 7 --config-file '%s' --output %s",
2530
                        xmltv_grabber.ascii(), configfile.ascii(),
2531
                        filename.ascii());
2532
    else if (xmltv_grabber == "tv_grab_nz")
2533
        command.sprintf("nice %s -n 1 -f %d -o '%s'",
2534
                        xmltv_grabber.ascii(), offset,
2535
                        filename.ascii());
2536
    else if (xmltv_grabber == "tv_grab_de")
2537
        command.sprintf("nice %s --days 7 --output %s",
2538
                        xmltv_grabber.ascii(),
2539
                        filename.ascii());
2540
    else if (xmltv_grabber == "tv_grab_fr")
2541
        command.sprintf("nice %s --days 7 '%s' --output %s",
2542
                        xmltv_grabber.ascii(), configfile.ascii(),
2543
                        filename.ascii());
2544
    else if (xmltv_grabber == "tv_grab_nl")
2545
        command.sprintf("nice %s --output %s",
2546
                        xmltv_grabber.ascii(),
2547
                        filename.ascii());
2548
    else if (xmltv_grabber == "tv_grab_fi")
2549
        // Use the default of 10 days for Finland's grabber
2550
        command.sprintf("nice %s --offset %d --config-file '%s' --output %s",
2551
                        xmltv_grabber.ascii(), offset,
2552
                        configfile.ascii(), filename.ascii());
2553
    else if (xmltv_grabber == "tv_grab_es")
2554
        // Use fixed interval of 3 days for Spanish grabber
2555
        command.sprintf("nice %s --days=4  --config-file '%s' --output %s",
2556
                        xmltv_grabber.ascii(), 
2557
                        configfile.ascii(), filename.ascii());
2558
    else if (xmltv_grabber == "tv_grab_jp")
2559
    {
2560
         // Use fixed interval of 7 days for Japanese grabber
2561
         command.sprintf("nice %s --days 7 --enable-readstr --config-file '%s' --output %s",
2562
                         xmltv_grabber.ascii(), configfile.ascii(),
2563
                         filename.ascii());
2564
         isJapan = true;
2565
    }
2566
    else if (xmltv_grabber == "tv_grab_no")
2567
        command.sprintf("nice %s --days 1 --offset %d --config-file '%s' --output %s",
2568
                        xmltv_grabber.ascii(), offset, configfile.ascii(),
2569
                        filename.ascii());
2570
    else if (xmltv_grabber == "tv_grab_se_swedb")
2571
         command.sprintf("nice %s --days 1 --offset %d --config-file '%s' --output %s",
2572
                         xmltv_grabber.ascii(), offset, configfile.ascii(),
2573
                         filename.ascii());
2574
    else if (xmltv_grabber == "tv_grab_dk")
2575
        // Use fixed interval of 7 days for Danish grabber
2576
        command.sprintf("nice %s --days 7 --config-file '%s' --output %s",
2577
                        xmltv_grabber.ascii(), configfile.ascii(),
2578
                        filename.ascii());
2579
    else if (xmltv_grabber == "tv_grab_pt")
2580
        // Use fixed interval of 3 days for Portuguese grabber
2581
        command.sprintf("nice %s --days=4  --config-file '%s' --output %s",
2582
                        xmltv_grabber.ascii(), 
2583
                        configfile.ascii(), filename.ascii());
2584
    else
2585
    {
2586
        isNorthAmerica = true;
2587
        command.sprintf("nice %s --days 1 --offset %d --config-file '%s' "
2588
                        "--output %s", xmltv_grabber.ascii(),
2589
                        offset, configfile.ascii(), filename.ascii());
2590
    }
2591
2592
    if (quiet &&
2593
        (xmltv_grabber == "tv_grab_na" ||
2594
         xmltv_grabber == "tv_grab_de" ||
2595
         xmltv_grabber == "tv_grab_fi" ||
2596
         xmltv_grabber == "tv_grab_es" ||
2597
         xmltv_grabber == "tv_grab_nz" ||
2598
         xmltv_grabber == "tv_grab_se_swedb" ||
2599
         xmltv_grabber == "tv_grab_no" ||
2600
         xmltv_grabber == "tv_grab_dk" ||
2601
         xmltv_grabber == "tv_grab_uk" ||
2602
         xmltv_grabber == "tv_grab_uk_rt" ||
2603
         xmltv_grabber == "tv_grab_nl" ||
2604
         xmltv_grabber == "tv_grab_fr" ||
2605
         xmltv_grabber == "tv_grab_fi" ||
2606
         xmltv_grabber == "tv_grab_jp" ||
2607
         xmltv_grabber == "tv_grab_pt"))
2608
         command += " --quiet";
2609
2610
2611
    command += graboptions;
2612
2613
    if (!quiet)
2614
         cout << "----------------- Start of XMLTV output -----------------" << endl;
2615
2616
    QDateTime qdtNow = QDateTime::currentDateTime();
2617
    MSqlQuery query;
2618
    QString status = "currently running.";
2619
2620
    query.exec(QString("UPDATE settings SET data ='%1' "
2621
                       "WHERE value='mythfilldatabaseLastRunStart'")
2622
                       .arg(qdtNow.toString("yyyy-MM-dd hh:mm")));
2623
2624
    query.exec(QString("UPDATE settings SET data ='%1' "
2625
                       "WHERE value='mythfilldatabaseLastRunStatus'")
2626
                       .arg(status));
2627
2628
    int systemcall_status = system(command.ascii());
2629
    bool succeeded = WIFEXITED(systemcall_status) &&
2630
         WEXITSTATUS(systemcall_status) == 0;
2631
2632
    qdtNow = QDateTime::currentDateTime();
2633
    query.exec(QString("UPDATE settings SET data ='%1' "
2634
                       "WHERE value='mythfilldatabaseLastRunEnd'")
2635
                       .arg(qdtNow.toString("yyyy-MM-dd hh:mm")));
2636
2637
    status = "Successful.";
2638
2639
    if (!succeeded)
2640
    {
2641
        status = QString("FAILED:  xmltv returned error code %1.")
2642
                         .arg(systemcall_status);
2643
2644
        query.exec(QString("UPDATE settings SET data ='%1' "
2645
                           "WHERE value='mythfilldatabaseLastRunStatus'")
2646
                           .arg(status));
2647
        if (WIFSIGNALED(systemcall_status) &&
2648
            (WTERMSIG(systemcall_status) == SIGINT || WTERMSIG(systemcall_status) == SIGQUIT))
2649
            interrupted = true;
2650
    }
2651
 
2652
    if (!quiet)
2653
         cout << "------------------ End of XMLTV output ------------------" << endl;
2654
2655
    grabDataFromFile(source.id, filename);
2656
2657
    QFile thefile(filename);
2658
    thefile.remove();
2659
2660
    return succeeded;
2661
}
2662
2663
void grabDataFromDDFile(int id, int offset, const QString &filename,
2664
        const QString &lineupid, QDate *qCurrentDate = 0)
2665
{
2666
    QDate *currentd = qCurrentDate;
2667
    QDate qcd = QDate::currentDate();
2668
    if (!currentd)
2669
        currentd = &qcd;
2670
2671
    ddprocessor.setInputFile(filename);
2672
    Source s;
2673
    s.id = id;
2674
    s.name = "";
2675
    s.xmltvgrabber = "datadirect";
2676
    s.userid = "fromfile";
2677
    s.password = "fromfile";
2678
    s.lineupid = lineupid;
2679
2680
    grabData(s, offset, currentd);
2681
}
2682
2683
void clearOldDBEntries(void)
2684
{
2685
    MSqlQuery query;
2686
    QString querystr;
2687
    int offset = 1;
2688
2689
    if (no_delete)
2690
        offset=7;
2691
2692
    querystr.sprintf("DELETE FROM oldprogram WHERE airdate < "
2693
                     "DATE_SUB(CURRENT_DATE, INTERVAL 320 DAY);");
2694
    query.exec(querystr);
2695
2696
    querystr.sprintf("REPLACE INTO oldprogram (oldtitle,airdate) "
2697
                     "SELECT title,starttime FROM program "
2698
                     "WHERE starttime < NOW() group by title;");
2699
    query.exec(querystr);
2700
2701
    querystr.sprintf("DELETE FROM program WHERE starttime <= "
2702
                     "DATE_SUB(CURRENT_DATE, INTERVAL %d DAY);", offset);
2703
    query.exec(querystr);
2704
2705
    querystr.sprintf("DELETE FROM programrating WHERE starttime <= "
2706
                     "DATE_SUB(CURRENT_DATE, INTERVAL %d DAY);", offset);
2707
    query.exec(querystr);
2708
2709
    querystr.sprintf("DELETE FROM programgenres WHERE starttime <= "
2710
                     "DATE_SUB(CURRENT_DATE, INTERVAL %d DAY);", offset);
2711
    query.exec(querystr);
2712
2713
    querystr.sprintf("DELETE FROM credits WHERE starttime <= "
2714
                     "DATE_SUB(CURRENT_DATE, INTERVAL %d DAY);", offset);
2715
    query.exec(querystr);
2716
2717
    querystr.sprintf("DELETE FROM record WHERE (type = %d "
2718
                     "OR type = %d OR type = %d) AND enddate < NOW();",
2719
                     kSingleRecord, kOverrideRecord, kDontRecord);
2720
    query.exec(querystr);
2721
}
2722
2723
bool fillData(QValueList<Source> &sourcelist)
2724
{
2725
    QValueList<Source>::Iterator it;
2726
2727
    QString status;
2728
    MSqlQuery query;
2729
    QDateTime GuideDataBefore, GuideDataAfter;
2730
2731
    query.exec(QString("SELECT MAX(endtime) FROM program;"));
2732
    if (query.isActive() && query.numRowsAffected() > 0)
2733
    {
2734
        query.next();
2735
2736
        if (!query.isNull(0))
2737
            GuideDataBefore = QDateTime::fromString(query.value(0).toString(),
2738
                                                    Qt::ISODate);
2739
    }
2740
2741
    int failures = 0;
2742
    for (it = sourcelist.begin(); it != sourcelist.end(); ++it) {
2743
        int chancnt = 0;
2744
        QString xmltv_grabber = (*it).xmltvgrabber;
2745
        if (xmltv_grabber == "tv_grab_uk" || xmltv_grabber == "tv_grab_de" ||
2746
            xmltv_grabber == "tv_grab_fi" || xmltv_grabber == "tv_grab_es" ||
2747
            xmltv_grabber == "tv_grab_nl" || xmltv_grabber == "tv_grab_au" ||
2748
            xmltv_grabber == "tv_grab_fr" || xmltv_grabber == "tv_grab_jp" ||
2749
            xmltv_grabber == "tv_grab_pt" || xmltv_grabber == "tv_grab_uk_rt")
2750
        {
2751
            // These don't support the --offset option, so just grab the max.
2752
            if (!grabData(*it, -1))
2753
                ++failures;
2754
        }
2755
        else if (xmltv_grabber == "tv_grab_dk")
2756
        {
2757
            if (!grabData(*it, 0))
2758
                ++failures;
2759
        }
2760
        else if (xmltv_grabber == "tv_grab_nz")
2761
        {
2762
            // tv_grab_nz only supports a 7-day "grab".
2763
            grabData(*it, 1);
2764
2765
            for (int i = 0; i < 7; i++)
2766
            {
2767
                QString querystr;
2768
                querystr.sprintf("SELECT COUNT(*) FROM program "
2769
                                 "LEFT JOIN channel USING (chanid) "
2770
                                 "WHERE sourceid = %d "
2771
                                 "AND starttime >= DATE_ADD(CURRENT_DATE(), "
2772
                                 "    INTERVAL %d DAY) "
2773
                                 "AND starttime < DATE_ADD(CURRENT_DATE(), "
2774
                                 "    INTERVAL %d DAY)",
2775
                                 (*it).id, i, i+1);
2776
 
2777
                MSqlQuery query;
2778
                query.exec(querystr);
2779
                
2780
                if (query.isActive()) 
2781
                {
2782
                    if (!query.numRowsAffected() ||
2783
                        (query.next() && query.value(0).toInt() <= 1))
2784
                    {
2785
                        if (!grabData(*it, i))
2786
                            ++failures;
2787
                    }
2788
                } 
2789
                else
2790
                    MythContext::DBError("checking existing program data", 
2791
                                         query);
2792
            }
2793
        }
2794
        else if (xmltv_grabber == "datadirect" && dd_grab_all)
2795
        {
2796
            QDate qCurrentDate = QDate::currentDate();
2797
2798
            grabData(*it, 0, &qCurrentDate);
2799
        }
2800
        else if (xmltv_grabber == "datadirect" ||
2801
                 xmltv_grabber == "tv_grab_se_swedb" ||
2802
                 xmltv_grabber == "tv_grab_no")
2803
        {
2804
            if (xmltv_grabber == "tv_grab_no")
2805
                listing_wrap_offset = 6 * 3600;
2806
2807
            QDate qCurrentDate = QDate::currentDate();
2808
2809
            if (refresh_today)
2810
            {
2811
                if (!quiet)
2812
                    cout << "Refreshing Today's data" << endl;
2813
                if (!grabData(*it, 0, &qCurrentDate))
2814
                    ++failures;
2815
            }
2816
2817
            if (refresh_tomorrow)
2818
            {
2819
                if (!quiet)
2820
                    cout << "Refreshing Tomorrow's data" << endl;
2821
                if (!grabData(*it, 1, &qCurrentDate))
2822
                    ++failures;
2823
            }
2824
2825
            if (refresh_second)
2826
            {
2827
                if (!quiet)
2828
                    cout << "Refreshing data for 2 days from today" << endl;
2829
                if (!grabData(*it, 2, &qCurrentDate))
2830
                    ++failures;
2831
            }
2832
2833
            int maxday = 9;
2834
2835
            if (xmltv_grabber == "datadirect")
2836
                maxday = 14;
2837
            else if (xmltv_grabber == "tv_grab_no")
2838
                maxday = 7;
2839
            else if (xmltv_grabber == "tv_grab_se_swedb")
2840
                maxday = 10;
2841
2842
            for (int i = 0; i < maxday; i++)
2843
            {
2844
                if ((i == 0 && refresh_today) || (i == 1 && refresh_tomorrow) ||
2845
                    (i == 2 && refresh_second))
2846
                    continue;
2847
2848
                // we need to check and see if the current date has changed 
2849
                // since we started in this loop.  If it has, we need to adjust
2850
                // the value of 'i' to compensate for this.
2851
                if (QDate::currentDate() != qCurrentDate)
2852
                {
2853
                    QDate newDate = QDate::currentDate();
2854
                    i += (newDate.daysTo(qCurrentDate));
2855
                    if (i < 0) 
2856
                        i = 0;
2857
                    qCurrentDate = newDate;
2858
                }
2859
2860
                // Check to see if we already downloaded data for this date
2861
                bool download_needed = false;
2862
                QString date(qCurrentDate.addDays(i).toString());
2863
2864
                if (!chancnt)
2865
                {
2866
                    QString qstr = QString("SELECT COUNT(*) FROM channel "
2867
                                           "WHERE sourceid = %1")
2868
                                           .arg((*it).id);
2869
                    MSqlQuery qchan;
2870
                    qchan.exec(qstr);
2871
                    if (qchan.isActive() && qchan.numRowsAffected() > 0) 
2872
                    {
2873
                        qchan.next();
2874
                        chancnt = qchan.value(0).toInt();
2875
                    }
2876
                    else
2877
                        MythContext::DBError("counting channels per source", 
2878
                                             qchan);
2879
                }
2880
                QString querystr;
2881
2882
                querystr.sprintf("SELECT COUNT(*) FROM program "
2883
                                 "LEFT JOIN channel USING (chanid) "
2884
                                 "WHERE sourceid = %d "
2885
                                 "AND starttime >= DATE_ADD(CURRENT_DATE(), "
2886
                                 "    INTERVAL '%d 18' DAY_HOUR) "
2887
                                 "AND starttime < DATE_ADD(CURRENT_DATE(), "
2888
                                 "    INTERVAL %d DAY)",
2889
                                 (*it).id, i, i+1);
2890
                MSqlQuery query;
2891
                query.exec(querystr);
2892
               
2893
                if (query.isActive()) 
2894
                {
2895
                    // We also need to get this day's data if there's only a 
2896
                    // suspiciously small amount in the DB.
2897
                    if (!query.numRowsAffected() ||
2898
                        (query.next() && query.value(0).toInt() < chancnt * 4))
2899
                    {
2900
                        download_needed = true;
2901
                    }
2902
                } 
2903
                else
2904
                    MythContext::DBError("checking existing program data", 
2905
                                         query);
2906
2907
                // Now look for programs marked as "To Be Announced" after noon
2908
                if (xmltv_grabber == "tv_grab_uk_rt" && 
2909
                    !download_needed && refresh_tba)
2910
                {
2911
                    querystr.sprintf("SELECT COUNT(*) FROM program "
2912
                                 "LEFT JOIN channel USING (chanid) "
2913
                                 "WHERE sourceid = %d "
2914
                                 "AND starttime >= DATE_ADD(CURRENT_DATE(), "
2915
                                 "    INTERVAL '%d 12' DAY_HOUR) "
2916
                                 "AND starttime < DATE_ADD(CURRENT_DATE(), "
2917
                                 "    INTERVAL %d DAY) "
2918
                                 "AND category = 'TBA'",
2919
                                 (*it).id, i, i+1);
2920
                    MSqlQuery query;
2921
                    query.exec(querystr);
2922
2923
                    if (query.isActive()) 
2924
                    {
2925
                        if (query.numRowsAffected() || 
2926
                            (query.next() && query.value(0).toInt() >= 1)) 
2927
                        {
2928
                            download_needed = true;
2929
                        }
2930
                    } 
2931
                    else
2932
                        MythContext::DBError("checking existing program data", 
2933
                                             query);
2934
                }
2935
2936
                if (download_needed)
2937
                {
2938
                    if (!quiet)
2939
                        cout << "Fetching data for " << date << endl;
2940
                    if (!grabData(*it, i, &qCurrentDate))
2941
                    {
2942
                        ++failures;
2943
                        if (interrupted)
2944
                        {
2945
                            break;
2946
                        }
2947
                    }
2948
                }
2949
                else
2950
                {
2951
                    if (!quiet)
2952
                        cout << "Data is already present for " << date
2953
                             << ", skipping\n";
2954
                }
2955
            }
2956
        }
2957
        else
2958
        {
2959
            cerr << "Grabbing XMLTV data using " << xmltv_grabber.ascii() 
2960
                 << " is not verified as working.\n";
2961
        }
2962
        if (interrupted)
2963
        {
2964
            break;
2965
        }
2966
    }
2967
2968
    query.exec(QString("SELECT MAX(endtime) FROM program;"));
2969
    if (query.isActive() && query.numRowsAffected() > 0)
2970
    {
2971
        query.next();
2972
2973
        if (!query.isNull(0))
2974
            GuideDataAfter = QDateTime::fromString(query.value(0).toString(),
2975
                                                   Qt::ISODate);
2976
    }
2977
2978
    if (failures == 0)
2979
    {
2980
        if (GuideDataAfter == GuideDataBefore)
2981
            status = "mythfilldatabase ran, but did not insert "
2982
                     "any new data into the Guide.  This can indicate a "
2983
                     "potential grabber failure."; 
2984
        else
2985
            status = "Successful.";
2986
2987
        query.exec(QString("UPDATE settings SET data ='%1' "
2988
                           "WHERE value='mythfilldatabaseLastRunStatus'")
2989
                           .arg(status));
2990
    }
2991
2992
    clearOldDBEntries();
2993
2994
    return (failures == 0);
2995
}
2996
2997
ChanInfo *xawtvChannel(QString &id, QString &channel, QString &fine)
2998
{
2999
    ChanInfo *chaninfo = new ChanInfo;
3000
    chaninfo->xmltvid = id;
3001
    chaninfo->name = id;
3002
    chaninfo->callsign = id;
3003
    if (channel_preset)
3004
        chaninfo->chanstr = id;
3005
    else
3006
        chaninfo->chanstr = channel;
3007
    chaninfo->finetune = fine;
3008
    chaninfo->freqid = channel;
3009
    chaninfo->iconpath = "";
3010
    chaninfo->tvformat = "Default";
3011
3012
    return chaninfo;
3013
}
3014
3015
void readXawtvChannels(int id, QString xawrcfile)
3016
{
3017
    fstream fin(xawrcfile.ascii(), ios::in);
3018
    if (!fin.is_open()) return;
3019
3020
    QValueList<ChanInfo> chanlist;
3021
3022
    QString xawid;
3023
    QString channel;
3024
    QString fine;
3025
3026
    string strLine;
3027
    int nSplitPoint = 0;
3028
3029
    while(!fin.eof())
3030
    {
3031
        getline(fin,strLine);
3032
3033
        if ((strLine[0] != '#') && (!strLine.empty()))
3034
        {
3035
            if (strLine[0] == '[')
3036
            {
3037
                if ((nSplitPoint = strLine.find(']')) > 1)
3038
                {
3039
                    if ((xawid != "") && (channel != ""))
3040
                    {
3041
                        ChanInfo *chinfo = xawtvChannel(xawid, channel, fine);
3042
                        chanlist.push_back(*chinfo);
3043
                        delete chinfo;
3044
                    }
3045
                    xawid = strLine.substr(1, nSplitPoint - 1).c_str();
3046
                    channel = "";
3047
                    fine = "";
3048
                }
3049
            }
3050
            else if ((nSplitPoint = strLine.find('=') + 1) > 0)
3051
            {
3052
                while (strLine.substr(nSplitPoint,1) == " ")
3053
                { ++nSplitPoint; }
3054
3055
                if (!strncmp(strLine.c_str(), "channel", 7))
3056
                {
3057
                    channel = strLine.substr(nSplitPoint, 
3058
                                             strLine.size()).c_str();
3059
                }
3060
                else if (!strncmp(strLine.c_str(), "fine", 4))
3061
                {
3062
                    fine = strLine.substr(nSplitPoint, strLine.size()).c_str();
3063
                }
3064
            }
3065
        }
3066
    }
3067
3068
    if ((xawid != "") && (channel != ""))
3069
    {
3070
        ChanInfo *chinfo = xawtvChannel(xawid, channel, fine);
3071
        chanlist.push_back(*chinfo);
3072
        delete chinfo;
3073
    }
3074
3075
    handleChannels(id, &chanlist);
3076
}
3077
3078
int fix_end_times(void)
3079
{
3080
    int count = 0;
3081
    QString chanid, starttime, endtime, querystr;
3082
    MSqlQuery query1, query2;
3083
3084
    querystr = "SELECT chanid, starttime, endtime FROM program "
3085
               "WHERE (DATE_FORMAT(endtime,\"%H%i\") = \"0000\") "
3086
               "ORDER BY chanid, starttime;";
3087
3088
    if (!query1.exec(querystr))
3089
    {
3090
        cerr << "fix_end_times:  " << querystr << " failed!\n";
3091
        return -1;
3092
    }
3093
3094
    while (query1.next())
3095
    {
3096
        starttime = query1.value(1).toString();
3097
        chanid = query1.value(0).toString();
3098
        endtime = query1.value(2).toString();
3099
3100
        querystr = QString("SELECT chanid, starttime, endtime FROM program "
3101
                           "WHERE (DATE_FORMAT(starttime, \"%%Y-%%m-%%d\") = "
3102
                           "\"%1\") AND chanid = \"%2\" "
3103
                           "ORDER BY starttime LIMIT 1;")
3104
                           .arg(endtime.left(10))
3105
                           .arg(chanid);
3106
3107
        if (!query2.exec(querystr))
3108
        {
3109
            cerr << "fix_end_times:  " << querystr << " failed!\n";
3110
            return -1;
3111
        }
3112
3113
        if (query2.next() && (endtime != query2.value(1).toString()))
3114
        {
3115
            count++;
3116
            endtime = query2.value(1).toString();
3117
            querystr = QString("UPDATE program SET starttime = \"%1\", "
3118
                               "endtime = \"%2\" WHERE (chanid = \"%3\" AND "
3119
                               "starttime = \"%4\");")
3120
                               .arg(starttime)
3121
                               .arg(endtime)
3122
                               .arg(chanid)
3123
                               .arg(starttime);
3124
3125
            if (!query2.exec(querystr)) 
3126
            {
3127
                cerr << "fix_end_times:  " << querystr << " failed!\n";
3128
                return -1;
3129
            }
3130
        }
3131
    }
3132
3133
    return count;
3134
}
3135
3136
int main(int argc, char *argv[])
3137
{
3138
    QApplication a(argc, argv, false);
3139
    int argpos = 1;
3140
    int fromfile_id = 1;
3141
    int fromfile_offset = 0;
3142
    QString fromfile_name;
3143
    bool from_xawfile = false;
3144
    int fromxawfile_id = 1;
3145
    QString fromxawfile_name;
3146
3147
    bool grab_data = true;
3148
3149
    bool export_iconmap = false;
3150
    bool import_iconmap = false;
3151
    bool reset_iconmap = false;
3152
    bool reset_iconmap_icons = false;
3153
    QString import_icon_map_filename("iconmap.xml");
3154
    QString export_icon_map_filename("iconmap.xml");
3155
3156
    bool update_icon_map = false;
3157
3158
    bool from_dd_file = false;
3159
    QString fromddfile_lineupid;
3160
3161
    while (argpos < a.argc())
3162
    {
3163
        // The manual and update flags should be mutually exclusive.
3164
        if (!strcmp(a.argv()[argpos], "--manual"))
3165
        {
3166
            cout << "###\n";
3167
            cout << "### Running in manual channel configuration mode.\n";
3168
            cout << "### This will ask you questions about every channel.\n";
3169
            cout << "###\n";
3170
            interactive = true;
3171
        }
3172
        else if (!strcmp(a.argv()[argpos], "--preset"))
3173
        {
3174
            // For using channel preset values instead of channel numbers.
3175
            cout << "###\n";
3176
            cout << "### Running in preset channel configuration mode.\n";
3177
            cout << "### This will assign channel ";
3178
            cout << "preset numbers to every channel.\n";
3179
            cout << "###\n";
3180
            channel_preset = true;
3181
        }
3182
        else if (!strcmp(a.argv()[argpos], "--update"))
3183
        {
3184
            // For running non-destructive updates on the database for
3185
            // users in xmltv zones that do not provide channel data.
3186
            non_us_updating = true;
3187
        }
3188
        else if (!strcmp(a.argv()[argpos], "--no-delete"))
3189
        {
3190
            // Do not delete old programs from the database until 7 days old.
3191
            // Do not delete existing programs from the database when updating.
3192
            no_delete = true;
3193
        }
3194
        else if (!strcmp(a.argv()[argpos], "--file"))
3195
        {
3196
            if (((argpos + 3) >= a.argc()) ||
3197
                !strncmp(a.argv()[argpos + 1], "--", 2) ||
3198
                !strncmp(a.argv()[argpos + 2], "--", 2) ||
3199
                !strncmp(a.argv()[argpos + 3], "--", 2))
3200
            {
3201
                printf("missing or invalid parameters for --file option\n");
3202
                return -1;
3203
            }
3204
3205
            fromfile_id = atoi(a.argv()[++argpos]);
3206
            fromfile_offset = atoi(a.argv()[++argpos]);
3207
            fromfile_name = a.argv()[++argpos];
3208
3209
            if (!quiet)
3210
                cout << "### bypassing grabbers, reading directly from file\n";
3211
            from_file = true;
3212
        }
3213
        else if (!strcmp(a.argv()[argpos], "--dd-file"))
3214
        {
3215
            if (((argpos + 4) >= a.argc()) ||
3216
                !strncmp(a.argv()[argpos + 1], "--", 2) ||
3217
                !strncmp(a.argv()[argpos + 2], "--", 2) ||
3218
                !strncmp(a.argv()[argpos + 3], "--", 2) ||
3219
                !strncmp(a.argv()[argpos + 4], "--", 2))
3220
            {
3221
                printf("missing or invalid parameters for --dd-file option\n");
3222
                return -1;
3223
            }
3224
3225
            fromfile_id = atoi(a.argv()[++argpos]);
3226
            fromfile_offset = atoi(a.argv()[++argpos]);
3227
            fromddfile_lineupid = a.argv()[++argpos];
3228
            fromfile_name = a.argv()[++argpos];
3229
3230
            if (!quiet)
3231
                cout << "### bypassing grabbers, reading directly from file\n";
3232
            from_dd_file = true;
3233
        }
3234
        else if (!strcmp(a.argv()[argpos], "--xawchannels"))
3235
        {
3236
            if (((argpos + 2) >= a.argc()) ||
3237
                !strncmp(a.argv()[argpos + 1], "--", 2) ||
3238
                !strncmp(a.argv()[argpos + 2], "--", 2))
3239
            {
3240
                printf("missing or invalid parameters for --xawchannels option\n");
3241
                return -1;
3242
            }
3243
3244
            fromxawfile_id = atoi(a.argv()[++argpos]);
3245
            fromxawfile_name = a.argv()[++argpos];
3246
3247
            if (!quiet)
3248
                 cout << "### reading channels from xawtv configfile\n";
3249
            from_xawfile = true;
3250
        }
3251
        else if (!strcmp(a.argv()[argpos], "--do_channel_updates"))
3252
        {
3253
            channel_updates = true;
3254
        }
3255
        else if (!strcmp(a.argv()[argpos], "--graboptions"))
3256
        {
3257
            if (((argpos + 1) >= a.argc()))
3258
            {
3259
                printf("missing parameter for --graboptions option\n");
3260
                return -1;
3261
            }
3262
3263
            graboptions = QString(" ") + QString(a.argv()[++argpos]);
3264
        }
3265
        else if (!strcmp(a.argv()[argpos], "--refresh-today"))
3266
        {
3267
            refresh_today = true;
3268
        }
3269
        else if (!strcmp(a.argv()[argpos], "--dont-refresh-tomorrow"))
3270
        {
3271
            refresh_tomorrow = false;
3272
        }
3273
        else if (!strcmp(a.argv()[argpos], "--refresh-second"))
3274
        {
3275
            refresh_second = true;
3276
        }
3277
        else if (!strcmp(a.argv()[argpos], "--dont-refresh-tba"))
3278
        {
3279
            refresh_tba = false;
3280
        }
3281
#if 0
3282
        else if (!strcmp(a.argv()[argpos], "--dd-grab-all"))
3283
        {
3284
            dd_grab_all = true;
3285
            refresh_today = false;
3286
            refresh_tomorrow = false;
3287
            refresh_second = false;
3288
        }
3289
#endif
3290
        else if (!strcmp(a.argv()[argpos], "--quiet"))
3291
        {
3292
             quiet = true;
3293
        }
3294
        else if (!strcmp(a.argv()[argpos], "--mark-repeats"))
3295
        {
3296
             mark_repeats = true;
3297
        }
3298
        else if (!strcmp(a.argv()[argpos], "--nomark-repeats"))
3299
        {
3300
             mark_repeats = false;
3301
        }
3302
        else if (!strcmp(a.argv()[argpos], "--export-icon-map"))
3303
        {
3304
            export_iconmap = true;
3305
            grab_data = false;
3306
3307
            if ((argpos + 1) >= a.argc() ||
3308
                    !strncmp(a.argv()[argpos + 1], "--", 2))
3309
            {
3310
                if (!isatty(fileno(stdout)))
3311
                {
3312
                    quiet = true;
3313
                    export_icon_map_filename = "-";
3314
                }
3315
            }
3316
            else
3317
            {
3318
                export_icon_map_filename = a.argv()[++argpos];
3319
            }
3320
        }
3321
        else if (!strcmp(a.argv()[argpos], "--import-icon-map"))
3322
        {
3323
            import_iconmap = true;
3324
            grab_data = false;
3325
3326
            if ((argpos + 1) >= a.argc() ||
3327
                    !strncmp(a.argv()[argpos + 1], "--", 2))
3328
            {
3329
                if (!isatty(fileno(stdin)))
3330
                {
3331
                    import_icon_map_filename = "-";
3332
                }
3333
            }
3334
            else
3335
            {
3336
                import_icon_map_filename = a.argv()[++argpos];
3337
            }
3338
        }
3339
        else if (!strcmp(a.argv()[argpos], "--update-icon-map"))
3340
        {
3341
            update_icon_map = true;
3342
            grab_data = false;
3343
        }
3344
        else if (!strcmp(a.argv()[argpos], "--reset-icon-map"))
3345
        {
3346
            reset_iconmap = true;
3347
            grab_data = false;
3348
3349
            if ((argpos + 1) < a.argc() &&
3350
                    strncmp(a.argv()[argpos + 1], "--", 2))
3351
            {
3352
                ++argpos;
3353
                if (QString(a.argv()[argpos]) == "all")
3354
                {
3355
                    reset_iconmap_icons = true;
3356
                }
3357
                else
3358
                {
3359
                    cerr << "Unknown icon group '" << a.argv()[argpos]
3360
                            << "' for --reset-icon-map option" << endl;
3361
                    return -1;
3362
                }
3363
            }
3364
        }
3365
        else if (!strcmp(a.argv()[argpos], "-h") ||
3366
                 !strcmp(a.argv()[argpos], "--help"))
3367
        {
3368
            cout << "usage:\n";
3369
            cout << "--manual\n";
3370
            cout << "   Run in manual channel configuration mode\n";
3371
            cout << "   This will ask you questions about every channel\n";
3372
            cout << "\n";
3373
            cout << "--update\n";
3374
            cout << "   For running non-destructive updates on the database for\n";
3375
            cout << "   users in xmltv zones that do not provide channel data\n";
3376
            cout << "\n";
3377
            cout << "--preset\n";
3378
            cout << "   Use it in case that you want to assign a preset number for\n";
3379
            cout << "   each channel, useful for non US countries where people\n";
3380
            cout << "   are used to assigning a sequenced number for each channel, i.e.:\n";
3381
            cout << "   1->TVE1(S41), 2->La 2(SE18), 3->TV3(21), 4->Canal 33(60)...\n";
3382
            cout << "\n";
3383
            cout << "--no-delete\n";
3384
            cout << "   Do not delete old programs from the database until 7 days old.\n";
3385
            cout << "   Do not delete existing programs from the database when updating.\n";
3386
            cout << "\n";
3387
            cout << "--file <sourceid> <offset> <xmlfile>\n";
3388
            cout << "   Bypass the grabbers and read data directly from a file\n";
3389
            cout << "   <sourceid> = cardinput\n";
3390
            cout << "   <offset>   = days from today that xmlfile defines\n";
3391
            cout << "                (-1 means to replace all data, up to 10 days)\n";
3392
            cout << "   <xmlfile>  = file to read\n";
3393
            cout << "\n";
3394
            cout << "--dd-file <sourceid> <offset> <lineupid> <xmlfile>\n";
3395
            cout << "   <sourceid> = see --file\n";
3396
            cout << "   <offset>   = see --file\n";
3397
            cout << "   <lineupid> = the lineup id\n";
3398
            cout << "   <xmlfile>  = see --file\n";
3399
            cout << "\n";
3400
            cout << "--xawchannels <sourceid> <xawtvrcfile>\n";
3401
            cout << "   (--manual flag works in combination with this)\n";
3402
            cout << "   Read channels as defined in xawtvrc file given\n";
3403
            cout << "   <sourceid>    = cardinput\n";
3404
            cout << "   <xawtvrcfile> = file to read\n";
3405
            cout << "\n";
3406
            cout << "--do_channel_updates\n";
3407
            cout << "   When using data direct, ask mythfilldatabase to\n";
3408
            cout << "   overwrite channel names, frequencies, etc. with the\n";
3409
            cout << "   values available from the data source. This will \n";
3410
            cout << "   override custom channel names, which is why it is\n";
3411
            cout << "   off by default.\n";
3412
            cout << "\n";
3413
            cout << "--graboptions <\"options\">\n";
3414
            cout << "   Pass options to grabber\n";
3415
            cout << "\n";
3416
            cout << "--refresh-today\n";
3417
            cout << "--refresh-second\n";
3418
            cout << "   (Only valid for grabbers: na, se_swedb, no)\n";
3419
            cout << "   Force a refresh today or two days from now, to catch the latest changes\n";
3420
            cout << "--dont-refresh-tomorrow\n";
3421
            cout << "   Tomorrow will be refreshed always unless this argument is used\n";
3422
            cout << "--dont-refresh-tba\n";
3423
            cout << "   \"To be announced\" progs will be refreshed always unless this argument is used\n";
3424
            cout << "--export-icon-map [<filename>]\n";
3425
            cout << "   Exports your current icon map to <filename> (default: "
3426
                    << export_icon_map_filename << ")\n";
3427
            cout << "--import-icon-map [<filename>]\n";
3428
            cout << "   Imports an icon map from <filename> (default: " <<
3429
                    import_icon_map_filename << ")\n";
3430
            cout << "--update-icon-map\n";
3431
            cout << "   Updates icon map icons only\n";
3432
            cout << "--reset-icon-map [all]\n";
3433
            cout << "   Resets your icon map (pass all to reset channel icons as well)\n";
3434
            cout << "--mark-repeats\n";
3435
            cout << "   Marks any programs with a OriginalAirDate earlier than their start date as a repeat\n";
3436
            cout << "\n";
3437
#if 0
3438
            cout << "--dd-grab-all\n";
3439
            cout << "   The DataDirect grabber will grab all available data\n";
3440
#endif
3441
            cout << "--help\n";
3442
            cout << "   This text\n";
3443
            cout << "\n";
3444
            cout << "\n";
3445
            cout << "  --manual and --update can not be used together.\n";
3446
            cout << "\n";
3447
            return -1;
3448
        }
3449
        else
3450
        {
3451
            fprintf(stderr, "illegal option: '%s' (use --help)\n",
3452
                    a.argv()[argpos]);
3453
            return -1;
3454
        }
3455
3456
        ++argpos;
3457
    }
3458
3459
    gContext = new MythContext(MYTH_BINARY_VERSION, false);
3460
3461
    QSqlDatabase *db = QSqlDatabase::addDatabase("QMYSQL3");
3462
    if (!gContext->OpenDatabase(db))
3463
    {
3464
        cerr << "couldn't open db\n";
3465
        return -1;
3466
    }
3467
3468
    gContext->LogEntry("mythfilldatabase", LP_INFO,
3469
                       "Listings Download Started", "");
3470
    
3471
    MSqlQuery query;
3472
    
3473
    if (!grab_data)
3474
    {
3475
    }
3476
    else if (from_xawfile)
3477
    {
3478
        readXawtvChannels(fromxawfile_id, fromxawfile_name);
3479
    }
3480
    else if (from_file)
3481
    {
3482
        QString status = "currently running.";
3483
        QDateTime GuideDataBefore, GuideDataAfter;
3484
3485
        query.exec(QString("UPDATE settings SET data ='%1' "
3486
                           "WHERE value='mythfilldatabaseLastRunStart'")
3487
                           .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm")));
3488
3489
        query.exec(QString("UPDATE settings SET data ='%1' "
3490
                           "WHERE value='mythfilldatabaseLastRunStatus'")
3491
                           .arg(status));
3492
3493
        query.exec(QString("SELECT MAX(endtime) FROM program;"));
3494
        if (query.isActive() && query.numRowsAffected() > 0)
3495
        {
3496
            query.next();
3497
3498
            if (!query.isNull(0))
3499
                GuideDataBefore = QDateTime::fromString(query.value(0).toString(),
3500
                                                    Qt::ISODate);
3501
        }
3502
3503
        grabDataFromFile(fromfile_id, fromfile_name);
3504
        clearOldDBEntries();
3505
3506
        query.exec(QString("UPDATE settings SET data ='%1' "
3507
                           "WHERE value='mythfilldatabaseLastRunEnd'")
3508
                          .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm")));
3509
3510
        query.exec(QString("SELECT MAX(endtime) FROM program;"));
3511
        if (query.isActive() && query.numRowsAffected() > 0)
3512
        {
3513
            query.next();
3514
3515
            if (!query.isNull(0))
3516
                GuideDataAfter = QDateTime::fromString(query.value(0).toString(),
3517
                                                   Qt::ISODate);
3518
        }
3519
3520
        if (GuideDataAfter == GuideDataBefore)
3521
            status = "mythfilldatabase ran, but did not insert "
3522
                     "any new data into the Guide.  This can indicate a "
3523
                     "potential problem with the XML file used for the update.";
3524
        else
3525
            status = "Successful.";
3526
3527
        query.exec(QString("UPDATE settings SET data ='%1' "
3528
                           "WHERE value='mythfilldatabaseLastRunStatus'")
3529
                           .arg(status));
3530
    }
3531
    else if (from_dd_file)
3532
    {
3533
        grabDataFromDDFile(fromfile_id, fromfile_offset, fromfile_name,
3534
                fromddfile_lineupid);
3535
        clearOldDBEntries();
3536
    }
3537
    else
3538
    {
3539
        QValueList<Source> sourcelist;
3540
3541
        MSqlQuery sourcequery;
3542
        QString querystr = QString("SELECT sourceid,name,xmltvgrabber,userid,"
3543
                                   "password,lineupid "
3544
                                   "FROM videosource ORDER BY sourceid;");
3545
        sourcequery.exec(querystr);
3546
        
3547
        if (sourcequery.isActive())
3548
        {
3549
             if (sourcequery.numRowsAffected() > 0)
3550
             {
3551
                  while (sourcequery.next())
3552
                  {
3553
                       Source newsource;
3554
            
3555
                       newsource.id = sourcequery.value(0).toInt();
3556
                       newsource.name = sourcequery.value(1).toString();
3557
                       newsource.xmltvgrabber = sourcequery.value(2).toString();
3558
                       newsource.userid = sourcequery.value(3).toString();
3559
                       newsource.password = sourcequery.value(4).toString();
3560
                       newsource.lineupid = sourcequery.value(5).toString();
3561
3562
                       sourcelist.append(newsource);
3563
                  }
3564
             }
3565
             else
3566
             {
3567
                  cerr << "There are no channel sources defined, did you run "
3568
                       << "the setup program?\n";
3569
                  gContext->LogEntry("mythfilldatabase", LP_CRITICAL,
3570
                                     "No channel sources defined",
3571
                                     "Could not find any defined channel "
3572
                                     "sources - did you run the setup "
3573
                                     "program?");
3574
                  exit(16);
3575
             }
3576
        }
3577
        else
3578
        {
3579
             MythContext::DBError("loading channel sources", sourcequery);
3580
             exit(17);
3581
        }
3582
    
3583
        if (!fillData(sourcelist))
3584
        {
3585
             cerr << "Failed to fetch some program info\n";
3586
             gContext->LogEntry("mythfilldatabase", LP_WARNING,
3587
                                "Failed to fetch some program info", "");
3588
             exit(18);
3589
        }
3590
    }
3591
3592
    if (reset_iconmap)
3593
    {
3594
        ResetIconMap(reset_iconmap_icons);
3595
    }
3596
3597
    if (import_iconmap)
3598
    {
3599
        ImportIconMap(import_icon_map_filename);
3600
    }
3601
3602
    if (export_iconmap)
3603
    {
3604
        ExportIconMap(export_icon_map_filename);
3605
    }
3606
3607
    if (update_icon_map)
3608
    {
3609
        query.exec("SELECT sourceid FROM videosource ORDER BY sourceid;");
3610
        if (query.isActive() && query.numRowsAffected() > 0)
3611
        {
3612
            while (query.next())
3613
            {
3614
                UpdateSourceIcons(query.value(0).toInt());
3615
            }
3616
        }
3617
    }
3618
3619
    if (grab_data)
3620
    {
3621
        if (!quiet)
3622
             cout << "Adjusting program database end times...\n";
3623
        int update_count = fix_end_times();
3624
        if (update_count == -1)
3625
             cerr << "fix_end_times failed!\a\n";
3626
        else if (!quiet)
3627
             cout << update_count << " replacements made.\n";
3628
3629
        gContext->LogEntry("mythfilldatabase", LP_INFO,
3630
                           "Listings Download Finished", "");
3631
    }
3632
    
3633
    if (mark_repeats)
3634
    {
3635
       if (!quiet)
3636
             cout << "Marking repeats...";
3637
       
3638
        int newEpiWindow = gContext->GetNumSetting( "NewEpisodeWindow", 14);
3639
        
3640
        query.exec( QString( "UPDATE program SET previouslyshown = 1 "
3641
                    "WHERE previouslyshown = 0 "
3642
                    "AND originalairdate is not null "
3643
                    "AND (to_days(starttime) - to_days(originalairdate)) > %1;")
3644
                    .arg(newEpiWindow));
3645
        
3646
        if (!quiet)
3647
            cout << "found " << query.numRowsAffected() << endl;
3648
            
3649
        if (!quiet)
3650
             cout << "Unmarking repeats from grabber that fall within our new episode window...";            
3651
             
3652
        query.exec( QString( "UPDATE program SET previouslyshown = 0 "
3653
                             "WHERE previouslyshown = 1 "
3654
                             "AND originalairdate is not null "
3655
                             "AND (to_days(starttime) - to_days(originalairdate)) <= %1;")
3656
                             .arg(newEpiWindow));             
3657
    
3658
        if (!quiet)
3659
            cout << "found " << query.numRowsAffected() << endl;
3660
    }
3661
3662
    query.exec( "SELECT count(previouslyshown) FROM program WHERE previouslyshown = 1;");
3663
    if (query.isActive() && query.numRowsAffected() > 0)
3664
    {
3665
        query.next();
3666
        if (query.value(0).toInt() != 0)
3667
            query.exec("UPDATE settings SET data = '1' WHERE value = 'HaveRepeats';");
3668
        else
3669
            query.exec("UPDATE settings SET data = '0' WHERE value = 'HaveRepeats';");
3670
    }
3671
3672
    if (grab_data || mark_repeats)
3673
        ScheduledRecording::signalChange(-1);
3674
3675
    delete gContext;
3676
3677
    return 0;
3678
}

Return to bug 87947