MythTV  master
progdetails.cpp
Go to the documentation of this file.
1 
2 // Qt
3 #include <QFile>
4 #include <QKeyEvent>
5 #include <QTextStream>
6 
7 // MythTV
9 #include "libmythbase/mythdate.h"
10 #include "libmythbase/mythdb.h"
15 #include "libmythui/mythuihelper.h"
16 
17 // MythFrontend
18 #include "progdetails.h"
19 
21 {
22  // Load the theme for this screen
23  bool foundtheme = LoadWindowFromXML("schedule-ui.xml", "programdetails", this);
24  if (!foundtheme)
25  return false;
26 
27  // Initialise details list
28  if (!m_infoList.Create(true))
29  {
30  LOG(VB_GENERAL, LOG_ERR, "Cannot load 'Info buttonlist'");
31  return false;
32  }
33 
35 
36  return true;
37 }
38 
39 QString ProgDetails::getRatings(bool recorded, uint chanid, const QDateTime& startts)
40 {
41  QString table = (recorded) ? "recordedrating" : "programrating";
42  QString sel = QString(
43  "SELECT `system`, rating FROM %1 "
44  "WHERE chanid = :CHANID "
45  "AND starttime = :STARTTIME").arg(table);
46 
48  query.prepare(sel);
49  query.bindValue(":CHANID", chanid);
50  query.bindValue(":STARTTIME", startts);
51 
52  if (!query.exec() || !query.isActive())
53  {
54  MythDB::DBError("ProgDetails::getRatings", query);
55  return "";
56  }
57 
58  QMap<QString,QString> main_ratings;
59  QString advisory;
60  while (query.next())
61  {
62  if (query.value(0).toString().toLower() == "advisory")
63  {
64  advisory += query.value(1).toString() + ", ";
65  continue;
66  }
67  main_ratings[query.value(0).toString()] = query.value(1).toString();
68  }
69 
70  advisory = advisory.left(advisory.length() - 2);
71 
72  if (main_ratings.empty())
73  return advisory;
74 
75  if (!advisory.isEmpty())
76  advisory = ": " + advisory;
77 
78  if (main_ratings.size() == 1)
79  {
80  return *main_ratings.begin() + advisory;
81  }
82 
83  QString ratings;
84  QMap<QString,QString>::const_iterator it;
85  for (it = main_ratings.cbegin(); it != main_ratings.cend(); ++it)
86  {
87  ratings += it.key() + ": " + *it + ", ";
88  }
89 
90  return ratings + "Advisory" + advisory;
91 }
92 
94 {
95  m_infoList.Hide();
96 }
97 
99 {
100  updatePage();
101 }
102 
104 {
105  if (m_data.isEmpty())
106  loadPage();
107 
109 }
110 
111 void ProgDetails::addItem(const QString &title, const QString &value,
113 {
114  if (value.isEmpty())
115  return;
116  ProgInfoList::DataItem item = std::make_tuple(title, value, level);
117  m_data.append(item);
118 }
119 
120 bool ProgDetails::keyPressEvent(QKeyEvent *event)
121 {
122  QStringList actions;
123  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
124 
125  for (int i = 0; i < actions.size() && !handled; ++i)
126  {
127  const QString& action = actions[i];
128  handled = true;
129 
130  if (action == "INFO" || action == "SELECT")
131  {
132  m_infoList.Toggle();
133  updatePage();
134  }
135  else if (action == "DOWN")
136  {
138  }
139  else if (action == "UP")
140  {
141  m_infoList.PageUp();
142  }
143  else
144  {
145  handled = false;
146  }
147  }
148 
149  if (!handled && MythScreenType::keyPressEvent(event))
150  handled = true;
151 
152  return handled;
153 }
154 
155 void ProgDetails::PowerPriorities(const QString & ptable)
156 {
157  int recordid = m_progInfo.GetRecordingRuleID();
158 
159  // If not a scheduled recording, abort .... ?
160  if (!recordid)
161  return;
162 
163  using string_pair = QPair<QString, QString>;
164  QVector<string_pair> tests;
165  QString recmatch;
166  QString pwrpri;
167  QString desc;
168  QString adjustmsg;
169  int total = 0;
170 
171  MSqlQuery query(MSqlQuery::InitCon());
172 
173  int prefinputpri = gCoreContext->GetNumSetting("PrefInputPriority", 2);
174  int hdtvpriority = gCoreContext->GetNumSetting("HDTVRecPriority", 0);
175  int wspriority = gCoreContext->GetNumSetting("WSRecPriority", 0);
176  int slpriority = gCoreContext->GetNumSetting("SignLangRecPriority", 0);
177  int onscrpriority = gCoreContext->GetNumSetting("OnScrSubRecPriority", 0);
178  int ccpriority = gCoreContext->GetNumSetting("CCRecPriority", 0);
179  int hhpriority = gCoreContext->GetNumSetting("HardHearRecPriority", 0);
180  int adpriority = gCoreContext->GetNumSetting("AudioDescRecPriority", 0);
181 
182  tests.append(qMakePair(QString("channel.recpriority"),
183  QString("channel.recpriority")));
184  tests.append(qMakePair(QString("capturecard.recpriority"),
185  QString("capturecard.recpriority")));
186 
187  if (prefinputpri)
188  {
189  pwrpri = QString("(capturecard.cardid = record.prefinput) * %1")
190  .arg(prefinputpri);
191  tests.append(qMakePair(pwrpri, pwrpri));
192  }
193  if (hdtvpriority)
194  {
195  pwrpri = QString("(program.hdtv > 0 OR "
196  "FIND_IN_SET('HDTV', program.videoprop) > 0) * %1")
197  .arg(hdtvpriority);
198  tests.append(qMakePair(pwrpri, pwrpri));
199  }
200  if (wspriority)
201  {
202  pwrpri = QString
203  ("(FIND_IN_SET('WIDESCREEN', program.videoprop) > 0) * %1")
204  .arg(wspriority);
205  tests.append(qMakePair(pwrpri, pwrpri));
206  }
207  if (slpriority)
208  {
209  pwrpri = QString
210  ("(FIND_IN_SET('SIGNED', program.subtitletypes) > 0) * %1")
211  .arg(slpriority);
212  tests.append(qMakePair(pwrpri, pwrpri));
213  }
214  if (onscrpriority)
215  {
216  pwrpri = QString
217  ("(FIND_IN_SET('ONSCREEN', program.subtitletypes) > 0) * %1")
218  .arg(onscrpriority);
219  tests.append(qMakePair(pwrpri, pwrpri));
220  }
221  if (ccpriority)
222  {
223  pwrpri = QString
224  ("(FIND_IN_SET('NORMAL', program.subtitletypes) > 0 OR "
225  "program.closecaptioned > 0 OR program.subtitled > 0) * %1")
226  .arg(ccpriority);
227  tests.append(qMakePair(pwrpri, pwrpri));
228  }
229  if (hhpriority)
230  {
231  pwrpri = QString
232  ("(FIND_IN_SET('HARDHEAR', program.subtitletypes) > 0 OR "
233  "FIND_IN_SET('HARDHEAR', program.audioprop) > 0) * %1")
234  .arg(hhpriority);
235  tests.append(qMakePair(pwrpri, pwrpri));
236  }
237  if (adpriority)
238  {
239  pwrpri = QString
240  ("(FIND_IN_SET('VISUALIMPAIR', program.audioprop) > 0) * %1")
241  .arg(adpriority);
242  tests.append(qMakePair(pwrpri, pwrpri));
243  }
244 
245  query.prepare("SELECT recpriority, selectclause FROM powerpriority;");
246 
247  if (!query.exec())
248  {
249  MythDB::DBError("Power Priority", query);
250  return;
251  }
252 
253  while (query.next())
254  {
255  int adj = query.value(0).toInt();
256  if (adj)
257  {
258  QString sclause = query.value(1).toString();
259  sclause.remove(RecordingInfo::kReLeadingAnd);
260  sclause.remove(';');
261  pwrpri = QString("(%1) * %2").arg(sclause)
262  .arg(query.value(0).toInt());
263  pwrpri.replace("RECTABLE", "record");
264 
265  desc = pwrpri;
266  pwrpri += QString(" AS powerpriority ");
267 
268  tests.append(qMakePair(desc, pwrpri));
269  }
270  }
271 
272  recmatch = QString("INNER JOIN record "
273  " ON ( record.recordid = %1 ) ")
274  .arg(recordid);
275 
276  for (const auto & [label, csqlStart] : std::as_const(tests))
277  {
278  QString sqlStart = csqlStart;
279  query.prepare("SELECT " + sqlStart.replace("program.", "p.")
280  + QString
281  (" FROM %1 as p "
282  "INNER JOIN channel "
283  " ON ( channel.chanid = p.chanid ) "
284  "INNER JOIN capturecard "
285  " ON ( channel.sourceid = capturecard.sourceid AND "
286  " ( capturecard.schedorder <> 0 OR "
287  " capturecard.parentid = 0 ) ) ").arg(ptable)
288  + recmatch +
289  "WHERE p.chanid = :CHANID AND"
290  " p.starttime = :STARTTIME ;");
291 
292  query.bindValue(":CHANID", m_progInfo.GetChanID());
293  query.bindValue(":STARTTIME", m_progInfo.GetScheduledStartTime());
294 
295  adjustmsg = QString("%1 : ").arg(label);
296  if (query.exec() && query.next())
297  {
298  int adj = query.value(0).toInt();
299  if (adj)
300  {
301  adjustmsg += tr(" MATCHED, adding %1").arg(adj);
302  total += adj;
303  }
304  else
305  {
306  adjustmsg += tr(" not matched");
307  }
308  }
309  else
310  {
311  adjustmsg += tr(" Query FAILED");
312  }
313 
314  addItem(tr("Recording Priority Adjustment"), adjustmsg,
316  }
317 
318  if (!tests.isEmpty())
319  addItem(tr("Priority Adjustment Total"), QString::number(total),
321 }
322 
324 {
325  MSqlQuery query(MSqlQuery::InitCon());
326  QString category_type;
327  QString showtype;
328  QString year;
329  QString syndicatedEpisodeNum;
330  QString rating;
331  QString colorcode;
332  QString title_pronounce;
333  float stars = 0.0;
334  int partnumber = 0;
335  int parttotal = 0;
336  int audioprop = 0;
337  int videoprop = 0;
338  int subtype = 0;
339  int generic = 0;
340  bool recorded = false;
341 
342  RecordingRule* record = nullptr;
344  {
345  record = new RecordingRule();
346  record->LoadByProgram(&m_progInfo);
347  }
348 
349  if (m_progInfo.GetFilesize())
350  recorded = true;
351 
352  QString ptable = recorded ? "recordedprogram" : "program";
353 
355  {
356  query.prepare(QString("SELECT category_type, airdate, stars,"
357  " partnumber, parttotal, audioprop+0, videoprop+0,"
358  " subtitletypes+0, syndicatedepisodenumber, generic,"
359  " showtype, colorcode, title_pronounce"
360  " FROM %1 WHERE chanid = :CHANID AND"
361  " starttime = :STARTTIME ;").arg(ptable));
362 
363  query.bindValue(":CHANID", m_progInfo.GetChanID());
364  query.bindValue(":STARTTIME", m_progInfo.GetScheduledStartTime());
365 
366  if (query.exec() && query.next())
367  {
368  category_type = query.value(0).toString();
369  year = query.value(1).toString();
370  stars = query.value(2).toFloat();
371  partnumber = query.value(3).toInt();
372  parttotal = query.value(4).toInt();
373  audioprop = query.value(5).toInt();
374  videoprop = query.value(6).toInt();
375  subtype = query.value(7).toInt();
376  syndicatedEpisodeNum = query.value(8).toString();
377  generic = query.value(9).toInt();
378  showtype = query.value(10).toString();
379  colorcode = query.value(11).toString();
380  title_pronounce = query.value(12).toString();
381  }
382  else if (!query.isActive())
383  {
384  MythDB::DBError("ProgDetails", query);
385  }
386 
387  rating = getRatings(
388  recorded, m_progInfo.GetChanID(),
390  }
391 
392  if (category_type.isEmpty() && !m_progInfo.GetProgramID().isEmpty())
393  {
394  QString prefix = m_progInfo.GetProgramID().left(2);
395 
396  if (prefix == "MV")
397  category_type = "movie";
398  else if (prefix == "EP")
399  category_type = "series";
400  else if (prefix == "SP")
401  category_type = "sports";
402  else if (prefix == "SH")
403  category_type = "tvshow";
404  }
405 
406  addItem(tr("Title"),
409 
410  addItem(tr("Title Pronounce"), title_pronounce, ProgInfoList::kLevel2);
411 
412  QString s = m_progInfo.GetDescription();
413 
414  QString attr;
415 
416  if (partnumber > 0)
417  attr += tr("Part %1 of %2, ").arg(partnumber).arg(parttotal);
418 
419  if (!rating.isEmpty() && rating != "NR")
420  attr += rating + ", ";
421  if (category_type == "movie")
422  {
423  if (!year.isEmpty())
424  attr += year + ", ";
425 
426  /* see #7810, was hardcoded to 4 star system, when every theme
427  * uses 10 stars / 5 stars with half stars
428  */
429  if (stars > 0.0F)
430  attr += tr("%n star(s)", "", roundf(stars * 10.0F)) + ", ";
431  }
432  if (!colorcode.isEmpty())
433  attr += colorcode + ", ";
434 
435  if (audioprop & AUD_MONO)
436  attr += tr("Mono") + ", ";
437  if (audioprop & AUD_STEREO)
438  attr += tr("Stereo") + ", ";
439  if (audioprop & AUD_SURROUND)
440  attr += tr("Surround Sound") + ", ";
441  if (audioprop & AUD_DOLBY)
442  attr += tr("Dolby Sound") + ", ";
443  if (audioprop & AUD_HARDHEAR)
444  attr += tr("Audio for Hearing Impaired") + ", ";
445  if (audioprop & AUD_VISUALIMPAIR)
446  attr += tr("Audio for Visually Impaired") + ", ";
447 
448  if (videoprop & VID_HDTV)
449  attr += tr("HDTV") + ", ";
450  if (videoprop & VID_WIDESCREEN)
451  attr += tr("Widescreen") + ", ";
452  if (videoprop & VID_AVC)
453  attr += tr("AVC/H.264") + ", ";
454  if (videoprop & VID_HEVC)
455  attr += tr("HEVC/H.265") + ", ";
456  if (videoprop & VID_720)
457  attr += tr("720p Resolution") + ", ";
458 
459  if (videoprop & VID_PROGRESSIVE)
460  {
461  if (videoprop & VID_1080)
462  attr += tr("1080p Resolution") + ", ";
463  if (videoprop & VID_4K)
464  attr += tr("4K Resolution") + ", ";
465  }
466  else
467  {
468  if (videoprop & VID_1080)
469  attr += tr("1080i Resolution") + ", ";
470  if (videoprop & VID_4K)
471  attr += tr("4Ki Resolution") + ", ";
472  }
473 
474  if (videoprop & VID_DAMAGED)
475  attr += tr("Damaged") + ", ";
476 
477  if (subtype & SUB_HARDHEAR)
478  attr += tr("CC","Closed Captioned") + ", ";
479  if (subtype & SUB_NORMAL)
480  attr += tr("Subtitles Available") + ", ";
481  if (subtype & SUB_ONSCREEN)
482  attr += tr("Subtitled") + ", ";
483  if (subtype & SUB_SIGNED)
484  attr += tr("Deaf Signing") + ", ";
485 
486  if (generic && category_type == "series")
487  attr += tr("Unidentified Episode") + ", ";
488  else if (m_progInfo.IsRepeat())
489  attr += tr("Repeat") + ", ";
490 
491  if (!attr.isEmpty())
492  {
493  attr.truncate(attr.lastIndexOf(','));
494  s += " (" + attr + ")";
495  }
496 
497  addItem(tr("Description"), s, ProgInfoList::kLevel1);
498 
499  QString actors;
500  QString directors;
501  QString producers;
502  QString execProducers;
503  QString writers;
504  QString guestStars;
505  QString hosts;
506  QString adapters;
507  QString presenters;
508  QString commentators;
509  QString guests;
510 
511  using string_pair = QPair<QString, QString>;
512  QVector<string_pair> actor_list;
513  QVector<string_pair> guest_star_list;
514  QVector<string_pair> guest_list;
515 
517  {
518  QString table;
519  if (recorded)
520  table = "recordedcredits";
521  else
522  table = "credits";
523 
524  query.prepare(QString("SELECT role, people.name, roles.name FROM %1"
525  " AS credits"
526  " LEFT JOIN people ON"
527  " credits.person = people.person"
528  " LEFT JOIN roles ON"
529  " credits.roleid = roles.roleid"
530  " WHERE credits.chanid = :CHANID"
531  " AND credits.starttime = :STARTTIME"
532  " ORDER BY role, priority;").arg(table));
533 
534  query.bindValue(":CHANID", m_progInfo.GetChanID());
535  query.bindValue(":STARTTIME", m_progInfo.GetScheduledStartTime());
536 
537  if (query.exec() && query.size() > 0)
538  {
539  QStringList plist;
540  QString rstr;
541  QString role;
542  QString pname;
543  QString character;
544 
545  while(query.next())
546  {
547  role = query.value(0).toString();
548  /* The people.name, roles.name columns uses utf8_bin collation.
549  * Qt-MySQL drivers use QVariant::ByteArray for string-type
550  * MySQL fields marked with the BINARY attribute (those using a
551  * *_bin collation) and QVariant::String for all others.
552  * Since QVariant::toString() uses QString::fromAscii()
553  * (through QVariant::convert()) when the QVariant's type is
554  * QVariant::ByteArray, we have to use QString::fromUtf8()
555  * explicitly to prevent corrupting characters.
556  * The following code should be changed to use the simpler
557  * toString() approach, as above, if we do a DB update to
558  * coalesce the people.name values that differ only in case and
559  * change the collation to utf8_general_ci, to match the
560  * majority of other columns, or we'll have the same problem in
561  * reverse.
562  */
563  pname = QString::fromUtf8(query.value(1)
564  .toByteArray().constData());
565  character = QString::fromUtf8(query.value(2)
566  .toByteArray().constData());
567 
568  if (!character.isEmpty())
569  {
570  if (role == "actor")
571  actor_list.append(qMakePair(pname, character));
572  else if (role == "guest_star")
573  guest_star_list.append(qMakePair(pname, character));
574  else if (role == "guest")
575  guest_list.append(qMakePair(pname, character));
576  }
577 
578  if (rstr != role)
579  {
580  plist.removeDuplicates();
581  if (rstr == "actor")
582  actors = plist.join(", ");
583  else if (rstr == "director")
584  directors = plist.join(", ");
585  else if (rstr == "producer")
586  producers = plist.join(", ");
587  else if (rstr == "executive_producer")
588  execProducers = plist.join(", ");
589  else if (rstr == "writer")
590  writers = plist.join(", ");
591  else if (rstr == "guest_star")
592  guestStars = plist.join(", ");
593  else if (rstr == "host")
594  hosts = plist.join(", ");
595  else if (rstr == "adapter")
596  adapters = plist.join(", ");
597  else if (rstr == "presenter")
598  presenters = plist.join(", ");
599  else if (rstr == "commentator")
600  commentators = plist.join(", ");
601  else if (rstr == "guest")
602  guests = plist.join(", ");
603 
604  rstr = role;
605  plist.clear();
606  }
607 
608  plist.append(pname);
609  }
610  plist.removeDuplicates();
611  if (rstr == "actor")
612  actors = plist.join(", ");
613  else if (rstr == "director")
614  directors = plist.join(", ");
615  else if (rstr == "producer")
616  producers = plist.join(", ");
617  else if (rstr == "executive_producer")
618  execProducers = plist.join(", ");
619  else if (rstr == "writer")
620  writers = plist.join(", ");
621  else if (rstr == "guest_star")
622  guestStars = plist.join(", ");
623  else if (rstr == "host")
624  hosts = plist.join(", ");
625  else if (rstr == "adapter")
626  adapters = plist.join(", ");
627  else if (rstr == "presenter")
628  presenters = plist.join(", ");
629  else if (rstr == "commentator")
630  commentators = plist.join(", ");
631  else if (rstr == "guest")
632  guests = plist.join(", ");
633  }
634  }
635  addItem(tr("Actors"), actors, ProgInfoList::kLevel1);
636  addItem(tr("Guest Star"), guestStars, ProgInfoList::kLevel1);
637  addItem(tr("Guest"), guests, ProgInfoList::kLevel1);
638 
639  if (!actor_list.isEmpty())
640  {
641  for (const auto & [actor, role] : std::as_const(actor_list))
642  addItem(role, actor, ProgInfoList::kLevel2);
643  }
644  if (!guest_star_list.isEmpty())
645  {
646  for (const auto & [actor, role] : std::as_const(guest_star_list))
647  addItem(role, actor, ProgInfoList::kLevel2);
648  }
649  if (!guest_list.isEmpty())
650  {
651  for (const auto & [actor, role] : std::as_const(guest_list))
652  if (!role.isEmpty())
653  addItem(role, actor, ProgInfoList::kLevel2);
654  }
655 
656  addItem(tr("Host"), hosts, ProgInfoList::kLevel1);
657  addItem(tr("Presenter"), presenters, ProgInfoList::kLevel1);
658  addItem(tr("Commentator"), commentators, ProgInfoList::kLevel1);
659  addItem(tr("Director"), directors, ProgInfoList::kLevel1);
660  addItem(tr("Producer"), producers, ProgInfoList::kLevel2);
661  addItem(tr("Executive Producer"), execProducers, ProgInfoList::kLevel2);
662  addItem(tr("Writer"), writers, ProgInfoList::kLevel2);
663  addItem(tr("Adapter"), adapters, ProgInfoList::kLevel2);
664 
666 
667  query.prepare("SELECT genre FROM programgenres "
668  "WHERE chanid = :CHANID AND starttime = :STARTTIME "
669  "AND relevance <> '0' ORDER BY relevance;");
670  query.bindValue(":CHANID", m_progInfo.GetChanID());
671  query.bindValue(":STARTTIME", m_progInfo.GetScheduledStartTime());
672 
673  if (query.exec())
674  {
675  s.clear();
676  while (query.next())
677  {
678  if (!s.isEmpty())
679  s += ", ";
680  s += query.value(0).toString();
681  }
682  addItem(tr("Genre"), s, ProgInfoList::kLevel1);
683  }
684 
685  s.clear();
686  if (!category_type.isEmpty())
687  {
688  s = category_type;
689  if (!m_progInfo.GetSeriesID().isEmpty())
690  s += " (" + m_progInfo.GetSeriesID() + ")";
691  if (!showtype.isEmpty())
692  s += " " + showtype;
693  }
694  addItem(tr("Type", "category_type"), s, ProgInfoList::kLevel1);
695 
696  s.clear();
697  if (m_progInfo.GetSeason() > 0)
698  s = QString::number(m_progInfo.GetSeason());
699  addItem(tr("Season"), s, ProgInfoList::kLevel1);
700 
701  s.clear();
702  if (m_progInfo.GetEpisode() > 0)
703  {
704  if (m_progInfo.GetEpisodeTotal() > 0)
705  {
706  s = tr("%1 of %2").arg(m_progInfo.GetEpisode())
707  .arg(m_progInfo.GetEpisodeTotal());
708  }
709  else
710  {
711  s = QString::number(m_progInfo.GetEpisode());
712  }
713  }
714  addItem(tr("Episode"), s, ProgInfoList::kLevel1);
715 
716  addItem(tr("Syndicated Episode Number"), syndicatedEpisodeNum,
718 
719  s.clear();
720  if (m_progInfo.GetOriginalAirDate().isValid() &&
721  category_type != "movie")
722  {
725  }
726  addItem(tr("Original Airdate"), s, ProgInfoList::kLevel1);
727 
729 
730  // Begin MythTV information not found in the listings info
731  QDateTime statusDate;
734  statusDate = m_progInfo.GetScheduledStartTime();
735 
736  RecordingType rectype = kSingleRecord; // avoid kNotRecording
738 
739  if (recstatus == RecStatus::PreviousRecording ||
740  recstatus == RecStatus::NeverRecord ||
741  recstatus == RecStatus::Unknown)
742  {
743  query.prepare("SELECT recstatus, starttime "
744  "FROM oldrecorded WHERE duplicate > 0 AND "
745  "future = 0 AND "
746  "((programid <> '' AND programid = :PROGRAMID) OR "
747  " (title <> '' AND title = :TITLE AND "
748  " subtitle <> '' AND subtitle = :SUBTITLE AND "
749  " description <> '' AND description = :DECRIPTION));");
750 
751  query.bindValue(":PROGRAMID", m_progInfo.GetProgramID());
752  query.bindValue(":TITLE", m_progInfo.GetTitle());
753  query.bindValue(":SUBTITLE", m_progInfo.GetSubtitle());
754  query.bindValue(":DECRIPTION", m_progInfo.GetDescription());
755 
756  if (!query.exec())
757  {
758  MythDB::DBError("showDetails", query);
759  }
760  else if (query.next())
761  {
762  if (recstatus == RecStatus::Unknown)
763  recstatus = RecStatus::Type(query.value(0).toInt());
764 
765  if (recstatus == RecStatus::PreviousRecording ||
766  recstatus == RecStatus::NeverRecord ||
767  recstatus == RecStatus::Recorded)
768  {
769  statusDate = MythDate::as_utc(query.value(1).toDateTime());
770  }
771  }
772  }
773 
774  if (recstatus == RecStatus::Unknown)
775  {
776  if (recorded)
777  {
778  recstatus = RecStatus::Recorded;
779  statusDate = m_progInfo.GetScheduledStartTime();
780  }
781  else
782  {
783  // re-enable "Not Recording" status text
784  rectype = m_progInfo.GetRecordingRuleType();
785  }
786  }
787 
788  s = RecStatus::toString(recstatus, rectype);
789 
790  if (statusDate.isValid())
791  s += " " + MythDate::toString(statusDate, MythDate::kDateFull |
793 
794  addItem(tr("MythTV Status"), s, ProgInfoList::kLevel1);
795 
796  QString recordingRule;
797  QString lastRecorded;
798  QString nextRecording;
799  QString averageTimeShift;
800  QString watchListScore;
801  QString watchListStatus;
802  QString searchPhrase;
803 
805  {
806  recordingRule = QString("%1, ").arg(m_progInfo.GetRecordingRuleID());
808  recordingRule += toString(m_progInfo.GetRecordingRuleType());
809  if (record && !record->m_title.isEmpty())
810  recordingRule += QString(" \"%2\"").arg(record->m_title);
811 
812  query.prepare("SELECT last_record, next_record, avg_delay "
813  "FROM record WHERE recordid = :RECORDID");
814  query.bindValue(":RECORDID", m_progInfo.GetRecordingRuleID());
815 
816  if (query.exec() && query.next())
817  {
818  if (query.value(0).toDateTime().isValid())
819  {
820  lastRecorded = MythDate::toString(
821  MythDate::as_utc(query.value(0).toDateTime()),
823  }
824  if (query.value(1).toDateTime().isValid())
825  {
826  nextRecording = MythDate::toString(
827  MythDate::as_utc(query.value(1).toDateTime()),
829  }
830  if (query.value(2).toInt() > 0)
831  averageTimeShift = tr("%n hour(s)", "",
832  query.value(2).toInt());
833  }
834  if (recorded)
835  {
837  watchListScore =
838  QString::number(m_progInfo.GetRecordingPriority2());
839 
841  {
843  {
844  case wlExpireOff:
845  watchListStatus = tr("Auto-expire off");
846  break;
847  case wlWatched:
848  watchListStatus = tr("Marked as 'watched'");
849  break;
850  case wlEarlier:
851  watchListStatus = tr("Not the earliest episode");
852  break;
853  case wlDeleted:
854  watchListStatus = tr("Recently deleted episode");
855  break;
856  }
857  }
858  }
859  if (record && record->m_searchType != kManualSearch &&
861  searchPhrase = record->m_description;
862  }
863  addItem(tr("Recording Rule"), recordingRule, ProgInfoList::kLevel1);
864  addItem(tr("Search Phrase"), searchPhrase, ProgInfoList::kLevel1);
865 
866  s.clear();
867  if (m_progInfo.GetFindID())
868  {
869  QDateTime fdate(QDate(1970, 1, 1),QTime(12,0,0));
870  fdate = fdate.addDays((int)m_progInfo.GetFindID() - 719528);
871  s = QString("%1 (%2)").arg(m_progInfo.GetFindID())
872  .arg(MythDate::toString(
874  }
875  addItem(tr("Find ID"), s, ProgInfoList::kLevel2);
876 
877  addItem(tr("Last Recorded"), lastRecorded, ProgInfoList::kLevel2);
878  addItem(tr("Next Recording"), nextRecording, ProgInfoList::kLevel2);
879  addItem(tr("Average Time Shift"), averageTimeShift, ProgInfoList::kLevel2);
880  addItem(tr("Watch List Score"), watchListScore, ProgInfoList::kLevel2);
881  addItem(tr("Watch List Status"), watchListStatus, ProgInfoList::kLevel2);
882 
883  QString recordingHost;
884  QString recordingInput;
885  QString recordedID;
886  QString recordedPathname;
887  QString recordedFilename;
888  QString recordedFileSize;
889  QString recordingGroup;
890  QString storageGroup;
891  QString playbackGroup;
892  QString recordingProfile;
893 
894  recordingHost = m_progInfo.GetHostname();
895  recordingInput = m_progInfo.GetInputName();
896 
897  if (recorded)
898  {
899  recordedID = QString::number(m_progInfo.GetRecordingID());
900  recordedPathname = m_progInfo.GetPathname();
901  recordedFilename = m_progInfo.GetBasename();
902  recordedFileSize = QString("%1 ")
903  .arg(m_progInfo.GetFilesize()/((double)(1<<30)),0,'f',2);
904  recordedFileSize += tr("GB", "GigaBytes");
905 
906  query.prepare("SELECT profile FROM recorded"
907  " WHERE chanid = :CHANID"
908  " AND starttime = :STARTTIME;");
909  query.bindValue(":CHANID", m_progInfo.GetChanID());
910  query.bindValue(":STARTTIME", m_progInfo.GetRecordingStartTime());
911 
912  if (query.exec() && query.next())
913  {
914  recordingProfile = ProgramInfo::i18n(query.value(0).toString());
915  }
916  recordingGroup = ProgramInfo::i18n(m_progInfo.GetRecordingGroup());
917  storageGroup = ProgramInfo::i18n(m_progInfo.GetStorageGroup());
918  playbackGroup = ProgramInfo::i18n(m_progInfo.GetPlaybackGroup());
919  }
920  else if (m_progInfo.GetRecordingRuleID())
921  {
922  recordingProfile = record ? record->m_recProfile : tr("Unknown");
923  }
924  addItem(tr("Recording Host"), recordingHost, ProgInfoList::kLevel2);
925  addItem(tr("Recording Input"), recordingInput, ProgInfoList::kLevel2);
926  addItem(tr("Recorded ID"), recordedID, ProgInfoList::kLevel2);
927  addItem(tr("Recorded Pathname"), recordedPathname, ProgInfoList::kLevel2);
928  addItem(tr("Recorded File Name"), recordedFilename, ProgInfoList::kLevel1);
929  addItem(tr("Recorded File Size"), recordedFileSize, ProgInfoList::kLevel1);
930  addItem(tr("Recording Profile"), recordingProfile, ProgInfoList::kLevel2);
931  addItem(tr("Recording Group"), recordingGroup, ProgInfoList::kLevel1);
932  addItem(tr("Storage Group"), storageGroup, ProgInfoList::kLevel2);
933  addItem(tr("Playback Group"), playbackGroup, ProgInfoList::kLevel2);
934 
935  PowerPriorities(ptable);
936 
937  delete record;
938 }
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:215
RecordingRule::LoadByProgram
bool LoadByProgram(const ProgramInfo *proginfo)
Definition: recordingrule.cpp:170
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
RecStatus::Type
Type
Definition: recordingstatus.h:16
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
ProgDetails::m_progInfo
ProgramInfo m_progInfo
Definition: progdetails.h:36
ProgDetails::~ProgDetails
~ProgDetails() override
Definition: progdetails.cpp:93
MythDate::toString
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:214
ProgramInfo::GetFilesize
virtual uint64_t GetFilesize(void) const
Definition: programinfo.cpp:6479
RecordingRule::m_description
QString m_description
Definition: recordingrule.h:81
ProgramInfo::GetHostname
QString GetHostname(void) const
Definition: programinfo.h:422
ProgramInfo::GetInputName
QString GetInputName(void) const
Definition: programinfo.h:468
ProgInfoList::DataItem
std::tuple< QString, QString, int > DataItem
Definition: proginfolist.h:20
mythdb.h
wlEarlier
@ wlEarlier
Definition: programtypes.h:187
MythDate::as_utc
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
Definition: mythdate.cpp:28
ProgDetails::addItem
void addItem(const QString &title, const QString &value, ProgInfoList::VisibleLevel level)
Definition: progdetails.cpp:111
RecStatus::NeverRecord
@ NeverRecord
Definition: recordingstatus.h:43
ProgDetails::updatePage
void updatePage(void)
Definition: progdetails.cpp:103
ProgramInfo::GetRecordingID
uint GetRecordingID(void) const
Definition: programinfo.h:450
RecordingRule::m_title
QString m_title
Definition: recordingrule.h:77
mythdialogbox.h
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
kNotRecording
@ kNotRecording
Definition: recordingtypes.h:21
ProgramInfo::GetCategory
QString GetCategory(void) const
Definition: programinfo.h:370
RecordingInfo::kReLeadingAnd
static const QRegularExpression kReLeadingAnd
Definition: recordinginfo.h:200
RecStatus::Unknown
@ Unknown
Definition: recordingstatus.h:32
RecordingRule
Internal representation of a recording rule, mirrors the record table.
Definition: recordingrule.h:28
ProgDetails::Create
bool Create(void) override
Definition: progdetails.cpp:20
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
RecStatus::Recorded
@ Recorded
Definition: recordingstatus.h:29
ProgramInfo::GetScheduledEndTime
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:398
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
kSingleRecord
@ kSingleRecord
Definition: recordingtypes.h:22
ProgramInfo::GetRecordingGroup
QString GetRecordingGroup(void) const
Definition: programinfo.h:420
progdetails.h
hardwareprofile.distros.mythtv_data.data_mythtv.prefix
string prefix
Definition: data_mythtv.py:37
ProgramInfo::GetRecordingStartTime
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:405
ProgramInfo::GetPathname
QString GetPathname(void) const
Definition: programinfo.h:344
ProgDetails::PowerPriorities
void PowerPriorities(const QString &ptable)
Definition: progdetails.cpp:155
ProgInfoList::PageUp
void PageUp(void)
Definition: proginfolist.h:34
RecStatus::toString
static QString toString(RecStatus::Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
Definition: recordingstatus.cpp:40
ProgDetails::getRatings
static QString getRatings(bool recorded, uint chanid, const QDateTime &startts)
Definition: progdetails.cpp:39
ProgramInfo::IsRepeat
bool IsRepeat(void) const
Definition: programinfo.h:492
RecStatus::WillRecord
@ WillRecord
Definition: recordingstatus.h:31
mythdate.h
ProgDetails::loadPage
void loadPage(void)
Definition: progdetails.cpp:323
ProgramInfo::GetScheduledStartTime
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:391
mythlogging.h
ProgramInfo::GetRecordingStatus
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:451
ProgInfoList::PageDown
void PageDown(void)
Definition: proginfolist.h:33
MythMainWindow::TranslateKeyPress
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
Definition: mythmainwindow.cpp:1111
ProgInfoList::Create
bool Create(bool focusable)
Initialise buttonlist from XML.
Definition: proginfolist.cpp:14
RecStatus::PreviousRecording
@ PreviousRecording
Definition: recordingstatus.h:34
ProgInfoList::Toggle
void Toggle(void)
Toggle infolist state. Focusable widgets toggle between Basic & Full info. Non-focusable widgets togg...
Definition: proginfolist.cpp:31
ProgramInfo::GetTitle
QString GetTitle(void) const
Definition: programinfo.h:362
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
ProgramInfo::GetDescription
QString GetDescription(void) const
Definition: programinfo.h:366
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
ProgramInfo::GetEpisodeTotal
uint GetEpisodeTotal(void) const
Definition: programinfo.h:369
MythScreenType::BuildFocusList
void BuildFocusList(void)
Definition: mythscreentype.cpp:204
hardwareprofile.scan.rating
def rating(profile, smoonURL, gate)
Definition: scan.py:36
RecordingRule::m_recProfile
QString m_recProfile
Definition: recordingrule.h:120
kManualSearch
@ kManualSearch
Definition: recordingtypes.h:84
RecordingRule::m_searchType
RecSearchType m_searchType
Definition: recordingrule.h:112
ProgramInfo::toString
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
Definition: programinfo.cpp:1955
ProgInfoList::Display
void Display(const DataList &data)
Build list of key:value buttons.
Definition: proginfolist.cpp:98
ProgramInfo::GetPlaybackGroup
QString GetPlaybackGroup(void) const
Definition: programinfo.h:421
ProgramInfo::GetFindID
uint GetFindID(void) const
Definition: programinfo.h:472
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
ProgramInfo::GetSeriesID
QString GetSeriesID(void) const
Definition: programinfo.h:439
ProgramInfo::i18n
static QString i18n(const QString &msg)
Translations for play,recording, & storage groups +.
Definition: programinfo.cpp:5487
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:918
ProgramInfo::GetStorageGroup
QString GetStorageGroup(void) const
Definition: programinfo.h:423
ProgramInfo::GetOriginalAirDate
QDate GetOriginalAirDate(void) const
Definition: programinfo.h:432
ProgInfoList::kLevel1
@ kLevel1
Definition: proginfolist.h:17
RecStatus::Pending
@ Pending
Definition: recordingstatus.h:17
mythuihelper.h
ProgDetails::Init
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
Definition: progdetails.cpp:98
ProgDetails::m_data
ProgInfoList::DataList m_data
Definition: progdetails.h:38
ProgramInfo::GetChanID
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:373
ProgramInfo::GetRecordingRuleType
RecordingType GetRecordingRuleType(void) const
Definition: programinfo.h:455
ProgramInfo::GetEpisode
uint GetEpisode(void) const
Definition: programinfo.h:368
ProgDetails::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: progdetails.cpp:120
ProgramInfo::kTitleSubtitle
@ kTitleSubtitle
Definition: programinfo.h:514
MythScreenType::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythscreentype.cpp:402
MythDate::kAddYear
@ kAddYear
Add year to string if not included.
Definition: mythdate.h:25
ProgramInfo::GetSeason
uint GetSeason(void) const
Definition: programinfo.h:367
mythcorecontext.h
wlExpireOff
@ wlExpireOff
Definition: programtypes.h:189
XMLParseBase::LoadWindowFromXML
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
Definition: xmlparsebase.cpp:701
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
ProgInfoList::kLevel2
@ kLevel2
Definition: proginfolist.h:17
wlDeleted
@ wlDeleted
Definition: programtypes.h:186
RecordingType
RecordingType
Definition: recordingtypes.h:19
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:104
build_compdb.action
action
Definition: build_compdb.py:9
MythDate::kDateFull
@ kDateFull
Default local time.
Definition: mythdate.h:19
ProgramInfo::GetProgramID
QString GetProgramID(void) const
Definition: programinfo.h:440
ProgramInfo::GetRecordingPriority2
int GetRecordingPriority2(void) const
Definition: programinfo.h:445
ProgramInfo::GetRecordingRuleID
uint GetRecordingRuleID(void) const
Definition: programinfo.h:453
recordingrule.h
ProgramInfo::GetBasename
QString GetBasename(void) const
Definition: programinfo.h:345
mythmainwindow.h
ProgInfoList::Hide
bool Hide(void)
Remove infolist from display.
Definition: proginfolist.cpp:61
ProgDetails::m_infoList
ProgInfoList m_infoList
Definition: progdetails.h:37
ProgInfoList::VisibleLevel
VisibleLevel
Definition: proginfolist.h:17
wlWatched
@ wlWatched
Definition: programtypes.h:188
uint
unsigned int uint
Definition: freesurround.h:24
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
ProgramInfo::GetSubtitle
QString GetSubtitle(void) const
Definition: programinfo.h:364