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