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
10#include "libmythbase/mythdb.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
39QString 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{
96}
97
99{
100 updatePage();
101}
102
104{
105 if (m_data.isEmpty())
106 loadPage();
107
109}
110
111void 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
120bool 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 {
133 updatePage();
134 }
135 else if (action == "DOWN")
136 {
138 }
139 else if (action == "UP")
140 {
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
155void 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
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{
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
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
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())
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
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())
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 }
919 }
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}
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
int GetNumSetting(const QString &key, int defaultval=0)
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
void BuildFocusList(void)
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
static QString getRatings(bool recorded, uint chanid, const QDateTime &startts)
Definition: progdetails.cpp:39
ProgInfoList m_infoList
Definition: progdetails.h:37
void PowerPriorities(const QString &ptable)
void addItem(const QString &title, const QString &value, ProgInfoList::VisibleLevel level)
void updatePage(void)
ProgInfoList::DataList m_data
Definition: progdetails.h:38
~ProgDetails() override
Definition: progdetails.cpp:93
void loadPage(void)
ProgramInfo m_progInfo
Definition: progdetails.h:36
bool Create(void) override
Definition: progdetails.cpp:20
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
void PageDown(void)
Definition: proginfolist.h:33
std::tuple< QString, QString, int > DataItem
Definition: proginfolist.h:20
void PageUp(void)
Definition: proginfolist.h:34
bool Create(bool focusable)
Initialise buttonlist from XML.
void Display(const DataList &data)
Build list of key:value buttons.
bool Hide(void)
Remove infolist from display.
void Toggle(void)
Toggle infolist state. Focusable widgets toggle between Basic & Full info. Non-focusable widgets togg...
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:373
uint GetRecordingRuleID(void) const
Definition: programinfo.h:453
QString GetBasename(void) const
Definition: programinfo.h:345
QString GetSeriesID(void) const
Definition: programinfo.h:439
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
uint GetEpisode(void) const
Definition: programinfo.h:368
QString GetProgramID(void) const
Definition: programinfo.h:440
QString GetRecordingGroup(void) const
Definition: programinfo.h:420
uint GetRecordingID(void) const
Definition: programinfo.h:450
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:398
bool IsRepeat(void) const
Definition: programinfo.h:492
QString GetHostname(void) const
Definition: programinfo.h:422
QString GetPlaybackGroup(void) const
Definition: programinfo.h:421
QString GetDescription(void) const
Definition: programinfo.h:366
QString GetStorageGroup(void) const
Definition: programinfo.h:423
QString GetTitle(void) const
Definition: programinfo.h:362
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:405
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:391
uint GetFindID(void) const
Definition: programinfo.h:472
QString GetInputName(void) const
Definition: programinfo.h:468
static QString i18n(const QString &msg)
Translations for play,recording, & storage groups +.
QString GetPathname(void) const
Definition: programinfo.h:344
QDate GetOriginalAirDate(void) const
Definition: programinfo.h:432
int GetRecordingPriority2(void) const
Definition: programinfo.h:445
virtual uint64_t GetFilesize(void) const
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:451
QString GetSubtitle(void) const
Definition: programinfo.h:364
QString GetCategory(void) const
Definition: programinfo.h:370
uint GetSeason(void) const
Definition: programinfo.h:367
RecordingType GetRecordingRuleType(void) const
Definition: programinfo.h:455
uint GetEpisodeTotal(void) const
Definition: programinfo.h:369
static QString toString(RecStatus::Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
static const QRegularExpression kReLeadingAnd
Internal representation of a recording rule, mirrors the record table.
Definition: recordingrule.h:30
RecSearchType m_searchType
bool LoadByProgram(const ProgramInfo *proginfo)
QString m_description
Definition: recordingrule.h:82
QString m_title
Definition: recordingrule.h:78
QString m_recProfile
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
unsigned int uint
Definition: freesurround.h:24
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
Definition: mythdate.cpp:28
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ kDateFull
Default local time.
Definition: mythdate.h:19
@ kAddYear
Add year to string if not included.
Definition: mythdate.h:25
def rating(profile, smoonURL, gate)
Definition: scan.py:36
@ wlDeleted
Definition: programtypes.h:186
@ wlEarlier
Definition: programtypes.h:187
@ wlWatched
Definition: programtypes.h:188
@ wlExpireOff
Definition: programtypes.h:189
@ kManualSearch
RecordingType
@ kNotRecording
@ kSingleRecord