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