1 | // -*- Mode: c++ -*- |
---|
2 | |
---|
3 | #include <limits.h> |
---|
4 | |
---|
5 | // C++ includes |
---|
6 | #include <algorithm> |
---|
7 | using namespace std; |
---|
8 | |
---|
9 | // Qt includes |
---|
10 | #include <QtCore> // for qAbs |
---|
11 | |
---|
12 | // MythTV headers |
---|
13 | #include "programdata.h" |
---|
14 | #include "channelutil.h" |
---|
15 | #include "mythdb.h" |
---|
16 | #include "mythlogging.h" |
---|
17 | #include "dvbdescriptors.h" |
---|
18 | |
---|
19 | #define LOC QString("ProgramData: ") |
---|
20 | |
---|
21 | static const char *roles[] = |
---|
22 | { |
---|
23 | "", |
---|
24 | "actor", "director", "producer", "executive_producer", |
---|
25 | "writer", "guest_star", "host", "adapter", |
---|
26 | "presenter", "commentator", "guest", |
---|
27 | }; |
---|
28 | |
---|
29 | static QString denullify(const QString &str) |
---|
30 | { |
---|
31 | return str.isNull() ? "" : str; |
---|
32 | } |
---|
33 | |
---|
34 | static QVariant denullify(const QDateTime &dt) |
---|
35 | { |
---|
36 | return dt.isNull() ? QVariant("0000-00-00 00:00:00") : QVariant(dt); |
---|
37 | } |
---|
38 | |
---|
39 | static void add_genres(MSqlQuery &query, const QStringList &genres, |
---|
40 | uint chanid, const QDateTime &starttime) |
---|
41 | { |
---|
42 | QString relevance = QStringLiteral("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); |
---|
43 | QStringList::const_iterator it = genres.constBegin(); |
---|
44 | for (; (it != genres.end()) && |
---|
45 | ((it - genres.constBegin()) < relevance.size()); ++it) |
---|
46 | { |
---|
47 | query.prepare( |
---|
48 | "INSERT INTO programgenres " |
---|
49 | " ( chanid, starttime, genre, relevance) " |
---|
50 | "VALUES (:CHANID, :START, :genre, :relevance)"); |
---|
51 | query.bindValue(":CHANID", chanid); |
---|
52 | query.bindValue(":START", starttime); |
---|
53 | query.bindValue(":genre", *it); |
---|
54 | query.bindValue(":relevance", relevance.at(it - genres.constBegin())); |
---|
55 | |
---|
56 | if (!query.exec()) |
---|
57 | MythDB::DBError("programgenres insert", query); |
---|
58 | } |
---|
59 | } |
---|
60 | |
---|
61 | DBPerson::DBPerson(const DBPerson &other) : |
---|
62 | role(other.role), name(other.name) |
---|
63 | { |
---|
64 | name.squeeze(); |
---|
65 | } |
---|
66 | |
---|
67 | DBPerson::DBPerson(Role _role, const QString &_name) : |
---|
68 | role(_role), name(_name) |
---|
69 | { |
---|
70 | name.squeeze(); |
---|
71 | } |
---|
72 | |
---|
73 | DBPerson::DBPerson(const QString &_role, const QString &_name) : |
---|
74 | role(kUnknown), name(_name) |
---|
75 | { |
---|
76 | if (!_role.isEmpty()) |
---|
77 | { |
---|
78 | for (uint i = 0; i < sizeof(roles) / sizeof(char *); i++) |
---|
79 | { |
---|
80 | if (_role == QString(roles[i])) |
---|
81 | role = (Role) i; |
---|
82 | } |
---|
83 | } |
---|
84 | name.squeeze(); |
---|
85 | } |
---|
86 | |
---|
87 | QString DBPerson::GetRole(void) const |
---|
88 | { |
---|
89 | if ((role < kActor) || (role > kGuest)) |
---|
90 | return "guest"; |
---|
91 | return roles[role]; |
---|
92 | } |
---|
93 | |
---|
94 | uint DBPerson::InsertDB(MSqlQuery &query, uint chanid, |
---|
95 | const QDateTime &starttime) const |
---|
96 | { |
---|
97 | uint personid = GetPersonDB(query); |
---|
98 | if (!personid && InsertPersonDB(query)) |
---|
99 | personid = GetPersonDB(query); |
---|
100 | |
---|
101 | return InsertCreditsDB(query, personid, chanid, starttime); |
---|
102 | } |
---|
103 | |
---|
104 | uint DBPerson::GetPersonDB(MSqlQuery &query) const |
---|
105 | { |
---|
106 | query.prepare( |
---|
107 | "SELECT person " |
---|
108 | "FROM people " |
---|
109 | "WHERE name = :NAME"); |
---|
110 | query.bindValue(":NAME", name); |
---|
111 | |
---|
112 | if (!query.exec()) |
---|
113 | MythDB::DBError("get_person", query); |
---|
114 | else if (query.next()) |
---|
115 | return query.value(0).toUInt(); |
---|
116 | |
---|
117 | return 0; |
---|
118 | } |
---|
119 | |
---|
120 | uint DBPerson::InsertPersonDB(MSqlQuery &query) const |
---|
121 | { |
---|
122 | query.prepare( |
---|
123 | "INSERT IGNORE INTO people (name) " |
---|
124 | "VALUES (:NAME);"); |
---|
125 | query.bindValue(":NAME", name); |
---|
126 | |
---|
127 | if (query.exec()) |
---|
128 | return 1; |
---|
129 | |
---|
130 | MythDB::DBError("insert_person", query); |
---|
131 | return 0; |
---|
132 | } |
---|
133 | |
---|
134 | uint DBPerson::InsertCreditsDB(MSqlQuery &query, uint personid, uint chanid, |
---|
135 | const QDateTime &starttime) const |
---|
136 | { |
---|
137 | if (!personid) |
---|
138 | return 0; |
---|
139 | |
---|
140 | query.prepare( |
---|
141 | "REPLACE INTO credits " |
---|
142 | " ( person, chanid, starttime, role) " |
---|
143 | "VALUES (:PERSON, :CHANID, :STARTTIME, :ROLE) "); |
---|
144 | query.bindValue(":PERSON", personid); |
---|
145 | query.bindValue(":CHANID", chanid); |
---|
146 | query.bindValue(":STARTTIME", starttime); |
---|
147 | query.bindValue(":ROLE", GetRole()); |
---|
148 | |
---|
149 | if (query.exec()) |
---|
150 | return 1; |
---|
151 | |
---|
152 | MythDB::DBError("insert_credits", query); |
---|
153 | return 0; |
---|
154 | } |
---|
155 | |
---|
156 | DBEvent &DBEvent::operator=(const DBEvent &other) |
---|
157 | { |
---|
158 | if (this == &other) |
---|
159 | return *this; |
---|
160 | |
---|
161 | title = other.title; |
---|
162 | subtitle = other.subtitle; |
---|
163 | description = other.description; |
---|
164 | category = other.category; |
---|
165 | starttime = other.starttime; |
---|
166 | endtime = other.endtime; |
---|
167 | airdate = other.airdate; |
---|
168 | originalairdate = other.originalairdate; |
---|
169 | |
---|
170 | if (credits != other.credits) |
---|
171 | { |
---|
172 | if (credits) |
---|
173 | { |
---|
174 | delete credits; |
---|
175 | credits = NULL; |
---|
176 | } |
---|
177 | |
---|
178 | if (other.credits) |
---|
179 | { |
---|
180 | credits = new DBCredits; |
---|
181 | credits->insert(credits->end(), |
---|
182 | other.credits->begin(), |
---|
183 | other.credits->end()); |
---|
184 | } |
---|
185 | } |
---|
186 | |
---|
187 | partnumber = other.partnumber; |
---|
188 | parttotal = other.parttotal; |
---|
189 | syndicatedepisodenumber = other.syndicatedepisodenumber; |
---|
190 | subtitleType = other.subtitleType; |
---|
191 | audioProps = other.audioProps; |
---|
192 | videoProps = other.videoProps; |
---|
193 | stars = other.stars; |
---|
194 | categoryType = other.categoryType; |
---|
195 | seriesId = other.seriesId; |
---|
196 | programId = other.programId; |
---|
197 | inetref = other.inetref; |
---|
198 | previouslyshown = other.previouslyshown; |
---|
199 | ratings = other.ratings; |
---|
200 | listingsource = other.listingsource; |
---|
201 | season = other.season; |
---|
202 | episode = other.episode; |
---|
203 | totalepisodes = other.totalepisodes; |
---|
204 | genres = other.genres; |
---|
205 | |
---|
206 | Squeeze(); |
---|
207 | |
---|
208 | return *this; |
---|
209 | } |
---|
210 | |
---|
211 | void DBEvent::Squeeze(void) |
---|
212 | { |
---|
213 | title.squeeze(); |
---|
214 | subtitle.squeeze(); |
---|
215 | description.squeeze(); |
---|
216 | category.squeeze(); |
---|
217 | syndicatedepisodenumber.squeeze(); |
---|
218 | seriesId.squeeze(); |
---|
219 | programId.squeeze(); |
---|
220 | inetref.squeeze(); |
---|
221 | } |
---|
222 | |
---|
223 | void DBEvent::AddPerson(DBPerson::Role role, const QString &name) |
---|
224 | { |
---|
225 | if (!credits) |
---|
226 | credits = new DBCredits; |
---|
227 | |
---|
228 | credits->push_back(DBPerson(role, name.simplified())); |
---|
229 | } |
---|
230 | |
---|
231 | void DBEvent::AddPerson(const QString &role, const QString &name) |
---|
232 | { |
---|
233 | if (!credits) |
---|
234 | credits = new DBCredits; |
---|
235 | |
---|
236 | credits->push_back(DBPerson(role, name.simplified())); |
---|
237 | } |
---|
238 | |
---|
239 | bool DBEvent::HasTimeConflict(const DBEvent &o) const |
---|
240 | { |
---|
241 | return ((starttime <= o.starttime && o.starttime < endtime) || |
---|
242 | (o.endtime <= endtime && starttime < o.endtime)); |
---|
243 | } |
---|
244 | |
---|
245 | // Processing new EIT entry starts here |
---|
246 | uint DBEvent::UpdateDB( |
---|
247 | MSqlQuery &query, uint chanid, int match_threshold) const |
---|
248 | { |
---|
249 | // List the program that we are going to add |
---|
250 | LOG(VB_EIT, LOG_DEBUG, |
---|
251 | QString("EIT: new program: %1 %2 '%3' chanid %4") |
---|
252 | .arg(starttime.toString(Qt::ISODate)) |
---|
253 | .arg(endtime.toString(Qt::ISODate)) |
---|
254 | .arg(title.left(35)) |
---|
255 | .arg(chanid)); |
---|
256 | |
---|
257 | // Do not insert or update when the program is in the past |
---|
258 | QDateTime now = QDateTime::currentDateTimeUtc(); |
---|
259 | if (endtime < now) |
---|
260 | { |
---|
261 | LOG(VB_EIT, LOG_DEBUG, |
---|
262 | QString("EIT: skip '%1' endtime is in the past") |
---|
263 | .arg(title.left(35))); |
---|
264 | return 0; |
---|
265 | } |
---|
266 | |
---|
267 | // Get all programs already in the database that overlap |
---|
268 | // with our new program. |
---|
269 | vector<DBEvent> programs; |
---|
270 | uint count = GetOverlappingPrograms(query, chanid, programs); |
---|
271 | int match = INT_MIN; |
---|
272 | int i = -1; |
---|
273 | |
---|
274 | // If there are no programs already in the database that overlap |
---|
275 | // with our new program then we can simply insert it in the database. |
---|
276 | if (!count) |
---|
277 | return InsertDB(query, chanid); |
---|
278 | |
---|
279 | // List all overlapping programs with start- and endtime. |
---|
280 | for (uint j=0; j<count; j++) |
---|
281 | { |
---|
282 | LOG(VB_EIT, LOG_DEBUG, |
---|
283 | QString("EIT: overlap[%1] : %2 %3 '%4'") |
---|
284 | .arg(j) |
---|
285 | .arg(programs[j].starttime.toString(Qt::ISODate)) |
---|
286 | .arg(programs[j].endtime.toString(Qt::ISODate)) |
---|
287 | .arg(programs[j].title.left(35))); |
---|
288 | } |
---|
289 | |
---|
290 | // Determine which of the overlapping programs is a match with |
---|
291 | // our new program; if we have a match then our new program is considered |
---|
292 | // to be an update of the matching program. |
---|
293 | // The 2nd parameter "i" is the index of the best matching program. |
---|
294 | match = GetMatch(programs, i); |
---|
295 | |
---|
296 | // Update an existing program or insert a new program. |
---|
297 | if (match >= match_threshold) |
---|
298 | { |
---|
299 | // We have a good match; update program[i] in the database |
---|
300 | // with the new program data and move the overlapping programs |
---|
301 | // out of the way. |
---|
302 | LOG(VB_EIT, LOG_DEBUG, |
---|
303 | QString("EIT: accept match[%1]: %2 '%3' vs. '%4'") |
---|
304 | .arg(i).arg(match).arg(title.left(35)) |
---|
305 | .arg(programs[i].title.left(35))); |
---|
306 | return UpdateDB(query, chanid, programs, i); |
---|
307 | } |
---|
308 | else |
---|
309 | { |
---|
310 | // If we are here then either we have a match but the match is |
---|
311 | // not good enough (the "i >= 0" case) or we did not find |
---|
312 | // a match at all. |
---|
313 | if (i >= 0) |
---|
314 | { |
---|
315 | LOG(VB_EIT, LOG_DEBUG, |
---|
316 | QString("EIT: reject match[%1]: %2 '%3' vs. '%4'") |
---|
317 | .arg(i).arg(match).arg(title.left(35)) |
---|
318 | .arg(programs[i].title.left(35))); |
---|
319 | } |
---|
320 | |
---|
321 | // Move the overlapping programs out of the way and |
---|
322 | // insert the new program. |
---|
323 | return UpdateDB(query, chanid, programs, -1); |
---|
324 | } |
---|
325 | } |
---|
326 | |
---|
327 | // Get all programs in the database that overlap with our new program. |
---|
328 | // We check for three ways in which we can have an overlap: |
---|
329 | // (1) Start of old program is inside our new program: |
---|
330 | // old program starts at or after our program AND |
---|
331 | // old program starts before end of our program; |
---|
332 | // e.g. new program s-------------e |
---|
333 | // old program s-------------e |
---|
334 | // or old program s-----e |
---|
335 | // This is the STIME1/ETIME1 comparison. |
---|
336 | // (2) End of old program is inside our new program: |
---|
337 | // old program ends after our program starts AND |
---|
338 | // old program ends before end of our program |
---|
339 | // e.g. new program s-------------e |
---|
340 | // old program s-------------e |
---|
341 | // or old program s-----e |
---|
342 | // This is the STIME2/ETIME2 comparison. |
---|
343 | // (3) We can have a new program is "inside" the old program: |
---|
344 | // old program starts before our program AND |
---|
345 | // old program ends after end of our program |
---|
346 | // e.g. new program s---------e |
---|
347 | // old program s-----------------e |
---|
348 | // This is the STIME3/ETIME3 comparison. |
---|
349 | // |
---|
350 | uint DBEvent::GetOverlappingPrograms( |
---|
351 | MSqlQuery &query, uint chanid, vector<DBEvent> &programs) const |
---|
352 | { |
---|
353 | uint count = 0; |
---|
354 | query.prepare( |
---|
355 | "SELECT title, subtitle, description, " |
---|
356 | " category, category_type, " |
---|
357 | " starttime, endtime, " |
---|
358 | " subtitletypes+0,audioprop+0, videoprop+0, " |
---|
359 | " seriesid, programid, " |
---|
360 | " partnumber, parttotal, " |
---|
361 | " syndicatedepisodenumber, " |
---|
362 | " airdate, originalairdate, " |
---|
363 | " previouslyshown,listingsource, " |
---|
364 | " stars+0, " |
---|
365 | " season, episode, totalepisodes, " |
---|
366 | " inetref " |
---|
367 | "FROM program " |
---|
368 | "WHERE chanid = :CHANID AND " |
---|
369 | " manualid = 0 AND " |
---|
370 | " ( ( starttime >= :STIME1 AND starttime < :ETIME1 ) OR " |
---|
371 | " ( endtime > :STIME2 AND endtime <= :ETIME2 ) OR " |
---|
372 | " ( starttime < :STIME3 AND endtime > :ETIME3 ) )"); |
---|
373 | query.bindValue(":CHANID", chanid); |
---|
374 | query.bindValue(":STIME1", starttime); |
---|
375 | query.bindValue(":ETIME1", endtime); |
---|
376 | query.bindValue(":STIME2", starttime); |
---|
377 | query.bindValue(":ETIME2", endtime); |
---|
378 | query.bindValue(":STIME3", starttime); |
---|
379 | query.bindValue(":ETIME3", endtime); |
---|
380 | |
---|
381 | if (!query.exec()) |
---|
382 | { |
---|
383 | MythDB::DBError("GetOverlappingPrograms 1", query); |
---|
384 | return 0; |
---|
385 | } |
---|
386 | |
---|
387 | while (query.next()) |
---|
388 | { |
---|
389 | ProgramInfo::CategoryType category_type = |
---|
390 | string_to_myth_category_type(query.value(4).toString()); |
---|
391 | |
---|
392 | DBEvent prog( |
---|
393 | query.value(0).toString(), |
---|
394 | query.value(1).toString(), |
---|
395 | query.value(2).toString(), |
---|
396 | query.value(3).toString(), |
---|
397 | category_type, |
---|
398 | MythDate::as_utc(query.value(5).toDateTime()), |
---|
399 | MythDate::as_utc(query.value(6).toDateTime()), |
---|
400 | query.value(7).toUInt(), |
---|
401 | query.value(8).toUInt(), |
---|
402 | query.value(9).toUInt(), |
---|
403 | query.value(19).toDouble(), |
---|
404 | query.value(10).toString(), |
---|
405 | query.value(11).toString(), |
---|
406 | query.value(18).toUInt(), |
---|
407 | query.value(20).toUInt(), // Season |
---|
408 | query.value(21).toUInt(), // Episode |
---|
409 | query.value(22).toUInt()); // Total Episodes |
---|
410 | |
---|
411 | prog.inetref = query.value(23).toString(); |
---|
412 | prog.partnumber = query.value(12).toUInt(); |
---|
413 | prog.parttotal = query.value(13).toUInt(); |
---|
414 | prog.syndicatedepisodenumber = query.value(14).toString(); |
---|
415 | prog.airdate = query.value(15).toUInt(); |
---|
416 | prog.originalairdate = query.value(16).toDate(); |
---|
417 | prog.previouslyshown = query.value(17).toBool(); |
---|
418 | |
---|
419 | programs.push_back(prog); |
---|
420 | count++; |
---|
421 | } |
---|
422 | |
---|
423 | return count; |
---|
424 | } |
---|
425 | |
---|
426 | |
---|
427 | static int score_words(const QStringList &al, const QStringList &bl) |
---|
428 | { |
---|
429 | QStringList::const_iterator ait = al.begin(); |
---|
430 | QStringList::const_iterator bit = bl.begin(); |
---|
431 | int score = 0; |
---|
432 | for (; (ait != al.end()) && (bit != bl.end()); ++ait) |
---|
433 | { |
---|
434 | QStringList::const_iterator bit2 = bit; |
---|
435 | int dist = 0; |
---|
436 | int bscore = 0; |
---|
437 | for (; bit2 != bl.end(); ++bit2) |
---|
438 | { |
---|
439 | if (*ait == *bit) |
---|
440 | { |
---|
441 | bscore = max(1000, 2000 - (dist * 500)); |
---|
442 | // lower score for short words |
---|
443 | if (ait->length() < 5) |
---|
444 | bscore /= 5 - ait->length(); |
---|
445 | break; |
---|
446 | } |
---|
447 | dist++; |
---|
448 | } |
---|
449 | if (bscore && dist < 3) |
---|
450 | { |
---|
451 | for (int i = 0; (i < dist) && bit != bl.end(); i++) |
---|
452 | ++bit; |
---|
453 | } |
---|
454 | score += bscore; |
---|
455 | } |
---|
456 | |
---|
457 | return score / al.size(); |
---|
458 | } |
---|
459 | |
---|
460 | static int score_match(const QString &a, const QString &b) |
---|
461 | { |
---|
462 | if (a.isEmpty() || b.isEmpty()) |
---|
463 | return 0; |
---|
464 | else if (a == b) |
---|
465 | return 1000; |
---|
466 | |
---|
467 | QString A = a.simplified().toUpper(); |
---|
468 | QString B = b.simplified().toUpper(); |
---|
469 | if (A == B) |
---|
470 | return 1000; |
---|
471 | |
---|
472 | QStringList al, bl; |
---|
473 | al = A.split(" ", QString::SkipEmptyParts); |
---|
474 | if (al.isEmpty()) |
---|
475 | return 0; |
---|
476 | |
---|
477 | bl = B.split(" ", QString::SkipEmptyParts); |
---|
478 | if (bl.isEmpty()) |
---|
479 | return 0; |
---|
480 | |
---|
481 | // score words symmetrically |
---|
482 | int score = (score_words(al, bl) + score_words(bl, al)) / 2; |
---|
483 | |
---|
484 | return min(900, score); |
---|
485 | } |
---|
486 | |
---|
487 | int DBEvent::GetMatch(const vector<DBEvent> &programs, int &bestmatch) const |
---|
488 | { |
---|
489 | bestmatch = -1; |
---|
490 | int match_val = INT_MIN; |
---|
491 | int overlap = 0; |
---|
492 | int duration = starttime.secsTo(endtime); |
---|
493 | |
---|
494 | for (uint i = 0; i < programs.size(); i++) |
---|
495 | { |
---|
496 | int mv = 0; |
---|
497 | int duration_loop = programs[i].starttime.secsTo(programs[i].endtime); |
---|
498 | |
---|
499 | mv -= qAbs(starttime.secsTo(programs[i].starttime)); |
---|
500 | mv -= qAbs(endtime.secsTo(programs[i].endtime)); |
---|
501 | mv -= qAbs(duration - duration_loop); |
---|
502 | mv += score_match(title, programs[i].title) * 10; |
---|
503 | mv += score_match(subtitle, programs[i].subtitle); |
---|
504 | mv += score_match(description, programs[i].description); |
---|
505 | |
---|
506 | /* determine overlap of both programs |
---|
507 | * we don't know which one starts first */ |
---|
508 | if (starttime < programs[i].starttime) |
---|
509 | overlap = programs[i].starttime.secsTo(endtime); |
---|
510 | else if (starttime > programs[i].starttime) |
---|
511 | overlap = starttime.secsTo(programs[i].endtime); |
---|
512 | else |
---|
513 | { |
---|
514 | if (endtime <= programs[i].endtime) |
---|
515 | overlap = starttime.secsTo(endtime); |
---|
516 | else |
---|
517 | overlap = starttime.secsTo(programs[i].endtime); |
---|
518 | } |
---|
519 | |
---|
520 | /* scale the score depending on the overlap length |
---|
521 | * full score is preserved if the overlap is at least 1/2 of the length |
---|
522 | * of the shorter program */ |
---|
523 | if (overlap > 0) |
---|
524 | { |
---|
525 | /* crappy providers apparently have events without duration |
---|
526 | * ensure that the minimal duration is 2 second to avoid |
---|
527 | * multiplying and more importantly dividing by zero */ |
---|
528 | int min_dur = max(2, min(duration, duration_loop)); |
---|
529 | overlap = min(overlap, min_dur/2); |
---|
530 | mv *= overlap * 2; |
---|
531 | mv /= min_dur; |
---|
532 | } |
---|
533 | else |
---|
534 | { |
---|
535 | LOG(VB_GENERAL, LOG_ERR, |
---|
536 | QString("Unexpected result: shows don't " |
---|
537 | "overlap\n\t%1: %2 - %3\n\t%4: %5 - %6") |
---|
538 | .arg(title.left(35)) |
---|
539 | .arg(starttime.toString(Qt::ISODate)) |
---|
540 | .arg(endtime.toString(Qt::ISODate)) |
---|
541 | .arg(programs[i].title.left(35), 35) |
---|
542 | .arg(programs[i].starttime.toString(Qt::ISODate)) |
---|
543 | .arg(programs[i].endtime.toString(Qt::ISODate)) |
---|
544 | ); |
---|
545 | } |
---|
546 | |
---|
547 | if (mv > match_val) |
---|
548 | { |
---|
549 | LOG(VB_EIT, LOG_DEBUG, |
---|
550 | QString("GM : '%1' new best match '%2' with score %3") |
---|
551 | .arg(title.left(35)) |
---|
552 | .arg(programs[i].title.left(35)).arg(mv)); |
---|
553 | bestmatch = i; |
---|
554 | match_val = mv; |
---|
555 | } |
---|
556 | } |
---|
557 | |
---|
558 | return match_val; |
---|
559 | } |
---|
560 | |
---|
561 | uint DBEvent::UpdateDB( |
---|
562 | MSqlQuery &q, uint chanid, const vector<DBEvent> &p, int match) const |
---|
563 | { |
---|
564 | // Adjust/delete overlaps; |
---|
565 | bool ok = true; |
---|
566 | for (uint i = 0; i < p.size(); i++) |
---|
567 | { |
---|
568 | if (i != (uint)match) |
---|
569 | ok &= MoveOutOfTheWayDB(q, chanid, p[i]); |
---|
570 | } |
---|
571 | |
---|
572 | // If we failed to move programs out of the way, don't insert new ones.. |
---|
573 | if (!ok) |
---|
574 | { |
---|
575 | LOG(VB_EIT, LOG_DEBUG, |
---|
576 | QString("EIT: cannot insert '%1' MoveOutOfTheWayDB failed") |
---|
577 | .arg(title.left(35))); |
---|
578 | return 0; |
---|
579 | } |
---|
580 | |
---|
581 | // No match, insert current item |
---|
582 | if ((match < 0) || ((uint)match >= p.size())) |
---|
583 | { |
---|
584 | LOG(VB_EIT, LOG_DEBUG, |
---|
585 | QString("EIT: insert '%1'") |
---|
586 | .arg(title.left(35))); |
---|
587 | return InsertDB(q, chanid); |
---|
588 | } |
---|
589 | |
---|
590 | // Changing a starttime of a program that is being recorded can |
---|
591 | // start another recording of the same program. |
---|
592 | // Therefore we skip updates that change a starttime in the past |
---|
593 | // unless the endtime is later. |
---|
594 | if (starttime != p[match].starttime) |
---|
595 | { |
---|
596 | QDateTime now = QDateTime::currentDateTimeUtc(); |
---|
597 | if (starttime < now && endtime <= p[match].endtime) |
---|
598 | { |
---|
599 | LOG(VB_EIT, LOG_DEBUG, |
---|
600 | QString("EIT: skip '%1' starttime is in the past") |
---|
601 | .arg(title.left(35))); |
---|
602 | return 0; |
---|
603 | } |
---|
604 | } |
---|
605 | |
---|
606 | // Update matched item with current data |
---|
607 | LOG(VB_EIT, LOG_DEBUG, |
---|
608 | QString("EIT: update '%1' with '%2'") |
---|
609 | .arg(p[match].title.left(35)) |
---|
610 | .arg(title.left(35))); |
---|
611 | return UpdateDB(q, chanid, p[match]); |
---|
612 | } |
---|
613 | |
---|
614 | // Update matched item with current data. |
---|
615 | // |
---|
616 | uint DBEvent::UpdateDB( |
---|
617 | MSqlQuery &query, uint chanid, const DBEvent &match) const |
---|
618 | { |
---|
619 | QString ltitle = title; |
---|
620 | QString lsubtitle = subtitle; |
---|
621 | QString ldesc = description; |
---|
622 | QString lcategory = category; |
---|
623 | uint16_t lairdate = airdate; |
---|
624 | QString lprogramId = programId; |
---|
625 | QString lseriesId = seriesId; |
---|
626 | QString linetref = inetref; |
---|
627 | QDate loriginalairdate = originalairdate; |
---|
628 | |
---|
629 | ProgramInfo::CategoryType tmp = categoryType; |
---|
630 | QString lcattype = myth_category_type_to_string(tmp); |
---|
631 | |
---|
632 | unsigned char lsubtype = subtitleType; |
---|
633 | unsigned char laudio = audioProps; |
---|
634 | unsigned char lvideo = videoProps; |
---|
635 | |
---|
636 | uint lseason = season; |
---|
637 | uint lepisode = episode; |
---|
638 | uint lepisodeTotal = totalepisodes; |
---|
639 | |
---|
640 | uint lpartnumber = partnumber; |
---|
641 | uint lparttotal = parttotal; |
---|
642 | |
---|
643 | bool lpreviouslyshown = previouslyshown; |
---|
644 | |
---|
645 | uint32_t llistingsource = listingsource; |
---|
646 | |
---|
647 | QString lsyndicatedepisodenumber = syndicatedepisodenumber; |
---|
648 | |
---|
649 | query.prepare( |
---|
650 | "UPDATE program " |
---|
651 | "SET title = :TITLE, subtitle = :SUBTITLE, " |
---|
652 | " description = :DESC, " |
---|
653 | " category = :CATEGORY, category_type = :CATTYPE, " |
---|
654 | " starttime = :STARTTIME, endtime = :ENDTIME, " |
---|
655 | " closecaptioned = :CC, subtitled = :HASSUBTITLES, " |
---|
656 | " stereo = :STEREO, hdtv = :HDTV, " |
---|
657 | " subtitletypes = :SUBTYPE, " |
---|
658 | " audioprop = :AUDIOPROP, videoprop = :VIDEOPROP, " |
---|
659 | " season = :SEASON, " |
---|
660 | " episode = :EPISODE, totalepisodes = :TOTALEPS, " |
---|
661 | " partnumber = :PARTNO, parttotal = :PARTTOTAL, " |
---|
662 | " syndicatedepisodenumber = :SYNDICATENO, " |
---|
663 | " airdate = :AIRDATE, originalairdate=:ORIGAIRDATE, " |
---|
664 | " listingsource = :LSOURCE, " |
---|
665 | " seriesid = :SERIESID, programid = :PROGRAMID, " |
---|
666 | " previouslyshown = :PREVSHOWN, inetref = :INETREF " |
---|
667 | "WHERE chanid = :CHANID AND " |
---|
668 | " starttime = :OLDSTART "); |
---|
669 | |
---|
670 | query.bindValue(":CHANID", chanid); |
---|
671 | query.bindValue(":OLDSTART", match.starttime); |
---|
672 | query.bindValue(":TITLE", denullify(ltitle)); |
---|
673 | query.bindValue(":SUBTITLE", denullify(lsubtitle)); |
---|
674 | query.bindValue(":DESC", denullify(ldesc)); |
---|
675 | query.bindValue(":CATEGORY", denullify(lcategory)); |
---|
676 | query.bindValue(":CATTYPE", lcattype); |
---|
677 | query.bindValue(":STARTTIME", starttime); |
---|
678 | query.bindValue(":ENDTIME", endtime); |
---|
679 | query.bindValue(":CC", (lsubtype & SUB_HARDHEAR) ? true : false); |
---|
680 | query.bindValue(":HASSUBTITLES",(lsubtype & SUB_NORMAL) ? true : false); |
---|
681 | query.bindValue(":STEREO", (laudio & AUD_STEREO) ? true : false); |
---|
682 | query.bindValue(":HDTV", (lvideo & VID_HDTV) ? true : false); |
---|
683 | query.bindValue(":SUBTYPE", lsubtype); |
---|
684 | query.bindValue(":AUDIOPROP", laudio); |
---|
685 | query.bindValue(":VIDEOPROP", lvideo); |
---|
686 | query.bindValue(":SEASON", lseason); |
---|
687 | query.bindValue(":EPISODE", lepisode); |
---|
688 | query.bindValue(":TOTALEPS", lepisodeTotal); |
---|
689 | query.bindValue(":PARTNO", lpartnumber); |
---|
690 | query.bindValue(":PARTTOTAL", lparttotal); |
---|
691 | query.bindValue(":SYNDICATENO", denullify(lsyndicatedepisodenumber)); |
---|
692 | query.bindValue(":AIRDATE", lairdate ? QString::number(lairdate) : "0000"); |
---|
693 | query.bindValue(":ORIGAIRDATE", loriginalairdate); |
---|
694 | query.bindValue(":LSOURCE", llistingsource); |
---|
695 | query.bindValue(":SERIESID", denullify(lseriesId)); |
---|
696 | query.bindValue(":PROGRAMID", denullify(lprogramId)); |
---|
697 | query.bindValue(":PREVSHOWN", lpreviouslyshown); |
---|
698 | query.bindValue(":INETREF", linetref); |
---|
699 | |
---|
700 | if (!query.exec()) |
---|
701 | { |
---|
702 | MythDB::DBError("InsertDB", query); |
---|
703 | return 0; |
---|
704 | } |
---|
705 | |
---|
706 | if (credits) |
---|
707 | { |
---|
708 | for (uint i = 0; i < credits->size(); i++) |
---|
709 | (*credits)[i].InsertDB(query, chanid, starttime); |
---|
710 | } |
---|
711 | |
---|
712 | QList<EventRating>::const_iterator j = ratings.begin(); |
---|
713 | for (; j != ratings.end(); ++j) |
---|
714 | { |
---|
715 | query.prepare( |
---|
716 | "INSERT IGNORE INTO programrating " |
---|
717 | " ( chanid, starttime, system, rating) " |
---|
718 | "VALUES (:CHANID, :START, :SYS, :RATING)"); |
---|
719 | query.bindValue(":CHANID", chanid); |
---|
720 | query.bindValue(":START", starttime); |
---|
721 | query.bindValue(":SYS", (*j).system); |
---|
722 | query.bindValue(":RATING", (*j).rating); |
---|
723 | |
---|
724 | if (!query.exec()) |
---|
725 | MythDB::DBError("programrating insert", query); |
---|
726 | } |
---|
727 | |
---|
728 | add_genres(query, genres, chanid, starttime); |
---|
729 | |
---|
730 | return 1; |
---|
731 | } |
---|
732 | |
---|
733 | static bool delete_program(MSqlQuery &query, uint chanid, const QDateTime &st) |
---|
734 | { |
---|
735 | query.prepare( |
---|
736 | "DELETE from program " |
---|
737 | "WHERE chanid = :CHANID AND " |
---|
738 | " starttime = :STARTTIME"); |
---|
739 | |
---|
740 | query.bindValue(":CHANID", chanid); |
---|
741 | query.bindValue(":STARTTIME", st); |
---|
742 | |
---|
743 | if (!query.exec()) |
---|
744 | { |
---|
745 | MythDB::DBError("delete_program", query); |
---|
746 | return false; |
---|
747 | } |
---|
748 | |
---|
749 | query.prepare( |
---|
750 | "DELETE from credits " |
---|
751 | "WHERE chanid = :CHANID AND " |
---|
752 | " starttime = :STARTTIME"); |
---|
753 | |
---|
754 | query.bindValue(":CHANID", chanid); |
---|
755 | query.bindValue(":STARTTIME", st); |
---|
756 | |
---|
757 | if (!query.exec()) |
---|
758 | { |
---|
759 | MythDB::DBError("delete_credits", query); |
---|
760 | return false; |
---|
761 | } |
---|
762 | |
---|
763 | query.prepare( |
---|
764 | "DELETE from programrating " |
---|
765 | "WHERE chanid = :CHANID AND " |
---|
766 | " starttime = :STARTTIME"); |
---|
767 | |
---|
768 | query.bindValue(":CHANID", chanid); |
---|
769 | query.bindValue(":STARTTIME", st); |
---|
770 | |
---|
771 | if (!query.exec()) |
---|
772 | { |
---|
773 | MythDB::DBError("delete_rating", query); |
---|
774 | return false; |
---|
775 | } |
---|
776 | |
---|
777 | query.prepare( |
---|
778 | "DELETE from programgenres " |
---|
779 | "WHERE chanid = :CHANID AND " |
---|
780 | " starttime = :STARTTIME"); |
---|
781 | |
---|
782 | query.bindValue(":CHANID", chanid); |
---|
783 | query.bindValue(":STARTTIME", st); |
---|
784 | |
---|
785 | if (!query.exec()) |
---|
786 | { |
---|
787 | MythDB::DBError("delete_genres", query); |
---|
788 | return false; |
---|
789 | } |
---|
790 | |
---|
791 | return true; |
---|
792 | } |
---|
793 | |
---|
794 | static bool program_exists(MSqlQuery &query, uint chanid, const QDateTime &st) |
---|
795 | { |
---|
796 | query.prepare( |
---|
797 | "SELECT title FROM program " |
---|
798 | "WHERE chanid = :CHANID AND " |
---|
799 | " starttime = :OLDSTART"); |
---|
800 | query.bindValue(":CHANID", chanid); |
---|
801 | query.bindValue(":OLDSTART", st); |
---|
802 | if (!query.exec()) |
---|
803 | { |
---|
804 | MythDB::DBError("program_exists", query); |
---|
805 | } |
---|
806 | if (query.next()) |
---|
807 | { |
---|
808 | return true; |
---|
809 | } |
---|
810 | return false; |
---|
811 | } |
---|
812 | |
---|
813 | static bool change_program(MSqlQuery &query, uint chanid, const QDateTime &st, |
---|
814 | const QDateTime &new_st, const QDateTime &new_end) |
---|
815 | { |
---|
816 | query.prepare( |
---|
817 | "UPDATE program " |
---|
818 | "SET starttime = :NEWSTART, " |
---|
819 | " endtime = :NEWEND " |
---|
820 | "WHERE chanid = :CHANID AND " |
---|
821 | " starttime = :OLDSTART"); |
---|
822 | |
---|
823 | query.bindValue(":CHANID", chanid); |
---|
824 | query.bindValue(":OLDSTART", st); |
---|
825 | query.bindValue(":NEWSTART", new_st); |
---|
826 | query.bindValue(":NEWEND", new_end); |
---|
827 | |
---|
828 | if (!query.exec()) |
---|
829 | { |
---|
830 | MythDB::DBError("change_program", query); |
---|
831 | return false; |
---|
832 | } |
---|
833 | |
---|
834 | query.prepare( |
---|
835 | "UPDATE credits " |
---|
836 | "SET starttime = :NEWSTART " |
---|
837 | "WHERE chanid = :CHANID AND " |
---|
838 | " starttime = :OLDSTART"); |
---|
839 | |
---|
840 | query.bindValue(":CHANID", chanid); |
---|
841 | query.bindValue(":OLDSTART", st); |
---|
842 | query.bindValue(":NEWSTART", new_st); |
---|
843 | |
---|
844 | if (!query.exec()) |
---|
845 | { |
---|
846 | MythDB::DBError("change_credits", query); |
---|
847 | return false; |
---|
848 | } |
---|
849 | |
---|
850 | query.prepare( |
---|
851 | "UPDATE programrating " |
---|
852 | "SET starttime = :NEWSTART " |
---|
853 | "WHERE chanid = :CHANID AND " |
---|
854 | " starttime = :OLDSTART"); |
---|
855 | |
---|
856 | query.bindValue(":CHANID", chanid); |
---|
857 | query.bindValue(":OLDSTART", st); |
---|
858 | query.bindValue(":NEWSTART", new_st); |
---|
859 | |
---|
860 | if (!query.exec()) |
---|
861 | { |
---|
862 | MythDB::DBError("change_rating", query); |
---|
863 | return false; |
---|
864 | } |
---|
865 | |
---|
866 | query.prepare( |
---|
867 | "UPDATE programgenres " |
---|
868 | "SET starttime = :NEWSTART " |
---|
869 | "WHERE chanid = :CHANID AND " |
---|
870 | " starttime = :OLDSTART"); |
---|
871 | |
---|
872 | query.bindValue(":CHANID", chanid); |
---|
873 | query.bindValue(":OLDSTART", st); |
---|
874 | query.bindValue(":NEWSTART", new_st); |
---|
875 | |
---|
876 | if (!query.exec()) |
---|
877 | { |
---|
878 | MythDB::DBError("change_genres", query); |
---|
879 | return false; |
---|
880 | } |
---|
881 | |
---|
882 | return true; |
---|
883 | } |
---|
884 | |
---|
885 | // Move the program "prog" (3rd parameter) out of the way |
---|
886 | // because it overlaps with our new program. |
---|
887 | bool DBEvent::MoveOutOfTheWayDB( |
---|
888 | MSqlQuery &query, uint chanid, const DBEvent &prog) const |
---|
889 | { |
---|
890 | if (prog.starttime >= starttime && prog.endtime <= endtime) |
---|
891 | { |
---|
892 | // Old program completely inside our new program. |
---|
893 | // Delete the old program completely. |
---|
894 | LOG(VB_EIT, LOG_DEBUG, |
---|
895 | QString("EIT: delete '%1' %2 - %3") |
---|
896 | .arg(prog.title.left(35)) |
---|
897 | .arg(prog.starttime.toString(Qt::ISODate)) |
---|
898 | .arg(prog.endtime.toString(Qt::ISODate))); |
---|
899 | return delete_program(query, chanid, prog.starttime); |
---|
900 | } |
---|
901 | else if (prog.starttime < starttime && prog.endtime > starttime) |
---|
902 | { |
---|
903 | // Old program starts before, but ends during or after our new program. |
---|
904 | // Adjust the end time of the old program to the start time |
---|
905 | // of our new program. |
---|
906 | // This will leave a hole after our new program when the end time of |
---|
907 | // the old program was after the end time of the new program!! |
---|
908 | LOG(VB_EIT, LOG_DEBUG, |
---|
909 | QString("EIT: change '%1' endtime to %2") |
---|
910 | .arg(prog.title.left(35)) |
---|
911 | .arg(starttime.toString(Qt::ISODate))); |
---|
912 | return change_program(query, chanid, prog.starttime, |
---|
913 | prog.starttime, // Keep the start time |
---|
914 | starttime); // New end time is our start time |
---|
915 | } |
---|
916 | else if (prog.starttime < endtime && prog.endtime > endtime) |
---|
917 | { |
---|
918 | // Old program starts during, but ends after our new program. |
---|
919 | // Adjust the starttime of the old program to the end time |
---|
920 | // of our new program. |
---|
921 | // If there is already a program starting just when our |
---|
922 | // new program ends we cannot move the old program |
---|
923 | // so then we have to delete the old program. |
---|
924 | if (program_exists(query, chanid, endtime)) |
---|
925 | { |
---|
926 | LOG(VB_EIT, LOG_DEBUG, |
---|
927 | QString("EIT: delete '%1' %2 - %3") |
---|
928 | .arg(prog.title.left(35)) |
---|
929 | .arg(prog.starttime.toString(Qt::ISODate)) |
---|
930 | .arg(prog.endtime.toString(Qt::ISODate))); |
---|
931 | return delete_program(query, chanid, prog.starttime); |
---|
932 | } |
---|
933 | LOG(VB_EIT, LOG_DEBUG, |
---|
934 | QString("EIT: change '%1' starttime to %2") |
---|
935 | .arg(prog.title.left(35)) |
---|
936 | .arg(endtime.toString(Qt::ISODate))); |
---|
937 | |
---|
938 | return change_program(query, chanid, prog.starttime, |
---|
939 | endtime, // New start time is our endtime |
---|
940 | prog.endtime); // Keep the end time |
---|
941 | } |
---|
942 | // must be non-conflicting... |
---|
943 | return true; |
---|
944 | } |
---|
945 | |
---|
946 | uint DBEvent::InsertDB(MSqlQuery &query, uint chanid) const |
---|
947 | { |
---|
948 | query.prepare( |
---|
949 | "REPLACE INTO program (" |
---|
950 | " chanid, title, subtitle, description, " |
---|
951 | " category, category_type, " |
---|
952 | " starttime, endtime, " |
---|
953 | " closecaptioned, stereo, hdtv, subtitled, " |
---|
954 | " subtitletypes, audioprop, videoprop, " |
---|
955 | " stars, partnumber, parttotal, " |
---|
956 | " syndicatedepisodenumber, " |
---|
957 | " airdate, originalairdate,listingsource, " |
---|
958 | " seriesid, programid, previouslyshown, " |
---|
959 | " season, episode, totalepisodes, " |
---|
960 | " inetref ) " |
---|
961 | "VALUES (" |
---|
962 | " :CHANID, :TITLE, :SUBTITLE, :DESCRIPTION, " |
---|
963 | " :CATEGORY, :CATTYPE, " |
---|
964 | " :STARTTIME, :ENDTIME, " |
---|
965 | " :CC, :STEREO, :HDTV, :HASSUBTITLES, " |
---|
966 | " :SUBTYPES, :AUDIOPROP, :VIDEOPROP, " |
---|
967 | " :STARS, :PARTNUMBER, :PARTTOTAL, " |
---|
968 | " :SYNDICATENO, " |
---|
969 | " :AIRDATE, :ORIGAIRDATE, :LSOURCE, " |
---|
970 | " :SERIESID, :PROGRAMID, :PREVSHOWN, " |
---|
971 | " :SEASON, :EPISODE, :TOTALEPISODES, " |
---|
972 | " :INETREF ) "); |
---|
973 | |
---|
974 | QString cattype = myth_category_type_to_string(categoryType); |
---|
975 | QString empty(""); |
---|
976 | query.bindValue(":CHANID", chanid); |
---|
977 | query.bindValue(":TITLE", denullify(title)); |
---|
978 | query.bindValue(":SUBTITLE", denullify(subtitle)); |
---|
979 | query.bindValue(":DESCRIPTION", denullify(description)); |
---|
980 | query.bindValue(":CATEGORY", denullify(category)); |
---|
981 | query.bindValue(":CATTYPE", cattype); |
---|
982 | query.bindValue(":STARTTIME", starttime); |
---|
983 | query.bindValue(":ENDTIME", endtime); |
---|
984 | query.bindValue(":CC", (subtitleType & SUB_HARDHEAR) ? true : false); |
---|
985 | query.bindValue(":STEREO", (audioProps & AUD_STEREO) ? true : false); |
---|
986 | query.bindValue(":HDTV", (videoProps & VID_HDTV) ? true : false); |
---|
987 | query.bindValue(":HASSUBTITLES",(subtitleType & SUB_NORMAL) ? true : false); |
---|
988 | query.bindValue(":SUBTYPES", subtitleType); |
---|
989 | query.bindValue(":AUDIOPROP", audioProps); |
---|
990 | query.bindValue(":VIDEOPROP", videoProps); |
---|
991 | query.bindValue(":STARS", stars); |
---|
992 | query.bindValue(":PARTNUMBER", partnumber); |
---|
993 | query.bindValue(":PARTTOTAL", parttotal); |
---|
994 | query.bindValue(":SYNDICATENO", denullify(syndicatedepisodenumber)); |
---|
995 | query.bindValue(":AIRDATE", airdate ? QString::number(airdate) : "0000"); |
---|
996 | query.bindValue(":ORIGAIRDATE", originalairdate); |
---|
997 | query.bindValue(":LSOURCE", listingsource); |
---|
998 | query.bindValue(":SERIESID", denullify(seriesId)); |
---|
999 | query.bindValue(":PROGRAMID", denullify(programId)); |
---|
1000 | query.bindValue(":PREVSHOWN", previouslyshown); |
---|
1001 | query.bindValue(":SEASON", season); |
---|
1002 | query.bindValue(":EPISODE", episode); |
---|
1003 | query.bindValue(":TOTALEPISODES", totalepisodes); |
---|
1004 | query.bindValue(":INETREF", inetref); |
---|
1005 | |
---|
1006 | if (!query.exec()) |
---|
1007 | { |
---|
1008 | MythDB::DBError("InsertDB", query); |
---|
1009 | return 0; |
---|
1010 | } |
---|
1011 | |
---|
1012 | QList<EventRating>::const_iterator j = ratings.begin(); |
---|
1013 | for (; j != ratings.end(); ++j) |
---|
1014 | { |
---|
1015 | query.prepare( |
---|
1016 | "INSERT IGNORE INTO programrating " |
---|
1017 | " ( chanid, starttime, system, rating) " |
---|
1018 | "VALUES (:CHANID, :START, :SYS, :RATING)"); |
---|
1019 | query.bindValue(":CHANID", chanid); |
---|
1020 | query.bindValue(":START", starttime); |
---|
1021 | query.bindValue(":SYS", (*j).system); |
---|
1022 | query.bindValue(":RATING", (*j).rating); |
---|
1023 | |
---|
1024 | if (!query.exec()) |
---|
1025 | MythDB::DBError("programrating insert", query); |
---|
1026 | } |
---|
1027 | |
---|
1028 | if (credits) |
---|
1029 | { |
---|
1030 | for (uint i = 0; i < credits->size(); i++) |
---|
1031 | (*credits)[i].InsertDB(query, chanid, starttime); |
---|
1032 | } |
---|
1033 | |
---|
1034 | add_genres(query, genres, chanid, starttime); |
---|
1035 | |
---|
1036 | return 1; |
---|
1037 | } |
---|
1038 | |
---|
1039 | ProgInfo::ProgInfo(const ProgInfo &other) : |
---|
1040 | DBEvent(other.listingsource) |
---|
1041 | { |
---|
1042 | *this = other; |
---|
1043 | } |
---|
1044 | |
---|
1045 | ProgInfo &ProgInfo::operator=(const ProgInfo &other) |
---|
1046 | { |
---|
1047 | if (this == &other) |
---|
1048 | return *this; |
---|
1049 | |
---|
1050 | DBEvent::operator=(other); |
---|
1051 | |
---|
1052 | channel = other.channel; |
---|
1053 | startts = other.startts; |
---|
1054 | endts = other.endts; |
---|
1055 | title_pronounce = other.title_pronounce; |
---|
1056 | showtype = other.showtype; |
---|
1057 | colorcode = other.colorcode; |
---|
1058 | clumpidx = other.clumpidx; |
---|
1059 | clumpmax = other.clumpmax; |
---|
1060 | |
---|
1061 | channel.squeeze(); |
---|
1062 | startts.squeeze(); |
---|
1063 | endts.squeeze(); |
---|
1064 | title_pronounce.squeeze(); |
---|
1065 | showtype.squeeze(); |
---|
1066 | colorcode.squeeze(); |
---|
1067 | clumpidx.squeeze(); |
---|
1068 | clumpmax.squeeze(); |
---|
1069 | |
---|
1070 | return *this; |
---|
1071 | } |
---|
1072 | |
---|
1073 | void ProgInfo::Squeeze(void) |
---|
1074 | { |
---|
1075 | DBEvent::Squeeze(); |
---|
1076 | channel.squeeze(); |
---|
1077 | startts.squeeze(); |
---|
1078 | endts.squeeze(); |
---|
1079 | title_pronounce.squeeze(); |
---|
1080 | showtype.squeeze(); |
---|
1081 | colorcode.squeeze(); |
---|
1082 | clumpidx.squeeze(); |
---|
1083 | clumpmax.squeeze(); |
---|
1084 | } |
---|
1085 | |
---|
1086 | uint ProgInfo::InsertDB(MSqlQuery &query, uint chanid) const |
---|
1087 | { |
---|
1088 | LOG(VB_XMLTV, LOG_INFO, |
---|
1089 | QString("Inserting new program : %1 - %2 %3 %4") |
---|
1090 | .arg(starttime.toString(Qt::ISODate)) |
---|
1091 | .arg(endtime.toString(Qt::ISODate)) |
---|
1092 | .arg(channel) |
---|
1093 | .arg(title)); |
---|
1094 | |
---|
1095 | query.prepare( |
---|
1096 | "REPLACE INTO program (" |
---|
1097 | " chanid, title, subtitle, description, " |
---|
1098 | " category, category_type, " |
---|
1099 | " starttime, endtime, " |
---|
1100 | " closecaptioned, stereo, hdtv, subtitled, " |
---|
1101 | " subtitletypes, audioprop, videoprop, " |
---|
1102 | " partnumber, parttotal, " |
---|
1103 | " syndicatedepisodenumber, " |
---|
1104 | " airdate, originalairdate,listingsource, " |
---|
1105 | " seriesid, programid, previouslyshown, " |
---|
1106 | " stars, showtype, title_pronounce, colorcode, " |
---|
1107 | " season, episode, totalepisodes, " |
---|
1108 | " inetref ) " |
---|
1109 | |
---|
1110 | "VALUES(" |
---|
1111 | " :CHANID, :TITLE, :SUBTITLE, :DESCRIPTION, " |
---|
1112 | " :CATEGORY, :CATTYPE, " |
---|
1113 | " :STARTTIME, :ENDTIME, " |
---|
1114 | " :CC, :STEREO, :HDTV, :HASSUBTITLES, " |
---|
1115 | " :SUBTYPES, :AUDIOPROP, :VIDEOPROP, " |
---|
1116 | " :PARTNUMBER, :PARTTOTAL, " |
---|
1117 | " :SYNDICATENO, " |
---|
1118 | " :AIRDATE, :ORIGAIRDATE, :LSOURCE, " |
---|
1119 | " :SERIESID, :PROGRAMID, :PREVSHOWN, " |
---|
1120 | " :STARS, :SHOWTYPE, :TITLEPRON, :COLORCODE, " |
---|
1121 | " :SEASON, :EPISODE, :TOTALEPISODES, " |
---|
1122 | " :INETREF )"); |
---|
1123 | |
---|
1124 | QString cattype = myth_category_type_to_string(categoryType); |
---|
1125 | |
---|
1126 | query.bindValue(":CHANID", chanid); |
---|
1127 | query.bindValue(":TITLE", denullify(title)); |
---|
1128 | query.bindValue(":SUBTITLE", denullify(subtitle)); |
---|
1129 | query.bindValue(":DESCRIPTION", denullify(description)); |
---|
1130 | query.bindValue(":CATEGORY", denullify(category)); |
---|
1131 | query.bindValue(":CATTYPE", cattype); |
---|
1132 | query.bindValue(":STARTTIME", starttime); |
---|
1133 | query.bindValue(":ENDTIME", denullify(endtime)); |
---|
1134 | query.bindValue(":CC", |
---|
1135 | (subtitleType & SUB_HARDHEAR) ? true : false); |
---|
1136 | query.bindValue(":STEREO", |
---|
1137 | (audioProps & AUD_STEREO) ? true : false); |
---|
1138 | query.bindValue(":HDTV", |
---|
1139 | (videoProps & VID_HDTV) ? true : false); |
---|
1140 | query.bindValue(":HASSUBTITLES", |
---|
1141 | (subtitleType & SUB_NORMAL) ? true : false); |
---|
1142 | query.bindValue(":SUBTYPES", subtitleType); |
---|
1143 | query.bindValue(":AUDIOPROP", audioProps); |
---|
1144 | query.bindValue(":VIDEOPROP", videoProps); |
---|
1145 | query.bindValue(":PARTNUMBER", partnumber); |
---|
1146 | query.bindValue(":PARTTOTAL", parttotal); |
---|
1147 | query.bindValue(":SYNDICATENO", denullify(syndicatedepisodenumber)); |
---|
1148 | query.bindValue(":AIRDATE", airdate ? QString::number(airdate):"0000"); |
---|
1149 | query.bindValue(":ORIGAIRDATE", originalairdate); |
---|
1150 | query.bindValue(":LSOURCE", listingsource); |
---|
1151 | query.bindValue(":SERIESID", denullify(seriesId)); |
---|
1152 | query.bindValue(":PROGRAMID", denullify(programId)); |
---|
1153 | query.bindValue(":PREVSHOWN", previouslyshown); |
---|
1154 | query.bindValue(":STARS", stars); |
---|
1155 | query.bindValue(":SHOWTYPE", showtype); |
---|
1156 | query.bindValue(":TITLEPRON", title_pronounce); |
---|
1157 | query.bindValue(":COLORCODE", colorcode); |
---|
1158 | query.bindValue(":SEASON", season); |
---|
1159 | query.bindValue(":EPISODE", episode); |
---|
1160 | query.bindValue(":TOTALEPISODES", totalepisodes); |
---|
1161 | query.bindValue(":INETREF", inetref); |
---|
1162 | |
---|
1163 | if (!query.exec()) |
---|
1164 | { |
---|
1165 | MythDB::DBError("program insert", query); |
---|
1166 | return 0; |
---|
1167 | } |
---|
1168 | |
---|
1169 | QList<EventRating>::const_iterator j = ratings.begin(); |
---|
1170 | for (; j != ratings.end(); ++j) |
---|
1171 | { |
---|
1172 | query.prepare( |
---|
1173 | "INSERT IGNORE INTO programrating " |
---|
1174 | " ( chanid, starttime, system, rating) " |
---|
1175 | "VALUES (:CHANID, :START, :SYS, :RATING)"); |
---|
1176 | query.bindValue(":CHANID", chanid); |
---|
1177 | query.bindValue(":START", starttime); |
---|
1178 | query.bindValue(":SYS", (*j).system); |
---|
1179 | query.bindValue(":RATING", (*j).rating); |
---|
1180 | |
---|
1181 | if (!query.exec()) |
---|
1182 | MythDB::DBError("programrating insert", query); |
---|
1183 | } |
---|
1184 | |
---|
1185 | if (credits) |
---|
1186 | { |
---|
1187 | for (uint i = 0; i < credits->size(); ++i) |
---|
1188 | (*credits)[i].InsertDB(query, chanid, starttime); |
---|
1189 | } |
---|
1190 | |
---|
1191 | add_genres(query, genres, chanid, starttime); |
---|
1192 | |
---|
1193 | return 1; |
---|
1194 | } |
---|
1195 | |
---|
1196 | bool ProgramData::ClearDataByChannel( |
---|
1197 | uint chanid, const QDateTime &from, const QDateTime &to, |
---|
1198 | bool use_channel_time_offset) |
---|
1199 | { |
---|
1200 | int secs = 0; |
---|
1201 | if (use_channel_time_offset) |
---|
1202 | secs = ChannelUtil::GetTimeOffset(chanid) * 60; |
---|
1203 | |
---|
1204 | QDateTime newFrom = from.addSecs(secs); |
---|
1205 | QDateTime newTo = to.addSecs(secs); |
---|
1206 | |
---|
1207 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1208 | query.prepare("DELETE FROM program " |
---|
1209 | "WHERE starttime >= :FROM AND starttime < :TO " |
---|
1210 | "AND chanid = :CHANID ;"); |
---|
1211 | query.bindValue(":FROM", newFrom); |
---|
1212 | query.bindValue(":TO", newTo); |
---|
1213 | query.bindValue(":CHANID", chanid); |
---|
1214 | bool ok = query.exec(); |
---|
1215 | |
---|
1216 | query.prepare("DELETE FROM programrating " |
---|
1217 | "WHERE starttime >= :FROM AND starttime < :TO " |
---|
1218 | "AND chanid = :CHANID ;"); |
---|
1219 | query.bindValue(":FROM", newFrom); |
---|
1220 | query.bindValue(":TO", newTo); |
---|
1221 | query.bindValue(":CHANID", chanid); |
---|
1222 | ok &= query.exec(); |
---|
1223 | |
---|
1224 | query.prepare("DELETE FROM credits " |
---|
1225 | "WHERE starttime >= :FROM AND starttime < :TO " |
---|
1226 | "AND chanid = :CHANID ;"); |
---|
1227 | query.bindValue(":FROM", newFrom); |
---|
1228 | query.bindValue(":TO", newTo); |
---|
1229 | query.bindValue(":CHANID", chanid); |
---|
1230 | ok &= query.exec(); |
---|
1231 | |
---|
1232 | query.prepare("DELETE FROM programgenres " |
---|
1233 | "WHERE starttime >= :FROM AND starttime < :TO " |
---|
1234 | "AND chanid = :CHANID ;"); |
---|
1235 | query.bindValue(":FROM", newFrom); |
---|
1236 | query.bindValue(":TO", newTo); |
---|
1237 | query.bindValue(":CHANID", chanid); |
---|
1238 | ok &= query.exec(); |
---|
1239 | |
---|
1240 | return ok; |
---|
1241 | } |
---|
1242 | |
---|
1243 | bool ProgramData::ClearDataBySource( |
---|
1244 | uint sourceid, const QDateTime &from, const QDateTime &to, |
---|
1245 | bool use_channel_time_offset) |
---|
1246 | { |
---|
1247 | vector<uint> chanids = ChannelUtil::GetChanIDs(sourceid); |
---|
1248 | |
---|
1249 | bool ok = true; |
---|
1250 | for (uint i = 0; i < chanids.size(); i++) |
---|
1251 | ok &= ClearDataByChannel(chanids[i], from, to, use_channel_time_offset); |
---|
1252 | |
---|
1253 | return ok; |
---|
1254 | } |
---|
1255 | |
---|
1256 | static bool start_time_less_than(const DBEvent *a, const DBEvent *b) |
---|
1257 | { |
---|
1258 | return (a->starttime < b->starttime); |
---|
1259 | } |
---|
1260 | |
---|
1261 | void ProgramData::FixProgramList(QList<ProgInfo*> &fixlist) |
---|
1262 | { |
---|
1263 | qStableSort(fixlist.begin(), fixlist.end(), start_time_less_than); |
---|
1264 | |
---|
1265 | QList<ProgInfo*>::iterator it = fixlist.begin(); |
---|
1266 | while (1) |
---|
1267 | { |
---|
1268 | QList<ProgInfo*>::iterator cur = it; |
---|
1269 | ++it; |
---|
1270 | |
---|
1271 | // fill in miss stop times |
---|
1272 | if ((*cur)->endts.isEmpty() || (*cur)->startts > (*cur)->endts) |
---|
1273 | { |
---|
1274 | if (it != fixlist.end()) |
---|
1275 | { |
---|
1276 | (*cur)->endts = (*it)->startts; |
---|
1277 | (*cur)->endtime = (*it)->starttime; |
---|
1278 | } |
---|
1279 | /* if its the last programme in the file then leave its |
---|
1280 | endtime as 0000-00-00 00:00:00 so we can find it easily in |
---|
1281 | fix_end_times() */ |
---|
1282 | } |
---|
1283 | |
---|
1284 | if (it == fixlist.end()) |
---|
1285 | break; |
---|
1286 | |
---|
1287 | // remove overlapping programs |
---|
1288 | if ((*cur)->HasTimeConflict(**it)) |
---|
1289 | { |
---|
1290 | QList<ProgInfo*>::iterator tokeep, todelete; |
---|
1291 | |
---|
1292 | if ((*cur)->endtime <= (*cur)->starttime) |
---|
1293 | tokeep = it, todelete = cur; |
---|
1294 | else if ((*it)->endtime <= (*it)->starttime) |
---|
1295 | tokeep = cur, todelete = it; |
---|
1296 | else if (!(*cur)->subtitle.isEmpty() && |
---|
1297 | (*it)->subtitle.isEmpty()) |
---|
1298 | tokeep = cur, todelete = it; |
---|
1299 | else if (!(*it)->subtitle.isEmpty() && |
---|
1300 | (*cur)->subtitle.isEmpty()) |
---|
1301 | tokeep = it, todelete = cur; |
---|
1302 | else if (!(*cur)->description.isEmpty() && |
---|
1303 | (*it)->description.isEmpty()) |
---|
1304 | tokeep = cur, todelete = it; |
---|
1305 | else |
---|
1306 | tokeep = it, todelete = cur; |
---|
1307 | |
---|
1308 | |
---|
1309 | LOG(VB_XMLTV, LOG_INFO, |
---|
1310 | QString("Removing conflicting program: %1 - %2 %3 %4") |
---|
1311 | .arg((*todelete)->starttime.toString(Qt::ISODate)) |
---|
1312 | .arg((*todelete)->endtime.toString(Qt::ISODate)) |
---|
1313 | .arg((*todelete)->channel) |
---|
1314 | .arg((*todelete)->title)); |
---|
1315 | |
---|
1316 | LOG(VB_XMLTV, LOG_INFO, |
---|
1317 | QString("Conflicted with : %1 - %2 %3 %4") |
---|
1318 | .arg((*tokeep)->starttime.toString(Qt::ISODate)) |
---|
1319 | .arg((*tokeep)->endtime.toString(Qt::ISODate)) |
---|
1320 | .arg((*tokeep)->channel) |
---|
1321 | .arg((*tokeep)->title)); |
---|
1322 | |
---|
1323 | bool step_back = todelete == it; |
---|
1324 | it = fixlist.erase(todelete); |
---|
1325 | if (step_back) |
---|
1326 | --it; |
---|
1327 | } |
---|
1328 | } |
---|
1329 | } |
---|
1330 | |
---|
1331 | void ProgramData::HandlePrograms( |
---|
1332 | uint sourceid, QMap<QString, QList<ProgInfo> > &proglist) |
---|
1333 | { |
---|
1334 | uint unchanged = 0, updated = 0; |
---|
1335 | |
---|
1336 | MSqlQuery query(MSqlQuery::InitCon()); |
---|
1337 | |
---|
1338 | QMap<QString, QList<ProgInfo> >::const_iterator mapiter; |
---|
1339 | for (mapiter = proglist.begin(); mapiter != proglist.end(); ++mapiter) |
---|
1340 | { |
---|
1341 | if (mapiter.key().isEmpty()) |
---|
1342 | continue; |
---|
1343 | |
---|
1344 | query.prepare( |
---|
1345 | "SELECT chanid " |
---|
1346 | "FROM channel " |
---|
1347 | "WHERE sourceid = :ID AND " |
---|
1348 | " xmltvid = :XMLTVID"); |
---|
1349 | query.bindValue(":ID", sourceid); |
---|
1350 | query.bindValue(":XMLTVID", mapiter.key()); |
---|
1351 | |
---|
1352 | if (!query.exec()) |
---|
1353 | { |
---|
1354 | MythDB::DBError("ProgramData::HandlePrograms", query); |
---|
1355 | continue; |
---|
1356 | } |
---|
1357 | |
---|
1358 | vector<uint> chanids; |
---|
1359 | while (query.next()) |
---|
1360 | chanids.push_back(query.value(0).toUInt()); |
---|
1361 | |
---|
1362 | if (chanids.empty()) |
---|
1363 | { |
---|
1364 | LOG(VB_GENERAL, LOG_NOTICE, |
---|
1365 | QString("Unknown xmltv channel identifier: %1" |
---|
1366 | " - Skipping channel.").arg(mapiter.key())); |
---|
1367 | continue; |
---|
1368 | } |
---|
1369 | |
---|
1370 | QList<ProgInfo> &list = proglist[mapiter.key()]; |
---|
1371 | QList<ProgInfo*> sortlist; |
---|
1372 | QList<ProgInfo>::iterator it = list.begin(); |
---|
1373 | for (; it != list.end(); ++it) |
---|
1374 | sortlist.push_back(&(*it)); |
---|
1375 | |
---|
1376 | FixProgramList(sortlist); |
---|
1377 | |
---|
1378 | for (uint i = 0; i < chanids.size(); ++i) |
---|
1379 | { |
---|
1380 | HandlePrograms(query, chanids[i], sortlist, unchanged, updated); |
---|
1381 | } |
---|
1382 | } |
---|
1383 | |
---|
1384 | LOG(VB_GENERAL, LOG_INFO, |
---|
1385 | QString("Updated programs: %1 Unchanged programs: %2") |
---|
1386 | .arg(updated) .arg(unchanged)); |
---|
1387 | } |
---|
1388 | |
---|
1389 | void ProgramData::HandlePrograms(MSqlQuery &query, |
---|
1390 | uint chanid, |
---|
1391 | const QList<ProgInfo*> &sortlist, |
---|
1392 | uint &unchanged, |
---|
1393 | uint &updated) |
---|
1394 | { |
---|
1395 | QList<ProgInfo*>::const_iterator it = sortlist.begin(); |
---|
1396 | for (; it != sortlist.end(); ++it) |
---|
1397 | { |
---|
1398 | if (IsUnchanged(query, chanid, **it)) |
---|
1399 | { |
---|
1400 | unchanged++; |
---|
1401 | continue; |
---|
1402 | } |
---|
1403 | |
---|
1404 | if (!DeleteOverlaps(query, chanid, **it)) |
---|
1405 | continue; |
---|
1406 | |
---|
1407 | updated += (*it)->InsertDB(query, chanid); |
---|
1408 | } |
---|
1409 | } |
---|
1410 | |
---|
1411 | int ProgramData::fix_end_times(void) |
---|
1412 | { |
---|
1413 | int count = 0; |
---|
1414 | QString chanid, starttime, endtime, querystr; |
---|
1415 | MSqlQuery query1(MSqlQuery::InitCon()), query2(MSqlQuery::InitCon()); |
---|
1416 | |
---|
1417 | querystr = "SELECT chanid, starttime, endtime FROM program " |
---|
1418 | "WHERE endtime = '0000-00-00 00:00:00' " |
---|
1419 | "ORDER BY chanid, starttime;"; |
---|
1420 | |
---|
1421 | if (!query1.exec(querystr)) |
---|
1422 | { |
---|
1423 | LOG(VB_GENERAL, LOG_ERR, |
---|
1424 | QString("fix_end_times query failed: %1").arg(querystr)); |
---|
1425 | return -1; |
---|
1426 | } |
---|
1427 | |
---|
1428 | while (query1.next()) |
---|
1429 | { |
---|
1430 | starttime = query1.value(1).toString(); |
---|
1431 | chanid = query1.value(0).toString(); |
---|
1432 | endtime = query1.value(2).toString(); |
---|
1433 | |
---|
1434 | querystr = QString("SELECT chanid, starttime, endtime FROM program " |
---|
1435 | "WHERE starttime > '%1' " |
---|
1436 | "AND chanid = '%2' " |
---|
1437 | "ORDER BY starttime LIMIT 1;") |
---|
1438 | .arg(starttime) |
---|
1439 | .arg(chanid); |
---|
1440 | |
---|
1441 | if (!query2.exec(querystr)) |
---|
1442 | { |
---|
1443 | LOG(VB_GENERAL, LOG_ERR, |
---|
1444 | QString("fix_end_times query failed: %1").arg(querystr)); |
---|
1445 | return -1; |
---|
1446 | } |
---|
1447 | |
---|
1448 | if (query2.next() && (endtime != query2.value(1).toString())) |
---|
1449 | { |
---|
1450 | count++; |
---|
1451 | endtime = query2.value(1).toString(); |
---|
1452 | querystr = QString("UPDATE program SET " |
---|
1453 | "endtime = '%2' WHERE (chanid = '%3' AND " |
---|
1454 | "starttime = '%4');") |
---|
1455 | .arg(endtime) |
---|
1456 | .arg(chanid) |
---|
1457 | .arg(starttime); |
---|
1458 | |
---|
1459 | if (!query2.exec(querystr)) |
---|
1460 | { |
---|
1461 | LOG(VB_GENERAL, LOG_ERR, |
---|
1462 | QString("fix_end_times query failed: %1").arg(querystr)); |
---|
1463 | return -1; |
---|
1464 | } |
---|
1465 | } |
---|
1466 | } |
---|
1467 | |
---|
1468 | return count; |
---|
1469 | } |
---|
1470 | |
---|
1471 | bool ProgramData::IsUnchanged( |
---|
1472 | MSqlQuery &query, uint chanid, const ProgInfo &pi) |
---|
1473 | { |
---|
1474 | query.prepare( |
---|
1475 | "SELECT count(*) " |
---|
1476 | "FROM program " |
---|
1477 | "WHERE chanid = :CHANID AND " |
---|
1478 | " starttime = :START AND " |
---|
1479 | " endtime = :END AND " |
---|
1480 | " title = :TITLE AND " |
---|
1481 | " subtitle = :SUBTITLE AND " |
---|
1482 | " description = :DESC AND " |
---|
1483 | " category = :CATEGORY AND " |
---|
1484 | " category_type = :CATEGORY_TYPE AND " |
---|
1485 | " airdate = :AIRDATE AND " |
---|
1486 | " stars >= (:STARS1 - 0.001) AND " |
---|
1487 | " stars <= (:STARS2 + 0.001) AND " |
---|
1488 | " previouslyshown = :PREVIOUSLYSHOWN AND " |
---|
1489 | " title_pronounce = :TITLE_PRONOUNCE AND " |
---|
1490 | " audioprop = :AUDIOPROP AND " |
---|
1491 | " videoprop = :VIDEOPROP AND " |
---|
1492 | " subtitletypes = :SUBTYPES AND " |
---|
1493 | " partnumber = :PARTNUMBER AND " |
---|
1494 | " parttotal = :PARTTOTAL AND " |
---|
1495 | " seriesid = :SERIESID AND " |
---|
1496 | " showtype = :SHOWTYPE AND " |
---|
1497 | " colorcode = :COLORCODE AND " |
---|
1498 | " syndicatedepisodenumber = :SYNDICATEDEPISODENUMBER AND " |
---|
1499 | " programid = :PROGRAMID AND " |
---|
1500 | " inetref = :INETREF"); |
---|
1501 | |
---|
1502 | QString cattype = myth_category_type_to_string(pi.categoryType); |
---|
1503 | |
---|
1504 | query.bindValue(":CHANID", chanid); |
---|
1505 | query.bindValue(":START", pi.starttime); |
---|
1506 | query.bindValue(":END", pi.endtime); |
---|
1507 | query.bindValue(":TITLE", denullify(pi.title)); |
---|
1508 | query.bindValue(":SUBTITLE", denullify(pi.subtitle)); |
---|
1509 | query.bindValue(":DESC", denullify(pi.description)); |
---|
1510 | query.bindValue(":CATEGORY", denullify(pi.category)); |
---|
1511 | query.bindValue(":CATEGORY_TYPE", cattype); |
---|
1512 | query.bindValue(":AIRDATE", pi.airdate); |
---|
1513 | query.bindValue(":STARS1", pi.stars); |
---|
1514 | query.bindValue(":STARS2", pi.stars); |
---|
1515 | query.bindValue(":PREVIOUSLYSHOWN", pi.previouslyshown); |
---|
1516 | query.bindValue(":TITLE_PRONOUNCE", pi.title_pronounce); |
---|
1517 | query.bindValue(":AUDIOPROP", pi.audioProps); |
---|
1518 | query.bindValue(":VIDEOPROP", pi.videoProps); |
---|
1519 | query.bindValue(":SUBTYPES", pi.subtitleType); |
---|
1520 | query.bindValue(":PARTNUMBER", pi.partnumber); |
---|
1521 | query.bindValue(":PARTTOTAL", pi.parttotal); |
---|
1522 | query.bindValue(":SERIESID", denullify(pi.seriesId)); |
---|
1523 | query.bindValue(":SHOWTYPE", pi.showtype); |
---|
1524 | query.bindValue(":COLORCODE", pi.colorcode); |
---|
1525 | query.bindValue(":SYNDICATEDEPISODENUMBER", |
---|
1526 | denullify(pi.syndicatedepisodenumber)); |
---|
1527 | query.bindValue(":PROGRAMID", denullify(pi.programId)); |
---|
1528 | query.bindValue(":INETREF", pi.inetref); |
---|
1529 | |
---|
1530 | if (query.exec() && query.next()) |
---|
1531 | return query.value(0).toUInt() > 0; |
---|
1532 | |
---|
1533 | return false; |
---|
1534 | } |
---|
1535 | |
---|
1536 | bool ProgramData::DeleteOverlaps( |
---|
1537 | MSqlQuery &query, uint chanid, const ProgInfo &pi) |
---|
1538 | { |
---|
1539 | if (VERBOSE_LEVEL_CHECK(VB_XMLTV, LOG_INFO)) |
---|
1540 | { |
---|
1541 | // Get overlaps.. |
---|
1542 | query.prepare( |
---|
1543 | "SELECT title,starttime,endtime " |
---|
1544 | "FROM program " |
---|
1545 | "WHERE chanid = :CHANID AND " |
---|
1546 | " starttime >= :START AND " |
---|
1547 | " starttime < :END;"); |
---|
1548 | query.bindValue(":CHANID", chanid); |
---|
1549 | query.bindValue(":START", pi.starttime); |
---|
1550 | query.bindValue(":END", pi.endtime); |
---|
1551 | |
---|
1552 | if (!query.exec()) |
---|
1553 | return false; |
---|
1554 | |
---|
1555 | if (!query.next()) |
---|
1556 | return true; |
---|
1557 | |
---|
1558 | do |
---|
1559 | { |
---|
1560 | LOG(VB_XMLTV, LOG_INFO, |
---|
1561 | QString("Removing existing program: %1 - %2 %3 %4") |
---|
1562 | .arg(MythDate::as_utc(query.value(1).toDateTime()).toString(Qt::ISODate)) |
---|
1563 | .arg(MythDate::as_utc(query.value(2).toDateTime()).toString(Qt::ISODate)) |
---|
1564 | .arg(pi.channel) |
---|
1565 | .arg(query.value(0).toString())); |
---|
1566 | } while (query.next()); |
---|
1567 | } |
---|
1568 | |
---|
1569 | if (!ClearDataByChannel(chanid, pi.starttime, pi.endtime, false)) |
---|
1570 | { |
---|
1571 | LOG(VB_XMLTV, LOG_ERR, |
---|
1572 | QString("Program delete failed : %1 - %2 %3 %4") |
---|
1573 | .arg(pi.starttime.toString(Qt::ISODate)) |
---|
1574 | .arg(pi.endtime.toString(Qt::ISODate)) |
---|
1575 | .arg(pi.channel) |
---|
1576 | .arg(pi.title)); |
---|
1577 | return false; |
---|
1578 | } |
---|
1579 | |
---|
1580 | return true; |
---|
1581 | } |
---|