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 ×tr, 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 |
} |