MythTV  master
programinfo.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 // POSIX headers
4 #include <unistd.h> // for getpid()
5 
6 // C++ headers
7 #include <algorithm>
8 
9 // Qt headers
10 #include <QMap>
11 #include <QUrl>
12 #include <QFile>
13 #include <QFileInfo>
14 #include <QDir>
15 #include <QTimeZone>
16 
17 // MythTV headers
18 #include "libmythbase/compat.h"
19 #include "libmythbase/mythcdrom.h"
21 #include "libmythbase/mythdb.h"
26 #include "libmythbase/remotefile.h"
28 #include "libmythbase/stringutil.h"
29 
30 #include "programinfo.h"
31 #include "programinfoupdater.h"
32 #include "remoteutil.h"
33 
34 #define LOC QString("ProgramInfo(%1): ").arg(GetBasename())
35 
36 //#define DEBUG_IN_USE
37 
38 static int init_tr(void);
39 
45 
46 static constexpr uint kInvalidDateTime { UINT_MAX };
47 static constexpr int64_t kLastUpdateOffset { 61LL * 60 };
48 
49 #define DEFINE_FLAGS_NAMES
51 #undef DEFINE_FLAGS_NAMES
52 
53 const QString ProgramInfo::kFromRecordedQuery =
54  "SELECT r.title, r.subtitle, r.description, "// 0-2
55  " r.season, r.episode, r.category, "// 3-5
56  " r.chanid, c.channum, c.callsign, "// 6-8
57  " c.name, c.outputfilters,r.recgroup, "// 9-11
58  " r.playgroup, r.storagegroup, r.basename, "//12-14
59  " r.hostname, r.recpriority, r.seriesid, "//15-17
60  " r.programid, r.inetref, r.filesize, "//18-20
61  " r.progstart, r.progend, r.stars, "//21-23
62  " r.starttime, r.endtime, p.airdate+0, "//24-26
63  " r.originalairdate, r.lastmodified, r.recordid, "//27-29
64  " c.commmethod, r.commflagged, r.previouslyshown, "//30-32
65  " r.transcoder, r.transcoded, r.deletepending, "//33-35
66  " r.preserve, r.cutlist, r.autoexpire, "//36-38
67  " r.editing, r.bookmark, r.watched, "//39-41
68  " p.audioprop+0, p.videoprop+0, p.subtitletypes+0, "//42-44
69  " r.findid, rec.dupin, rec.dupmethod, "//45-47
70  " p.syndicatedepisodenumber, p.partnumber, p.parttotal, "//48-50
71  " p.season, p.episode, p.totalepisodes, "//51-53
72  " p.category_type, r.recordedid, r.inputname, "//54-56
73  " r.bookmarkupdate, r.lastplay "//57-58
74  "FROM recorded AS r "
75  "LEFT JOIN channel AS c "
76  "ON (r.chanid = c.chanid) "
77  "LEFT JOIN recordedprogram AS p "
78  "ON (r.chanid = p.chanid AND "
79  " r.progstart = p.starttime) "
80  "LEFT JOIN record AS rec "
81  "ON (r.recordid = rec.recordid) ";
82 
83 static void set_flag(uint32_t &flags, int flag_to_set, bool is_set)
84 {
85  flags &= ~flag_to_set;
86  if (is_set)
87  flags |= flag_to_set;
88 }
89 
90 static QString determineURLType(const QString& url)
91 {
92  QString result = url;
93 
94  if (!url.startsWith("dvd:") && !url.startsWith("bd:"))
95  {
96  if(url.endsWith(".img", Qt::CaseInsensitive) ||
97  url.endsWith(".iso", Qt::CaseInsensitive))
98  {
99  switch (MythCDROM::inspectImage(url))
100  {
101  case MythCDROM::kBluray:
102  result = "bd:" + url;
103  break;
104 
105  case MythCDROM::kDVD:
106  result = "dvd:" + url;
107  break;
108 
109  case MythCDROM::kUnknown:
110  // Quiet compiler warning.
111  break;
112  }
113  }
114  else
115  {
116  if (QDir(url + "/BDMV").exists())
117  result = "bd:" + url;
118  else if (QDir(url + "/VIDEO_TS").exists())
119  result = "dvd:" + url;
120  }
121  }
122 
123  return result;
124 }
125 
126 static const std::array<const QString,ProgramInfo::kNumCatTypes> kCatName
127 { "", "movie", "series", "sports", "tvshow" };
128 
130 {
131  if ((category_type > ProgramInfo::kCategoryNone) &&
132  (category_type < kCatName.size()))
133  return kCatName[category_type];
134 
135  return "";
136 }
137 
139 {
140  for (size_t i = 1; i < kCatName.size(); i++)
141  if (category_type == kCatName[i])
142  return (ProgramInfo::CategoryType) i;
144 }
145 
150  m_title(other.m_title),
151  m_sortTitle(other.m_sortTitle),
152  m_subtitle(other.m_subtitle),
153  m_sortSubtitle(other.m_sortSubtitle),
154  m_description(other.m_description),
155  m_season(other.m_season),
156  m_episode(other.m_episode),
157  m_totalEpisodes(other.m_totalEpisodes),
158  m_syndicatedEpisode(other.m_syndicatedEpisode),
159  m_category(other.m_category),
160  m_director(other.m_director),
161 
162  m_recPriority(other.m_recPriority),
163 
164  m_chanId(other.m_chanId),
165  m_chanStr(other.m_chanStr),
166  m_chanSign(other.m_chanSign),
167  m_chanName(other.m_chanName),
168  m_chanPlaybackFilters(other.m_chanPlaybackFilters),
169 
170  m_recGroup(other.m_recGroup),
171  m_playGroup(other.m_playGroup),
172 
173  m_pathname(other.m_pathname),
174 
175  m_hostname(other.m_hostname),
176  m_storageGroup(other.m_storageGroup),
177 
178  m_seriesId(other.m_seriesId),
179  m_programId(other.m_programId),
180  m_inetRef(other.m_inetRef),
181  m_catType(other.m_catType),
182 
183  m_fileSize(other.m_fileSize),
184 
185  m_startTs(other.m_startTs),
186  m_endTs(other.m_endTs),
187  m_recStartTs(other.m_recStartTs),
188  m_recEndTs(other.m_recEndTs),
189 
190  m_stars(other.m_stars),
191 
192  m_originalAirDate(other.m_originalAirDate),
193  m_lastModified(other.m_lastModified),
194  m_lastInUseTime(MythDate::current().addSecs(-kLastInUseOffset)),
195 
196  m_recPriority2(other.m_recPriority2),
197  m_recordId(other.m_recordId),
198  m_parentId(other.m_parentId),
199 
200  m_sourceId(other.m_sourceId),
201  m_inputId(other.m_inputId),
202 
203  m_findId(other.m_findId),
204  m_programFlags(other.m_programFlags),
205  m_videoProperties(other.m_videoProperties),
206  m_audioProperties(other.m_audioProperties),
207  m_subtitleProperties(other.m_subtitleProperties),
208  m_year(other.m_year),
209  m_partNumber(other.m_partNumber),
210  m_partTotal(other.m_partTotal),
211 
212  m_recStatus(other.m_recStatus),
213  m_recType(other.m_recType),
214  m_dupIn(other.m_dupIn),
215  m_dupMethod(other.m_dupMethod),
216 
217  m_recordedId(other.m_recordedId),
218  m_inputName(other.m_inputName),
219  m_bookmarkUpdate(other.m_bookmarkUpdate),
220 
221  // everything below this line is not serialized
222  m_availableStatus(other.m_availableStatus),
223  m_spread(other.m_spread),
224  m_startCol(other.m_startCol),
225 
226  // Private
227  m_positionMapDBReplacement(other.m_positionMapDBReplacement)
228 {
230 }
231 
236 {
238 
239  MSqlQuery query(MSqlQuery::InitCon());
240  query.prepare(
241  "SELECT chanid, starttime "
242  "FROM recorded "
243  "WHERE recordedid = :RECORDEDID");
244  query.bindValue(":RECORDEDID", _recordedid);
245 
246  if (query.exec() && query.next())
247  {
248  uint _chanid = query.value(0).toUInt();
249  QDateTime _recstartts = MythDate::as_utc(query.value(1).toDateTime());
250  LoadProgramFromRecorded(_chanid, _recstartts);
251  }
252  else
253  {
254  LOG(VB_GENERAL, LOG_CRIT, LOC +
255  QString("Failed to find recorded entry for %1.")
256  .arg(_recordedid));
258  }
259 
261 }
262 
266 ProgramInfo::ProgramInfo(uint _chanid, const QDateTime &_recstartts)
267 {
269 
270  LoadProgramFromRecorded(_chanid, _recstartts);
272 }
273 
278  uint _recordedid,
279  QString _title,
280  QString _sortTitle,
281  QString _subtitle,
282  QString _sortSubtitle,
283  QString _description,
284  uint _season,
285  uint _episode,
286  uint _totalepisodes,
287  QString _syndicatedepisode,
288  QString _category,
289 
290  uint _chanid,
291  QString _channum,
292  QString _chansign,
293  QString _channame,
294  QString _chanplaybackfilters,
295 
296  QString _recgroup,
297  QString _playgroup,
298 
299  const QString &_pathname,
300 
301  QString _hostname,
302  QString _storagegroup,
303 
304  QString _seriesid,
305  QString _programid,
306  QString _inetref,
307  CategoryType _catType,
308 
309  int _recpriority,
310 
311  uint64_t _filesize,
312 
313  QDateTime _startts,
314  QDateTime _endts,
315  QDateTime _recstartts,
316  QDateTime _recendts,
317 
318  float _stars,
319 
320  uint _year,
321  uint _partnumber,
322  uint _parttotal,
323  QDate _originalAirDate,
324  QDateTime _lastmodified,
325 
326  RecStatus::Type _recstatus,
327 
328  uint _recordid,
329 
330  RecordingDupInType _dupin,
331  RecordingDupMethodType _dupmethod,
332 
333  uint _findid,
334 
335  uint _programflags,
336  uint _audioproperties,
337  uint _videoproperties,
338  uint _subtitleType,
339  QString _inputname,
340  QDateTime _bookmarkupdate) :
341  m_title(std::move(_title)),
342  m_sortTitle(std::move(_sortTitle)),
343  m_subtitle(std::move(_subtitle)),
344  m_sortSubtitle(std::move(_sortSubtitle)),
345  m_description(std::move(_description)),
346  m_season(_season),
347  m_episode(_episode),
348  m_totalEpisodes(_totalepisodes),
349  m_syndicatedEpisode(std::move(_syndicatedepisode)),
350  m_category(std::move(_category)),
351 
352  m_recPriority(_recpriority),
353 
354  m_chanId(_chanid),
355  m_chanStr(std::move(_channum)),
356  m_chanSign(std::move(_chansign)),
357  m_chanName(std::move(_channame)),
358  m_chanPlaybackFilters(std::move(_chanplaybackfilters)),
359 
360  m_recGroup(std::move(_recgroup)),
361  m_playGroup(std::move(_playgroup)),
362 
363  m_pathname(_pathname),
364 
365  m_hostname(std::move(_hostname)),
366  m_storageGroup(std::move(_storagegroup)),
367 
368  m_seriesId(std::move(_seriesid)),
369  m_programId(std::move(_programid)),
370  m_inetRef(std::move(_inetref)),
371  m_catType(_catType),
372 
373  m_fileSize(_filesize),
374 
375  m_startTs(std::move(_startts)),
376  m_endTs(std::move(_endts)),
377  m_recStartTs(std::move(_recstartts)),
378  m_recEndTs(std::move(_recendts)),
379 
380  m_stars(std::clamp(_stars, 0.0F, 1.0F)),
381 
382  m_originalAirDate(_originalAirDate),
383  m_lastModified(std::move(_lastmodified)),
384  m_lastInUseTime(MythDate::current().addSecs(-kLastInUseOffset)),
385 
386  m_recordId(_recordid),
387  m_findId(_findid),
388 
389  m_programFlags(_programflags),
390  m_videoProperties(_videoproperties),
391  m_audioProperties(_audioproperties),
392  m_subtitleProperties(_subtitleType),
393  m_year(_year),
394  m_partNumber(_partnumber),
395  m_partTotal(_parttotal),
396 
397  m_recStatus(_recstatus),
398  m_dupIn(_dupin),
399  m_dupMethod(_dupmethod),
400 
401  m_recordedId(_recordedid),
402  m_inputName(std::move(_inputname)),
403  m_bookmarkUpdate(std::move(_bookmarkupdate))
404 {
405  if (m_originalAirDate.isValid() && m_originalAirDate < QDate(1895, 12, 28))
406  m_originalAirDate = QDate();
407 
408  SetPathname(_pathname);
410 }
411 
416  QString _title,
417  QString _sortTitle,
418  QString _subtitle,
419  QString _sortSubtitle,
420  QString _description,
421  uint _season,
422  uint _episode,
423  QString _category,
424 
425  uint _chanid,
426  QString _channum,
427  QString _chansign,
428  QString _channame,
429 
430  QString _seriesid,
431  QString _programid,
432  QString _inetref,
433 
434  QDateTime _startts,
435  QDateTime _endts,
436  QDateTime _recstartts,
437  QDateTime _recendts,
438 
439  RecStatus::Type _recstatus,
440 
441  uint _recordid,
442 
443  RecordingType _rectype,
444 
445  uint _findid,
446 
447  bool duplicate) :
448  m_title(std::move(_title)),
449  m_sortTitle(std::move(_sortTitle)),
450  m_subtitle(std::move(_subtitle)),
451  m_sortSubtitle(std::move(_sortSubtitle)),
452  m_description(std::move(_description)),
453  m_season(_season),
454  m_episode(_episode),
455  m_category(std::move(_category)),
456 
457  m_chanId(_chanid),
458  m_chanStr(std::move(_channum)),
459  m_chanSign(std::move(_chansign)),
460  m_chanName(std::move(_channame)),
461 
462  m_seriesId(std::move(_seriesid)),
463  m_programId(std::move(_programid)),
464  m_inetRef(std::move(_inetref)),
465 
466  m_startTs(std::move(_startts)),
467  m_endTs(std::move(_endts)),
468  m_recStartTs(std::move(_recstartts)),
469  m_recEndTs(std::move(_recendts)),
470 
471  m_lastModified(m_startTs),
472  m_lastInUseTime(MythDate::current().addSecs(-kLastInUseOffset)),
473 
474  m_recordId(_recordid),
475  m_findId(_findid),
476 
477  m_programFlags((duplicate) ? FL_DUPLICATE : FL_NONE),
478 
479  m_recStatus(_recstatus),
480  m_recType(_rectype),
481  m_dupIn(kDupsUnset),
482  m_dupMethod(kDupCheckUnset)
483 {
485 }
486 
491  QString _title,
492  QString _sortTitle,
493  QString _subtitle,
494  QString _sortSubtitle,
495  QString _description,
496  QString _syndicatedepisode,
497  QString _category,
498 
499  uint _chanid,
500  QString _channum,
501  QString _chansign,
502  QString _channame,
503  QString _chanplaybackfilters,
504 
505  QDateTime _startts,
506  QDateTime _endts,
507  QDateTime _recstartts,
508  QDateTime _recendts,
509 
510  QString _seriesid,
511  QString _programid,
512  const CategoryType _catType,
513 
514  float _stars,
515  uint _year,
516  uint _partnumber,
517  uint _parttotal,
518 
519  QDate _originalAirDate,
520  RecStatus::Type _recstatus,
521  uint _recordid,
522  RecordingType _rectype,
523  uint _findid,
524 
525  bool commfree,
526  bool repeat,
527 
528  uint _videoproperties,
529  uint _audioproperties,
530  uint _subtitleType,
531 
532  uint _season,
533  uint _episode,
534  uint _totalepisodes,
535 
536  const ProgramList &schedList) :
537  m_title(std::move(_title)),
538  m_sortTitle(std::move(_sortTitle)),
539  m_subtitle(std::move(_subtitle)),
540  m_sortSubtitle(std::move(_sortSubtitle)),
541  m_description(std::move(_description)),
542  m_season(_season),
543  m_episode(_episode),
544  m_totalEpisodes(_totalepisodes),
545  m_syndicatedEpisode(std::move(_syndicatedepisode)),
546  m_category(std::move(_category)),
547 
548  m_chanId(_chanid),
549  m_chanStr(std::move(_channum)),
550  m_chanSign(std::move(_chansign)),
551  m_chanName(std::move(_channame)),
552  m_chanPlaybackFilters(std::move(_chanplaybackfilters)),
553 
554  m_seriesId(std::move(_seriesid)),
555  m_programId(std::move(_programid)),
556  m_catType(_catType),
557 
558  m_startTs(std::move(_startts)),
559  m_endTs(std::move(_endts)),
560  m_recStartTs(std::move(_recstartts)),
561  m_recEndTs(std::move(_recendts)),
562 
563  m_stars(std::clamp(_stars, 0.0F, 1.0F)),
564 
565  m_originalAirDate(_originalAirDate),
566  m_lastModified(m_startTs),
567  m_lastInUseTime(m_startTs.addSecs(-kLastInUseOffset)),
568 
569  m_recordId(_recordid),
570  m_findId(_findid),
571 
572  m_videoProperties(_videoproperties),
573  m_audioProperties(_audioproperties),
574  m_subtitleProperties(_subtitleType),
575  m_year(_year),
576  m_partNumber(_partnumber),
577  m_partTotal(_parttotal),
578 
579  m_recStatus(_recstatus),
580  m_recType(_rectype)
581 {
582  m_programFlags |= (commfree) ? FL_CHANCOMMFREE : FL_NONE;
583  m_programFlags |= (repeat) ? FL_REPEAT : FL_NONE;
584 
585  if (m_originalAirDate.isValid() && m_originalAirDate < QDate(1895, 12, 28))
586  m_originalAirDate = QDate();
587 
588  for (auto *it : schedList)
589  {
590  // If this showing is scheduled to be recorded, then we need to copy
591  // some of the information from the scheduler
592  //
593  // This applies even if the showing may be on a different channel
594  // to the one which is actually being recorded e.g. A regional or HD
595  // variant of the same channel
596  if (!IsSameProgramAndStartTime(*it))
597  continue;
598 
599  const ProgramInfo &s = *it;
601  m_recType = s.m_recType;
605  m_inputId = s.m_inputId;
606  m_dupIn = s.m_dupIn;
608  m_findId = s.m_findId;
612 
613  // This is the exact showing (same chanid or callsign)
614  // which will be recorded
615  if (IsSameChannel(s))
616  {
618  break;
619  }
620 
627  }
629 }
630 
635  QString _title,
636  QString _sortTitle,
637  QString _subtitle,
638  QString _sortSubtitle,
639  QString _description,
640  uint _season,
641  uint _episode,
642  uint _totalepisodes,
643  QString _category,
644 
645  uint _chanid,
646  QString _channum,
647  QString _chansign,
648  QString _channame,
649  QString _chanplaybackfilters,
650 
651  QString _recgroup,
652  QString _playgroup,
653 
654  QDateTime _startts,
655  QDateTime _endts,
656  QDateTime _recstartts,
657  QDateTime _recendts,
658 
659  QString _seriesid,
660  QString _programid,
661  QString _inetref,
662  QString _inputname) :
663  m_title(std::move(_title)),
664  m_sortTitle(std::move(_sortTitle)),
665  m_subtitle(std::move(_subtitle)),
666  m_sortSubtitle(std::move(_sortSubtitle)),
667  m_description(std::move(_description)),
668  m_season(_season),
669  m_episode(_episode),
670  m_totalEpisodes(_totalepisodes),
671  m_category(std::move(_category)),
672 
673  m_chanId(_chanid),
674  m_chanStr(std::move(_channum)),
675  m_chanSign(std::move(_chansign)),
676  m_chanName(std::move(_channame)),
677  m_chanPlaybackFilters(std::move(_chanplaybackfilters)),
678 
679  m_recGroup(std::move(_recgroup)),
680  m_playGroup(std::move(_playgroup)),
681 
682  m_seriesId(std::move(_seriesid)),
683  m_programId(std::move(_programid)),
684  m_inetRef(std::move(_inetref)),
685 
686  m_startTs(std::move(_startts)),
687  m_endTs(std::move(_endts)),
688  m_recStartTs(std::move(_recstartts)),
689  m_recEndTs(std::move(_recendts)),
690 
691  m_lastModified(MythDate::current()),
692  m_lastInUseTime(m_lastModified.addSecs(-kLastInUseOffset)),
693 
694  m_inputName(std::move(_inputname))
695 {
697 }
698 
702 ProgramInfo::ProgramInfo(const QString &_pathname)
703 {
705  if (_pathname.isEmpty())
706  {
707  return;
708  }
709 
710  uint _chanid = 0;
711  QDateTime _recstartts;
713  QueryKeyFromPathname(_pathname, _chanid, _recstartts) &&
714  LoadProgramFromRecorded(_chanid, _recstartts))
715  {
716  return;
717  }
718 
720 
721  QDateTime cur = MythDate::current();
722  m_recStartTs = m_startTs = cur.addSecs(-kLastInUseOffset - 1);
723  m_recEndTs = m_endTs = cur.addSecs(-1);
724 
725  QString basename = _pathname.section('/', -1);
726  if (_pathname == basename)
727  SetPathname(QDir::currentPath() + '/' + _pathname);
728  else if (_pathname.contains("./") && !_pathname.contains(":"))
729  SetPathname(QFileInfo(_pathname).absoluteFilePath());
730  else
731  SetPathname(_pathname);
733 }
734 
738 ProgramInfo::ProgramInfo(const QString &_pathname,
739  const QString &_plot,
740  const QString &_title,
741  const QString &_sortTitle,
742  const QString &_subtitle,
743  const QString &_sortSubtitle,
744  const QString &_director,
745  int _season, int _episode,
746  const QString &_inetref,
747  std::chrono::minutes length_in_minutes,
748  uint _year,
749  const QString &_programid)
750 {
752 
753  //NOLINTBEGIN(cppcoreguidelines-prefer-member-initializer)
754  // These have to remain after the call to ::clear().
755  m_title = _title;
756  m_sortTitle = _sortTitle;
757  m_subtitle = _subtitle;
758  m_sortSubtitle = _sortSubtitle;
759  m_description = _plot;
760  m_season = _season;
761  m_episode = _episode;
762  m_director = _director;
763  m_programId = _programid;
764  m_inetRef = _inetref;
765  m_year = _year;
766  //NOLINTEND(cppcoreguidelines-prefer-member-initializer)
767 
768  QDateTime cur = MythDate::current();
769  int64_t minutes = length_in_minutes.count();
770  m_recStartTs = cur.addSecs((minutes + 1) * -60);
771  m_recEndTs = m_recStartTs.addSecs(minutes * 60);
772 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
773  m_startTs = QDateTime(QDate(m_year,1,1),QTime(0,0,0), Qt::UTC);
774 #else
775  m_startTs = QDateTime(QDate(m_year,1,1),QTime(0,0,0),
776  QTimeZone(QTimeZone::UTC));
777 #endif
778  m_endTs = m_startTs.addSecs(minutes * 60);
779 
780  QString pn = _pathname;
781  if (!_pathname.startsWith("myth://"))
782  pn = determineURLType(_pathname);
783 
784  SetPathname(pn);
786 }
787 
791 ProgramInfo::ProgramInfo(const QString &_title, uint _chanid,
792  const QDateTime &_startts,
793  const QDateTime &_endts)
794 {
796 
797  MSqlQuery query(MSqlQuery::InitCon());
798  query.prepare(
799  "SELECT chanid, channum, callsign, name, outputfilters, commmethod "
800  "FROM channel "
801  "WHERE chanid=:CHANID");
802  query.bindValue(":CHANID", _chanid);
803  if (query.exec() && query.next())
804  {
805  m_chanStr = query.value(1).toString();
806  m_chanSign = query.value(2).toString();
807  m_chanName = query.value(3).toString();
808  m_chanPlaybackFilters = query.value(4).toString();
809  set_flag(m_programFlags, FL_CHANCOMMFREE,
810  query.value(5).toInt() == COMM_DETECT_COMMFREE);
811  }
812 
813  m_chanId = _chanid;
814  m_startTs = _startts;
815  m_endTs = _endts;
816 
817  m_title = _title;
818  if (m_title.isEmpty())
819  {
820  QString channelFormat =
821  gCoreContext->GetSetting("ChannelFormat", "<num> <sign>");
822 
823  m_title = QString("%1 - %2").arg(ChannelText(channelFormat),
825  }
826 
828  QString("%1 (%2)").arg(m_title, QObject::tr("Manual Record"));
830 }
831 
836 {
837  if (this == &other)
838  return *this;
839 
840  clone(other);
841  return *this;
842 }
843 
845 void ProgramInfo::clone(const ProgramInfo &other,
846  bool ignore_non_serialized_data)
847 {
848  bool is_same =
849  ((m_chanId != 0U) && m_recStartTs.isValid() && m_startTs.isValid() &&
850  m_chanId == other.m_chanId && m_recStartTs == other.m_recStartTs &&
851  m_startTs == other.m_startTs);
852 
853  m_title = other.m_title;
854  m_sortTitle = other.m_sortTitle;
855  m_subtitle = other.m_subtitle;
858  m_season = other.m_season;
859  m_episode = other.m_episode;
862  m_category = other.m_category;
863  m_director = other.m_director;
864 
865  m_chanId = other.m_chanId;
866  m_chanStr = other.m_chanStr;
867  m_chanSign = other.m_chanSign;
868  m_chanName = other.m_chanName;
870 
871  m_recGroup = other.m_recGroup;
872  m_playGroup = other.m_playGroup;
873 
874  if (!ignore_non_serialized_data || !is_same ||
875  (GetBasename() != other.GetBasename()))
876  {
877  m_pathname = other.m_pathname;
878  }
879 
880  m_hostname = other.m_hostname;
882 
883  m_seriesId = other.m_seriesId;
884  m_programId = other.m_programId;
885  m_inetRef = other.m_inetRef;
886  m_catType = other.m_catType;
887 
889 
890  m_fileSize = other.m_fileSize;
891 
892  m_startTs = other.m_startTs;
893  m_endTs = other.m_endTs;
894  m_recStartTs = other.m_recStartTs;
895  m_recEndTs = other.m_recEndTs;
896 
897  m_stars = other.m_stars;
898 
899  m_year = other.m_year;
900  m_partNumber = other.m_partNumber;
901  m_partTotal = other.m_partTotal;
902 
906 
907  m_recStatus = other.m_recStatus;
908 
910  m_recordId = other.m_recordId;
911  m_parentId = other.m_parentId;
912 
913  m_recType = other.m_recType;
914  m_dupIn = other.m_dupIn;
915  m_dupMethod = other.m_dupMethod;
916 
917  m_recordedId = other.m_recordedId;
918  m_inputName = other.m_inputName;
920 
921  m_sourceId = other.m_sourceId;
922  m_inputId = other.m_inputId;
923 
924  m_findId = other.m_findId;
929 
930  if (!ignore_non_serialized_data)
931  {
932  m_spread = other.m_spread;
933  m_startCol = other.m_startCol;
935 
938  }
939 }
940 
942 {
943  m_title.clear();
944  m_sortTitle.clear();
945  m_subtitle.clear();
946  m_sortSubtitle.clear();
947  m_description.clear();
948  m_season = 0;
949  m_episode = 0;
950  m_totalEpisodes = 0;
951  m_syndicatedEpisode.clear();
952  m_category.clear();
953  m_director.clear();
954 
955  m_chanId = 0;
956  m_chanStr.clear();
957  m_chanSign.clear();
958  m_chanName.clear();
959  m_chanPlaybackFilters.clear();
960 
961  m_recGroup = "Default";
962  m_playGroup = "Default";
963 
964  m_pathname.clear();
965 
966  m_hostname.clear();
967  m_storageGroup = "Default";
968 
969  m_year = 0;
970  m_partNumber = 0;
971  m_partTotal = 0;
972 
973  m_seriesId.clear();
974  m_programId.clear();
975  m_inetRef.clear();
977 
978  m_recPriority = 0;
979 
980  m_fileSize = 0ULL;
981 
983  m_endTs = m_startTs;
986 
987  m_stars = 0.0F;
988 
989  m_originalAirDate = QDate();
992 
994 
995  m_recPriority2 = 0;
996  m_recordId = 0;
997  m_parentId = 0;
998 
1000  m_dupIn = kDupsInAll;
1002 
1003  m_recordedId = 0;
1004  m_inputName.clear();
1005  m_bookmarkUpdate = QDateTime();
1006 
1007  m_sourceId = 0;
1008  m_inputId = 0;
1009 
1010  m_findId = 0;
1011 
1012  m_programFlags = FL_NONE;
1013  m_videoProperties = VID_UNKNOWN;
1014  m_audioProperties = AUD_UNKNOWN;
1015  m_subtitleProperties = SUB_UNKNOWN;
1016 
1017  // everything below this line is not serialized
1018  m_spread = -1;
1019  m_startCol = -1;
1021 
1022  // Private
1023  m_inUseForWhat.clear();
1024  m_positionMapDBReplacement = nullptr;
1025 }
1026 
1031 bool qstringEqualOrDefault(const QString& a, const QString& b);
1032 bool qstringEqualOrDefault(const QString& a, const QString& b)
1033 {
1034  if (a == b)
1035  return true;
1036  if (a.isEmpty() and (b == "Default"))
1037  return true;
1038  if ((a == "Default") and b.isEmpty())
1039  return true;
1040  return false;
1041 }
1042 
1054 {
1055  if ((m_title != rhs.m_title) ||
1056  (m_subtitle != rhs.m_subtitle) ||
1057  (m_description != rhs.m_description) ||
1058  (m_season != rhs.m_season) ||
1059  (m_episode != rhs.m_episode) ||
1060  (m_totalEpisodes != rhs.m_totalEpisodes) ||
1062  (m_category != rhs.m_category)
1063 #if 0
1064  || (m_director != rhs.m_director)
1065 #endif
1066  )
1067  return false;
1068 
1069  if (m_recPriority != rhs.m_recPriority)
1070  return false;
1071 
1072  if ((m_chanId != rhs.m_chanId) ||
1073  (m_chanStr != rhs.m_chanStr) ||
1074  (m_chanSign != rhs.m_chanSign) ||
1075  (m_chanName != rhs.m_chanName) ||
1077  return false;
1078 
1081  return false;
1082 
1083  if (m_pathname != rhs.m_pathname)
1084  return false;
1085 
1086  if ((m_hostname != rhs.m_hostname) ||
1088  return false;
1089 
1090  if ((m_seriesId != rhs.m_seriesId) ||
1091  (m_programId != rhs.m_programId) ||
1092  (m_inetRef != rhs.m_inetRef) ||
1093  (m_catType != rhs.m_catType))
1094  return false;
1095 
1096  if (m_fileSize != rhs.m_fileSize)
1097  return false;
1098 
1099  if ((m_startTs != rhs.m_startTs) ||
1100  (m_endTs != rhs.m_endTs) ||
1101  (m_recStartTs != rhs.m_recStartTs) ||
1102  (m_recEndTs != rhs.m_recEndTs))
1103  return false;
1104 
1105  if ((m_stars != rhs.m_stars) ||
1108 #if 0
1109  || (m_lastInUseTime != rhs.m_lastInUseTime)
1110 #endif
1111  )
1112  return false;
1113 
1114  if (m_recPriority2 != rhs.m_recPriority2)
1115  return false;
1116 
1117  if ((m_recordId != rhs.m_recordId) ||
1118  (m_parentId != rhs.m_parentId))
1119  return false;
1120 
1121  if ((m_sourceId != rhs.m_sourceId) ||
1122  (m_inputId != rhs.m_inputId) ||
1123  (m_findId != rhs.m_findId))
1124  return false;
1125 
1126  if ((m_programFlags != rhs.m_programFlags) ||
1130  (m_year != rhs.m_year) ||
1131  (m_partNumber != rhs.m_partNumber) ||
1132  (m_partTotal != rhs.m_partTotal))
1133  return false;
1134 
1135  if ((m_recStatus != rhs.m_recStatus) ||
1136  (m_recType != rhs.m_recType) ||
1137  (m_dupIn != rhs.m_dupIn) ||
1138  (m_dupMethod != rhs.m_dupMethod))
1139  return false;
1140 
1141  if ((m_recordedId != rhs.m_recordedId) ||
1142  (m_inputName != rhs.m_inputName) ||
1144  return false;
1145 
1146  return true;
1147 }
1148 
1153 {
1154  std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
1155 
1156  if (m_sortTitle.isEmpty() and not m_title.isEmpty())
1157  m_sortTitle = sh->doTitle(m_title);
1158  if (m_sortSubtitle.isEmpty() and not m_subtitle.isEmpty())
1160 }
1161 
1162 void ProgramInfo::SetTitle(const QString &t, const QString &st)
1163 {
1164  m_title = t;
1165  m_sortTitle = st;
1166  ensureSortFields();
1167 }
1168 
1169 void ProgramInfo::SetSubtitle(const QString &st, const QString &sst)
1170 {
1171  m_subtitle = st;
1172  m_sortSubtitle = sst;
1173  ensureSortFields();
1174 }
1175 
1178  uint chanid, const QDateTime &recstartts)
1179 {
1180  return QString("%1_%2").arg(chanid).arg(recstartts.toString(Qt::ISODate));
1181 }
1182 
1186 bool ProgramInfo::ExtractKey(const QString &uniquekey,
1187  uint &chanid, QDateTime &recstartts)
1188 {
1189  QStringList keyParts = uniquekey.split('_');
1190  if (keyParts.size() != 2)
1191  return false;
1192  chanid = keyParts[0].toUInt();
1193  recstartts = MythDate::fromString(keyParts[1]);
1194  return (chanid != 0U) && recstartts.isValid();
1195 }
1196 
1198  const QString &pathname, uint &chanid, QDateTime &recstartts)
1199 {
1200  QString basename = pathname.section('/', -1);
1201  if (basename.isEmpty())
1202  return false;
1203 
1204  QStringList lr = basename.split("_");
1205  if (lr.size() == 2)
1206  {
1207  chanid = lr[0].toUInt();
1208  QStringList ts = lr[1].split(".");
1209  if (chanid && !ts.empty())
1210  {
1211  recstartts = MythDate::fromString(ts[0]);
1212  return recstartts.isValid();
1213  }
1214  }
1215 
1216  return false;
1217 }
1218 
1220  const QString &pathname, uint &chanid, QDateTime &recstartts)
1221 {
1222  QString basename = pathname.section('/', -1);
1223  if (basename.isEmpty())
1224  return false;
1225 
1226  MSqlQuery query(MSqlQuery::InitCon());
1227  query.prepare(
1228  "SELECT chanid, starttime "
1229  "FROM recorded "
1230  "WHERE basename = :BASENAME");
1231  query.bindValue(":BASENAME", basename);
1232  if (query.exec() && query.next())
1233  {
1234  chanid = query.value(0).toUInt();
1235  recstartts = MythDate::as_utc(query.value(1).toDateTime());
1236  return true;
1237  }
1238 
1239  return ExtractKeyFromPathname(pathname, chanid, recstartts);
1240 }
1241 
1242 bool ProgramInfo::QueryRecordedIdFromPathname(const QString &pathname,
1243  uint &recordedid)
1244 {
1245  QString basename = pathname.section('/', -1);
1246  if (basename.isEmpty())
1247  return false;
1248 
1249  MSqlQuery query(MSqlQuery::InitCon());
1250  query.prepare(
1251  "SELECT recordedid "
1252  "FROM recorded "
1253  "WHERE basename = :BASENAME");
1254  query.bindValue(":BASENAME", basename);
1255  if (query.exec() && query.next())
1256  {
1257  recordedid = query.value(0).toUInt();
1258  return true;
1259  }
1260 
1261  return false;
1262 }
1263 
1264 static inline QString DateTimeToListInt(const QDateTime& x) {
1265  if (x.isValid())
1266  return QString::number(x.toSecsSinceEpoch());
1267  return QString::number(kInvalidDateTime);
1268 }
1269 
1276 void ProgramInfo::ToStringList(QStringList &list) const
1277 {
1278  list << m_title; // 0
1279  list << m_subtitle; // 1
1280  list << m_description; // 2
1281  list << QString::number(m_season ); // 3
1282  list << QString::number(m_episode ); // 4
1283  list << QString::number(m_totalEpisodes); // 5
1284  list << m_syndicatedEpisode; // 6
1285  list << m_category; // 7
1286  list << QString::number(m_chanId); // 8
1287  list << m_chanStr; // 9
1288  list << m_chanSign; // 10
1289  list << m_chanName; // 11
1290  list << m_pathname; // 12
1291  list << QString::number(m_fileSize); // 13
1292 
1293  list << DateTimeToListInt(m_startTs); // 14
1294  list << DateTimeToListInt(m_endTs); // 15
1295  list << QString::number(m_findId); // 16
1296  list << m_hostname; // 17
1297  list << QString::number(m_sourceId); // 18
1298  list << QString::number(m_inputId); // 19 (m_formerly cardid)
1299  list << QString::number(m_inputId); // 20
1300  list << QString::number(m_recPriority); // 21
1301  list << QString::number(m_recStatus); // 22
1302  list << QString::number(m_recordId); // 23
1303 
1304  list << QString::number(m_recType); // 24
1305  list << QString::number(m_dupIn); // 25
1306  list << QString::number(m_dupMethod); // 26
1307  list << DateTimeToListInt(m_recStartTs); // 27
1308  list << DateTimeToListInt(m_recEndTs); // 28
1309  list << QString::number(m_programFlags); // 29
1310  list << (!m_recGroup.isEmpty() ? m_recGroup : "Default"); // 30
1311  list << m_chanPlaybackFilters; // 31
1312  list << m_seriesId; // 32
1313  list << m_programId; // 33
1314  list << m_inetRef; // 34
1315 
1316  list << DateTimeToListInt(m_lastModified); // 35
1317  list << QString("%1").arg(m_stars); // 36
1318  list << m_originalAirDate.toString(Qt::ISODate); // 37
1319  list << (!m_playGroup.isEmpty() ? m_playGroup : "Default"); // 38
1320  list << QString::number(m_recPriority2); // 39
1321  list << QString::number(m_parentId); // 40
1322  list << (!m_storageGroup.isEmpty() ? m_storageGroup : "Default"); // 41
1323  list << QString::number(m_audioProperties); // 42
1324  list << QString::number(m_videoProperties); // 43
1325  list << QString::number(m_subtitleProperties); // 44
1326 
1327  list << QString::number(m_year); // 45
1328  list << QString::number(m_partNumber); // 46
1329  list << QString::number(m_partTotal); // 47
1330  list << QString::number(m_catType); // 48
1331 
1332  list << QString::number(m_recordedId); // 49
1333  list << m_inputName; // 50
1334  list << DateTimeToListInt(m_bookmarkUpdate); // 51
1335 /* do not forget to update the NUMPROGRAMLINES defines! */
1336 }
1337 
1338 // QStringList::const_iterator it = list.begin()+offset;
1339 
1340 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1341 #define NEXT_STR() do { if (it == listend) \
1342  { \
1343  LOG(VB_GENERAL, LOG_ERR, listerror); \
1344  clear(); \
1345  return false; \
1346  } \
1347  ts = *it++; } while (false)
1348 
1349 static inline QDateTime DateTimeFromListItem(const QString& str)
1350 {
1351  if (str.isEmpty() or (str.toUInt() == kInvalidDateTime))
1352  return {};
1353  return MythDate::fromSecsSinceEpoch(str.toLongLong());
1354 }
1355 
1356 static inline QDate DateFromListItem(const QString& str)
1357 {
1358  if (str.isEmpty() || (str == "0000-00-00"))
1359  return {};
1360  return QDate::fromString(str, Qt::ISODate);
1361 }
1362 
1363 
1375 bool ProgramInfo::FromStringList(QStringList::const_iterator &it,
1376  const QStringList::const_iterator& listend)
1377 {
1378  QString listerror = LOC + "FromStringList, not enough items in list.";
1379  QString ts;
1380 
1381  uint origChanid = m_chanId;
1382  QDateTime origRecstartts = m_recStartTs;
1383 
1384  NEXT_STR(); m_title = ts; // 0
1385  NEXT_STR(); m_subtitle = ts; // 1
1386  NEXT_STR(); m_description = ts; // 2
1387  NEXT_STR(); m_season = ts.toLongLong(); // 3
1388  NEXT_STR(); m_episode = ts.toLongLong(); // 4
1389  NEXT_STR(); m_totalEpisodes = ts.toLongLong(); // 5
1390  NEXT_STR(); m_syndicatedEpisode = ts; // 6
1391  NEXT_STR(); m_category = ts; // 7
1392  NEXT_STR(); m_chanId = ts.toLongLong(); // 8
1393  NEXT_STR(); m_chanStr = ts; // 9
1394  NEXT_STR(); m_chanSign = ts; // 10
1395  NEXT_STR(); m_chanName = ts; // 11
1396  NEXT_STR(); m_pathname = ts; // 12
1397  NEXT_STR(); m_fileSize = ts.toLongLong(); // 13
1398 
1399  NEXT_STR(); m_startTs = DateTimeFromListItem(ts); // 14
1400  NEXT_STR(); m_endTs = DateTimeFromListItem(ts); // 15
1401  NEXT_STR(); m_findId = ts.toLongLong(); // 16
1402  NEXT_STR(); m_hostname = ts; // 17
1403  NEXT_STR(); m_sourceId = ts.toLongLong(); // 18
1404  NEXT_STR(); // 19 (formerly cardid)
1405  NEXT_STR(); m_inputId = ts.toLongLong(); // 20
1406  NEXT_STR(); m_recPriority = ts.toLongLong(); // 21
1407  NEXT_STR(); m_recStatus = (RecStatus::Type)ts.toInt(); // 22
1408  NEXT_STR(); m_recordId = ts.toLongLong(); // 23
1409 
1410  NEXT_STR(); m_recType = (RecordingType)ts.toInt(); // 24
1411  NEXT_STR(); m_dupIn = (RecordingDupInType)ts.toInt(); // 25
1412  NEXT_STR(); m_dupMethod = (RecordingDupMethodType)ts.toInt(); // 26
1414  NEXT_STR(); m_recEndTs = DateTimeFromListItem(ts); // 28
1415  NEXT_STR(); m_programFlags = ts.toLongLong(); // 29
1416  NEXT_STR(); m_recGroup = ts; // 30
1417  NEXT_STR(); m_chanPlaybackFilters = ts; // 31
1418  NEXT_STR(); m_seriesId = ts; // 32
1419  NEXT_STR(); m_programId = ts; // 33
1420  NEXT_STR(); m_inetRef = ts; // 34
1421 
1423  NEXT_STR(); (m_stars) = ts.toFloat(); // 36
1425  NEXT_STR(); m_playGroup = ts; // 38
1426  NEXT_STR(); m_recPriority2 = ts.toLongLong(); // 39
1427  NEXT_STR(); m_parentId = ts.toLongLong(); // 40
1428  NEXT_STR(); m_storageGroup = ts; // 41
1429  NEXT_STR(); m_audioProperties = ts.toLongLong(); // 42
1430  NEXT_STR(); m_videoProperties = ts.toLongLong(); // 43
1431  NEXT_STR(); m_subtitleProperties = ts.toLongLong(); // 44
1432 
1433  NEXT_STR(); m_year = ts.toLongLong(); // 45
1434  NEXT_STR(); m_partNumber = ts.toLongLong(); // 46
1435  NEXT_STR(); m_partTotal = ts.toLongLong(); // 47
1436  NEXT_STR(); m_catType = (CategoryType)ts.toInt(); // 48
1437 
1438  NEXT_STR(); m_recordedId = ts.toLongLong(); // 49
1439  NEXT_STR(); m_inputName = ts; // 50
1441 
1442  if (!origChanid || !origRecstartts.isValid() ||
1443  (origChanid != m_chanId) || (origRecstartts != m_recStartTs))
1444  {
1446  m_spread = -1;
1447  m_startCol = -1;
1448  m_inUseForWhat = QString();
1449  m_positionMapDBReplacement = nullptr;
1450  }
1451 
1452  ensureSortFields();
1453 
1454  return true;
1455 }
1456 
1457 template <typename T>
1458 QString propsValueToString (const QString& name, QMap<T,QString> propNames,
1459  T props)
1460 {
1461  if (props == 0)
1462  return propNames[0];
1463 
1464  QStringList result;
1465  for (uint i = 0; i < sizeof(T)*8 - 1; ++i)
1466  {
1467  uint bit = 1<<i;
1468  if ((props & bit) == 0)
1469  continue;
1470  if (propNames.contains(bit))
1471  {
1472  result += propNames[bit];
1473  continue;
1474  }
1475  QString tmp = QString("0x%1").arg(bit, sizeof(T)*2,16,QChar('0'));
1476  LOG(VB_GENERAL, LOG_ERR, QString("Unknown name for %1 flag 0x%2.")
1477  .arg(name, tmp));
1478  result += tmp;
1479  }
1480  return result.join('|');
1481 }
1482 
1483 template <typename T>
1484 uint propsValueFromString (const QString& name, const QMap<T,QString>& propNames,
1485  const QString& props)
1486 {
1487  if (props.isEmpty())
1488  return 0;
1489 
1490  uint result = 0;
1491 
1492  QStringList names = props.split('|');
1493  for ( const auto& n : std::as_const(names) )
1494  {
1495  uint bit = propNames.key(n, 0);
1496  if (bit == 0)
1497  {
1498  LOG(VB_GENERAL, LOG_ERR, QString("Unknown flag for %1 %2")
1499  .arg(name, n));
1500  }
1501  else
1502  {
1503  result |= bit;
1504  }
1505  }
1506  return result;
1507 }
1508 
1510 {
1511  return propsValueToString("program", ProgramFlagNames, m_programFlags);
1512 }
1513 
1514 
1515 QString ProgramInfo::GetRecTypeStatus(bool showrerecord) const
1516 {
1517  QString tmp_rec = ::toString(GetRecordingRuleType());
1519  {
1520  QDateTime timeNow = MythDate::current();
1521  if (((m_recEndTs > timeNow) && (m_recStatus <= RecStatus::WillRecord)) ||
1523  {
1524  tmp_rec += QString(" %1%2").arg(m_recPriority > 0 ? "+" : "").arg(m_recPriority);
1525  if (m_recPriority2)
1526  tmp_rec += QString("/%1%2").arg(m_recPriority2 > 0 ? "+" : "").arg(m_recPriority2);
1527  tmp_rec += " ";
1528  }
1529  else
1530  {
1531  tmp_rec += " -- ";
1532  }
1533  if (showrerecord && (GetRecordingStatus() == RecStatus::Recorded) &&
1534  !IsDuplicate())
1535  {
1536  tmp_rec += QObject::tr("Re-Record");
1537  }
1538  else
1539  {
1541  }
1542  }
1543  return tmp_rec;
1544 }
1545 
1546 
1547 
1548 
1550 {
1551  return propsValueToString("subtitle", SubtitlePropsNames,
1553 }
1554 
1556 {
1557  return propsValueToString("video", VideoPropsNames, m_videoProperties);
1558 }
1559 
1561 {
1562  return propsValueToString("audio", AudioPropsNames, m_audioProperties);
1563 }
1564 
1566 {
1567  return propsValueFromString("subtitle", SubtitlePropsNames, names);
1568 }
1569 
1571 {
1572  return propsValueFromString("video", VideoPropsNames, names);
1573 }
1574 
1576 {
1577  return propsValueFromString("audio", AudioPropsNames, names);
1578 }
1579 
1580 void ProgramInfo::ProgramFlagsFromNames(const QString & names)
1581 {
1582  m_programFlags = propsValueFromString("program", ProgramFlagNames, names);
1583 }
1584 
1589  bool showrerecord,
1590  uint star_range,
1591  uint date_format) const
1592 {
1593  QLocale locale = gCoreContext->GetQLocale();
1594  // NOTE: Format changes and relevant additions made here should be
1595  // reflected in RecordingRule
1596  QString channelFormat =
1597  gCoreContext->GetSetting("ChannelFormat", "<num> <sign>");
1598  QString longChannelFormat =
1599  gCoreContext->GetSetting("LongChannelFormat", "<num> <name>");
1600 
1601  QDateTime timeNow = MythDate::current();
1602 
1603  progMap["title"] = m_title;
1604  progMap["subtitle"] = m_subtitle;
1605  progMap["sorttitle"] = m_sortTitle;
1606  progMap["sortsubtitle"] = m_sortSubtitle;
1607 
1608  QString tempSubTitle = m_title;
1609  QString tempSortSubtitle = m_sortTitle;
1610  if (!m_subtitle.trimmed().isEmpty())
1611  {
1612  tempSubTitle = QString("%1 - \"%2\"")
1613  .arg(tempSubTitle, m_subtitle);
1614  tempSortSubtitle = QString("%1 - \"%2\"")
1615  .arg(tempSortSubtitle, m_sortSubtitle);
1616  }
1617 
1618  progMap["titlesubtitle"] = tempSubTitle;
1619  progMap["sorttitlesubtitle"] = tempSortSubtitle;
1620 
1621  progMap["description"] = progMap["description0"] = m_description;
1622 
1623  if (m_season > 0 || m_episode > 0)
1624  {
1625  progMap["season"] = StringUtil::intToPaddedString(m_season, 1);
1626  progMap["episode"] = StringUtil::intToPaddedString(m_episode, 1);
1627  progMap["totalepisodes"] = StringUtil::intToPaddedString(m_totalEpisodes, 1);
1628  progMap["s00e00"] = QString("s%1e%2")
1631  progMap["00x00"] = QString("%1x%2")
1634  }
1635  else
1636  {
1637  progMap["season"] = progMap["episode"] = "";
1638  progMap["totalepisodes"] = "";
1639  progMap["s00e00"] = progMap["00x00"] = "";
1640  }
1641  progMap["syndicatedepisode"] = m_syndicatedEpisode;
1642 
1643  progMap["category"] = m_category;
1644  progMap["director"] = m_director;
1645 
1646  progMap["callsign"] = m_chanSign;
1647  progMap["commfree"] = (m_programFlags & FL_CHANCOMMFREE) ? "1" : "0";
1648  progMap["outputfilters"] = m_chanPlaybackFilters;
1649  if (IsVideo())
1650  {
1651  progMap["starttime"] = "";
1652  progMap["startdate"] = "";
1653  progMap["endtime"] = "";
1654  progMap["enddate"] = "";
1655  progMap["recstarttime"] = "";
1656  progMap["recstartdate"] = "";
1657  progMap["recendtime"] = "";
1658  progMap["recenddate"] = "";
1659 
1660  if (m_startTs.date().year() == 1895)
1661  {
1662  progMap["startdate"] = "";
1663  progMap["recstartdate"] = "";
1664  }
1665  else
1666  {
1667  progMap["startdate"] = m_startTs.toLocalTime().toString("yyyy");
1668  progMap["recstartdate"] = m_startTs.toLocalTime().toString("yyyy");
1669  }
1670  }
1671  else // if (IsRecording())
1672  {
1673  using namespace MythDate;
1674  progMap["starttime"] = MythDate::toString(m_startTs, date_format | kTime);
1675  progMap["startdate"] =
1676  MythDate::toString(m_startTs, date_format | kDateFull | kSimplify);
1677  progMap["shortstartdate"] = MythDate::toString(m_startTs, date_format | kDateShort);
1678  progMap["endtime"] = MythDate::toString(m_endTs, date_format | kTime);
1679  progMap["enddate"] = MythDate::toString(m_endTs, date_format | kDateFull | kSimplify);
1680  progMap["shortenddate"] = MythDate::toString(m_endTs, date_format | kDateShort);
1681  progMap["recstarttime"] = MythDate::toString(m_recStartTs, date_format | kTime);
1682  progMap["recstartdate"] = MythDate::toString(m_recStartTs, date_format | kDateShort);
1683  progMap["recendtime"] = MythDate::toString(m_recEndTs, date_format | kTime);
1684  progMap["recenddate"] = MythDate::toString(m_recEndTs, date_format | kDateShort);
1685  progMap["startts"] = QString::number(m_startTs.toSecsSinceEpoch());
1686  progMap["endts"] = QString::number(m_endTs.toSecsSinceEpoch());
1687  if (timeNow.toLocalTime().date().year() !=
1688  m_startTs.toLocalTime().date().year())
1689  progMap["startyear"] = m_startTs.toLocalTime().toString("yyyy");
1690  if (timeNow.toLocalTime().date().year() !=
1691  m_endTs.toLocalTime().date().year())
1692  progMap["endyear"] = m_endTs.toLocalTime().toString("yyyy");
1693  }
1694 
1695  using namespace MythDate;
1696  progMap["timedate"] =
1697  MythDate::toString(m_recStartTs, date_format | kDateTimeFull | kSimplify) + " - " +
1698  MythDate::toString(m_recEndTs, date_format | kTime);
1699 
1700  progMap["shorttimedate"] =
1701  MythDate::toString(m_recStartTs, date_format | kDateTimeShort | kSimplify) + " - " +
1702  MythDate::toString(m_recEndTs, date_format | kTime);
1703 
1704  progMap["starttimedate"] =
1706 
1707  progMap["shortstarttimedate"] =
1709 
1710  progMap["lastmodifiedtime"] = MythDate::toString(m_lastModified, date_format | kTime);
1711  progMap["lastmodifieddate"] =
1713  progMap["lastmodified"] =
1715 
1716  if (m_recordedId)
1717  progMap["recordedid"] = QString::number(m_recordedId);
1718 
1719  progMap["channum"] = m_chanStr;
1720  progMap["chanid"] = QString::number(m_chanId);
1721  progMap["channame"] = m_chanName;
1722  progMap["channel"] = ChannelText(channelFormat);
1723  progMap["longchannel"] = ChannelText(longChannelFormat);
1724 
1725  QString tmpSize = locale.toString(m_fileSize * (1.0 / (1024.0 * 1024.0 * 1024.0)), 'f', 2);
1726  progMap["filesize_str"] = QObject::tr("%1 GB", "GigaBytes").arg(tmpSize);
1727 
1728  progMap["filesize"] = locale.toString((quint64)m_fileSize);
1729 
1730  int seconds = m_recStartTs.secsTo(m_recEndTs);
1731  int minutes = seconds / 60;
1732 
1733  QString min_str = QObject::tr("%n minute(s)","",minutes);
1734 
1735  progMap["lenmins"] = min_str;
1736  int hours = minutes / 60;
1737  minutes = minutes % 60;
1738 
1739  progMap["lentime"] = min_str;
1740  if (hours > 0 && minutes > 0)
1741  {
1742  min_str = QObject::tr("%n minute(s)","",minutes);
1743  progMap["lentime"] = QString("%1 %2")
1744  .arg(QObject::tr("%n hour(s)","", hours), min_str);
1745  }
1746  else if (hours > 0)
1747  {
1748  progMap["lentime"] = QObject::tr("%n hour(s)","", hours);
1749  }
1750 
1751  progMap["recordedpercent"] =
1752  (m_recordedPercent >= 0)
1753  ? QString::number(m_recordedPercent) : QString();
1754  progMap["watchedpercent"] =
1755  ((m_watchedPercent > 0) && !IsWatched())
1756  ? QString::number(m_watchedPercent) : QString();
1757 
1758  // This is calling toChar from recordingtypes.cpp, not the QChar
1759  // constructor.
1760  progMap["rectypechar"] = toQChar(GetRecordingRuleType());
1761  progMap["rectype"] = ::toString(GetRecordingRuleType());
1762  progMap["rectypestatus"] = GetRecTypeStatus(showrerecord);
1763 
1764  progMap["card"] = RecStatus::toString(GetRecordingStatus(),
1765  GetShortInputName());
1766  progMap["input"] = RecStatus::toString(GetRecordingStatus(), m_inputId);
1767  progMap["inputname"] = m_inputName;
1768  // Don't add bookmarkupdate to progMap, for now.
1769 
1770  progMap["recpriority"] = QString::number(m_recPriority);
1771  progMap["recpriority2"] = QString::number(m_recPriority2);
1772  progMap["recordinggroup"] = (m_recGroup == "Default")
1773  ? QObject::tr("Default") : m_recGroup;
1774  progMap["playgroup"] = m_playGroup;
1775 
1776  if (m_storageGroup == "Default")
1777  progMap["storagegroup"] = QObject::tr("Default");
1778  else if (StorageGroup::kSpecialGroups.contains(m_storageGroup))
1779  {
1780  // This relies upon the translation established in the
1781  // definition of StorageGroup::kSpecialGroups.
1782  // clazy:exclude=tr-non-literal
1783  progMap["storagegroup"] = QObject::tr(m_storageGroup.toUtf8().constData());
1784  }
1785  else
1786  {
1787  progMap["storagegroup"] = m_storageGroup;
1788  }
1789 
1790  progMap["programflags"] = QString::number(m_programFlags);
1791  progMap["audioproperties"] = QString::number(m_audioProperties);
1792  progMap["videoproperties"] = QString::number(m_videoProperties);
1793  progMap["subtitleType"] = QString::number(m_subtitleProperties);
1794  progMap["programflags_names"] = GetProgramFlagNames();
1795  progMap["audioproperties_names"] = GetAudioPropertyNames();
1796  progMap["videoproperties_names"] = GetVideoPropertyNames();
1797  progMap["subtitleType_names"] = GetSubtitleTypeNames();
1798 
1799  progMap["recstatus"] = RecStatus::toString(GetRecordingStatus(),
1801  progMap["recstatuslong"] = RecStatus::toDescription(GetRecordingStatus(),
1804 
1805  if (IsRepeat())
1806  {
1807  progMap["repeat"] = QString("(%1) ").arg(QObject::tr("Repeat"));
1808  progMap["longrepeat"] = progMap["repeat"];
1809  if (m_originalAirDate.isValid())
1810  {
1811  progMap["longrepeat"] = QString("(%1 %2) ")
1812  .arg(QObject::tr("Repeat"),
1815  date_format | MythDate::kDateFull | MythDate::kAddYear));
1816  }
1817  }
1818  else
1819  {
1820  progMap["repeat"] = "";
1821  progMap["longrepeat"] = "";
1822  }
1823 
1824  progMap["seriesid"] = m_seriesId;
1825  progMap["programid"] = m_programId;
1826  progMap["inetref"] = m_inetRef;
1827  progMap["catType"] = myth_category_type_to_string(m_catType);
1828 
1829  progMap["year"] = m_year > 1895 ? QString::number(m_year) : "";
1830 
1831  progMap["partnumber"] = m_partNumber ? QString::number(m_partNumber) : "";
1832  progMap["parttotal"] = m_partTotal ? QString::number(m_partTotal) : "";
1833 
1834  QString star_str = (m_stars != 0.0F) ?
1835  QObject::tr("%n star(s)", "", GetStars(star_range)) : "";
1836  progMap["stars"] = star_str;
1837  progMap["numstars"] = QString::number(GetStars(star_range));
1838 
1839  if (m_stars != 0.0F && m_year)
1840  progMap["yearstars"] = QString("(%1, %2)").arg(m_year).arg(star_str);
1841  else if (m_stars != 0.0F)
1842  progMap["yearstars"] = QString("(%1)").arg(star_str);
1843  else if (m_year)
1844  progMap["yearstars"] = QString("(%1)").arg(m_year);
1845  else
1846  progMap["yearstars"] = "";
1847 
1848  if (!m_originalAirDate.isValid() ||
1849  (!m_programId.isEmpty() && m_programId.startsWith("MV")))
1850  {
1851  progMap["originalairdate"] = "";
1852  progMap["shortoriginalairdate"] = "";
1853  }
1854  else
1855  {
1856  progMap["originalairdate"] = MythDate::toString(
1857  m_originalAirDate, date_format | MythDate::kDateFull);
1858  progMap["shortoriginalairdate"] = MythDate::toString(
1859  m_originalAirDate, date_format | MythDate::kDateShort);
1860  }
1861 
1862  // 'mediatype' for a statetype, so untranslated
1863  // 'mediatypestring' for textarea, so translated
1864  // TODO Move to a dedicated ToState() method?
1865  QString mediaType;
1866  QString mediaTypeString;
1867  switch (GetProgramInfoType())
1868  {
1870  mediaType = "video";
1871  mediaTypeString = QObject::tr("Video");
1872  break;
1874  mediaType = "dvd";
1875  mediaTypeString = QObject::tr("DVD");
1876  break;
1878  mediaType = "httpstream";
1879  mediaTypeString = QObject::tr("HTTP Streaming");
1880  break;
1882  mediaType = "rtspstream";
1883  mediaTypeString = QObject::tr("RTSP Streaming");
1884  break;
1886  mediaType = "bluraydisc";
1887  mediaTypeString = QObject::tr("Blu-ray Disc");
1888  break;
1889  case kProgramInfoTypeRecording : // Fall through
1890  default :
1891  mediaType = "recording";
1892  mediaTypeString = QObject::tr("Recording",
1893  "Recorded file, object not action");
1894  }
1895  progMap["mediatype"] = mediaType;
1896  progMap["mediatypestring"] = mediaTypeString;
1897 }
1898 
1900 std::chrono::seconds ProgramInfo::GetSecondsInRecording(void) const
1901 {
1902  auto recsecs = std::chrono::seconds(m_recStartTs.secsTo(m_endTs));
1903  auto duration = std::chrono::seconds(m_startTs.secsTo(m_endTs));
1904  return (recsecs > 0s) ? recsecs : std::max(duration, 0s);
1905 }
1906 
1909 {
1911 }
1912 
1915 {
1916  uint64_t last_frame = 0;
1917  frm_pos_map_t posMap;
1919  if (posMap.empty())
1920  {
1922  if (posMap.empty())
1924  }
1925  if (!posMap.empty())
1926  {
1927  frm_pos_map_t::const_iterator it = posMap.constEnd();
1928  --it;
1929  last_frame = it.key();
1930  }
1931  return last_frame;
1932 }
1933 
1935 {
1936  if (qsizetype idx = m_inputName.indexOf('/'); idx >= 0)
1937  {
1938  return m_inputName.isRightToLeft() ?
1939  m_inputName.left(idx) : m_inputName.right(idx);
1940  }
1941 
1942  return m_inputName.isRightToLeft() ?
1943  m_inputName.left(2) : m_inputName.right(2);
1944 }
1945 
1946 bool ProgramInfo::IsGeneric(void) const
1947 {
1948  return
1949  (m_programId.isEmpty() && m_subtitle.isEmpty() &&
1950  m_description.isEmpty()) ||
1951  (!m_programId.isEmpty() && m_programId.endsWith("0000")
1952  && m_catType == kCategorySeries);
1953 }
1954 
1955 QString ProgramInfo::toString(const Verbosity v, const QString& sep, const QString& grp)
1956  const
1957 {
1958  QString str;
1959  switch (v)
1960  {
1961  case kLongDescription:
1962  str = LOC + "channame(" + m_chanName + ")\n";
1963  str += " startts(" +
1964  m_startTs.toString() + ") endts(" + m_endTs.toString() + ")\n";
1965  str += " recstartts(" + m_recStartTs.toString() +
1966  ") recendts(" + m_recEndTs.toString() + ")\n";
1967  str += " title(" + m_title + ")";
1968  break;
1969  case kTitleSubtitle:
1970  str = m_title.contains(' ') ?
1971  QString("%1%2%3").arg(grp, m_title, grp) : m_title;
1972  if (!m_subtitle.isEmpty())
1973  {
1974  str += m_subtitle.contains(' ') ?
1975  QString("%1%2%3%4").arg(sep, grp, m_subtitle, grp) :
1976  QString("%1%2").arg(sep, m_subtitle);
1977  }
1978  break;
1979  case kRecordingKey:
1980  str = QString("%1 at %2")
1982  break;
1983  case kSchedulingKey:
1984  str = QString("%1 @ %2")
1986  break;
1987  }
1988 
1989  return str;
1990 }
1991 
1993 {
1995  if (test.GetChanID())
1996  {
1997  clone(test, true);
1998  return true;
1999  }
2000  return false;
2001 }
2002 
2007  const uint _chanid, const QDateTime &_recstartts)
2008 {
2009  if (!_chanid || !_recstartts.isValid())
2010  {
2012  return false;
2013  }
2014 
2015  MSqlQuery query(MSqlQuery::InitCon());
2016  query.prepare(
2018  "WHERE r.chanid = :CHANID AND "
2019  " r.starttime = :RECSTARTTS");
2020  query.bindValue(":CHANID", _chanid);
2021  query.bindValue(":RECSTARTTS", _recstartts);
2022 
2023  if (!query.exec())
2024  {
2025  MythDB::DBError("LoadProgramFromRecorded", query);
2027  return false;
2028  }
2029 
2030  if (!query.next())
2031  {
2033  return false;
2034  }
2035 
2036  bool is_reload = (m_chanId == _chanid) && (m_recStartTs == _recstartts);
2037  if (!is_reload)
2038  {
2039  // These items are not initialized below so they need to be cleared
2040  // if we're loading in a different program into this ProgramInfo
2044  m_recPriority2 = 0;
2045  m_parentId = 0;
2046  m_sourceId = 0;
2047  m_inputId = 0;
2048 
2049  // everything below this line (in context) is not serialized
2050  m_spread = m_startCol = -1;
2052  m_inUseForWhat.clear();
2053  m_positionMapDBReplacement = nullptr;
2054  }
2055 
2056  m_title = query.value(0).toString();
2057  m_subtitle = query.value(1).toString();
2058  m_description = query.value(2).toString();
2059  m_season = query.value(3).toUInt();
2060  if (m_season == 0)
2061  m_season = query.value(51).toUInt();
2062  m_episode = query.value(4).toUInt();
2063  if (m_episode == 0)
2064  m_episode = query.value(52).toUInt();
2065  m_totalEpisodes = query.value(53).toUInt();
2066  m_syndicatedEpisode = query.value(48).toString();
2067  m_category = query.value(5).toString();
2068 
2069  m_chanId = _chanid;
2070  m_chanStr = QString("#%1").arg(m_chanId);
2073  m_chanPlaybackFilters.clear();
2074  if (!query.value(7).toString().isEmpty())
2075  {
2076  m_chanStr = query.value(7).toString();
2077  m_chanSign = query.value(8).toString();
2078  m_chanName = query.value(9).toString();
2079  m_chanPlaybackFilters = query.value(10).toString();
2080  }
2081 
2082  m_recGroup = query.value(11).toString();
2083  m_playGroup = query.value(12).toString();
2084 
2085  // We don't want to update the pathname if the basename is
2086  // the same as we may have already expanded pathname from
2087  // a simple basename to a localized path.
2088  QString new_basename = query.value(14).toString();
2089  if ((GetBasename() != new_basename) || !is_reload)
2090  {
2091  if (is_reload)
2092  {
2093  LOG(VB_FILE, LOG_INFO, LOC +
2094  QString("Updated pathname '%1':'%2' -> '%3'")
2095  .arg(m_pathname, GetBasename(), new_basename));
2096  }
2097  SetPathname(new_basename);
2098  }
2099 
2100  m_hostname = query.value(15).toString();
2101  m_storageGroup = query.value(13).toString();
2102 
2103  m_seriesId = query.value(17).toString();
2104  m_programId = query.value(18).toString();
2105  m_inetRef = query.value(19).toString();
2106  m_catType = string_to_myth_category_type(query.value(54).toString());
2107 
2108  m_recPriority = query.value(16).toInt();
2109 
2110  m_fileSize = query.value(20).toULongLong();
2111 
2112  m_startTs = MythDate::as_utc(query.value(21).toDateTime());
2113  m_endTs = MythDate::as_utc(query.value(22).toDateTime());
2114  m_recStartTs = MythDate::as_utc(query.value(24).toDateTime());
2115  m_recEndTs = MythDate::as_utc(query.value(25).toDateTime());
2116 
2117  m_stars = std::clamp((float)query.value(23).toDouble(), 0.0F, 1.0F);
2118 
2119  m_year = query.value(26).toUInt();
2120  m_partNumber = query.value(49).toUInt();
2121  m_partTotal = query.value(50).toUInt();
2122 
2123  m_originalAirDate = query.value(27).toDate();
2124  m_lastModified = MythDate::as_utc(query.value(28).toDateTime());
2125  //m_lastInUseTime;
2126 
2128 
2129  //m_recPriority2;
2130 
2131  m_recordId = query.value(29).toUInt();
2132  //m_parentId;
2133 
2134  //m_sourcid;
2135  //m_inputId;
2136  //m_cardid;
2137  m_findId = query.value(45).toUInt();
2138 
2139  //m_recType;
2140  m_dupIn = RecordingDupInType(query.value(46).toInt());
2141  m_dupMethod = RecordingDupMethodType(query.value(47).toInt());
2142 
2143  m_recordedId = query.value(55).toUInt();
2144  m_inputName = query.value(56).toString();
2145  m_bookmarkUpdate = MythDate::as_utc(query.value(57).toDateTime());
2146 
2147  // ancillary data -- begin
2148  m_programFlags = FL_NONE;
2149  set_flag(m_programFlags, FL_CHANCOMMFREE,
2150  query.value(30).toInt() == COMM_DETECT_COMMFREE);
2151  set_flag(m_programFlags, FL_COMMFLAG,
2152  query.value(31).toInt() == COMM_FLAG_DONE);
2153  set_flag(m_programFlags, FL_COMMPROCESSING ,
2154  query.value(31).toInt() == COMM_FLAG_PROCESSING);
2155  set_flag(m_programFlags, FL_REPEAT, query.value(32).toBool());
2156  set_flag(m_programFlags, FL_TRANSCODED,
2157  query.value(34).toInt() == TRANSCODING_COMPLETE);
2158  set_flag(m_programFlags, FL_DELETEPENDING, query.value(35).toBool());
2159  set_flag(m_programFlags, FL_PRESERVED, query.value(36).toBool());
2160  set_flag(m_programFlags, FL_CUTLIST, query.value(37).toBool());
2161  set_flag(m_programFlags, FL_AUTOEXP, query.value(38).toBool());
2162  set_flag(m_programFlags, FL_REALLYEDITING, query.value(39).toBool());
2163  set_flag(m_programFlags, FL_BOOKMARK, query.value(40).toBool());
2164  set_flag(m_programFlags, FL_WATCHED, query.value(41).toBool());
2165  set_flag(m_programFlags, FL_LASTPLAYPOS, query.value(58).toBool());
2166  set_flag(m_programFlags, FL_EDITING,
2167  ((m_programFlags & FL_REALLYEDITING) != 0U) ||
2168  ((m_programFlags & FL_COMMPROCESSING) != 0U));
2169 
2170  m_audioProperties = query.value(42).toUInt();
2171  m_videoProperties = query.value(43).toUInt();
2172  m_subtitleProperties = query.value(44).toUInt();
2173  // ancillary data -- end
2174 
2175  if (m_originalAirDate.isValid() && m_originalAirDate < QDate(1895, 12, 28))
2176  m_originalAirDate = QDate();
2177 
2178  // Extra stuff which is not serialized and may get lost.
2179  // m_spread
2180  // m_startCol
2181  // m_availableStatus
2182  // m_inUseForWhat
2183  // m_postitionMapDBReplacement
2184 
2185  return true;
2186 }
2187 
2193 {
2194  return (m_title == other.m_title &&
2195  m_chanId == other.m_chanId &&
2196  m_startTs == other.m_startTs);
2197 }
2198 
2204 {
2206  return m_recordId == other.m_recordId;
2207 
2208  if (m_findId && m_findId == other.m_findId &&
2209  (m_recordId == other.m_recordId || m_recordId == other.m_parentId))
2210  return true;
2211 
2212  if (m_dupMethod & kDupCheckNone)
2213  return false;
2214 
2215  if (m_title.compare(other.m_title, Qt::CaseInsensitive) != 0)
2216  return false;
2217 
2218  if (m_catType == kCategorySeries)
2219  {
2220  if (m_programId.endsWith("0000"))
2221  return false;
2222  }
2223 
2224  if (!m_programId.isEmpty() && !other.m_programId.isEmpty())
2225  {
2226  if (s_usingProgIDAuth)
2227  {
2228  int index = m_programId.indexOf('/');
2229  int oindex = other.m_programId.indexOf('/');
2230  if (index == oindex && (index < 0 ||
2231  m_programId.left(index) == other.m_programId.left(oindex)))
2232  return m_programId == other.m_programId;
2233  }
2234  else
2235  {
2236  return m_programId == other.m_programId;
2237  }
2238  }
2239 
2240  if ((m_dupMethod & kDupCheckSub) &&
2241  ((m_subtitle.isEmpty()) ||
2242  (m_subtitle.compare(other.m_subtitle, Qt::CaseInsensitive) != 0)))
2243  return false;
2244 
2245  if ((m_dupMethod & kDupCheckDesc) &&
2246  ((m_description.isEmpty()) ||
2247  (m_description.compare(other.m_description, Qt::CaseInsensitive) != 0)))
2248  return false;
2249 
2251  ((m_subtitle.isEmpty() &&
2252  ((!other.m_subtitle.isEmpty() &&
2253  m_description.compare(other.m_subtitle, Qt::CaseInsensitive) != 0) ||
2254  (other.m_subtitle.isEmpty() &&
2255  m_description.compare(other.m_description, Qt::CaseInsensitive) != 0))) ||
2256  (!m_subtitle.isEmpty() &&
2257  ((other.m_subtitle.isEmpty() &&
2258  m_subtitle.compare(other.m_description, Qt::CaseInsensitive) != 0) ||
2259  (!other.m_subtitle.isEmpty() &&
2260  m_subtitle.compare(other.m_subtitle, Qt::CaseInsensitive) != 0)))))
2261  return false;
2262 
2263  return true;
2264 }
2265 
2272 bool ProgramInfo::IsSameProgram(const ProgramInfo& other) const
2273 {
2274  if (m_title.compare(other.m_title, Qt::CaseInsensitive) != 0)
2275  return false;
2276 
2277  if (!m_programId.isEmpty() && !other.m_programId.isEmpty())
2278  {
2279  if (m_catType == kCategorySeries)
2280  {
2281  if (m_programId.endsWith("0000"))
2282  return false;
2283  }
2284 
2285  if (s_usingProgIDAuth)
2286  {
2287  int index = m_programId.indexOf('/');
2288  int oindex = other.m_programId.indexOf('/');
2289  if (index == oindex && (index < 0 ||
2290  m_programId.left(index) == other.m_programId.left(oindex)))
2291  return m_programId == other.m_programId;
2292  }
2293  else
2294  {
2295  return m_programId == other.m_programId;
2296  }
2297  }
2298 
2299  if ((m_dupMethod & kDupCheckSub) &&
2300  ((m_subtitle.isEmpty()) ||
2301  (m_subtitle.compare(other.m_subtitle, Qt::CaseInsensitive) != 0)))
2302  return false;
2303 
2304  if ((m_dupMethod & kDupCheckDesc) &&
2305  ((m_description.isEmpty()) ||
2306  (m_description.compare(other.m_description, Qt::CaseInsensitive) != 0)))
2307  return false;
2308 
2310  ((m_subtitle.isEmpty() &&
2311  ((!other.m_subtitle.isEmpty() &&
2312  m_description.compare(other.m_subtitle, Qt::CaseInsensitive) != 0) ||
2313  (other.m_subtitle.isEmpty() &&
2314  m_description.compare(other.m_description, Qt::CaseInsensitive) != 0))) ||
2315  (!m_subtitle.isEmpty() &&
2316  ((other.m_subtitle.isEmpty() &&
2317  m_subtitle.compare(other.m_description, Qt::CaseInsensitive) != 0) ||
2318  (!other.m_subtitle.isEmpty() &&
2319  m_subtitle.compare(other.m_subtitle, Qt::CaseInsensitive) != 0)))))
2320  return false;
2321 
2322  return true;
2323 }
2324 
2331 {
2332  if (m_startTs != other.m_startTs)
2333  return false;
2334  if (IsSameChannel(other))
2335  return true;
2336  if (!IsSameProgram(other))
2337  return false;
2338  return true;
2339 }
2340 
2347 {
2348  if (m_title.compare(other.m_title, Qt::CaseInsensitive) != 0)
2349  return false;
2350  return m_startTs == other.m_startTs &&
2351  IsSameChannel(other);
2352 }
2353 
2361 {
2362  if (m_title.compare(other.m_title, Qt::CaseInsensitive) != 0)
2363  return false;
2364  return IsSameChannel(other) &&
2365  m_startTs < other.m_endTs &&
2366  m_endTs > other.m_startTs;
2367 }
2368 
2375 bool ProgramInfo::IsSameChannel(const ProgramInfo& other) const
2376 {
2377  return m_chanId == other.m_chanId ||
2378  (!m_chanSign.isEmpty() &&
2379  m_chanSign.compare(other.m_chanSign, Qt::CaseInsensitive) == 0);
2380 }
2381 
2383 {
2384  QMap<QString, int> authMap;
2385  std::array<QString,3> tables { "program", "recorded", "oldrecorded" };
2386  MSqlQuery query(MSqlQuery::InitCon());
2387 
2388  for (const QString& table : tables)
2389  {
2390  query.prepare(QString(
2391  "SELECT DISTINCT LEFT(programid, LOCATE('/', programid)) "
2392  "FROM %1 WHERE programid <> ''").arg(table));
2393  if (!query.exec())
2394  MythDB::DBError("CheckProgramIDAuthorities", query);
2395  else
2396  {
2397  while (query.next())
2398  authMap[query.value(0).toString()] = 1;
2399  }
2400  }
2401 
2402  int numAuths = authMap.count();
2403  LOG(VB_GENERAL, LOG_INFO,
2404  QString("Found %1 distinct programid authorities").arg(numAuths));
2405 
2406  s_usingProgIDAuth = (numAuths > 1);
2407 }
2408 
2413 QString ProgramInfo::CreateRecordBasename(const QString &ext) const
2414 {
2416 
2417  QString retval = QString("%1_%2.%3")
2418  .arg(QString::number(m_chanId), starts, ext);
2419 
2420  return retval;
2421 }
2422 
2424  uint chanid, const QString &pathname, bool use_remote)
2425 {
2426  QString fn_lower = pathname.toLower();
2428  if (chanid)
2430  else if (fn_lower.startsWith("http:"))
2432  else if (fn_lower.startsWith("rtsp:"))
2434  else
2435  {
2436  fn_lower = determineURLType(pathname);
2437 
2438  if (fn_lower.startsWith("dvd:"))
2439  {
2441  }
2442  else if (fn_lower.startsWith("bd:"))
2443  {
2445  }
2446  else if (use_remote && fn_lower.startsWith("myth://"))
2447  {
2448  QString tmpFileDVD = pathname + "/VIDEO_TS";
2449  QString tmpFileBD = pathname + "/BDMV";
2450  if (RemoteFile::Exists(tmpFileDVD))
2452  else if (RemoteFile::Exists(tmpFileBD))
2454  }
2455  }
2456  return pit;
2457 }
2458 
2459 void ProgramInfo::SetPathname(const QString &pn)
2460 {
2461  m_pathname = pn;
2462 
2464  SetProgramInfoType(pit);
2465 }
2466 
2468 {
2470 }
2471 
2473  AvailableStatusType status, const QString &where)
2474 {
2475  if (status != m_availableStatus)
2476  {
2477  LOG(VB_GUI, LOG_INFO,
2478  toString(kTitleSubtitle) + QString(": %1 -> %2 in %3")
2480  ::toString(status),
2481  where));
2482  }
2483  m_availableStatus = status;
2484 }
2485 
2489 bool ProgramInfo::SaveBasename(const QString &basename)
2490 {
2491  MSqlQuery query(MSqlQuery::InitCon());
2492  query.prepare("UPDATE recorded "
2493  "SET basename = :BASENAME "
2494  "WHERE recordedid = :RECORDEDID;");
2495  query.bindValue(":RECORDEDID", m_recordedId);
2496  query.bindValue(":BASENAME", basename);
2497 
2498  if (!query.exec())
2499  {
2500  MythDB::DBError("SetRecordBasename", query);
2501  return false;
2502  }
2503 
2504  query.prepare("UPDATE recordedfile "
2505  "SET basename = :BASENAME "
2506  "WHERE recordedid = :RECORDEDID;");
2507  query.bindValue(":RECORDEDID", m_recordedId);
2508  query.bindValue(":BASENAME", basename);
2509 
2510  if (!query.exec())
2511  {
2512  MythDB::DBError("SetRecordBasename", query);
2513  return false;
2514  }
2515 
2516  SetPathname(basename);
2517 
2518  SendUpdateEvent();
2519  return true;
2520 }
2521 
2529 QString ProgramInfo::QueryBasename(void) const
2530 {
2531  QString bn = GetBasename();
2532  if (!bn.isEmpty())
2533  return bn;
2534 
2535  MSqlQuery query(MSqlQuery::InitCon());
2536  query.prepare(
2537  "SELECT basename "
2538  "FROM recordedfile "
2539  "WHERE recordedid = :RECORDEDID;");
2540  query.bindValue(":RECORDEDID", m_recordedId);
2541 
2542  if (!query.exec())
2543  {
2544  MythDB::DBError("QueryBasename", query);
2545  }
2546  else if (query.next())
2547  {
2548  return query.value(0).toString();
2549  }
2550  else
2551  {
2552  LOG(VB_GENERAL, LOG_INFO,
2553  QString("QueryBasename found no entry for recording ID %1")
2554  .arg(m_recordedId));
2555  }
2556 
2557  return {};
2558 }
2559 
2568  bool checkMaster, bool forceCheckLocal)
2569 {
2570  // return the original path if BD or DVD URI
2571  if (IsVideoBD() || IsVideoDVD())
2572  return GetPathname();
2573 
2574  QString basename = QueryBasename();
2575  if (basename.isEmpty())
2576  return "";
2577 
2578  bool checklocal = !gCoreContext->GetBoolSetting("AlwaysStreamFiles", false) ||
2579  forceCheckLocal;
2580 
2581  if (IsVideo())
2582  {
2583  QString fullpath = GetPathname();
2584  if (!fullpath.startsWith("myth://", Qt::CaseInsensitive) || !checklocal)
2585  return fullpath;
2586 
2587  QUrl url = QUrl(fullpath);
2588  QString path = url.path();
2589  QString host = url.toString(QUrl::RemovePath).mid(7);
2590  QStringList list = host.split(":", Qt::SkipEmptyParts);
2591  if (!list.empty())
2592  {
2593  host = list[0];
2594  list = host.split("@", Qt::SkipEmptyParts);
2595  QString group;
2596  if (!list.empty() && list.size() < 3)
2597  {
2598  host = list.size() == 1 ? list[0] : list[1];
2599  group = list.size() == 1 ? QString() : list[0];
2600  StorageGroup sg = StorageGroup(group, host);
2601  QString local = sg.FindFile(path);
2602  if (!local.isEmpty() && sg.FileExists(local))
2603  return local;
2604  }
2605  }
2606  return fullpath;
2607  }
2608 
2609  QString tmpURL;
2610  if (checklocal)
2611  {
2612  // Check to see if the file exists locally
2613  StorageGroup sgroup(m_storageGroup);
2614 #if 0
2615  LOG(VB_FILE, LOG_DEBUG, LOC +
2616  QString("GetPlaybackURL: CHECKING SG : %1 : ").arg(tmpURL));
2617 #endif
2618  tmpURL = sgroup.FindFile(basename);
2619 
2620  if (!tmpURL.isEmpty())
2621  {
2622  LOG(VB_FILE, LOG_INFO, LOC +
2623  QString("GetPlaybackURL: File is local: '%1'") .arg(tmpURL));
2624  return tmpURL;
2625  }
2627  {
2628  LOG(VB_GENERAL, LOG_ERR, LOC +
2629  QString("GetPlaybackURL: '%1' should be local, but it can "
2630  "not be found.").arg(basename));
2631  // Note do not preceed with "/" that will cause existing code
2632  // to look for a local file with this name...
2633  return QString("GetPlaybackURL/UNABLE/TO/FIND/LOCAL/FILE/ON/%1/%2")
2634  .arg(m_hostname, basename);
2635  }
2636  }
2637 
2638  // Check to see if we should stream from the master backend
2639  if ((checkMaster) &&
2640  (gCoreContext->GetBoolSetting("MasterBackendOverride", false)) &&
2641  (RemoteCheckFile(this, false)))
2642  {
2645  basename);
2646 
2647  LOG(VB_FILE, LOG_INFO, LOC +
2648  QString("GetPlaybackURL: Found @ '%1'").arg(tmpURL));
2649  return tmpURL;
2650  }
2651 
2652  // Fallback to streaming from the backend the recording was created on
2655  basename);
2656 
2657  LOG(VB_FILE, LOG_INFO, LOC +
2658  QString("GetPlaybackURL: Using default of: '%1'") .arg(tmpURL));
2659 
2660  return tmpURL;
2661 }
2662 
2666 {
2667  uint ret = 0U;
2668  if (m_chanId)
2669  {
2670  MSqlQuery query(MSqlQuery::InitCon());
2671 
2672  query.prepare("SELECT mplexid FROM channel "
2673  "WHERE chanid = :CHANID");
2674  query.bindValue(":CHANID", m_chanId);
2675 
2676  if (!query.exec())
2677  MythDB::DBError("QueryMplexID", query);
2678  else if (query.next())
2679  ret = query.value(0).toUInt();
2680 
2681  // clear out bogus mplexid's
2682  ret = (32767 == ret) ? 0 : ret;
2683  }
2684 
2685  return ret;
2686 }
2687 
2690 void ProgramInfo::SaveBookmark(uint64_t frame)
2691 {
2693 
2694  bool is_valid = (frame > 0);
2695  if (is_valid)
2696  {
2697  frm_dir_map_t bookmarkmap;
2698  bookmarkmap[frame] = MARK_BOOKMARK;
2699  SaveMarkupMap(bookmarkmap);
2700  }
2701 
2702  set_flag(m_programFlags, FL_BOOKMARK, is_valid);
2703 
2704  UpdateMarkTimeStamp(is_valid);
2705  SendUpdateEvent();
2706 }
2707 
2708 void ProgramInfo::UpdateMarkTimeStamp(bool bookmarked) const
2709 {
2710  if (IsRecording())
2711  {
2712  MSqlQuery query(MSqlQuery::InitCon());
2713  query.prepare(
2714  "UPDATE recorded "
2715  "SET bookmarkupdate = CURRENT_TIMESTAMP, "
2716  " bookmark = :BOOKMARKFLAG "
2717  "WHERE recordedid = :RECORDEDID");
2718 
2719  query.bindValue(":BOOKMARKFLAG", bookmarked);
2720  query.bindValue(":RECORDEDID", m_recordedId);
2721 
2722  if (!query.exec())
2723  MythDB::DBError("bookmark flag update", query);
2724  }
2725 }
2726 
2727 void ProgramInfo::SaveLastPlayPos(uint64_t frame)
2728 {
2730 
2731  bool isValid = frame > 0;
2732  if (isValid)
2733  {
2734  frm_dir_map_t lastPlayPosMap;
2735  lastPlayPosMap[frame] = MARK_UTIL_LASTPLAYPOS;
2736  SaveMarkupMap(lastPlayPosMap, MARK_UTIL_LASTPLAYPOS);
2737  }
2738 
2739  set_flag(m_programFlags, FL_LASTPLAYPOS, isValid);
2740 
2741  UpdateLastPlayTimeStamp(isValid);
2742  SendUpdateEvent();
2743 }
2744 
2745 // This function overloads the 'bookmarkupdate' field to force the UI
2746 // to update when the last play timestamp is updated. The alternative
2747 // is adding another field to the database and to the programinfo
2748 // serialization.
2749 void ProgramInfo::UpdateLastPlayTimeStamp(bool hasLastPlay) const
2750 {
2751  if (IsRecording())
2752  {
2753  MSqlQuery query(MSqlQuery::InitCon());
2754  query.prepare(
2755  "UPDATE recorded "
2756  "SET bookmarkupdate = CURRENT_TIMESTAMP, "
2757  " lastplay = :LASTPLAYFLAG "
2758  "WHERE recordedid = :RECORDEDID");
2759 
2760  query.bindValue(":LASTPLAYFLAG", hasLastPlay);
2761  query.bindValue(":RECORDEDID", m_recordedId);
2762 
2763  if (!query.exec())
2764  MythDB::DBError("lastplay flag update", query);
2765  }
2766 }
2767 
2769 {
2770  if (IsRecording())
2772 }
2773 
2775 {
2777 }
2778 
2780 {
2782 }
2783 
2788 {
2789  MSqlQuery query(MSqlQuery::InitCon());
2790  query.prepare(
2791  "SELECT bookmarkupdate "
2792  "FROM recorded "
2793  "WHERE chanid = :CHANID AND"
2794  " starttime = :STARTTIME");
2795  query.bindValue(":CHANID", m_chanId);
2796  query.bindValue(":STARTTIME", m_recStartTs);
2797 
2798  QDateTime ts;
2799 
2800  if (!query.exec())
2801  MythDB::DBError("ProgramInfo::GetBookmarkTimeStamp()", query);
2802  else if (query.next())
2803  ts = MythDate::as_utc(query.value(0).toDateTime());
2804 
2805  return ts;
2806 }
2807 
2814 uint64_t ProgramInfo::QueryBookmark(void) const
2815 {
2816  if (m_programFlags & FL_IGNOREBOOKMARK)
2817  return 0;
2818 
2819  frm_dir_map_t bookmarkmap;
2820  QueryMarkupMap(bookmarkmap, MARK_BOOKMARK);
2821 
2822  return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key();
2823 }
2824 
2825 uint64_t ProgramInfo::QueryBookmark(uint chanid, const QDateTime &recstartts)
2826 {
2827  frm_dir_map_t bookmarkmap;
2829  chanid, recstartts,
2830  bookmarkmap, MARK_BOOKMARK);
2831 
2832  return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key();
2833 }
2834 
2842 {
2843  if (m_programFlags & FL_IGNORELASTPLAYPOS)
2844  return 0;
2845 
2846  frm_dir_map_t bookmarkmap;
2847  QueryMarkupMap(bookmarkmap, MARK_UTIL_LASTPLAYPOS);
2848 
2849  return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key();
2850 }
2851 
2858 uint64_t ProgramInfo::QueryProgStart(void) const
2859 {
2860  if (m_programFlags & FL_IGNOREPROGSTART)
2861  return 0;
2862 
2863  frm_dir_map_t bookmarkmap;
2864  QueryMarkupMap(bookmarkmap, MARK_UTIL_PROGSTART);
2865 
2866  return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key();
2867 }
2868 
2869 uint64_t ProgramInfo::QueryStartMark(void) const
2870 {
2871  uint64_t start = QueryLastPlayPos();
2872  if (start > 0)
2873  {
2874  LOG(VB_PLAYBACK, LOG_INFO, QString("Using last position @ %1").arg(start));
2875  return start;
2876  }
2877 
2878  start = QueryBookmark();
2879  if (start > 0)
2880  {
2881  LOG(VB_PLAYBACK, LOG_INFO, QString("Using bookmark @ %1").arg(start));
2882  return start;
2883  }
2884 
2885  if (HasCutlist())
2886  {
2887  // Disable progstart if the program has a cutlist.
2888  LOG(VB_PLAYBACK, LOG_INFO, "Ignoring progstart as cutlist exists");
2889  return 0;
2890  }
2891 
2892  start = QueryProgStart();
2893  if (start > 0)
2894  {
2895  LOG(VB_PLAYBACK, LOG_INFO, QString("Using progstart @ %1").arg(start));
2896  return start;
2897  }
2898 
2899  LOG(VB_PLAYBACK, LOG_INFO, "Using file start");
2900  return 0;
2901 }
2902 
2909  const QString &serialid) const
2910 {
2911  QStringList fields = QStringList();
2912  MSqlQuery query(MSqlQuery::InitCon());
2913 
2914  if (!(m_programFlags & FL_IGNOREBOOKMARK))
2915  {
2916  query.prepare(" SELECT dvdstate, title, framenum, audionum, subtitlenum "
2917  " FROM dvdbookmark "
2918  " WHERE serialid = :SERIALID ");
2919  query.bindValue(":SERIALID", serialid);
2920 
2921  if (query.exec() && query.next())
2922  {
2923  QString dvdstate = query.value(0).toString();
2924 
2925  if (!dvdstate.isEmpty())
2926  {
2927  fields.append(dvdstate);
2928  }
2929  else
2930  {
2931  // Legacy bookmark
2932  for(int i = 1; i < 5; i++)
2933  fields.append(query.value(i).toString());
2934  }
2935  }
2936  }
2937 
2938  return fields;
2939 }
2940 
2941 void ProgramInfo::SaveDVDBookmark(const QStringList &fields)
2942 {
2943  QStringList::const_iterator it = fields.begin();
2944  MSqlQuery query(MSqlQuery::InitCon());
2945 
2946  QString serialid = *(it);
2947  QString name = *(++it);
2948 
2949  if( fields.count() == 3 )
2950  {
2951  // We have a state field, so update/create the bookmark
2952  QString state = *(++it);
2953 
2954  query.prepare("INSERT IGNORE INTO dvdbookmark "
2955  " (serialid, name)"
2956  " VALUES ( :SERIALID, :NAME );");
2957  query.bindValue(":SERIALID", serialid);
2958  query.bindValue(":NAME", name);
2959 
2960  if (!query.exec())
2961  MythDB::DBError("SetDVDBookmark inserting", query);
2962 
2963  query.prepare(" UPDATE dvdbookmark "
2964  " SET dvdstate = :STATE , "
2965  " timestamp = NOW() "
2966  " WHERE serialid = :SERIALID");
2967  query.bindValue(":STATE",state);
2968  query.bindValue(":SERIALID",serialid);
2969  }
2970  else
2971  {
2972  // No state field, delete the bookmark
2973  query.prepare("DELETE FROM dvdbookmark "
2974  "WHERE serialid = :SERIALID");
2975  query.bindValue(":SERIALID",serialid);
2976  }
2977 
2978  if (!query.exec())
2979  MythDB::DBError("SetDVDBookmark updating", query);
2980 }
2981 
2985 QStringList ProgramInfo::QueryBDBookmark(const QString &serialid) const
2986 {
2987  QStringList fields = QStringList();
2988  MSqlQuery query(MSqlQuery::InitCon());
2989 
2990  if (!(m_programFlags & FL_IGNOREBOOKMARK))
2991  {
2992  query.prepare(" SELECT bdstate FROM bdbookmark "
2993  " WHERE serialid = :SERIALID ");
2994  query.bindValue(":SERIALID", serialid);
2995 
2996  if (query.exec() && query.next())
2997  fields.append(query.value(0).toString());
2998  }
2999 
3000  return fields;
3001 }
3002 
3003 void ProgramInfo::SaveBDBookmark(const QStringList &fields)
3004 {
3005  QStringList::const_iterator it = fields.begin();
3006  MSqlQuery query(MSqlQuery::InitCon());
3007 
3008  QString serialid = *(it);
3009  QString name = *(++it);
3010 
3011  if( fields.count() == 3 )
3012  {
3013  // We have a state field, so update/create the bookmark
3014  QString state = *(++it);
3015 
3016  query.prepare("INSERT IGNORE INTO bdbookmark "
3017  " (serialid, name)"
3018  " VALUES ( :SERIALID, :NAME );");
3019  query.bindValue(":SERIALID", serialid);
3020  query.bindValue(":NAME", name);
3021 
3022  if (!query.exec())
3023  MythDB::DBError("SetBDBookmark inserting", query);
3024 
3025  query.prepare(" UPDATE bdbookmark "
3026  " SET bdstate = :STATE , "
3027  " timestamp = NOW() "
3028  " WHERE serialid = :SERIALID");
3029  query.bindValue(":STATE",state);
3030  query.bindValue(":SERIALID",serialid);
3031  }
3032  else
3033  {
3034  // No state field, delete the bookmark
3035  query.prepare("DELETE FROM bdbookmark "
3036  "WHERE serialid = :SERIALID");
3037  query.bindValue(":SERIALID",serialid);
3038  }
3039 
3040  if (!query.exec())
3041  MythDB::DBError("SetBDBookmark updating", query);
3042 }
3043 
3050 {
3052 
3053  MSqlQuery query(MSqlQuery::InitCon());
3054 
3055  query.prepare(" SELECT category_type "
3056  " FROM recordedprogram "
3057  " WHERE chanid = :CHANID "
3058  " AND starttime = :STARTTIME;");
3059 
3060  query.bindValue(":CHANID", m_chanId);
3061  query.bindValue(":STARTTIME", m_startTs);
3062 
3063  if (query.exec() && query.next())
3064  {
3065  ret = string_to_myth_category_type(query.value(0).toString());
3066  }
3067 
3068  return ret;
3069 }
3070 
3072 void ProgramInfo::SaveWatched(bool watched)
3073 {
3074  if (IsRecording())
3075  {
3076  MSqlQuery query(MSqlQuery::InitCon());
3077 
3078  query.prepare("UPDATE recorded"
3079  " SET watched = :WATCHEDFLAG"
3080  " WHERE chanid = :CHANID"
3081  " AND starttime = :STARTTIME ;");
3082  query.bindValue(":CHANID", m_chanId);
3083  query.bindValue(":STARTTIME", m_recStartTs);
3084  query.bindValue(":WATCHEDFLAG", watched);
3085 
3086  if (!query.exec())
3087  MythDB::DBError("Set watched flag", query);
3088  else
3089  UpdateLastDelete(watched);
3090 
3091  SendUpdateEvent();
3092  }
3093  else if (IsVideoFile())
3094  {
3095  QString url = m_pathname;
3096  if (url.startsWith("myth://"))
3097  {
3098  url = QUrl(url).path();
3099  url.remove(0,1);
3100  }
3101 
3102  MSqlQuery query(MSqlQuery::InitCon());
3103  query.prepare("UPDATE videometadata"
3104  " SET watched = :WATCHEDFLAG"
3105  " WHERE title = :TITLE"
3106  " AND subtitle = :SUBTITLE"
3107  " AND filename = :FILENAME ;");
3108  query.bindValue(":TITLE", m_title);
3109  query.bindValue(":SUBTITLE", m_subtitle);
3110  query.bindValue(":FILENAME", url);
3111  query.bindValue(":WATCHEDFLAG", watched);
3112 
3113  if (!query.exec())
3114  MythDB::DBError("Set watched flag", query);
3115  }
3116 
3117  set_flag(m_programFlags, FL_WATCHED, watched);
3118 }
3119 
3125 {
3126  bool editing = (m_programFlags & FL_REALLYEDITING) != 0U;
3127 
3128  MSqlQuery query(MSqlQuery::InitCon());
3129 
3130  query.prepare("SELECT editing FROM recorded"
3131  " WHERE chanid = :CHANID"
3132  " AND starttime = :STARTTIME ;");
3133  query.bindValue(":CHANID", m_chanId);
3134  query.bindValue(":STARTTIME", m_recStartTs);
3135 
3136  if (query.exec() && query.next())
3137  editing = query.value(0).toBool();
3138 
3139  /*
3140  set_flag(programflags, FL_REALLYEDITING, editing);
3141  set_flag(programflags, FL_EDITING, ((programflags & FL_REALLYEDITING) ||
3142  (programflags & COMM_FLAG_PROCESSING)));
3143  */
3144  return editing;
3145 }
3146 
3151 {
3152  MSqlQuery query(MSqlQuery::InitCon());
3153 
3154  query.prepare("UPDATE recorded"
3155  " SET editing = :EDIT"
3156  " WHERE chanid = :CHANID"
3157  " AND starttime = :STARTTIME ;");
3158  query.bindValue(":EDIT", edit);
3159  query.bindValue(":CHANID", m_chanId);
3160  query.bindValue(":STARTTIME", m_recStartTs);
3161 
3162  if (!query.exec())
3163  MythDB::DBError("Edit status update", query);
3164 
3165  set_flag(m_programFlags, FL_REALLYEDITING, edit);
3166  set_flag(m_programFlags, FL_EDITING, (((m_programFlags & FL_REALLYEDITING) != 0U) ||
3167  ((m_programFlags & COMM_FLAG_PROCESSING) != 0U)));
3168 
3169  SendUpdateEvent();
3170 }
3171 
3176 {
3177  MSqlQuery query(MSqlQuery::InitCon());
3178 
3179  query.prepare("UPDATE recorded"
3180  " SET deletepending = :DELETEFLAG, "
3181  " duplicate = 0 "
3182  " WHERE chanid = :CHANID"
3183  " AND starttime = :STARTTIME ;");
3184  query.bindValue(":CHANID", m_chanId);
3185  query.bindValue(":STARTTIME", m_recStartTs);
3186  query.bindValue(":DELETEFLAG", deleteFlag);
3187 
3188  if (!query.exec())
3189  MythDB::DBError("SaveDeletePendingFlag", query);
3190 
3191  set_flag(m_programFlags, FL_DELETEPENDING, deleteFlag);
3192 
3193  if (!deleteFlag)
3194  SendAddedEvent();
3195 
3196  SendUpdateEvent();
3197 }
3198 
3203 bool ProgramInfo::QueryIsInUse(QStringList &byWho) const
3204 {
3205  if (!IsRecording())
3206  return false;
3207 
3208  QDateTime oneHourAgo = MythDate::current().addSecs(-kLastUpdateOffset);
3209  MSqlQuery query(MSqlQuery::InitCon());
3210 
3211  query.prepare("SELECT hostname, recusage FROM inuseprograms "
3212  " WHERE chanid = :CHANID"
3213  " AND starttime = :STARTTIME "
3214  " AND lastupdatetime > :ONEHOURAGO ;");
3215  query.bindValue(":CHANID", m_chanId);
3216  query.bindValue(":STARTTIME", m_recStartTs);
3217  query.bindValue(":ONEHOURAGO", oneHourAgo);
3218 
3219  byWho.clear();
3220  if (query.exec() && query.size() > 0)
3221  {
3222  QString usageStr;
3223  QString recusage;
3224  while (query.next())
3225  {
3226  usageStr = QObject::tr("Unknown");
3227  recusage = query.value(1).toString();
3228 
3229  if (recusage == kPlayerInUseID)
3230  usageStr = QObject::tr("Playing");
3231  else if (recusage == kPIPPlayerInUseID)
3232  usageStr = QObject::tr("PIP");
3233  else if (recusage == kPBPPlayerInUseID)
3234  usageStr = QObject::tr("PBP");
3235  else if ((recusage == kRecorderInUseID) ||
3236  (recusage == kImportRecorderInUseID))
3237  usageStr = QObject::tr("Recording");
3238  else if (recusage == kFileTransferInUseID)
3239  usageStr = QObject::tr("File transfer");
3240  else if (recusage == kTruncatingDeleteInUseID)
3241  usageStr = QObject::tr("Delete");
3242  else if (recusage == kFlaggerInUseID)
3243  usageStr = QObject::tr("Commercial Detection");
3244  else if (recusage == kTranscoderInUseID)
3245  usageStr = QObject::tr("Transcoding");
3246  else if (recusage == kPreviewGeneratorInUseID)
3247  usageStr = QObject::tr("Preview Generation");
3248  else if (recusage == kJobQueueInUseID)
3249  usageStr = QObject::tr("User Job");
3250 
3251  byWho.push_back(recusage);
3252  byWho.push_back(query.value(0).toString());
3253  byWho.push_back(query.value(0).toString() + " (" + usageStr + ")");
3254  }
3255 
3256  return true;
3257  }
3258 
3259  return false;
3260 }
3261 
3266 bool ProgramInfo::QueryIsInUse(QString &byWho) const
3267 {
3268  QStringList users;
3269  bool inuse = QueryIsInUse(users);
3270  byWho.clear();
3271  for (int i = 0; i+2 < users.size(); i+=3)
3272  byWho += users[i+2] + "\n";
3273  return inuse;
3274 }
3275 
3276 
3283 bool ProgramInfo::QueryIsDeleteCandidate(bool one_playback_allowed) const
3284 {
3285  if (!IsRecording())
3286  return false;
3287 
3288  // gCoreContext->GetNumSetting("AutoExpireInsteadOfDelete", 0) &&
3289  if (GetRecordingGroup() != "Deleted" && GetRecordingGroup() != "LiveTV")
3290  return true;
3291 
3292  bool ok = true;
3293  QStringList byWho;
3294  if (QueryIsInUse(byWho) && !byWho.isEmpty())
3295  {
3296  uint play_cnt = 0;
3297  uint ft_cnt = 0;
3298  uint jq_cnt = 0;
3299  for (uint i = 0; (i+2 < (uint)byWho.size()) && ok; i+=3)
3300  {
3301  play_cnt += byWho[i].contains(kPlayerInUseID) ? 1 : 0;
3302  ft_cnt += (byWho[i].contains(kFlaggerInUseID) ||
3303  byWho[i].contains(kTranscoderInUseID)) ? 1 : 0;
3304  jq_cnt += (byWho[i].contains(kJobQueueInUseID)) ? 1 : 0;
3305  ok = ok && (byWho[i].contains(kRecorderInUseID) ||
3306  byWho[i].contains(kFlaggerInUseID) ||
3307  byWho[i].contains(kTranscoderInUseID) ||
3308  byWho[i].contains(kJobQueueInUseID) ||
3309  (one_playback_allowed && (play_cnt <= 1)));
3310  }
3311  ok = ok && (ft_cnt == jq_cnt);
3312  }
3313 
3314  return ok;
3315 }
3316 
3319 {
3320  MSqlQuery query(MSqlQuery::InitCon());
3321 
3322  query.prepare("SELECT transcoded FROM recorded"
3323  " WHERE chanid = :CHANID"
3324  " AND starttime = :STARTTIME ;");
3325  query.bindValue(":CHANID", m_chanId);
3326  query.bindValue(":STARTTIME", m_recStartTs);
3327 
3328  if (query.exec() && query.next())
3329  return (TranscodingStatus) query.value(0).toUInt();
3331 }
3332 
3339 {
3340  MSqlQuery query(MSqlQuery::InitCon());
3341 
3342  query.prepare(
3343  "UPDATE recorded "
3344  "SET transcoded = :VALUE "
3345  "WHERE chanid = :CHANID AND"
3346  " starttime = :STARTTIME");
3347  query.bindValue(":VALUE", (uint)trans);
3348  query.bindValue(":CHANID", m_chanId);
3349  query.bindValue(":STARTTIME", m_recStartTs);
3350 
3351  if (!query.exec())
3352  MythDB::DBError("Transcoded status update", query);
3353 
3354  set_flag(m_programFlags, FL_TRANSCODED, TRANSCODING_COMPLETE == trans);
3355  SendUpdateEvent();
3356 }
3357 
3362 {
3363  MSqlQuery query(MSqlQuery::InitCon());
3364 
3365  query.prepare("UPDATE recorded"
3366  " SET commflagged = :FLAG"
3367  " WHERE chanid = :CHANID"
3368  " AND starttime = :STARTTIME ;");
3369  query.bindValue(":FLAG", (int)flag);
3370  query.bindValue(":CHANID", m_chanId);
3371  query.bindValue(":STARTTIME", m_recStartTs);
3372 
3373  if (!query.exec())
3374  MythDB::DBError("Commercial Flagged status update", query);
3375 
3376  set_flag(m_programFlags, FL_COMMFLAG, COMM_FLAG_DONE == flag);
3377  set_flag(m_programFlags, FL_COMMPROCESSING, COMM_FLAG_PROCESSING == flag);
3378  set_flag(m_programFlags, FL_EDITING, (((m_programFlags & FL_REALLYEDITING) != 0U) ||
3379  ((m_programFlags & COMM_FLAG_PROCESSING) != 0U)));
3380  SendUpdateEvent();
3381 }
3382 
3383 
3387 void ProgramInfo::SavePreserve(bool preserveEpisode)
3388 {
3389  MSqlQuery query(MSqlQuery::InitCon());
3390 
3391  query.prepare("UPDATE recorded"
3392  " SET preserve = :PRESERVE"
3393  " WHERE chanid = :CHANID"
3394  " AND starttime = :STARTTIME ;");
3395  query.bindValue(":PRESERVE", preserveEpisode);
3396  query.bindValue(":CHANID", m_chanId);
3397  query.bindValue(":STARTTIME", m_recStartTs);
3398 
3399  if (!query.exec())
3400  MythDB::DBError("PreserveEpisode update", query);
3401  else
3402  UpdateLastDelete(false);
3403 
3404  set_flag(m_programFlags, FL_PRESERVED, preserveEpisode);
3405 
3406  SendUpdateEvent();
3407 }
3408 
3414 void ProgramInfo::SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete)
3415 {
3416  MSqlQuery query(MSqlQuery::InitCon());
3417 
3418  query.prepare("UPDATE recorded"
3419  " SET autoexpire = :AUTOEXPIRE"
3420  " WHERE chanid = :CHANID"
3421  " AND starttime = :STARTTIME ;");
3422  query.bindValue(":AUTOEXPIRE", (uint)autoExpire);
3423  query.bindValue(":CHANID", m_chanId);
3424  query.bindValue(":STARTTIME", m_recStartTs);
3425 
3426  if (!query.exec())
3427  MythDB::DBError("AutoExpire update", query);
3428  else if (updateDelete)
3429  UpdateLastDelete(true);
3430 
3431  set_flag(m_programFlags, FL_AUTOEXP, autoExpire != kDisableAutoExpire);
3432 
3433  SendUpdateEvent();
3434 }
3435 
3440 void ProgramInfo::UpdateLastDelete(bool setTime) const
3441 {
3442  MSqlQuery query(MSqlQuery::InitCon());
3443 
3444  if (setTime)
3445  {
3446  QDateTime timeNow = MythDate::current();
3447  auto delay_secs = std::chrono::seconds(m_recStartTs.secsTo(timeNow));
3448  auto delay = duration_cast<std::chrono::hours>(delay_secs);
3449  delay = std::clamp(delay, 1h, 200h);
3450 
3451  query.prepare("UPDATE record SET last_delete = :TIME, "
3452  "avg_delay = (avg_delay * 3 + :DELAY) / 4 "
3453  "WHERE recordid = :RECORDID");
3454  query.bindValue(":TIME", timeNow);
3455  query.bindValue(":DELAY", static_cast<qint64>(delay.count()));
3456  }
3457  else
3458  {
3459  query.prepare("UPDATE record SET last_delete = NULL "
3460  "WHERE recordid = :RECORDID");
3461  }
3462  query.bindValue(":RECORDID", m_recordId);
3463 
3464  if (!query.exec())
3465  MythDB::DBError("Update last_delete", query);
3466 }
3467 
3470 {
3471  MSqlQuery query(MSqlQuery::InitCon());
3472 
3473  query.prepare("SELECT autoexpire FROM recorded"
3474  " WHERE chanid = :CHANID"
3475  " AND starttime = :STARTTIME ;");
3476  query.bindValue(":CHANID", m_chanId);
3477  query.bindValue(":STARTTIME", m_recStartTs);
3478 
3479  if (query.exec() && query.next())
3480  return (AutoExpireType) query.value(0).toInt();
3481 
3482  return kDisableAutoExpire;
3483 }
3484 
3485 bool ProgramInfo::QueryCutList(frm_dir_map_t &delMap, bool loadAutoSave) const
3486 {
3487  if (loadAutoSave)
3488  {
3489  frm_dir_map_t autosaveMap;
3490  QueryMarkupMap(autosaveMap, MARK_TMP_CUT_START);
3491  QueryMarkupMap(autosaveMap, MARK_TMP_CUT_END, true);
3492  QueryMarkupMap(autosaveMap, MARK_PLACEHOLDER, true);
3493  // Convert the temporary marks into regular marks.
3494  delMap.clear();
3495  // NOLINTNEXTLINE(modernize-loop-convert)
3496  for (auto i = autosaveMap.constBegin(); i != autosaveMap.constEnd(); ++i)
3497  {
3498  uint64_t frame = i.key();
3499  MarkTypes mark = i.value();
3500  if (mark == MARK_TMP_CUT_START)
3501  mark = MARK_CUT_START;
3502  else if (mark == MARK_TMP_CUT_END)
3503  mark = MARK_CUT_END;
3504  delMap[frame] = mark;
3505  }
3506  }
3507  else
3508  {
3509  QueryMarkupMap(delMap, MARK_CUT_START);
3510  QueryMarkupMap(delMap, MARK_CUT_END, true);
3511  QueryMarkupMap(delMap, MARK_PLACEHOLDER, true);
3512  }
3513 
3514  return !delMap.isEmpty();
3515 }
3516 
3517 void ProgramInfo::SaveCutList(frm_dir_map_t &delMap, bool isAutoSave) const
3518 {
3519  if (!isAutoSave)
3520  {
3523  }
3527 
3528  frm_dir_map_t tmpDelMap;
3529  // NOLINTNEXTLINE(modernize-loop-convert)
3530  for (auto i = delMap.constBegin(); i != delMap.constEnd(); ++i)
3531  {
3532  uint64_t frame = i.key();
3533  MarkTypes mark = i.value();
3534  if (isAutoSave)
3535  {
3536  if (mark == MARK_CUT_START)
3537  mark = MARK_TMP_CUT_START;
3538  else if (mark == MARK_CUT_END)
3539  mark = MARK_TMP_CUT_END;
3540  }
3541  tmpDelMap[frame] = mark;
3542  }
3543  SaveMarkupMap(tmpDelMap);
3544 
3545  if (IsRecording())
3546  {
3547  MSqlQuery query(MSqlQuery::InitCon());
3548 
3549  // Flag the existence of a cutlist
3550  query.prepare("UPDATE recorded"
3551  " SET cutlist = :CUTLIST"
3552  " WHERE chanid = :CHANID"
3553  " AND starttime = :STARTTIME ;");
3554 
3555  query.bindValue(":CUTLIST", delMap.isEmpty() ? 0 : 1);
3556  query.bindValue(":CHANID", m_chanId);
3557  query.bindValue(":STARTTIME", m_recStartTs);
3558 
3559  if (!query.exec())
3560  MythDB::DBError("cutlist flag update", query);
3561  }
3562 }
3563 
3565 {
3568  SaveMarkupMap(frames);
3569 }
3570 
3572 {
3574  QueryMarkupMap(frames, MARK_COMM_END, true);
3575 }
3576 
3578  MarkTypes type, int64_t min_frame, int64_t max_frame) const
3579 {
3580  MSqlQuery query(MSqlQuery::InitCon());
3581  QString comp;
3582 
3583  if (min_frame >= 0)
3584  comp += QString(" AND mark >= %1 ").arg(min_frame);
3585 
3586  if (max_frame >= 0)
3587  comp += QString(" AND mark <= %1 ").arg(max_frame);
3588 
3589  if (type != MARK_ALL)
3590  comp += QString(" AND type = :TYPE ");
3591 
3592  if (IsVideo())
3593  {
3594  query.prepare("DELETE FROM filemarkup"
3595  " WHERE filename = :PATH "
3596  + comp + ";");
3598  }
3599  else if (IsRecording())
3600  {
3601  query.prepare("DELETE FROM recordedmarkup"
3602  " WHERE chanid = :CHANID"
3603  " AND STARTTIME = :STARTTIME"
3604  + comp + ';');
3605  query.bindValue(":CHANID", m_chanId);
3606  query.bindValue(":STARTTIME", m_recStartTs);
3607  }
3608  else
3609  {
3610  return;
3611  }
3612  query.bindValue(":TYPE", type);
3613 
3614  if (!query.exec())
3615  MythDB::DBError("ClearMarkupMap deleting", query);
3616 }
3617 
3619  const frm_dir_map_t &marks, MarkTypes type,
3620  int64_t min_frame, int64_t max_frame) const
3621 {
3622  MSqlQuery query(MSqlQuery::InitCon());
3623  QString videoPath;
3624 
3625  if (IsVideo())
3626  {
3628  }
3629  else if (IsRecording())
3630  {
3631  // check to make sure the show still exists before saving markups
3632  query.prepare("SELECT starttime FROM recorded"
3633  " WHERE chanid = :CHANID"
3634  " AND starttime = :STARTTIME ;");
3635  query.bindValue(":CHANID", m_chanId);
3636  query.bindValue(":STARTTIME", m_recStartTs);
3637 
3638  if (!query.exec())
3639  MythDB::DBError("SaveMarkupMap checking record table", query);
3640 
3641  if (!query.next())
3642  return;
3643  }
3644  else
3645  {
3646  return;
3647  }
3648 
3649  frm_dir_map_t::const_iterator it;
3650  for (it = marks.begin(); it != marks.end(); ++it)
3651  {
3652  uint64_t frame = it.key();
3653 
3654  if ((min_frame >= 0) && (frame < (uint64_t)min_frame))
3655  continue;
3656 
3657  if ((max_frame >= 0) && (frame > (uint64_t)max_frame))
3658  continue;
3659 
3660  int mark_type = (type != MARK_ALL) ? type : *it;
3661 
3662  if (IsVideo())
3663  {
3664  query.prepare("INSERT INTO filemarkup (filename, mark, type)"
3665  " VALUES ( :PATH , :MARK , :TYPE );");
3666  query.bindValue(":PATH", videoPath);
3667  }
3668  else // if (IsRecording())
3669  {
3670  query.prepare("INSERT INTO recordedmarkup"
3671  " (chanid, starttime, mark, type)"
3672  " VALUES ( :CHANID , :STARTTIME , :MARK , :TYPE );");
3673  query.bindValue(":CHANID", m_chanId);
3674  query.bindValue(":STARTTIME", m_recStartTs);
3675  }
3676  query.bindValue(":MARK", (quint64)frame);
3677  query.bindValue(":TYPE", mark_type);
3678 
3679  if (!query.exec())
3680  MythDB::DBError("SaveMarkupMap inserting", query);
3681  }
3682 }
3683 
3685  frm_dir_map_t &marks, MarkTypes type, bool merge) const
3686 {
3687  if (!merge)
3688  marks.clear();
3689 
3690  if (IsVideo())
3691  {
3693  marks, type, merge);
3694  }
3695  else if (IsRecording())
3696  {
3697  QueryMarkupMap(m_chanId, m_recStartTs, marks, type, merge);
3698  }
3699 }
3700 
3702  const QString &video_pathname,
3703  frm_dir_map_t &marks,
3704  MarkTypes type, bool mergeIntoMap)
3705 {
3706  if (!mergeIntoMap)
3707  marks.clear();
3708 
3709  MSqlQuery query(MSqlQuery::InitCon());
3710 
3711  query.prepare("SELECT mark, type, `offset` "
3712  "FROM filemarkup "
3713  "WHERE filename = :PATH AND "
3714  " type = :TYPE "
3715  "ORDER BY mark");
3716  query.bindValue(":PATH", video_pathname);
3717  query.bindValue(":TYPE", type);
3718 
3719  if (!query.exec())
3720  {
3721  MythDB::DBError("QueryMarkupMap", query);
3722  return;
3723  }
3724 
3725  while (query.next())
3726  {
3727  // marks[query.value(0).toLongLong()] =
3728  // (MarkTypes) query.value(1).toInt();
3729  int entryType = query.value(1).toInt();
3730  if (entryType == MARK_VIDEO_RATE)
3731  marks[query.value(2).toLongLong()] = (MarkTypes) entryType;
3732  else
3733  marks[query.value(0).toLongLong()] = (MarkTypes) entryType;
3734 
3735  }
3736 }
3737 
3739  uint chanid, const QDateTime &recstartts,
3740  frm_dir_map_t &marks,
3741  MarkTypes type, bool mergeIntoMap)
3742 {
3743  if (!mergeIntoMap)
3744  marks.clear();
3745 
3746  MSqlQuery query(MSqlQuery::InitCon());
3747  query.prepare("SELECT mark, type, data "
3748  "FROM recordedmarkup "
3749  "WHERE chanid = :CHANID AND "
3750  " starttime = :STARTTIME AND"
3751  " type = :TYPE "
3752  "ORDER BY mark");
3753  query.bindValue(":CHANID", chanid);
3754  query.bindValue(":STARTTIME", recstartts);
3755  query.bindValue(":TYPE", type);
3756 
3757  if (!query.exec())
3758  {
3759  MythDB::DBError("QueryMarkupMap", query);
3760  return;
3761  }
3762 
3763  while (query.next())
3764  {
3765  // marks[query.value(0).toULongLong()] =
3766  // (MarkTypes) query.value(1).toInt();
3767  int entryType = query.value(1).toInt();
3768  if (entryType == MARK_VIDEO_RATE)
3769  marks[query.value(2).toULongLong()] = (MarkTypes) entryType;
3770  else
3771  marks[query.value(0).toULongLong()] = (MarkTypes) entryType;
3772  }
3773 }
3774 
3777 {
3778  frm_dir_map_t flagMap;
3779 
3780  QueryMarkupMap(flagMap, type);
3781 
3782  return flagMap.contains(0);
3783 }
3784 
3787 {
3789  frm_dir_map_t flagMap;
3790  flagMap[0] = type;
3791  SaveMarkupMap(flagMap, type);
3792 }
3793 
3795  frm_pos_map_t &posMap, MarkTypes type) const
3796 {
3798  {
3799  QMutexLocker locker(m_positionMapDBReplacement->lock);
3800  posMap = m_positionMapDBReplacement->map[type];
3801 
3802  return;
3803  }
3804 
3805  posMap.clear();
3806  MSqlQuery query(MSqlQuery::InitCon());
3807 
3808  if (IsVideo())
3809  {
3810  query.prepare("SELECT mark, `offset` FROM filemarkup"
3811  " WHERE filename = :PATH"
3812  " AND type = :TYPE ;");
3814  }
3815  else if (IsRecording())
3816  {
3817  query.prepare("SELECT mark, `offset` FROM recordedseek"
3818  " WHERE chanid = :CHANID"
3819  " AND starttime = :STARTTIME"
3820  " AND type = :TYPE ;");
3821  query.bindValue(":CHANID", m_chanId);
3822  query.bindValue(":STARTTIME", m_recStartTs);
3823  }
3824  else
3825  {
3826  return;
3827  }
3828  query.bindValue(":TYPE", type);
3829 
3830  if (!query.exec())
3831  {
3832  MythDB::DBError("QueryPositionMap", query);
3833  return;
3834  }
3835 
3836  while (query.next())
3837  posMap[query.value(0).toULongLong()] = query.value(1).toULongLong();
3838 }
3839 
3841 {
3843  {
3844  QMutexLocker locker(m_positionMapDBReplacement->lock);
3846  return;
3847  }
3848 
3849  MSqlQuery query(MSqlQuery::InitCon());
3850 
3851  if (IsVideo())
3852  {
3853  query.prepare("DELETE FROM filemarkup"
3854  " WHERE filename = :PATH"
3855  " AND type = :TYPE ;");
3857  }
3858  else if (IsRecording())
3859  {
3860  query.prepare("DELETE FROM recordedseek"
3861  " WHERE chanid = :CHANID"
3862  " AND starttime = :STARTTIME"
3863  " AND type = :TYPE ;");
3864  query.bindValue(":CHANID", m_chanId);
3865  query.bindValue(":STARTTIME", m_recStartTs);
3866  }
3867  else
3868  {
3869  return;
3870  }
3871 
3872  query.bindValue(":TYPE", type);
3873 
3874  if (!query.exec())
3875  MythDB::DBError("clear position map", query);
3876 }
3877 
3879  frm_pos_map_t &posMap, MarkTypes type,
3880  int64_t min_frame, int64_t max_frame) const
3881 {
3883  {
3884  QMutexLocker locker(m_positionMapDBReplacement->lock);
3885 
3886  if ((min_frame >= 0) || (max_frame >= 0))
3887  {
3888  frm_pos_map_t new_map;
3889  // NOLINTNEXTLINE(modernize-loop-convert)
3890  for (auto it = m_positionMapDBReplacement->map[type].begin();
3891  it != m_positionMapDBReplacement->map[type].end();
3892  ++it)
3893  {
3894  uint64_t frame = it.key();
3895  if ((min_frame >= 0) && (frame >= (uint64_t)min_frame))
3896  continue;
3897  if ((min_frame >= 0) && (frame <= (uint64_t)max_frame))
3898  continue;
3899  new_map.insert(it.key(), *it);
3900  }
3901  m_positionMapDBReplacement->map[type] = new_map;
3902  }
3903  else
3904  {
3906  }
3907 
3908  // NOLINTNEXTLINE(modernize-loop-convert)
3909  for (auto it = posMap.cbegin(); it != posMap.cend(); ++it)
3910  {
3911  uint64_t frame = it.key();
3912  if ((min_frame >= 0) && (frame >= (uint64_t)min_frame))
3913  continue;
3914  if ((min_frame >= 0) && (frame <= (uint64_t)max_frame))
3915  continue;
3916 
3918  .insert(frame, *it);
3919  }
3920 
3921  return;
3922  }
3923 
3924  MSqlQuery query(MSqlQuery::InitCon());
3925  QString comp;
3926 
3927  if (min_frame >= 0)
3928  comp += " AND mark >= :MIN_FRAME ";
3929  if (max_frame >= 0)
3930  comp += " AND mark <= :MAX_FRAME ";
3931 
3932  QString videoPath;
3933  if (IsVideo())
3934  {
3936 
3937  query.prepare("DELETE FROM filemarkup"
3938  " WHERE filename = :PATH"
3939  " AND type = :TYPE"
3940  + comp + ';');
3941  query.bindValue(":PATH", videoPath);
3942  }
3943  else if (IsRecording())
3944  {
3945  query.prepare("DELETE FROM recordedseek"
3946  " WHERE chanid = :CHANID"
3947  " AND starttime = :STARTTIME"
3948  " AND type = :TYPE"
3949  + comp + ';');
3950  query.bindValue(":CHANID", m_chanId);
3951  query.bindValue(":STARTTIME", m_recStartTs);
3952  }
3953  else
3954  {
3955  return;
3956  }
3957 
3958  query.bindValue(":TYPE", type);
3959  if (min_frame >= 0)
3960  query.bindValue(":MIN_FRAME", (quint64)min_frame);
3961  if (max_frame >= 0)
3962  query.bindValue(":MAX_FRAME", (quint64)max_frame);
3963 
3964  if (!query.exec())
3965  MythDB::DBError("position map clear", query);
3966 
3967  if (posMap.isEmpty())
3968  return;
3969 
3970  // Use the multi-value insert syntax to reduce database I/O
3971  QStringList q("INSERT INTO ");
3972  QString qfields;
3973  if (IsVideo())
3974  {
3975  q << "filemarkup (filename, type, mark, `offset`)";
3976  qfields = QString("('%1',%2,") .
3977  // ideally, this should be escaped
3978  arg(videoPath) .
3979  arg(type);
3980  }
3981  else // if (IsRecording())
3982  {
3983  q << "recordedseek (chanid, starttime, type, mark, `offset`)";
3984  qfields = QString("(%1,'%2',%3,") .
3985  arg(m_chanId) .
3986  arg(m_recStartTs.toString(Qt::ISODate)) .
3987  arg(type);
3988  }
3989  q << " VALUES ";
3990 
3991  bool add_comma = false;
3992  frm_pos_map_t::iterator it;
3993  for (it = posMap.begin(); it != posMap.end(); ++it)
3994  {
3995  uint64_t frame = it.key();
3996 
3997  if ((min_frame >= 0) && (frame < (uint64_t)min_frame))
3998  continue;
3999 
4000  if ((max_frame >= 0) && (frame > (uint64_t)max_frame))
4001  continue;
4002 
4003  uint64_t offset = *it;
4004 
4005  if (add_comma)
4006  {
4007  q << ",";
4008  }
4009  else
4010  {
4011  add_comma = true;
4012  }
4013  q << qfields << QString("%1,%2)").arg(frame).arg(offset);
4014  }
4015  query.prepare(q.join(""));
4016  if (!query.exec())
4017  {
4018  MythDB::DBError("position map insert", query);
4019  }
4020 }
4021 
4023  frm_pos_map_t &posMap, MarkTypes type) const
4024 {
4025  if (posMap.isEmpty())
4026  return;
4027 
4029  {
4030  QMutexLocker locker(m_positionMapDBReplacement->lock);
4031 
4032  for (auto it = posMap.cbegin(); it != posMap.cend(); ++it)
4033  m_positionMapDBReplacement->map[type].insert(it.key(), *it);
4034 
4035  return;
4036  }
4037 
4038  // Use the multi-value insert syntax to reduce database I/O
4039  QStringList q("INSERT INTO ");
4040  QString qfields;
4041  if (IsVideo())
4042  {
4043  q << "filemarkup (filename, type, mark, `offset`)";
4044  qfields = QString("('%1',%2,") .
4045  // ideally, this should be escaped
4047  arg(type);
4048  }
4049  else if (IsRecording())
4050  {
4051  q << "recordedseek (chanid, starttime, type, mark, `offset`)";
4052  qfields = QString("(%1,'%2',%3,") .
4053  arg(m_chanId) .
4054  arg(m_recStartTs.toString(Qt::ISODate)) .
4055  arg(type);
4056  }
4057  else
4058  {
4059  return;
4060  }
4061  q << " VALUES ";
4062 
4063  bool add_comma = false;
4064  frm_pos_map_t::iterator it;
4065  for (it = posMap.begin(); it != posMap.end(); ++it)
4066  {
4067  uint64_t frame = it.key();
4068  uint64_t offset = *it;
4069 
4070  if (add_comma)
4071  {
4072  q << ",";
4073  }
4074  else
4075  {
4076  add_comma = true;
4077  }
4078  q << qfields << QString("%1,%2)").arg(frame).arg(offset);
4079  }
4080 
4081  MSqlQuery query(MSqlQuery::InitCon());
4082  query.prepare(q.join(""));
4083  if (!query.exec())
4084  {
4085  MythDB::DBError("delta position map insert", query);
4086  }
4087 }
4088 
4089 static const char *from_filemarkup_offset_asc =
4090  "SELECT mark, `offset` FROM filemarkup"
4091  " WHERE filename = :PATH"
4092  " AND type = :TYPE"
4093  " AND mark >= :QUERY_ARG"
4094  " ORDER BY filename ASC, type ASC, mark ASC LIMIT 1;";
4095 static const char *from_filemarkup_offset_desc =
4096  "SELECT mark, `offset` FROM filemarkup"
4097  " WHERE filename = :PATH"
4098  " AND type = :TYPE"
4099  " AND mark <= :QUERY_ARG"
4100  " ORDER BY filename DESC, type DESC, mark DESC LIMIT 1;";
4101 static const char *from_recordedseek_offset_asc =
4102  "SELECT mark, `offset` FROM recordedseek"
4103  " WHERE chanid = :CHANID"
4104  " AND starttime = :STARTTIME"
4105  " AND type = :TYPE"
4106  " AND mark >= :QUERY_ARG"
4107  " ORDER BY chanid ASC, starttime ASC, type ASC, mark ASC LIMIT 1;";
4108 static const char *from_recordedseek_offset_desc =
4109  "SELECT mark, `offset` FROM recordedseek"
4110  " WHERE chanid = :CHANID"
4111  " AND starttime = :STARTTIME"
4112  " AND type = :TYPE"
4113  " AND mark <= :QUERY_ARG"
4114  " ORDER BY chanid DESC, starttime DESC, type DESC, mark DESC LIMIT 1;";
4115 static const char *from_filemarkup_mark_asc =
4116  "SELECT `offset`,mark FROM filemarkup"
4117  " WHERE filename = :PATH"
4118  " AND type = :TYPE"
4119  " AND `offset` >= :QUERY_ARG"
4120  " ORDER BY filename ASC, type ASC, mark ASC LIMIT 1;";
4121 static const char *from_filemarkup_mark_desc =
4122  "SELECT `offset`,mark FROM filemarkup"
4123  " WHERE filename = :PATH"
4124  " AND type = :TYPE"
4125  " AND `offset` <= :QUERY_ARG"
4126  " ORDER BY filename DESC, type DESC, mark DESC LIMIT 1;";
4127 static const char *from_recordedseek_mark_asc =
4128  "SELECT `offset`,mark FROM recordedseek"
4129  " WHERE chanid = :CHANID"
4130  " AND starttime = :STARTTIME"
4131  " AND type = :TYPE"
4132  " AND `offset` >= :QUERY_ARG"
4133  " ORDER BY chanid ASC, starttime ASC, type ASC, mark ASC LIMIT 1;";
4134 static const char *from_recordedseek_mark_desc =
4135  "SELECT `offset`,mark FROM recordedseek"
4136  " WHERE chanid = :CHANID"
4137  " AND starttime = :STARTTIME"
4138  " AND type = :TYPE"
4139  " AND `offset` <= :QUERY_ARG"
4140  " ORDER BY chanid DESC, starttime DESC, type DESC, mark DESC LIMIT 1;";
4141 
4142 bool ProgramInfo::QueryKeyFrameInfo(uint64_t * result,
4143  uint64_t position_or_keyframe,
4144  bool backwards,
4145  MarkTypes type,
4146  const char *from_filemarkup_asc,
4147  const char *from_filemarkup_desc,
4148  const char *from_recordedseek_asc,
4149  const char *from_recordedseek_desc) const
4150 {
4151  MSqlQuery query(MSqlQuery::InitCon());
4152 
4153  if (IsVideo())
4154  {
4155  if (backwards)
4156  query.prepare(from_filemarkup_desc);
4157  else
4158  query.prepare(from_filemarkup_asc);
4160  }
4161  else if (IsRecording())
4162  {
4163  if (backwards)
4164  query.prepare(from_recordedseek_desc);
4165  else
4166  query.prepare(from_recordedseek_asc);
4167  query.bindValue(":CHANID", m_chanId);
4168  query.bindValue(":STARTTIME", m_recStartTs);
4169  }
4170  query.bindValue(":TYPE", type);
4171  query.bindValue(":QUERY_ARG", (unsigned long long)position_or_keyframe);
4172 
4173  if (!query.exec())
4174  {
4175  MythDB::DBError("QueryKeyFrameInfo", query);
4176  return false;
4177  }
4178 
4179  if (query.next())
4180  {
4181  *result = query.value(1).toULongLong();
4182  return true;
4183  }
4184 
4185  if (IsVideo())
4186  {
4187  if (backwards)
4188  query.prepare(from_filemarkup_asc);
4189  else
4190  query.prepare(from_filemarkup_desc);
4192  }
4193  else if (IsRecording())
4194  {
4195  if (backwards)
4196  query.prepare(from_recordedseek_asc);
4197  else
4198  query.prepare(from_recordedseek_desc);
4199  query.bindValue(":CHANID", m_chanId);
4200  query.bindValue(":STARTTIME", m_recStartTs);
4201  }
4202  query.bindValue(":TYPE", type);
4203  query.bindValue(":QUERY_ARG", (unsigned long long)position_or_keyframe);
4204 
4205  if (!query.exec())
4206  {
4207  MythDB::DBError("QueryKeyFrameInfo", query);
4208  return false;
4209  }
4210 
4211  if (query.next())
4212  {
4213  *result = query.value(1).toULongLong();
4214  return true;
4215  }
4216 
4217  return false;
4218 
4219 }
4220 
4221 bool ProgramInfo::QueryPositionKeyFrame(uint64_t *keyframe, uint64_t position,
4222  bool backwards) const
4223 {
4224  return QueryKeyFrameInfo(keyframe, position, backwards, MARK_GOP_BYFRAME,
4229 }
4230 bool ProgramInfo::QueryKeyFramePosition(uint64_t *position, uint64_t keyframe,
4231  bool backwards) const
4232 {
4233  return QueryKeyFrameInfo(position, keyframe, backwards, MARK_GOP_BYFRAME,
4238 }
4239 bool ProgramInfo::QueryDurationKeyFrame(uint64_t *keyframe, uint64_t duration,
4240  bool backwards) const
4241 {
4242  return QueryKeyFrameInfo(keyframe, duration, backwards, MARK_DURATION_MS,
4247 }
4248 bool ProgramInfo::QueryKeyFrameDuration(uint64_t *duration, uint64_t keyframe,
4249  bool backwards) const
4250 {
4251  return QueryKeyFrameInfo(duration, keyframe, backwards, MARK_DURATION_MS,
4256 }
4257 
4262  uint64_t frame, MarkTypes type, uint customAspect)
4263 {
4264  if (!IsRecording())
4265  return;
4266 
4267  MSqlQuery query(MSqlQuery::InitCon());
4268 
4269  query.prepare("INSERT INTO recordedmarkup"
4270  " (chanid, starttime, mark, type, data)"
4271  " VALUES"
4272  " ( :CHANID, :STARTTIME, :MARK, :TYPE, :DATA);");
4273  query.bindValue(":CHANID", m_chanId);
4274  query.bindValue(":STARTTIME", m_recStartTs);
4275 
4276  query.bindValue(":MARK", (quint64)frame);
4277  query.bindValue(":TYPE", type);
4278 
4279  if (type == MARK_ASPECT_CUSTOM)
4280  query.bindValue(":DATA", customAspect);
4281  else
4282  {
4283  // create NULL value
4284 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
4285  query.bindValue(":DATA", QVariant(QVariant::UInt));
4286 #else
4287  query.bindValue(":DATA", QVariant(QMetaType(QMetaType::UInt)));
4288 #endif
4289  }
4290 
4291  if (!query.exec())
4292  MythDB::DBError("aspect ratio change insert", query);
4293 }
4294 
4298 void ProgramInfo::SaveFrameRate(uint64_t frame, uint framerate)
4299 {
4300  if (!IsRecording())
4301  return;
4302 
4303  MSqlQuery query(MSqlQuery::InitCon());
4304 
4305  query.prepare("INSERT INTO recordedmarkup"
4306  " (chanid, starttime, mark, type, data)"
4307  " VALUES"
4308  " ( :CHANID, :STARTTIME, :MARK, :TYPE, :DATA);");
4309  query.bindValue(":CHANID", m_chanId);
4310  query.bindValue(":STARTTIME", m_recStartTs);
4311  query.bindValue(":MARK", (quint64)frame);
4312  query.bindValue(":TYPE", MARK_VIDEO_RATE);
4313  query.bindValue(":DATA", framerate);
4314 
4315  if (!query.exec())
4316  MythDB::DBError("Frame rate insert", query);
4317 }
4318 
4319 
4323 void ProgramInfo::SaveVideoScanType(uint64_t frame, bool progressive)
4324 {
4325  if (!IsRecording())
4326  return;
4327 
4328  MSqlQuery query(MSqlQuery::InitCon());
4329 
4330  query.prepare("INSERT INTO recordedmarkup"
4331  " (chanid, starttime, mark, type, data)"
4332  " VALUES"
4333  " ( :CHANID, :STARTTIME, :MARK, :TYPE, :DATA);");
4334  query.bindValue(":CHANID", m_chanId);
4335  query.bindValue(":STARTTIME", m_recStartTs);
4336  query.bindValue(":MARK", (quint64)frame);
4337  query.bindValue(":TYPE", MARK_SCAN_PROGRESSIVE);
4338  query.bindValue(":DATA", progressive);
4339 
4340  if (!query.exec())
4341  MythDB::DBError("Video scan type insert", query);
4342 }
4343 
4344 
4346  MarkTypes type, uint chanid, const QDateTime &recstartts)
4347 {
4348  MSqlQuery query(MSqlQuery::InitCon());
4349 
4350  query.prepare("DELETE FROM recordedmarkup "
4351  " WHERE chanid=:CHANID "
4352  " AND starttime=:STARTTIME "
4353  " AND type=:TYPE");
4354  query.bindValue(":CHANID", chanid);
4355  query.bindValue(":STARTTIME", recstartts);
4356  query.bindValue(":TYPE", type);
4357 
4358  if (!query.exec())
4359  MythDB::DBError("delete_markup_datum", query);
4360 }
4361 
4363  MarkTypes type, const QString &videoPath)
4364 {
4365  MSqlQuery query(MSqlQuery::InitCon());
4366 
4367  query.prepare("DELETE FROM filemarkup"
4368  " WHERE filename = :PATH "
4369  " AND type = :TYPE ;");
4370  query.bindValue(":PATH", videoPath);
4371  query.bindValue(":TYPE", type);
4372 
4373  if (!query.exec())
4374  MythDB::DBError("delete_markup_datum", query);
4375 }
4376 
4378  MarkTypes type, uint mark, uint offset, const QString &videoPath)
4379 {
4380  MSqlQuery query(MSqlQuery::InitCon());
4381 
4382  query.prepare("INSERT INTO filemarkup"
4383  " (filename, mark, type, `offset`)"
4384  " VALUES"
4385  " ( :PATH , :MARK , :TYPE, :OFFSET );");
4386  query.bindValue(":PATH", videoPath);
4387  query.bindValue(":OFFSET", offset);
4388  query.bindValue(":TYPE", type);
4389  query.bindValue(":MARK", mark);
4390 
4391  if (!query.exec())
4392  MythDB::DBError("insert_markup_datum", query);
4393 }
4394 
4396  MarkTypes type, uint mark, uint data, uint chanid, const QDateTime &recstartts)
4397 {
4398  MSqlQuery query(MSqlQuery::InitCon());
4399 
4400  query.prepare("INSERT INTO recordedmarkup"
4401  " (chanid, starttime, mark, type, data)"
4402  " VALUES"
4403  " ( :CHANID, :STARTTIME, :MARK, :TYPE, :DATA);");
4404  query.bindValue(":CHANID", chanid);
4405  query.bindValue(":STARTTIME", recstartts);
4406  query.bindValue(":DATA", data);
4407  query.bindValue(":TYPE", type);
4408  query.bindValue(":MARK", mark);
4409 
4410  if (!query.exec())
4411  MythDB::DBError("insert_markup_datum", query);
4412 }
4413 
4414 
4416 void ProgramInfo::SaveTotalDuration(std::chrono::milliseconds duration)
4417 {
4418  if (IsVideo())
4419  {
4422  insert_markup_datum(MARK_DURATION_MS, 0, duration.count(), videoPath);
4423  }
4424  else if (IsRecording())
4425  {
4428  }
4429 }
4430 
4432 void ProgramInfo::SaveTotalFrames(int64_t frames)
4433 {
4434  if (IsVideo())
4435  {
4438  insert_markup_datum(MARK_TOTAL_FRAMES, 0, frames, videoPath);
4439  }
4440  else if (IsRecording())
4441  {
4444  }
4445 }
4446 
4450 void ProgramInfo::SaveResolution(uint64_t frame, uint width, uint height)
4451 {
4452  if (!IsRecording())
4453  return;
4454 
4455  MSqlQuery query(MSqlQuery::InitCon());
4456 
4457  query.prepare("INSERT INTO recordedmarkup"
4458  " (chanid, starttime, mark, type, data)"
4459  " VALUES"
4460  " ( :CHANID, :STARTTIME, :MARK, :TYPE, :DATA);");
4461  query.bindValue(":CHANID", m_chanId);
4462  query.bindValue(":STARTTIME", m_recStartTs);
4463  query.bindValue(":MARK", (quint64)frame);
4464  query.bindValue(":TYPE", MARK_VIDEO_WIDTH);
4465  query.bindValue(":DATA", width);
4466 
4467  if (!query.exec())
4468  MythDB::DBError("Resolution insert", query);
4469 
4470  query.prepare("INSERT INTO recordedmarkup"
4471  " (chanid, starttime, mark, type, data)"
4472  " VALUES"
4473  " ( :CHANID, :STARTTIME, :MARK, :TYPE, :DATA);");
4474  query.bindValue(":CHANID", m_chanId);
4475  query.bindValue(":STARTTIME", m_recStartTs);
4476  query.bindValue(":MARK", (quint64)frame);
4477  query.bindValue(":TYPE", MARK_VIDEO_HEIGHT);
4478  query.bindValue(":DATA", height);
4479 
4480  if (!query.exec())
4481  MythDB::DBError("Resolution insert", query);
4482 }
4483 
4485  MarkTypes type, uint chanid, const QDateTime &recstartts)
4486 {
4487  QString qstr = QString(
4488  "SELECT recordedmarkup.data "
4489  "FROM recordedmarkup "
4490  "WHERE recordedmarkup.chanid = :CHANID AND "
4491  " recordedmarkup.starttime = :STARTTIME AND "
4492  " recordedmarkup.type = :TYPE "
4493  "GROUP BY recordedmarkup.data "
4494  "ORDER BY SUM( ( SELECT IFNULL(rm.mark, recordedmarkup.mark)"
4495  " FROM recordedmarkup AS rm "
4496  " WHERE rm.chanid = recordedmarkup.chanid AND "
4497  " rm.starttime = recordedmarkup.starttime AND "
4498  " rm.type = recordedmarkup.type AND "
4499  " rm.mark > recordedmarkup.mark "
4500  " ORDER BY rm.mark ASC LIMIT 1 "
4501  " ) - recordedmarkup.mark "
4502  " ) DESC "
4503  "LIMIT 1");
4504 
4505  MSqlQuery query(MSqlQuery::InitCon());
4506  query.prepare(qstr);
4507  query.bindValue(":TYPE", (int)type);
4508  query.bindValue(":CHANID", chanid);
4509  query.bindValue(":STARTTIME", recstartts);
4510 
4511  if (!query.exec())
4512  {
4513  MythDB::DBError("load_markup_datum", query);
4514  return 0;
4515  }
4516 
4517  return (query.next()) ? query.value(0).toUInt() : 0;
4518 }
4519 
4521  MarkTypes type, const QString &videoPath)
4522 {
4523  QString qstr = QString(
4524  "SELECT filemarkup.`offset` "
4525  "FROM filemarkup "
4526  "WHERE filemarkup.filename = :PATH AND "
4527  " filemarkup.type = :TYPE "
4528  "GROUP BY filemarkup.`offset` "
4529  "ORDER BY SUM( ( SELECT IFNULL(fm.mark, filemarkup.mark)"
4530  " FROM filemarkup AS fm "
4531  " WHERE fm.filename = filemarkup.filename AND "
4532  " fm.type = filemarkup.type AND "
4533  " fm.mark > filemarkup.mark "
4534  " ORDER BY fm.mark ASC LIMIT 1 "
4535  " ) - filemarkup.mark "
4536  " ) DESC "
4537  "LIMIT 1");
4538 
4539  MSqlQuery query(MSqlQuery::InitCon());
4540  query.prepare(qstr);
4541  query.bindValue(":TYPE", (int)type);
4542  query.bindValue(":PATH", videoPath);
4543 
4544  if (!query.exec())
4545  {
4546  MythDB::DBError("load_markup_datum", query);
4547  return 0;
4548  }
4549 
4550  return (query.next()) ? query.value(0).toUInt() : 0;
4551 }
4552 
4553 
4559 {
4561 }
4562 
4568 {
4570 }
4571 
4577 {
4579 }
4580 
4582 {
4583  MSqlQuery query(MSqlQuery::InitCon());
4584  query.prepare("SELECT recordedmarkup.type "
4585  "FROM recordedmarkup "
4586  "WHERE recordedmarkup.chanid = :CHANID AND "
4587  " recordedmarkup.starttime = :STARTTIME AND "
4588  " recordedmarkup.type >= :ASPECTSTART AND "
4589  " recordedmarkup.type <= :ASPECTEND "
4590  "GROUP BY recordedmarkup.type "
4591  "ORDER BY SUM( ( SELECT IFNULL(rm.mark, ( "
4592  " SELECT MAX(rmmax.mark) "
4593  " FROM recordedmarkup AS rmmax "
4594  " WHERE rmmax.chanid = recordedmarkup.chanid "
4595  " AND rmmax.starttime = recordedmarkup.starttime "
4596  " ) ) "
4597  " FROM recordedmarkup AS rm "
4598  " WHERE rm.chanid = recordedmarkup.chanid AND "
4599  " rm.starttime = recordedmarkup.starttime AND "
4600  " rm.type >= :ASPECTSTART2 AND "
4601  " rm.type <= :ASPECTEND2 AND "
4602  " rm.mark > recordedmarkup.mark "
4603  " ORDER BY rm.mark ASC LIMIT 1 "
4604  " ) - recordedmarkup.mark "
4605  " ) DESC "
4606  "LIMIT 1");
4607  query.bindValue(":CHANID", m_chanId);
4608  query.bindValue(":STARTTIME", m_recStartTs);
4609  query.bindValue(":ASPECTSTART", MARK_ASPECT_4_3); // 11
4610  query.bindValue(":ASPECTEND", MARK_ASPECT_CUSTOM); // 14
4611  query.bindValue(":ASPECTSTART2", MARK_ASPECT_4_3); // 11
4612  query.bindValue(":ASPECTEND2", MARK_ASPECT_CUSTOM); // 14
4613 
4614  if (!query.exec())
4615  {
4616  MythDB::DBError("QueryAverageAspectRatio", query);
4617  return MARK_UNSET;
4618  }
4619 
4620  if (!query.next())
4621  return MARK_UNSET;
4622 
4623  return static_cast<MarkTypes>(query.value(0).toInt());
4624 }
4625 
4632 {
4633  return static_cast<bool>(load_markup_datum(MARK_SCAN_PROGRESSIVE,
4635 }
4636 
4642 std::chrono::milliseconds ProgramInfo::QueryTotalDuration(void) const
4643 {
4645  return 0ms;
4646 
4647  auto msec = std::chrono::milliseconds(load_markup_datum(MARK_DURATION_MS, m_chanId, m_recStartTs));
4648 
4649 // Impossible condition, load_markup_datum returns an unsigned int
4650 // if (msec < 0ms)
4651 // return 0ms;
4652 
4653  return msec;
4654 }
4655 
4660 {
4661  if (IsVideo())
4662  {
4664  return frames;
4665  }
4666  if (IsRecording())
4667  {
4669  return frames;
4670  }
4671  return 0;
4672 }
4673 
4674 void ProgramInfo::QueryMarkup(QVector<MarkupEntry> &mapMark,
4675  QVector<MarkupEntry> &mapSeek) const
4676 {
4677  MSqlQuery query(MSqlQuery::InitCon());
4678  // Get the markup
4679  if (IsVideo())
4680  {
4681  query.prepare("SELECT type, mark, `offset` FROM filemarkup"
4682  " WHERE filename = :PATH"
4683  " AND type NOT IN (:KEYFRAME,:DURATION)"
4684  " ORDER BY mark, type;");
4686  query.bindValue(":KEYFRAME", MARK_GOP_BYFRAME);
4687  query.bindValue(":DURATION", MARK_DURATION_MS);
4688  }
4689  else if (IsRecording())
4690  {
4691  query.prepare("SELECT type, mark, data FROM recordedmarkup"
4692  " WHERE chanid = :CHANID"
4693  " AND STARTTIME = :STARTTIME"
4694  " ORDER BY mark, type");
4695  query.bindValue(":CHANID", m_chanId);
4696  query.bindValue(":STARTTIME", m_recStartTs);
4697  }
4698  else
4699  {
4700  return;
4701  }
4702  if (!query.exec())
4703  {
4704  MythDB::DBError("QueryMarkup markup data", query);
4705  return;
4706  }
4707  while (query.next())
4708  {
4709  int type = query.value(0).toInt();
4710  uint64_t frame = query.value(1).toLongLong();
4711  uint64_t data = 0;
4712  bool isDataNull = query.value(2).isNull();
4713  if (!isDataNull)
4714  data = query.value(2).toLongLong();
4715  mapMark.append(MarkupEntry(type, frame, data, isDataNull));
4716  }
4717 
4718  // Get the seektable
4719  if (IsVideo())
4720  {
4721  query.prepare("SELECT type, mark, `offset` FROM filemarkup"
4722  " WHERE filename = :PATH"
4723  " AND type IN (:KEYFRAME,:DURATION)"
4724  " ORDER BY mark, type;");
4726  query.bindValue(":KEYFRAME", MARK_GOP_BYFRAME);
4727  query.bindValue(":DURATION", MARK_DURATION_MS);
4728  }
4729  else if (IsRecording())
4730  {
4731  query.prepare("SELECT type, mark, `offset` FROM recordedseek"
4732  " WHERE chanid = :CHANID"
4733  " AND STARTTIME = :STARTTIME"
4734  " ORDER BY mark, type");
4735  query.bindValue(":CHANID", m_chanId);
4736  query.bindValue(":STARTTIME", m_recStartTs);
4737  }
4738  if (!query.exec())
4739  {
4740  MythDB::DBError("QueryMarkup seektable data", query);
4741  return;
4742  }
4743  while (query.next())
4744  {
4745  int type = query.value(0).toInt();
4746  uint64_t frame = query.value(1).toLongLong();
4747  uint64_t data = 0;
4748  bool isDataNull = query.value(2).isNull();
4749  if (!isDataNull)
4750  data = query.value(2).toLongLong();
4751  mapSeek.append(MarkupEntry(type, frame, data, isDataNull));
4752  }
4753 }
4754 
4755 void ProgramInfo::SaveMarkup(const QVector<MarkupEntry> &mapMark,
4756  const QVector<MarkupEntry> &mapSeek) const
4757 {
4758  MSqlQuery query(MSqlQuery::InitCon());
4759  if (IsVideo())
4760  {
4762  if (mapMark.isEmpty())
4763  {
4764  LOG(VB_GENERAL, LOG_INFO,
4765  QString("No mark entries in input, "
4766  "not removing marks from DB"));
4767  }
4768  else
4769  {
4770  query.prepare("DELETE FROM filemarkup"
4771  " WHERE filename = :PATH"
4772  " AND type NOT IN (:KEYFRAME,:DURATION)");
4773  query.bindValue(":PATH", path);
4774  query.bindValue(":KEYFRAME", MARK_GOP_BYFRAME);
4775  query.bindValue(":DURATION", MARK_DURATION_MS);
4776  if (!query.exec())
4777  {
4778  MythDB::DBError("SaveMarkup seektable data", query);
4779  return;
4780  }
4781  for (const auto& entry : std::as_const(mapMark))
4782  {
4783  if (entry.type == MARK_DURATION_MS)
4784  continue;
4785  if (entry.isDataNull)
4786  {
4787  query.prepare("INSERT INTO filemarkup"
4788  " (filename,type,mark)"
4789  " VALUES (:PATH,:TYPE,:MARK)");
4790  }
4791  else
4792  {
4793  query.prepare("INSERT INTO filemarkup"
4794  " (filename,type,mark,`offset`)"
4795  " VALUES (:PATH,:TYPE,:MARK,:OFFSET)");
4796  query.bindValue(":OFFSET", (quint64)entry.data);
4797  }
4798  query.bindValue(":PATH", path);
4799  query.bindValue(":TYPE", entry.type);
4800  query.bindValue(":MARK", (quint64)entry.frame);
4801  if (!query.exec())
4802  {
4803  MythDB::DBError("SaveMarkup seektable data", query);
4804  return;
4805  }
4806  }
4807  }
4808  if (mapSeek.isEmpty())
4809  {
4810  LOG(VB_GENERAL, LOG_INFO,
4811  QString("No seek entries in input, "
4812  "not removing marks from DB"));
4813  }
4814  else
4815  {
4816  query.prepare("DELETE FROM filemarkup"
4817  " WHERE filename = :PATH"
4818  " AND type IN (:KEYFRAME,:DURATION)");
4819  query.bindValue(":PATH", path);
4820  query.bindValue(":KEYFRAME", MARK_GOP_BYFRAME);
4821  query.bindValue(":DURATION", MARK_DURATION_MS);
4822  if (!query.exec())
4823  {
4824  MythDB::DBError("SaveMarkup seektable data", query);
4825  return;
4826  }
4827  for (int i = 0; i < mapSeek.size(); ++i)
4828  {
4829  if (i > 0 && (i % 1000 == 0))
4830  {
4831  LOG(VB_GENERAL, LOG_INFO,
4832  QString("Inserted %1 of %2 records")
4833  .arg(i).arg(mapSeek.size()));
4834  }
4835  const MarkupEntry &entry = mapSeek[i];
4836  query.prepare("INSERT INTO filemarkup"
4837  " (filename,type,mark,`offset`)"
4838  " VALUES (:PATH,:TYPE,:MARK,:OFFSET)");
4839  query.bindValue(":PATH", path);
4840  query.bindValue(":TYPE", entry.type);
4841  query.bindValue(":MARK", (quint64)entry.frame);
4842  query.bindValue(":OFFSET", (quint64)entry.data);
4843  if (!query.exec())
4844  {
4845  MythDB::DBError("SaveMarkup seektable data", query);
4846  return;
4847  }
4848  }
4849  }
4850  }
4851  else if (IsRecording())
4852  {
4853  if (mapMark.isEmpty())
4854  {
4855  LOG(VB_GENERAL, LOG_INFO,
4856  QString("No mark entries in input, "
4857  "not removing marks from DB"));
4858  }
4859  else
4860  {
4861  query.prepare("DELETE FROM recordedmarkup"
4862  " WHERE chanid = :CHANID"
4863  " AND starttime = :STARTTIME");
4864  query.bindValue(":CHANID", m_chanId);
4865  query.bindValue(":STARTTIME", m_recStartTs);
4866  if (!query.exec())
4867  {
4868  MythDB::DBError("SaveMarkup seektable data", query);
4869  return;
4870  }
4871  for (const auto& entry : std::as_const(mapMark))
4872  {
4873  if (entry.isDataNull)
4874  {
4875  query.prepare("INSERT INTO recordedmarkup"
4876  " (chanid,starttime,type,mark)"
4877  " VALUES (:CHANID,:STARTTIME,:TYPE,:MARK)");
4878  }
4879  else
4880  {
4881  query.prepare("INSERT INTO recordedmarkup"
4882  " (chanid,starttime,type,mark,data)"
4883  " VALUES (:CHANID,:STARTTIME,"
4884  " :TYPE,:MARK,:OFFSET)");
4885  query.bindValue(":OFFSET", (quint64)entry.data);
4886  }
4887  query.bindValue(":CHANID", m_chanId);
4888  query.bindValue(":STARTTIME", m_recStartTs);
4889  query.bindValue(":TYPE", entry.type);
4890  query.bindValue(":MARK", (quint64)entry.frame);
4891  if (!query.exec())
4892  {
4893  MythDB::DBError("SaveMarkup seektable data", query);
4894  return;
4895  }
4896  }
4897  }
4898  if (mapSeek.isEmpty())
4899  {
4900  LOG(VB_GENERAL, LOG_INFO,
4901  QString("No seek entries in input, "
4902  "not removing marks from DB"));
4903  }
4904  else
4905  {
4906  query.prepare("DELETE FROM recordedseek"
4907  " WHERE chanid = :CHANID"
4908  " AND starttime = :STARTTIME");
4909  query.bindValue(":CHANID", m_chanId);
4910  query.bindValue(":STARTTIME", m_recStartTs);
4911  if (!query.exec())
4912  {
4913  MythDB::DBError("SaveMarkup seektable data", query);
4914  return;
4915  }
4916  for (int i = 0; i < mapSeek.size(); ++i)
4917  {
4918  if (i > 0 && (i % 1000 == 0))
4919  {
4920  LOG(VB_GENERAL, LOG_INFO,
4921  QString("Inserted %1 of %2 records")
4922  .arg(i).arg(mapSeek.size()));
4923  }
4924  const MarkupEntry &entry = mapSeek[i];
4925  query.prepare("INSERT INTO recordedseek"
4926  " (chanid,starttime,type,mark,`offset`)"
4927  " VALUES (:CHANID,:STARTTIME,"
4928  " :TYPE,:MARK,:OFFSET)");
4929  query.bindValue(":CHANID", m_chanId);
4930  query.bindValue(":STARTTIME", m_recStartTs);
4931  query.bindValue(":TYPE", entry.type);
4932  query.bindValue(":MARK", (quint64)entry.frame);
4933  query.bindValue(":OFFSET", (quint64)entry.data);
4934  if (!query.exec())
4935  {
4936  MythDB::DBError("SaveMarkup seektable data", query);
4937  return;
4938  }
4939  }
4940  }
4941  }
4942 }
4943 
4944 void ProgramInfo::SaveVideoProperties(uint mask, uint video_property_flags)
4945 {
4946  MSqlQuery query(MSqlQuery::InitCon());
4947 
4948  LOG(VB_RECORD, LOG_INFO,
4949  QString("SaveVideoProperties(0x%1, 0x%2)")
4950  .arg(mask,2,16,QChar('0')).arg(video_property_flags,2,16,QChar('0')));
4951 
4952  query.prepare(
4953  "UPDATE recordedprogram "
4954  "SET videoprop = ((videoprop+0) & :OTHERFLAGS) | :FLAGS "
4955  "WHERE chanid = :CHANID AND starttime = :STARTTIME");
4956 
4957  query.bindValue(":OTHERFLAGS", ~mask);
4958  query.bindValue(":FLAGS", video_property_flags);
4959  query.bindValue(":CHANID", m_chanId);
4960  query.bindValue(":STARTTIME", m_startTs);
4961  if (!query.exec())
4962  {
4963  MythDB::DBError("SaveVideoProperties", query);
4964  return;
4965  }
4966 
4967  uint videoproperties = m_videoProperties;
4968  videoproperties &= ~mask;
4969  videoproperties |= video_property_flags;
4970  m_videoProperties = videoproperties;
4971 
4972  SendUpdateEvent();
4973 }
4974 
4985 QString ProgramInfo::ChannelText(const QString &format) const
4986 {
4987  QString chan(format);
4988  chan.replace("<num>", m_chanStr)
4989  .replace("<sign>", m_chanSign)
4990  .replace("<name>", m_chanName);
4991  return chan;
4992 }
4993 
4995 {
4996 #ifdef DEBUG_IN_USE
4997  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("UpdateInUseMark(%1) '%2'")
4998  .arg(force?"force":"no force").arg(m_inUseForWhat));
4999 #endif
5000 
5001  if (!IsRecording())
5002  return;
5003 
5004  if (m_inUseForWhat.isEmpty())
5005  return;
5006 
5007  if (force || MythDate::secsInPast(m_lastInUseTime) > 15min)
5008  MarkAsInUse(true);
5009 }
5010 
5012 {
5013  MSqlQuery query(MSqlQuery::InitCon());
5014 
5015  query.prepare(
5016  "UPDATE recorded "
5017  "SET season = :SEASON, episode = :EPISODE "
5018  "WHERE chanid = :CHANID AND starttime = :STARTTIME "
5019  "AND recordid = :RECORDID");
5020 
5021  query.bindValue(":SEASON", seas);
5022  query.bindValue(":EPISODE", ep);
5023  query.bindValue(":CHANID", m_chanId);
5024  query.bindValue(":STARTTIME", m_recStartTs);
5025  query.bindValue(":RECORDID", m_recordId);
5026  if (!query.exec())
5027  {
5028  MythDB::DBError("SaveSeasonEpisode", query);
5029  return;
5030  }
5031 
5032  SendUpdateEvent();
5033 }
5034 
5035 void ProgramInfo::SaveInetRef(const QString &inet)
5036 {
5037  MSqlQuery query(MSqlQuery::InitCon());
5038 
5039  query.prepare(
5040  "UPDATE recorded "
5041  "SET inetref = :INETREF "
5042  "WHERE chanid = :CHANID AND starttime = :STARTTIME "
5043  "AND recordid = :RECORDID");
5044 
5045  query.bindValue(":INETREF", inet);
5046  query.bindValue(":CHANID", m_chanId);
5047  query.bindValue(":STARTTIME", m_recStartTs);
5048  query.bindValue(":RECORDID", m_recordId);
5049  query.exec();
5050 
5051  SendUpdateEvent();
5052 }
5053 
5061 {
5062  if (IsLocal() && QFileInfo(m_pathname).isReadable())
5063  return true;
5064 
5065  if (!IsMythStream())
5066  m_pathname = GetPlaybackURL(true, false);
5067 
5068  if (IsMythStream())
5069  return RemoteCheckFile(this);
5070 
5071  if (IsLocal())
5072  return QFileInfo(m_pathname).isReadable();
5073 
5074  return false;
5075 }
5076 
5077 QString ProgramInfo::QueryRecordingGroupPassword(const QString &group)
5078 {
5079  QString result;
5080 
5081  MSqlQuery query(MSqlQuery::InitCon());
5082  query.prepare("SELECT password FROM recgroups "
5083  "WHERE recgroup = :GROUP");
5084  query.bindValue(":GROUP", group);
5085 
5086  if (query.exec() && query.next())
5087  result = query.value(0).toString();
5088 
5089  return(result);
5090 }
5091 
5095 {
5096  MSqlQuery query(MSqlQuery::InitCon());
5097  query.prepare("SELECT recgroup FROM recorded "
5098  "WHERE chanid = :CHANID AND "
5099  " starttime = :START");
5100  query.bindValue(":START", m_recStartTs);
5101  query.bindValue(":CHANID", m_chanId);
5102 
5103  QString grp = m_recGroup;
5104  if (query.exec() && query.next())
5105  grp = query.value(0).toString();
5106 
5107  return grp;
5108 }
5109 
5111 {
5112  MSqlQuery query(MSqlQuery::InitCon());
5113  query.prepare("SELECT transcoder FROM recorded "
5114  "WHERE chanid = :CHANID AND "
5115  " starttime = :START");
5116  query.bindValue(":CHANID", m_chanId);
5117  query.bindValue(":START", m_recStartTs);
5118 
5119  if (query.exec() && query.next())
5120  return query.value(0).toUInt();
5121 
5122  return 0;
5123 }
5124 
5131 {
5132  if (!IsLocal())
5133  {
5134  if (!gCoreContext->IsBackend())
5135  return "";
5136 
5137  QString path = GetPlaybackURL(false, true);
5138  if (path.startsWith("/"))
5139  {
5140  QFileInfo testFile(path);
5141  return testFile.path();
5142  }
5143 
5144  return "";
5145  }
5146 
5147  QFileInfo testFile(m_pathname);
5148  if (testFile.exists() || (gCoreContext->GetHostName() == m_hostname))
5149  {
5150  // we may be recording this file and it may not exist yet so we need
5151  // to do some checking to see what is in pathname
5152  if (testFile.exists())
5153  {
5154  if (testFile.isSymLink())
5155  testFile.setFile(getSymlinkTarget(m_pathname));
5156 
5157  if (testFile.isFile())
5158  return testFile.path();
5159  if (testFile.isDir())
5160  return testFile.filePath();
5161  }
5162  else
5163  {
5164  testFile.setFile(testFile.absolutePath());
5165  if (testFile.exists())
5166  {
5167  if (testFile.isSymLink())
5168  testFile.setFile(getSymlinkTarget(testFile.path()));
5169 
5170  if (testFile.isDir())
5171  return testFile.filePath();
5172  }
5173  }
5174  }
5175 
5176  return "";
5177 }
5178 
5185 void ProgramInfo::MarkAsInUse(bool inuse, const QString& usedFor)
5186 {
5187  if (!IsRecording())
5188  return;
5189 
5190  bool notifyOfChange = false;
5191 
5192  if (inuse &&
5193  (m_inUseForWhat.isEmpty() ||
5194  (!usedFor.isEmpty() && usedFor != m_inUseForWhat)))
5195  {
5196  if (!usedFor.isEmpty())
5197  {
5198 
5199 #ifdef DEBUG_IN_USE
5200  if (!m_inUseForWhat.isEmpty())
5201  {
5202  LOG(VB_GENERAL, LOG_INFO, LOC +
5203  QString("MarkAsInUse(true, '%1'->'%2')")
5204  .arg(m_inUseForWhat).arg(usedFor) +
5205  " -- use has changed");
5206  }
5207  else
5208  {
5209  LOG(VB_GENERAL, LOG_INFO, LOC +
5210  QString("MarkAsInUse(true, ''->'%1')").arg(usedFor));
5211  }
5212 #endif // DEBUG_IN_USE
5213 
5214  m_inUseForWhat = usedFor;
5215  }
5216  else if (m_inUseForWhat.isEmpty())
5217  {
5218  m_inUseForWhat = QString("%1 [%2]")
5219  .arg(QObject::tr("Unknown")).arg(getpid());
5220  LOG(VB_GENERAL, LOG_WARNING, LOC +
5221  QString("MarkAsInUse(true, ''->'%1')").arg(m_inUseForWhat) +
5222  " -- use was not explicitly set");
5223  }
5224 
5225  notifyOfChange = true;
5226  }
5227 
5228  if (!inuse && !m_inUseForWhat.isEmpty() && usedFor != m_inUseForWhat)
5229  {
5230  LOG(VB_GENERAL, LOG_WARNING, LOC +
5231  QString("MarkAsInUse(false, '%1'->'%2')")
5232  .arg(m_inUseForWhat, usedFor) +
5233  " -- use has changed since first setting as in use.");
5234  }
5235 #ifdef DEBUG_IN_USE
5236  else if (!inuse)
5237  {
5238  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("MarkAsInUse(false, '%1')")
5239  .arg(m_inUseForWhat));
5240  }
5241 #endif // DEBUG_IN_USE
5242 
5243  if (!inuse && m_inUseForWhat.isEmpty())
5244  m_inUseForWhat = usedFor;
5245 
5246  if (!inuse && m_inUseForWhat.isEmpty())
5247  {
5248  LOG(VB_GENERAL, LOG_WARNING, LOC +
5249  "MarkAsInUse requires a key to delete in use mark");
5250  return; // can't delete if we don't have a key
5251  }
5252 
5253  if (!inuse)
5254  {
5255  MSqlQuery query(MSqlQuery::InitCon());
5256  query.prepare(
5257  "DELETE FROM inuseprograms "
5258  "WHERE chanid = :CHANID AND starttime = :STARTTIME AND "
5259  " hostname = :HOSTNAME AND recusage = :RECUSAGE");
5260  query.bindValue(":CHANID", m_chanId);
5261  query.bindValue(":STARTTIME", m_recStartTs);
5262  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
5263  query.bindValue(":RECUSAGE", m_inUseForWhat);
5264 
5265  if (!query.exec())
5266  MythDB::DBError("MarkAsInUse -- delete", query);
5267 
5268  m_inUseForWhat.clear();
5270  SendUpdateEvent();
5271  return;
5272  }
5273 
5274  if (m_pathname == GetBasename())
5275  m_pathname = GetPlaybackURL(false, true);
5276 
5277  QString recDir = DiscoverRecordingDirectory();
5278 
5279  QDateTime inUseTime = MythDate::current(true);
5280 
5281  MSqlQuery query(MSqlQuery::InitCon());
5282  query.prepare(
5283  "SELECT count(*) "
5284  "FROM inuseprograms "
5285  "WHERE chanid = :CHANID AND starttime = :STARTTIME AND "
5286  " hostname = :HOSTNAME AND recusage = :RECUSAGE");
5287  query.bindValue(":CHANID", m_chanId);
5288  query.bindValue(":STARTTIME", m_recStartTs);
5289  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
5290  query.bindValue(":RECUSAGE", m_inUseForWhat);
5291 
5292  if (!query.exec())
5293  {
5294  MythDB::DBError("MarkAsInUse -- select", query);
5295  }
5296  else if (!query.next())
5297  {
5298  LOG(VB_GENERAL, LOG_ERR, LOC + "MarkAsInUse -- select query failed");
5299  }
5300  else if (query.value(0).toBool())
5301  {
5302  query.prepare(
5303  "UPDATE inuseprograms "
5304  "SET lastupdatetime = :UPDATETIME "
5305  "WHERE chanid = :CHANID AND starttime = :STARTTIME AND "
5306  " hostname = :HOSTNAME AND recusage = :RECUSAGE");
5307  query.bindValue(":CHANID", m_chanId);
5308  query.bindValue(":STARTTIME", m_recStartTs);
5309  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
5310  query.bindValue(":RECUSAGE", m_inUseForWhat);
5311  query.bindValue(":UPDATETIME", inUseTime);
5312 
5313  if (!query.exec())
5314  MythDB::DBError("MarkAsInUse -- update failed", query);
5315  else
5316  m_lastInUseTime = inUseTime;
5317  }
5318  else // if (!query.value(0).toUInt())
5319  {
5320  query.prepare(
5321  "INSERT INTO inuseprograms "
5322  " (chanid, starttime, recusage, hostname, "
5323  " lastupdatetime, rechost, recdir) "
5324  "VALUES "
5325  " (:CHANID, :STARTTIME, :RECUSAGE, :HOSTNAME, "
5326  " :UPDATETIME, :RECHOST, :RECDIR)");
5327  query.bindValue(":CHANID", m_chanId);
5328  query.bindValue(":STARTTIME", m_recStartTs);
5329  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
5330  query.bindValue(":RECUSAGE", m_inUseForWhat);
5331  query.bindValue(":UPDATETIME", inUseTime);
5332  query.bindValue(":RECHOST",
5333  m_hostname.isEmpty() ? gCoreContext->GetHostName()
5334  : m_hostname);
5335  query.bindValue(":RECDIR", recDir);
5336 
5337  if (!query.exec())
5338  MythDB::DBError("MarkAsInUse -- insert failed", query);
5339  else
5340  m_lastInUseTime = inUseTime;
5341  }
5342 
5343  if (!notifyOfChange)
5344  return;
5345 
5346  // Let others know we changed status
5347  QDateTime oneHourAgo = MythDate::current().addSecs(-kLastUpdateOffset);
5348  query.prepare("SELECT DISTINCT recusage "
5349  "FROM inuseprograms "
5350  "WHERE lastupdatetime >= :ONEHOURAGO AND "
5351  " chanid = :CHANID AND "
5352  " starttime = :STARTTIME");
5353  query.bindValue(":CHANID", m_chanId);
5354  query.bindValue(":STARTTIME", m_recStartTs);
5355  query.bindValue(":ONEHOURAGO", oneHourAgo);
5356  if (!query.exec())
5357  return; // not safe to send update event...
5358 
5359  m_programFlags &= ~(FL_INUSEPLAYING | FL_INUSERECORDING | FL_INUSEOTHER);
5360  while (query.next())
5361  {
5362  QString inUseForWhat = query.value(0).toString();
5363  if (inUseForWhat.contains(kPlayerInUseID))
5364  m_programFlags |= FL_INUSEPLAYING;
5365  else if (inUseForWhat == kRecorderInUseID)
5366  m_programFlags |= FL_INUSERECORDING;
5367  else
5368  m_programFlags |= FL_INUSEOTHER;
5369  }
5370  SendUpdateEvent();
5371 }
5372 
5378 bool ProgramInfo::QueryTuningInfo(QString &channum, QString &input) const
5379 {
5380  channum.clear();
5381  input.clear();
5382  MSqlQuery query(MSqlQuery::InitCon());
5383 
5384  query.prepare("SELECT channel.channum, capturecard.inputname "
5385  "FROM channel, capturecard "
5386  "WHERE channel.chanid = :CHANID AND "
5387  " capturecard.sourceid = :SOURCEID AND "
5388  " capturecard.cardid = :INPUTID");
5389  query.bindValue(":CHANID", m_chanId);
5390  query.bindValue(":SOURCEID", m_sourceId);
5391  query.bindValue(":INPUTID", m_inputId);
5392 
5393  if (query.exec() && query.next())
5394  {
5395  channum = query.value(0).toString();
5396  input = query.value(1).toString();
5397  return true;
5398  }
5399  MythDB::DBError("GetChannel(ProgInfo...)", query);
5400  return false;
5401 }
5402 
5403 static int init_tr(void)
5404 {
5405  static bool s_done = false;
5406  static QMutex s_initTrLock;
5407  QMutexLocker locker(&s_initTrLock);
5408  if (s_done)
5409  return 1;
5410 
5411  QString rec_profile_names =
5412  QObject::tr("Default", "Recording Profile Default") +
5413  QObject::tr("High Quality", "Recording Profile High Quality") +
5414  QObject::tr("Live TV", "Recording Profile Live TV") +
5415  QObject::tr("Low Quality", "Recording Profile Low Quality") +
5416  QObject::tr("Medium Quality", "Recording Profile Medium Quality") +
5417  QObject::tr("MPEG-2", "Recording Profile MPEG-2") +
5418  QObject::tr("RTjpeg/MPEG-4", "Recording Profile RTjpeg/MPEG-4");
5419 
5420 
5421  QString rec_profile_groups =
5422  QObject::tr("CRC IP Recorders",
5423  "Recording Profile Group Name") +
5424  QObject::tr("FireWire Input",
5425  "Recording Profile Group Name") +
5426  QObject::tr("Freebox Input",
5427  "Recording Profile Group Name") +
5428  QObject::tr("Hardware DVB Encoders",
5429  "Recording Profile Group Name") +
5430  QObject::tr("Hardware HDTV",
5431  "Recording Profile Group Name") +
5432  QObject::tr("Hardware MJPEG Encoders (Matrox G200-TV, Miro DC10, etc)",
5433  "Recording Profile Group Name") +
5434  QObject::tr("HD-PVR Recorders",
5435  "Recording Profile Group Name") +
5436  QObject::tr("HDHomeRun Recorders",
5437  "Recording Profile Group Name") +
5438  QObject::tr("MPEG-2 Encoders (PVR-x50, PVR-500)",
5439  "Recording Profile Group Name") +
5440  QObject::tr("Software Encoders (V4L based)",
5441  "Recording Profile Group Name") +
5442  QObject::tr("Transcoders",
5443  "Recording Profile Group Name") +
5444  QObject::tr("USB MPEG-4 Encoder (Plextor ConvertX, etc)",
5445  "Recording Profile Group Name") +
5446  QObject::tr("V4L2 Encoders",
5447  "Recording Profile Group Name");
5448 
5449  QString display_rec_groups =
5450  QObject::tr("All Programs", "Recording Group All Programs") +
5451  QObject::tr("All", "Recording Group All Programs -- short form") +
5452  QObject::tr("Live TV", "Recording Group Live TV") +
5453  QObject::tr("Default", "Recording Group Default") +
5454  QObject::tr("Deleted", "Recording Group Deleted");
5455 
5456  QString special_program_groups =
5457  QObject::tr("All Programs - %1",
5458  "Show all programs from a specific recording group");
5459 
5460  QString storage_groups =
5461  QObject::tr("Default", "Storage Group Name") +
5462  QObject::tr("Live TV", "Storage Group Name") +
5463  QObject::tr("Thumbnails", "Storage Group Name") +
5464  QObject::tr("DB Backups", "Storage Group Name");
5465 
5466  QString play_groups =
5467  QObject::tr("Default", "Playback Group Name");
5468 
5469  s_done = true;
5470  return (rec_profile_names.length() +
5471  rec_profile_groups.length() +
5472  display_rec_groups.length() +
5473  special_program_groups.length() +
5474  storage_groups.length() +
5475  play_groups.length());
5476 }
5477 
5479 {
5480  QMutexLocker locker(&s_staticDataLock);
5481  if (!s_updater)
5482  s_updater = new ProgramInfoUpdater();
5483  return 1;
5484 }
5485 
5487 QString ProgramInfo::i18n(const QString &msg)
5488 {
5489  init_tr();
5490  QByteArray msg_arr = msg.toLatin1();
5491  QString msg_i18n = QObject::tr(msg_arr.constData());
5492  QByteArray msg_i18n_arr = msg_i18n.toLatin1();
5493  return (msg_arr == msg_i18n_arr) ? msg : msg_i18n;
5494 }
5495 
5503 {
5504  QString pburl = GetPlaybackURL(false, true);
5505  if (pburl.startsWith("myth://"))
5506  {
5507  str.replace(QString("%DIR%"), pburl);
5508  }
5509  else
5510  {
5511  QFileInfo dirInfo(pburl);
5512  str.replace(QString("%DIR%"), dirInfo.path());
5513  }
5514 
5515  // N.B. Contents of %MATCH% string variables get parsed into a single
5516  // QStringLists. Quotes in strings will cause lost/truncated output.
5517  // Replace QUOTATION MARK (U+0022)j with MODIFIER LETTER DOUBLE PRIME (U+02BA).
5518 
5519  str.replace(QString("%FILE%"), GetBasename());
5520  str.replace(QString("%TITLE%"), m_title.replace("\"", "ʺ"));
5521  str.replace(QString("%SUBTITLE%"), m_subtitle.replace("\"", "ʺ"));
5522  str.replace(QString("%SEASON%"), QString::number(m_season));
5523  str.replace(QString("%EPISODE%"), QString::number(m_episode));
5524  str.replace(QString("%TOTALEPISODES%"), QString::number(m_totalEpisodes));
5525  str.replace(QString("%SYNDICATEDEPISODE%"), m_syndicatedEpisode);
5526  str.replace(QString("%DESCRIPTION%"), m_description.replace("\"", "ʺ"));
5527  str.replace(QString("%HOSTNAME%"), m_hostname);
5528  str.replace(QString("%CATEGORY%"), m_category);
5529  str.replace(QString("%RECGROUP%"), m_recGroup);
5530  str.replace(QString("%PLAYGROUP%"), m_playGroup);
5531  str.replace(QString("%CHANID%"), QString::number(m_chanId));
5532  str.replace(QString("%INETREF%"), m_inetRef);
5533  str.replace(QString("%PARTNUMBER%"), QString::number(m_partNumber));
5534  str.replace(QString("%PARTTOTAL%"), QString::number(m_partTotal));
5535  str.replace(QString("%ORIGINALAIRDATE%"),
5536  m_originalAirDate.toString(Qt::ISODate));
5537  static const std::array<const QString,4> s_timeStr
5538  { "STARTTIME", "ENDTIME", "PROGSTART", "PROGEND", };
5539  const std::array<const QDateTime *,4> time_dtr
5541  for (size_t i = 0; i < s_timeStr.size(); i++)
5542  {
5543  str.replace(QString("%%1%").arg(s_timeStr[i]),
5544  (time_dtr[i]->toLocalTime()).toString("yyyyMMddhhmmss"));
5545  str.replace(QString("%%1ISO%").arg(s_timeStr[i]),
5546  (time_dtr[i]->toLocalTime()).toString(Qt::ISODate));
5547  str.replace(QString("%%1UTC%").arg(s_timeStr[i]),
5548  time_dtr[i]->toString("yyyyMMddhhmmss"));
5549  str.replace(QString("%%1ISOUTC%").arg(s_timeStr[i]),
5550  time_dtr[i]->toString(Qt::ISODate));
5551  }
5552  str.replace(QString("%RECORDEDID%"), QString::number(m_recordedId));
5553 }
5554 
5555 QMap<QString,uint32_t> ProgramInfo::QueryInUseMap(void)
5556 {
5557  QMap<QString, uint32_t> inUseMap;
5558  QDateTime oneHourAgo = MythDate::current().addSecs(-kLastUpdateOffset);
5559 
5560  MSqlQuery query(MSqlQuery::InitCon());
5561 
5562  query.prepare("SELECT DISTINCT chanid, starttime, recusage "
5563  "FROM inuseprograms WHERE lastupdatetime >= :ONEHOURAGO");
5564  query.bindValue(":ONEHOURAGO", oneHourAgo);
5565 
5566  if (!query.exec())
5567  return inUseMap;
5568 
5569  while (query.next())
5570  {
5571  QString inUseKey = ProgramInfo::MakeUniqueKey(
5572  query.value(0).toUInt(),
5573  MythDate::as_utc(query.value(1).toDateTime()));
5574 
5575  QString inUseForWhat = query.value(2).toString();
5576 
5577  if (!inUseMap.contains(inUseKey))
5578  inUseMap[inUseKey] = 0;
5579 
5580  if (inUseForWhat.contains(kPlayerInUseID))
5581  inUseMap[inUseKey] |= FL_INUSEPLAYING;
5582  else if (inUseForWhat == kRecorderInUseID)
5583  inUseMap[inUseKey] |= FL_INUSERECORDING;
5584  else
5585  inUseMap[inUseKey] |= FL_INUSEOTHER;
5586  }
5587 
5588  return inUseMap;
5589 }
5590 
5591 QMap<QString,bool> ProgramInfo::QueryJobsRunning(int type)
5592 {
5593  QMap<QString,bool> is_job_running;
5594 
5595  MSqlQuery query(MSqlQuery::InitCon());
5596  query.prepare("SELECT chanid, starttime, status FROM jobqueue "
5597  "WHERE type = :TYPE");
5598  query.bindValue(":TYPE", type);
5599  if (!query.exec())
5600  return is_job_running;
5601 
5602  while (query.next())
5603  {
5604  uint chanid = query.value(0).toUInt();
5605  QDateTime recstartts = MythDate::as_utc(query.value(1).toDateTime());
5606  int tmpStatus = query.value(2).toInt();
5607  if ((tmpStatus != /*JOB_UNKNOWN*/ 0x0000) &&
5608  (tmpStatus != /*JOB_QUEUED*/ 0x0001) &&
5609  (!(tmpStatus & /*JOB_DONE*/ 0x0100)))
5610  {
5611  is_job_running[
5612  ProgramInfo::MakeUniqueKey(chanid,recstartts)] = true;
5613  }
5614  }
5615 
5616  return is_job_running;
5617 }
5618 
5620  const QString &tmptable, int recordid)
5621 {
5622  QStringList slist;
5623 
5625  if (sched && tmptable.isEmpty())
5626  {
5627  sched->GetAllPending(slist);
5628  return slist;
5629  }
5630 
5631  if (sched)
5632  {
5633  LOG(VB_GENERAL, LOG_ERR,
5634  "Called from master backend\n\t\t\t"
5635  "with recordid or tmptable, this is not currently supported");
5636  return slist;
5637  }
5638 
5639  slist.push_back(
5640  (tmptable.isEmpty()) ?
5641  QString("QUERY_GETALLPENDING") :
5642  QString("QUERY_GETALLPENDING %1 %2").arg(tmptable).arg(recordid));
5643 
5644  if (!gCoreContext->SendReceiveStringList(slist) || slist.size() < 2)
5645  {
5646  LOG(VB_GENERAL, LOG_ALERT,
5647  "LoadFromScheduler(): Error querying master.");
5648  slist.clear();
5649  }
5650 
5651  return slist;
5652 }
5653 
5654 // NOTE: This may look ugly, and in some ways it is, but it's designed this
5655 // way for with two purposes in mind - Consistent behaviour and speed
5656 // So if you do make changes then carefully test that it doesn't result
5657 // in any regressions in total speed of execution or adversely affect the
5658 // results returned for any of it's users.
5659 static bool FromProgramQuery(const QString &sql, const MSqlBindings &bindings,
5660  MSqlQuery &query, const uint start,
5661  const uint limit, uint &count,
5662  ProgGroupBy::Type groupBy)
5663 {
5664  count = 0;
5665 
5666  QString columns = QString(
5667  "program.chanid, program.starttime, program.endtime, " // 0-2
5668  "program.title, program.subtitle, program.description, " // 3-5
5669  "program.category, channel.channum, channel.callsign, " // 6-8
5670  "channel.name, program.previouslyshown, channel.commmethod, " // 9-11
5671  "channel.outputfilters, program.seriesid, program.programid, " // 12-14
5672  "program.airdate, program.stars, program.originalairdate, " // 15-17
5673  "program.category_type, oldrecstatus.recordid, " // 18-19
5674  "oldrecstatus.rectype, oldrecstatus.recstatus, " // 20-21
5675  "oldrecstatus.findid, program.videoprop+0, program.audioprop+0, " // 22-24
5676  "program.subtitletypes+0, program.syndicatedepisodenumber, " // 25-26
5677  "program.partnumber, program.parttotal, " // 27-28
5678  "program.season, program.episode, program.totalepisodes "); // 29-31
5679 
5680  QString querystr = QString(
5681  "SELECT %1 FROM ( "
5682  " SELECT %2 "
5683  " FROM program "
5684  " LEFT JOIN channel ON program.chanid = channel.chanid "
5685  " LEFT JOIN oldrecorded AS oldrecstatus ON "
5686  " oldrecstatus.future = 0 AND "
5687  " program.title = oldrecstatus.title AND "
5688  " channel.callsign = oldrecstatus.station AND "
5689  " program.starttime = oldrecstatus.starttime "
5690  ) + sql +
5691  ") groupsq ";
5692 
5693  // If a ProgGroupBy option is specified, wrap the query in an outer
5694  // query using row_number() and select only rows with value 1. We
5695  // do this instead of relying on MySQL's liberal support for group
5696  // by on non-aggregated columns because it is deterministic.
5697  if (groupBy != ProgGroupBy::None)
5698  {
5699  columns +=
5700  ", row_number() over ( "
5701  " partition by ";
5702  if (groupBy == ProgGroupBy::ChanNum)
5703  columns += "channel.channum, "
5704  " channel.callsign, ";
5705  else if (groupBy == ProgGroupBy::CallSign)
5706  columns += "channel.callsign, ";
5707  else if (groupBy == ProgGroupBy::ProgramId)
5708  columns += "program.programid, ";
5709  columns +=
5710  " program.title, "
5711  " program.starttime "
5712  " order by channel.recpriority desc, "
5713  " channel.sourceid, "
5714  " channel.channum+0 "
5715  ") grouprn ";
5716  querystr += "WHERE grouprn = 1 ";
5717  }
5718 
5719  // If a limit arg was given then append the LIMIT, otherwise set a hard
5720  // limit of 20000.
5721  if (limit > 0)
5722  querystr += QString("LIMIT %1 ").arg(limit);
5723  else if (!querystr.contains(" LIMIT "))
5724  querystr += " LIMIT 20000 "; // For performance reasons we have to have an upper limit
5725 
5726  MSqlBindings::const_iterator it;
5727  // Some end users need to know the total number of matching records,
5728  // irrespective of any LIMIT clause
5729  //
5730  // SQL_CALC_FOUND_ROWS is better than COUNT(*), as COUNT(*) won't work
5731  // with any GROUP BY clauses. COUNT is marginally faster but not enough to
5732  // matter
5733  //
5734  // It's considerably faster in my tests to do a separate query which returns
5735  // no data using SQL_CALC_FOUND_ROWS than it is to use SQL_CALC_FOUND_ROWS
5736  // with the full query. Unfortunate but true.
5737  //
5738  // e.g. Fetching all programs for the next 14 days with a LIMIT of 10 - 220ms
5739  // Same query with SQL_CALC_FOUND_ROWS - 1920ms
5740  // Same query but only one column and with SQL_CALC_FOUND_ROWS - 370ms
5741  // Total to fetch both the count and the data = 590ms vs 1920ms
5742  // Therefore two queries is 1.4 seconds faster than one query.
5743  if (start > 0 || limit > 0)
5744  {
5745  QString countStr = querystr
5746  .arg("SQL_CALC_FOUND_ROWS chanid", columns);
5747  query.prepare(countStr);
5748  for (it = bindings.begin(); it != bindings.end(); ++it)
5749  {
5750  if (countStr.contains(it.key()))
5751  query.bindValue(it.key(), it.value());
5752  }
5753 
5754  if (!query.exec())
5755  {
5756  MythDB::DBError("LoadFromProgramQuery", query);
5757  return false;
5758  }
5759 
5760  if (query.exec("SELECT FOUND_ROWS()") && query.next())
5761  count = query.value(0).toUInt();
5762  }
5763 
5764  if (start > 0)
5765  querystr += QString("OFFSET %1 ").arg(start);
5766 
5767  querystr = querystr.arg("*", columns);
5768  query.prepare(querystr);
5769  for (it = bindings.begin(); it != bindings.end(); ++it)
5770  {
5771  if (querystr.contains(it.key()))
5772  query.bindValue(it.key(), it.value());
5773  }
5774 
5775  if (!query.exec())
5776  {
5777  MythDB::DBError("LoadFromProgramQuery", query);
5778  return false;
5779  }
5780 
5781  return true;
5782 }
5783 
5784 bool LoadFromProgram(ProgramList &destination, const QString &where,
5785  const QString &groupBy, const QString &orderBy,
5786  const MSqlBindings &bindings, const ProgramList &schedList)
5787 {
5788  uint count = 0;
5789 
5790  QString queryStr = "";
5791 
5792  if (!where.isEmpty())
5793  queryStr.append(QString("WHERE %1 ").arg(where));
5794 
5795  if (!groupBy.isEmpty())
5796  queryStr.append(QString("GROUP BY %1 ").arg(groupBy));
5797 
5798  if (!orderBy.isEmpty())
5799  queryStr.append(QString("ORDER BY %1 ").arg(orderBy));
5800 
5801  // ------------------------------------------------------------------------
5802 
5803  return LoadFromProgram(destination, queryStr, bindings, schedList, 0, 0,
5804  count);
5805 }
5806 
5807 bool LoadFromProgram(ProgramList &destination,
5808  const QString &sql, const MSqlBindings &bindings,
5809  const ProgramList &schedList, ProgGroupBy::Type groupBy)
5810 {
5811  uint count = 0;
5812 
5813  QString queryStr = sql;
5814  // ------------------------------------------------------------------------
5815  // FIXME: Remove the following. These all make assumptions about the content
5816  // of the sql passed in, they can end up breaking that query by
5817  // inserting a WHERE clause after a GROUP BY, or a GROUP BY after
5818  // an ORDER BY. These should be part of the sql passed in otherwise
5819  // the caller isn't getting what they asked for and to fix that they
5820  // are forced to include a GROUP BY, ORDER BY or WHERE that they
5821  // do not want
5822  //
5823  // See the new version of this method above. The plan is to convert existing
5824  // users of this method and have them supply all of their own data for the
5825  // queries (no defaults.)
5826 
5827  if (!queryStr.contains("WHERE"))
5828  queryStr += " WHERE deleted IS NULL AND visible > 0 ";
5829 
5830  if (!queryStr.contains("ORDER BY"))
5831  {
5832  queryStr += " ORDER BY program.starttime, ";
5833  QString chanorder =
5834  gCoreContext->GetSetting("ChannelOrdering", "channum");
5835  if (chanorder != "channum")
5836  queryStr += chanorder + " ";
5837  else // approximation which the DB can handle
5838  queryStr += "atsc_major_chan,atsc_minor_chan,channum,callsign ";
5839  }
5840 
5841  // ------------------------------------------------------------------------
5842 
5843  return LoadFromProgram(destination, queryStr, bindings, schedList, 0, 0,
5844  count, groupBy);
5845 }
5846 
5847 bool LoadFromProgram( ProgramList &destination,
5848  const QString &sql, const MSqlBindings &bindings,
5849  const ProgramList &schedList,
5850  const uint start, const uint limit, uint &count,
5851  ProgGroupBy::Type groupBy)
5852 {
5853  destination.clear();
5854 
5855  if (sql.contains(" OFFSET", Qt::CaseInsensitive))
5856  {
5857  LOG(VB_GENERAL, LOG_WARNING, "LoadFromProgram(): SQL contains OFFSET "
5858  "clause, caller should be updated to use "
5859  "start parameter instead");
5860  }
5861 
5862  if (sql.contains(" LIMIT", Qt::CaseInsensitive))
5863  {
5864  LOG(VB_GENERAL, LOG_WARNING, "LoadFromProgram(): SQL contains LIMIT "
5865  "clause, caller should be updated to use "
5866  "limit parameter instead");
5867  }
5868 
5869  MSqlQuery query(MSqlQuery::InitCon());
5870  query.setForwardOnly(true);
5871  if (!FromProgramQuery(sql, bindings, query, start, limit, count, groupBy))
5872  return false;
5873 
5874  if (count == 0)
5875  count = query.size();
5876 
5877  while (query.next())
5878  {
5879  destination.push_back(
5880  new ProgramInfo(
5881  query.value(3).toString(), // title
5882  QString(), // sortTitle
5883  query.value(4).toString(), // subtitle
5884  QString(), // sortSubtitle
5885  query.value(5).toString(), // description
5886  query.value(26).toString(), // syndicatedepisodenumber
5887  query.value(6).toString(), // category
5888 
5889  query.value(0).toUInt(), // chanid
5890  query.value(7).toString(), // channum
5891  query.value(8).toString(), // chansign
5892  query.value(9).toString(), // channame
5893  query.value(12).toString(), // chanplaybackfilters
5894 
5895  MythDate::as_utc(query.value(1).toDateTime()), // startts
5896  MythDate::as_utc(query.value(2).toDateTime()), // endts
5897  MythDate::as_utc(query.value(1).toDateTime()), // recstartts
5898  MythDate::as_utc(query.value(2).toDateTime()), // recendts
5899 
5900  query.value(13).toString(), // seriesid
5901  query.value(14).toString(), // programid
5902  string_to_myth_category_type(query.value(18).toString()), // catType
5903 
5904  query.value(16).toFloat(), // stars
5905  query.value(15).toUInt(), // year
5906  query.value(27).toUInt(), // partnumber
5907  query.value(28).toUInt(), // parttotal
5908  query.value(17).toDate(), // originalAirDate
5909  RecStatus::Type(query.value(21).toInt()), // recstatus
5910  query.value(19).toUInt(), // recordid
5911  RecordingType(query.value(20).toInt()), // rectype
5912  query.value(22).toUInt(), // findid
5913 
5914  query.value(11).toInt() == COMM_DETECT_COMMFREE, // commfree
5915  query.value(10).toBool(), // repeat
5916  query.value(23).toInt(), // videoprop
5917  query.value(24).toInt(), // audioprop
5918  query.value(25).toInt(), // subtitletypes
5919  query.value(29).toUInt(), // season
5920  query.value(30).toUInt(), // episode
5921  query.value(31).toUInt(), // totalepisodes
5922 
5923  schedList));
5924  }
5925 
5926  return true;
5927 }
5928 
5930  const QDateTime& starttime)
5931 {
5932  ProgramInfo *progInfo = nullptr;
5933 
5934  // Build add'l SQL statement for Program Listing
5935 
5936  MSqlBindings bindings;
5937  QString sSQL = "WHERE program.chanid = :ChanId "
5938  "AND program.starttime = :StartTime ";
5939 
5940  bindings[":ChanId" ] = chanid;
5941  bindings[":StartTime"] = starttime;
5942 
5943  // Get all Pending Scheduled Programs
5944 
5945  ProgramList schedList;
5946  bool hasConflicts = false;
5947  LoadFromScheduler(schedList, hasConflicts);
5948 
5949  // ----------------------------------------------------------------------
5950 
5951  ProgramList progList;
5952 
5953  LoadFromProgram( progList, sSQL, bindings, schedList );
5954 
5955  if (progList.empty())
5956  return progInfo;
5957 
5958  // progList is an Auto-delete deque, the object will be deleted with the
5959  // list, so we need to make a copy
5960  progInfo = new ProgramInfo(*(progList[0]));
5961 
5962  return progInfo;
5963 }
5964 
5966  ProgramList &destination, const QString &sql, const MSqlBindings &bindings)
5967 {
5968  uint count = 0;
5969  return LoadFromOldRecorded(destination, sql, bindings, 0, 0, count);
5970 }
5971 
5972 bool LoadFromOldRecorded(ProgramList &destination, const QString &sql,
5973  const MSqlBindings &bindings,
5974  const uint start, const uint limit, uint &count)
5975 {
5976  destination.clear();
5977 
5978  MSqlQuery query(MSqlQuery::InitCon());
5979  query.setForwardOnly(true);
5980 
5981  QString columns =
5982  "oldrecorded.chanid, starttime, endtime, "
5983  "title, subtitle, description, season, episode, category, seriesid, "
5984  "programid, inetref, channel.channum, channel.callsign, "
5985  "channel.name, findid, rectype, recstatus, recordid, duplicate ";
5986 
5987  QString querystr =
5988  "SELECT %1 "
5989  "FROM oldrecorded "
5990  "LEFT JOIN channel ON oldrecorded.chanid = channel.chanid "
5991  "WHERE oldrecorded.future = 0 "
5992  + sql;
5993 
5994  bool hasLimit = querystr.contains(" LIMIT ",Qt::CaseInsensitive);
5995 
5996  // make sure the most recent rows are retrieved first in case
5997  // there are more than the limit to be set below
5998  if (!hasLimit && !querystr.contains(" ORDER ",Qt::CaseInsensitive))
5999  querystr += " ORDER BY starttime DESC ";
6000 
6001  // If a limit arg was given then append the LIMIT, otherwise set a hard
6002  // limit of 20000, which can be overridden by a setting
6003  if (limit > 0)
6004  querystr += QString("LIMIT %1 ").arg(limit);
6005  else if (!hasLimit)
6006  {
6007  // For performance reasons we have to have an upper limit
6008  int nLimit = gCoreContext->GetNumSetting("PrevRecLimit", 20000);
6009  // For sanity sake at least 100
6010  nLimit = std::max(nLimit, 100);
6011  querystr += QString("LIMIT %1 ").arg(nLimit);
6012  }
6013 
6014  MSqlBindings::const_iterator it;
6015  // If count is non-zero then also return total number of matching records,
6016  // irrespective of any LIMIT clause
6017  //
6018  // SQL_CALC_FOUND_ROWS is better than COUNT(*), as COUNT(*) won't work
6019  // with any GROUP BY clauses. COUNT is marginally faster but not enough to
6020  // matter
6021  //
6022  // It's considerably faster in my tests to do a separate query which returns
6023  // no data using SQL_CALC_FOUND_ROWS than it is to use SQL_CALC_FOUND_ROWS
6024  // with the full query. Unfortunate but true.
6025  //
6026  // e.g. Fetching all programs for the next 14 days with a LIMIT of 10 - 220ms
6027  // Same query with SQL_CALC_FOUND_ROWS - 1920ms
6028  // Same query but only one column and with SQL_CALC_FOUND_ROWS - 370ms
6029  // Total to fetch both the count and the data = 590ms vs 1920ms
6030  // Therefore two queries is 1.4 seconds faster than one query.
6031  if (count > 0 && (start > 0 || limit > 0))
6032  {
6033  QString countStr = querystr.arg("SQL_CALC_FOUND_ROWS oldrecorded.chanid");
6034  query.prepare(countStr);
6035  for (it = bindings.begin(); it != bindings.end(); ++it)
6036  {
6037  if (countStr.contains(it.key()))
6038  query.bindValue(it.key(), it.value());
6039  }
6040 
6041  if (!query.exec())
6042  {
6043  MythDB::DBError("LoadFromOldRecorded", query);
6044  return false;
6045  }
6046 
6047  if (query.exec("SELECT FOUND_ROWS()") && query.next())
6048  count = query.value(0).toUInt();
6049  }
6050 
6051  if (start > 0)
6052  querystr += QString("OFFSET %1 ").arg(start);
6053 
6054  querystr = querystr.arg(columns);
6055  query.prepare(querystr);
6056  for (it = bindings.begin(); it != bindings.end(); ++it)
6057  {
6058  if (querystr.contains(it.key()))
6059  query.bindValue(it.key(), it.value());
6060  }
6061 
6062  if (!query.exec())
6063  {
6064  MythDB::DBError("LoadFromOldRecorded", query);
6065  return false;
6066  }
6067 
6068  while (query.next())
6069  {
6070  uint chanid = query.value(0).toUInt();
6071  QString channum = QString("#%1").arg(chanid);
6072  QString chansign = channum;
6073  QString channame = channum;
6074  if (!query.value(12).toString().isEmpty())
6075  {
6076  channum = query.value(12).toString();
6077  chansign = query.value(13).toString();
6078  channame = query.value(14).toString();
6079  }
6080 
6081  destination.push_back(new ProgramInfo(
6082  query.value(3).toString(),
6083  QString(),
6084  query.value(4).toString(),
6085  QString(),
6086  query.value(5).toString(),
6087  query.value(6).toUInt(),
6088  query.value(7).toUInt(),
6089  query.value(8).toString(),
6090 
6091  chanid, channum, chansign, channame,
6092 
6093  query.value(9).toString(), query.value(10).toString(),
6094  query.value(11).toString(),
6095 
6096  MythDate::as_utc(query.value(1).toDateTime()),
6097  MythDate::as_utc(query.value(2).toDateTime()),
6098  MythDate::as_utc(query.value(1).toDateTime()),
6099  MythDate::as_utc(query.value(2).toDateTime()),
6100 
6101  RecStatus::Type(query.value(17).toInt()),
6102  query.value(18).toUInt(),
6103  RecordingType(query.value(16).toInt()),
6104  query.value(15).toUInt(),
6105 
6106  query.value(19).toBool()));
6107  }
6108 
6109  return true;
6110 }
6111 
6131  ProgramList &destination,
6132  bool possiblyInProgressRecordingsOnly,
6133  const QMap<QString,uint32_t> &inUseMap,
6134  const QMap<QString,bool> &isJobRunning,
6135  const QMap<QString, ProgramInfo*> &recMap,
6136  int sort,
6137  const QString &sortBy,
6138  bool ignoreLiveTV,
6139  bool ignoreDeleted)
6140 {
6141  destination.clear();
6142 
6143  QDateTime rectime = MythDate::current().addSecs(
6144  -gCoreContext->GetNumSetting("RecordOverTime"));
6145 
6146  // ----------------------------------------------------------------------
6147 
6148  QString thequery = ProgramInfo::kFromRecordedQuery;
6149  if (possiblyInProgressRecordingsOnly || ignoreLiveTV || ignoreDeleted)
6150  {
6151  thequery += "WHERE ";
6152  if (possiblyInProgressRecordingsOnly)
6153  {
6154  thequery += "(r.endtime >= NOW() AND r.starttime <= NOW()) ";
6155  }
6156  if (ignoreLiveTV)
6157  {
6158  thequery += QString("%1 r.recgroup != 'LiveTV' ")
6159  .arg(possiblyInProgressRecordingsOnly ? "AND" : "");
6160  }
6161  if (ignoreDeleted)
6162  {
6163  thequery += QString("%1 r.recgroup != 'Deleted' ")
6164  .arg((possiblyInProgressRecordingsOnly || ignoreLiveTV)
6165  ? "AND" : "");
6166  }
6167  }
6168 
6169  if (sortBy.isEmpty())
6170  {
6171  if (sort)
6172  thequery += "ORDER BY r.starttime ";
6173  if (sort < 0)
6174  thequery += "DESC ";
6175  }
6176  else
6177  {
6178  QStringList sortByFields;
6179  sortByFields << "starttime" << "title" << "subtitle" << "season" << "episode" << "category"
6180  << "watched" << "stars" << "originalairdate" << "recgroup" << "storagegroup"
6181  << "channum" << "callsign" << "name";
6182 
6183  // sanity check the fields are one of the above fields
6184  QString sSortBy;
6185  QStringList fields = sortBy.split(",");
6186  for (const QString& oneField : std::as_const(fields))
6187  {
6188  bool ascending = true;
6189  QString field = oneField.simplified().toLower();
6190 
6191  if (field.endsWith("desc"))
6192  {
6193  ascending = false;
6194  field = field.remove("desc");
6195  }
6196 
6197  if (field.endsWith("asc"))
6198  {
6199  ascending = true;
6200  field = field.remove("asc");
6201  }
6202 
6203  field = field.simplified();
6204 
6205  if (field == "channelname")
6206  field = "name";
6207 
6208  if (sortByFields.contains(field))
6209  {
6210  QString table;
6211  if (field == "channum" || field == "callsign" || field == "name")
6212  table = "c";
6213  else
6214  table = "r";
6215 
6216  if (sSortBy.isEmpty())
6217  sSortBy = QString("%1.%2 %3").arg(table, field, ascending ? "ASC" : "DESC");
6218  else
6219  sSortBy += QString(",%1.%2 %3").arg(table, field, ascending ? "ASC" : "DESC");
6220  }
6221  else
6222  {
6223  LOG(VB_GENERAL, LOG_WARNING, QString("ProgramInfo::LoadFromRecorded() got an unknown sort field '%1' - ignoring").arg(oneField));
6224  }
6225  }
6226 
6227  thequery += "ORDER BY " + sSortBy;
6228  }
6229 
6230  MSqlQuery query(MSqlQuery::InitCon());
6231  query.prepare(thequery);
6232 
6233  if (!query.exec())
6234  {
6235  MythDB::DBError("ProgramList::FromRecorded", query);
6236  return true;
6237  }
6238 
6239  while (query.next())
6240  {
6241  const uint chanid = query.value(6).toUInt();
6242  QString channum = QString("#%1").arg(chanid);
6243  QString chansign = channum;
6244  QString channame = channum;
6245  QString chanfilt;
6246  if (!query.value(7).toString().isEmpty())
6247  {
6248  channum = query.value(7).toString();
6249  chansign = query.value(8).toString();
6250  channame = query.value(9).toString();
6251  chanfilt = query.value(10).toString();
6252  }
6253 
6254  QString hostname = query.value(15).toString();
6255  if (hostname.isEmpty())
6257 
6259  QDateTime recstartts = MythDate::as_utc(query.value(24).toDateTime());
6260 
6261  QString key = ProgramInfo::MakeUniqueKey(chanid, recstartts);
6262  if (MythDate::as_utc(query.value(25).toDateTime()) > rectime &&
6263  recMap.contains(key))
6264  {
6265  recstatus = RecStatus::Recording;
6266  }
6267 
6268  bool save_not_commflagged = false;
6269  uint flags = 0;
6270 
6271  set_flag(flags, FL_CHANCOMMFREE,
6272  query.value(30).toInt() == COMM_DETECT_COMMFREE);
6273  set_flag(flags, FL_COMMFLAG,
6274  query.value(31).toInt() == COMM_FLAG_DONE);
6275  set_flag(flags, FL_COMMPROCESSING ,
6276  query.value(31).toInt() == COMM_FLAG_PROCESSING);
6277  set_flag(flags, FL_REPEAT, query.value(32).toBool());
6278  set_flag(flags, FL_TRANSCODED,
6279  query.value(34).toInt() == TRANSCODING_COMPLETE);
6280  set_flag(flags, FL_DELETEPENDING, query.value(35).toBool());
6281  set_flag(flags, FL_PRESERVED, query.value(36).toBool());
6282  set_flag(flags, FL_CUTLIST, query.value(37).toBool());
6283  set_flag(flags, FL_AUTOEXP, query.value(38).toBool());
6284  set_flag(flags, FL_REALLYEDITING, query.value(39).toBool());
6285  set_flag(flags, FL_BOOKMARK, query.value(40).toBool());
6286  set_flag(flags, FL_WATCHED, query.value(41).toBool());
6287  set_flag(flags, FL_LASTPLAYPOS, query.value(58).toBool());
6288 
6289  if (inUseMap.contains(key))
6290  flags |= inUseMap[key];
6291 
6292  if (((flags & FL_COMMPROCESSING) != 0U) &&
6293  (isJobRunning.find(key) == isJobRunning.end()))
6294  {
6295  flags &= ~FL_COMMPROCESSING;
6296  save_not_commflagged = true;
6297  }
6298 
6299  set_flag(flags, FL_EDITING,
6300  ((flags & FL_REALLYEDITING) != 0U) ||
6301  ((flags & COMM_FLAG_PROCESSING) != 0U));
6302 
6303  // User/metadata defined season from recorded
6304  uint season = query.value(3).toUInt();
6305  if (season == 0)
6306  season = query.value(51).toUInt(); // Guide defined season from recordedprogram
6307 
6308  // User/metadata defined episode from recorded
6309  uint episode = query.value(4).toUInt();
6310  if (episode == 0)
6311  episode = query.value(52).toUInt(); // Guide defined episode from recordedprogram
6312 
6313  // Guide defined total episodes from recordedprogram
6314  uint totalepisodes = query.value(53).toUInt();
6315 
6316  destination.push_back(
6317  new ProgramInfo(
6318  query.value(55).toUInt(),
6319  query.value(0).toString(),
6320  QString(),
6321  query.value(1).toString(),
6322  QString(),
6323  query.value(2).toString(),
6324  season,
6325  episode,
6326  totalepisodes,
6327  query.value(48).toString(), // syndicatedepisode
6328  query.value(5).toString(), // category
6329 
6330  chanid, channum, chansign, channame, chanfilt,
6331 
6332  query.value(11).toString(), query.value(12).toString(),
6333 
6334  query.value(14).toString(), // pathname
6335 
6336  hostname, query.value(13).toString(),
6337 
6338  query.value(17).toString(), query.value(18).toString(),
6339  query.value(19).toString(), // inetref
6340  string_to_myth_category_type(query.value(54).toString()), // category_type
6341 
6342  query.value(16).toInt(), // recpriority
6343 
6344  query.value(20).toULongLong(), // filesize
6345 
6346  MythDate::as_utc(query.value(21).toDateTime()), //startts
6347  MythDate::as_utc(query.value(22).toDateTime()), // endts
6348  MythDate::as_utc(query.value(24).toDateTime()), // recstartts
6349  MythDate::as_utc(query.value(25).toDateTime()), // recendts
6350 
6351  query.value(23).toFloat(), // stars
6352 
6353  query.value(26).toUInt(), // year
6354  query.value(49).toUInt(), // partnumber
6355  query.value(50).toUInt(), // parttotal
6356  query.value(27).toDate(), // originalAirdate
6357  MythDate::as_utc(query.value(28).toDateTime()), // lastmodified
6358 
6359  recstatus,
6360 
6361  query.value(29).toUInt(), // recordid
6362 
6363  RecordingDupInType(query.value(46).toInt()),
6364  RecordingDupMethodType(query.value(47).toInt()),
6365 
6366  query.value(45).toUInt(), // findid
6367 
6368  flags,
6369  query.value(42).toUInt(), // audioproperties
6370  query.value(43).toUInt(), // videoproperties
6371  query.value(44).toUInt(), // subtitleType
6372  query.value(56).toString(), // inputname
6373  MythDate::as_utc(query.value(57)
6374  .toDateTime()))); // bookmarkupdate
6375 
6376  if (save_not_commflagged)
6377  destination.back()->SaveCommFlagged(COMM_FLAG_NOT_FLAGGED);
6378  }
6379 
6380  return true;
6381 }
6382 
6383 bool GetNextRecordingList(QDateTime &nextRecordingStart,
6384  bool *hasConflicts,
6385  std::vector<ProgramInfo> *list)
6386 {
6387  nextRecordingStart = QDateTime();
6388 
6389  bool dummy = false;
6390  bool *conflicts = (hasConflicts) ? hasConflicts : &dummy;
6391 
6392  ProgramList progList;
6393  if (!LoadFromScheduler(progList, *conflicts))
6394  return false;
6395 
6396  // find the earliest scheduled recording
6397  for (auto *prog : progList)
6398  {
6399  if ((prog->GetRecordingStatus() == RecStatus::WillRecord ||
6400  prog->GetRecordingStatus() == RecStatus::Pending) &&
6401  (nextRecordingStart.isNull() ||
6402  nextRecordingStart > prog->GetRecordingStartTime()))
6403  {
6404  nextRecordingStart = prog->GetRecordingStartTime();
6405  }
6406  }
6407 
6408  if (!list)
6409  return true;
6410 
6411  // save the details of the earliest recording(s)
6412  for (auto & prog : progList)
6413  {
6414  if ((prog->GetRecordingStatus() == RecStatus::WillRecord ||
6415  prog->GetRecordingStatus() == RecStatus::Pending) &&
6416  (prog->GetRecordingStartTime() == nextRecordingStart))
6417  {
6418  list->push_back(*prog);
6419  }
6420  }
6421 
6422  return true;
6423 }
6424 
6426 {
6427 }
6428 
6430 {
6431  delete lock;
6432 }
6433 
6434 // ---------------------------------------------------------------------------
6435 // DEPRECATED CODE FOLLOWS
6436 // ---------------------------------------------------------------------------
6437 
6438 void ProgramInfo::SetFilesize(uint64_t sz)
6439 {
6440  //LOG(VB_GENERAL, LOG_DEBUG, "FIXME: ProgramInfo::SetFilesize() called instead of RecordingInfo::SetFilesize()");
6441  m_fileSize = sz;
6442 }
6443 
6444 
6448 void ProgramInfo::SaveFilesize(uint64_t fsize)
6449 {
6450  //LOG(VB_GENERAL, LOG_DEBUG, "FIXME: ProgramInfo::SaveFilesize() called instead of RecordingInfo::SaveFilesize()");
6451  SetFilesize(fsize);
6452 
6453  MSqlQuery query(MSqlQuery::InitCon());
6454  query.prepare(
6455  "UPDATE recorded "
6456  "SET filesize = :FILESIZE "
6457  "WHERE chanid = :CHANID AND "
6458  " starttime = :STARTTIME");
6459  query.bindValue(":FILESIZE", (quint64)fsize);
6460  query.bindValue(":CHANID", m_chanId);
6461  query.bindValue(":STARTTIME", m_recStartTs);
6462 
6463  if (!query.exec())
6464  MythDB::DBError("File size update", query);
6465 
6467 }
6468 
6469 //uint64_t ProgramInfo::GetFilesize(void) const
6470 //{
6471  //LOG(VB_GENERAL, LOG_DEBUG, "FIXME: ProgramInfo::GetFilesize() called instead of RecordingInfo::GetFilesize()");
6472 // return m_fileSize;
6473 //}
6474 
6475 // Restore the original query. When a recording finishes, a
6476 // check is made on the filesize and it's 0 at times. If
6477 // less than 1000, commflag isn't queued. See #12290.
6478 
6479 uint64_t ProgramInfo::GetFilesize(void) const
6480 {
6481 
6482  if (m_fileSize)
6483  return m_fileSize;
6484 
6485  // Always query in the 0 case because we can't
6486  // tell if the filesize was 'lost'. Other than
6487  // the 0 case, tests showed the DB and RI sizes
6488  // were equal.
6489 
6490  uint64_t db_filesize = 0;
6491 
6492  MSqlQuery query(MSqlQuery::InitCon());
6493 
6494  query.prepare(
6495  "SELECT filesize "
6496  "FROM recorded "
6497  "WHERE chanid = :CHANID AND "
6498  " starttime = :STARTTIME");
6499  query.bindValue(":CHANID", m_chanId);
6500  query.bindValue(":STARTTIME", m_recStartTs);
6501  if (query.exec() && query.next())
6502  db_filesize = query.value(0).toULongLong();
6503 
6504  if (db_filesize)
6505  LOG(VB_RECORD, LOG_INFO, LOC + QString("RI Filesize=0, DB Filesize=%1")
6506  .arg(db_filesize));
6507 
6508  return db_filesize;
6509 }
6510 
6512 {
6513  m_recordedPercent = -1;
6515  return;
6516 
6517  QDateTime startTime = m_recStartTs;
6518  QDateTime now = MythDate::current();
6519  if (now < startTime)
6520  return;
6521 
6522  QDateTime endTime = m_recEndTs;
6523  int current = startTime.secsTo(now);
6524  int duration = startTime.secsTo(endTime);
6525  if (duration < 1)
6526  return;
6527 
6528  m_recordedPercent = std::clamp(current * 100 / duration, 0, 100);
6529  LOG(VB_GUI, LOG_DEBUG, QString("%1 recorded percent %2/%3 = %4%")
6530  .arg(m_title).arg(current).arg(duration).arg(m_recordedPercent));
6531 }
6532 
6534 {
6535  if (pos == 0)
6536  {
6537  m_watchedPercent = -1;
6538  return;
6539  }
6540 
6541  uint64_t total = 0;
6542  if (IsVideo())
6543  {
6544  total = std::max((int64_t)0, QueryTotalFrames());
6545  }
6546  else if (IsRecording())
6547  {
6548  switch (m_recStatus)
6549  {
6550  case RecStatus::Recorded:
6551  total = std::max((int64_t)0, QueryTotalFrames());
6552  break;
6553  case RecStatus::Recording:
6554  {
6555  // Compute expected total frames based on frame rate.
6556  int64_t rate1000 = QueryAverageFrameRate();
6557  int64_t duration = m_recStartTs.secsTo(m_recEndTs);
6558  total = rate1000 * duration / 1000;
6559  }
6560  break;
6561  default:
6562  break;
6563  }
6564  }
6565 
6566  if (total == 0)
6567  {
6568  if (IsVideo())
6569  {
6570  LOG(VB_GUI, LOG_DEBUG,
6571  QString("%1 %2 no frame count. Please rebuild seek table for this video.")
6572  .arg(m_pathname, m_title));
6573  }
6574  else if (IsRecording())
6575  {
6576  LOG(VB_GUI, LOG_DEBUG,
6577  QString("%1 %2 no frame count. Please rebuild seek table for this recording.")
6578  .arg(m_recordedId).arg(m_title));
6579  }
6580  m_watchedPercent = 0;
6581  return;
6582  }
6583 
6584  m_watchedPercent = std::clamp(100 * pos / total, (uint64_t)0, (uint64_t)100);
6585  LOG(VB_GUI, LOG_DEBUG, QString("%1 %2 watched percent %3/%4 = %5%")
6586  .arg(m_recordedId).arg(m_title)
6587  .arg(pos).arg(total).arg(m_watchedPercent));
6588 }
6589 
6591 {
6594 }
6595 
6597 {
6598  switch (groupBy)
6599  {
6600  case ProgGroupBy::None:
6601  return tr("None");
6602  case ProgGroupBy::ChanNum:
6603  return tr("Channel Number");
6604  case ProgGroupBy::CallSign:
6605  return tr("CallSign");
6607  return tr("ProgramId");
6608  default:
6609  return tr("Unknown");
6610  }
6611 }
6612 
6613 
6614 /* vim: set expandtab tabstop=4 shiftwidth=4: */
ProgGroupBy::ChanNum
@ ChanNum
Definition: programinfo.h:876
ProgramInfo::SetProgramInfoType
void SetProgramInfoType(ProgramInfoType t)
Definition: programinfo.h:524
ProgramInfo::SaveDVDBookmark
static void SaveDVDBookmark(const QStringList &fields)
Definition: programinfo.cpp:2941
MSqlBindings
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:100
ProgramInfo::m_startTs
QDateTime m_startTs
Definition: programinfo.h:803
ProgramInfo::SaveTranscodeStatus
void SaveTranscodeStatus(TranscodingStatus trans)
Set "transcoded" field in "recorded" table to "trans".
Definition: programinfo.cpp:3338
ProgramInfo::QueryIsDeleteCandidate
bool QueryIsDeleteCandidate(bool one_playback_allowed=false) const
Returns true iff this is a recording, it is not in use (except by the recorder), and at most one play...
Definition: programinfo.cpp:3283
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
ProgramInfo::SetAvailableStatus
void SetAvailableStatus(AvailableStatusType status, const QString &where)
Definition: programinfo.cpp:2472
RecStatus::Type
Type
Definition: recordingstatus.h:16
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
ProgramInfo::IsMythStream
bool IsMythStream(void) const
Definition: programinfo.h:357
ProgramInfo::SaveFilesize
virtual void SaveFilesize(uint64_t fsize)
Sets recording file size in database, and sets "filesize" field.
Definition: programinfo.cpp:6448
RecStatus::LaterShowing
@ LaterShowing
Definition: recordingstatus.h:40
kJobQueueInUseID
const QString kJobQueueInUseID
Definition: programtypes.cpp:26
ProgramInfo::MakeUniqueKey
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:340
ProgramInfo::DiscoverRecordingDirectory
QString DiscoverRecordingDirectory(void)
Definition: programinfo.cpp:5130
ProgramInfo::m_startCol
int8_t m_startCol
Definition: programinfo.h:850
kDupsUnset
@ kDupsUnset
Definition: recordingtypes.h:46
ProgramInfo::SaveMarkupMap
void SaveMarkupMap(const frm_dir_map_t &marks, MarkTypes type=MARK_ALL, int64_t min_frame=-1, int64_t max_frame=-1) const
Definition: programinfo.cpp:3618
ProgramInfo::UpdateLastDelete
void UpdateLastDelete(bool setTime) const
Set or unset the record.last_delete field.
Definition: programinfo.cpp:3440
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::CalculateWatchedProgress
void CalculateWatchedProgress(uint64_t pos)
Definition: programinfo.cpp:6533
ProgramInfo::m_fileSize
uint64_t m_fileSize
Definition: programinfo.h:801
from_recordedseek_mark_desc
static const char * from_recordedseek_mark_desc
Definition: programinfo.cpp:4134
MythCoreContext::GetMasterHostName
QString GetMasterHostName(void)
Definition: mythcorecontext.cpp:813
ProgramInfo::IsDuplicateProgram
bool IsDuplicateProgram(const ProgramInfo &other) const
Checks for duplicates according to dupmethod.
Definition: programinfo.cpp:2203
propsValueFromString
uint propsValueFromString(const QString &name, const QMap< T, QString > &propNames, const QString &props)
Definition: programinfo.cpp:1484
ProgramInfo::SaveEditing
void SaveEditing(bool edit)
Sets "editing" field in "recorded" table to "edit".
Definition: programinfo.cpp:3150
ProgramInfo::GetFilesize
virtual uint64_t GetFilesize(void) const
Definition: programinfo.cpp:6479
MARK_KEYFRAME
@ MARK_KEYFRAME
Definition: programtypes.h:61
MARK_COMM_END
@ MARK_COMM_END
Definition: programtypes.h:59
ProgramInfo::IsDuplicate
bool IsDuplicate(void) const
Definition: programinfo.h:493
ProgramInfo::operator==
bool operator==(const ProgramInfo &rhs)
Definition: programinfo.cpp:1053
ProgramInfo::m_recEndTs
QDateTime m_recEndTs
Definition: programinfo.h:806
MARK_BOOKMARK
@ MARK_BOOKMARK
Definition: programtypes.h:56
kProgramInfoTypeVideoDVD
@ kProgramInfoTypeVideoDVD
Definition: programtypes.h:169
ProgramInfo::m_catType
CategoryType m_catType
Definition: programinfo.h:799
ProgramInfo::m_inputId
uint32_t m_inputId
Definition: programinfo.h:820
ProgramInfo::m_title
QString m_title
Definition: programinfo.h:768
MARK_PLACEHOLDER
@ MARK_PLACEHOLDER
Definition: programtypes.h:53
getMythSortHelper
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
Definition: mythsorthelper.cpp:130
ProgramInfo::SaveBookmark
void SaveBookmark(uint64_t frame)
Clears any existing bookmark in DB and if frame is greater than 0 sets a new bookmark.
Definition: programinfo.cpp:2690
kDupsInAll
@ kDupsInAll
Definition: recordingtypes.h:49
ProgramInfo::m_dupMethod
uint8_t m_dupMethod
Definition: programinfo.h:834
MythScheduler
This is an generic interface to a program scheduler.
Definition: mythscheduler.h:17
ProgramInfo::QueryAverageFrameRate
uint QueryAverageFrameRate(void) const
If present in recording this loads average frame rate of the main video stream from database's stream...
Definition: programinfo.cpp:4576
ProgramInfo::m_inUseForWhat
QString m_inUseForWhat
Definition: programinfo.h:855
ProgramInfo::m_storageGroup
QString m_storageGroup
Definition: programinfo.h:794
ProgramInfo::QueryMplexID
uint QueryMplexID(void) const
Queries multiplex any recording would be made on, zero if unknown.
Definition: programinfo.cpp:2665
MythCoreContext::GetScheduler
MythScheduler * GetScheduler(void)
Definition: mythcorecontext.cpp:1915
ProgramInfo::m_chanId
uint32_t m_chanId
Definition: programinfo.h:782
ProgramInfo::kCategorySeries
@ kCategorySeries
Definition: programinfo.h:77
mythdb.h
DateTimeFromListItem
static QDateTime DateTimeFromListItem(const QString &str)
Definition: programinfo.cpp:1349
ProgramInfo::m_dupIn
uint8_t m_dupIn
Definition: programinfo.h:833
ProgramInfo::QueryPositionMap
void QueryPositionMap(frm_pos_map_t &posMap, MarkTypes type) const
Definition: programinfo.cpp:3794
ProgramInfo::QueryBasename
QString QueryBasename(void) const
Gets the basename, from the DB if necessary.
Definition: programinfo.cpp:2529
LoadProgramFromProgram
ProgramInfo * LoadProgramFromProgram(const uint chanid, const QDateTime &starttime)
Definition: programinfo.cpp:5929
determineURLType
static QString determineURLType(const QString &url)
Definition: programinfo.cpp:90
MARK_TMP_CUT_END
@ MARK_TMP_CUT_END
Definition: programtypes.h:50
MARK_GOP_START
@ MARK_GOP_START
Definition: programtypes.h:60
MythCoreContext::SendReceiveStringList
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
Definition: mythcorecontext.cpp:1381
ProgramInfo::m_audioProperties
AudioPropsType m_audioProperties
Definition: programinfo.h:825
MythDate::as_utc
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
Definition: mythdate.cpp:28
ProgramInfo::m_recordId
uint32_t m_recordId
Definition: programinfo.h:816
CommFlagStatus
CommFlagStatus
Definition: programtypes.h:119
ProgramInfo::m_recStartTs
QDateTime m_recStartTs
Definition: programinfo.h:805
kRecorderInUseID
const QString kRecorderInUseID
Definition: programtypes.cpp:20
delete_markup_datum
static void delete_markup_datum(MarkTypes type, uint chanid, const QDateTime &recstartts)
Definition: programinfo.cpp:4345
ProgramInfo::m_totalEpisodes
uint m_totalEpisodes
Definition: programinfo.h:775
kPIUpdate
@ kPIUpdate
Definition: programinfoupdater.h:21
ProgramInfo::SavePositionMapDelta
void SavePositionMapDelta(frm_pos_map_t &posMap, MarkTypes type) const
Definition: programinfo.cpp:4022
ProgramInfo::m_description
QString m_description
Definition: programinfo.h:772
RemoteFile::Exists
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:461
ProgramInfo::SaveTotalFrames
void SaveTotalFrames(int64_t frames)
Store the Total Frames at frame 0 in the recordedmarkup table.
Definition: programinfo.cpp:4432
ProgramInfo::s_staticDataLock
static QMutex s_staticDataLock
Definition: programinfo.h:858
RecStatus::Tuning
@ Tuning
Definition: recordingstatus.h:22
ProgramInfo::GetRecTypeStatus
QString GetRecTypeStatus(bool showrerecord) const
Definition: programinfo.cpp:1515
MarkTypes
MarkTypes
Definition: programtypes.h:46
ProgramInfo::m_availableStatus
uint8_t m_availableStatus
Definition: programinfo.h:841
ProgramInfo::QueryAverageScanProgressive
bool QueryAverageScanProgressive(void) const
If present in recording this loads average video scan type of the main video stream from database's s...
Definition: programinfo.cpp:4631
mythcdrom.h
ProgramInfo::QueryMarkup
void QueryMarkup(QVector< MarkupEntry > &mapMark, QVector< MarkupEntry > &mapSeek) const
Definition: programinfo.cpp:4674
StorageGroup::FindFile
QString FindFile(const QString &filename)
Definition: storagegroup.cpp:597
qstringEqualOrDefault
bool qstringEqualOrDefault(const QString &a, const QString &b)
Definition: programinfo.cpp:1032
from_filemarkup_offset_desc
static const char * from_filemarkup_offset_desc
Definition: programinfo.cpp:4095
ProgramInfo::clone
virtual void clone(const ProgramInfo &other, bool ignore_non_serialized_data=false)
Copies important fields from other ProgramInfo.
Definition: programinfo.cpp:845
init_tr
static int init_tr(void)
Definition: programinfo.cpp:5403
AutoDeleteDeque::clear
void clear(void)
Definition: autodeletedeque.h:40
ProgramInfo::CategoryType
CategoryType
Definition: programinfo.h:76
ProgramInfo::QueryStartMark
uint64_t QueryStartMark(void) const
Definition: programinfo.cpp:2869
sched
Scheduler * sched
ProgramInfo::QueryBookmarkTimeStamp
QDateTime QueryBookmarkTimeStamp(void) const
Queries Latest bookmark timestamp from the database.
Definition: programinfo.cpp:2787
ProgramInfo::QueryJobsRunning
static QMap< QString, bool > QueryJobsRunning(int type)
Definition: programinfo.cpp:5591
ProgramInfo::QueryPositionKeyFrame
bool QueryPositionKeyFrame(uint64_t *keyframe, uint64_t position, bool backwards) const
Definition: programinfo.cpp:4221
AutoDeleteDeque::empty
bool empty(void) const
Definition: autodeletedeque.h:66
MythCoreContext::IsDatabaseIgnored
bool IsDatabaseIgnored(void) const
/brief Returns true if database is being ignored.
Definition: mythcorecontext.cpp:882
xbmcvfs.exists
bool exists(str path)
Definition: xbmcvfs.py:51
ProgramInfo::m_subtitleProperties
SubtitlePropsType m_subtitleProperties
Definition: programinfo.h:826
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
kNotRecording
@ kNotRecording
Definition: recordingtypes.h:21
frm_dir_map_t
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:117
ProgramInfo::QueryRecordingGroupPassword
static QString QueryRecordingGroupPassword(const QString &group)
Definition: programinfo.cpp:5077
ProgramInfo::IsSameTitleStartTimeAndChannel
bool IsSameTitleStartTimeAndChannel(const ProgramInfo &other) const
Checks title, chanid or callsign and start times for equality.
Definition: programinfo.cpp:2346
getSymlinkTarget
QString getSymlinkTarget(const QString &start_file, QStringList *intermediaries, unsigned maxLinks)
Definition: mythmiscutil.cpp:451
RecStatus::Unknown
@ Unknown
Definition: recordingstatus.h:32
ProgramInfo::s_usingProgIDAuth
static bool s_usingProgIDAuth
Definition: programinfo.h:860
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
ProgramInfo::ExtractKey
static bool ExtractKey(const QString &uniquekey, uint &chanid, QDateTime &recstartts)
Extracts chanid and recstartts from a unique key generated by MakeUniqueKey().
Definition: programinfo.cpp:1186
ProgramInfo::m_recPriority
int32_t m_recPriority
Definition: programinfo.h:780
kProgramInfoTypeVideoFile
@ kProgramInfoTypeVideoFile
Definition: programtypes.h:168
ProgramInfo::m_season
uint m_season
Definition: programinfo.h:773
ProgramInfo::SaveCommFlagged
void SaveCommFlagged(CommFlagStatus flag)
Set "commflagged" field in "recorded" table to "flag".
Definition: programinfo.cpp:3361
RecStatus::Recorded
@ Recorded
Definition: recordingstatus.h:29
from_recordedseek_mark_asc
static const char * from_recordedseek_mark_asc
Definition: programinfo.cpp:4127
MARK_VIDEO_HEIGHT
@ MARK_VIDEO_HEIGHT
Definition: programtypes.h:71
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
ProgramInfo::QueryKeyFrameDuration
bool QueryKeyFrameDuration(uint64_t *duration, uint64_t keyframe, bool backwards) const
Definition: programinfo.cpp:4248
LoadFromRecorded
bool LoadFromRecorded(ProgramList &destination, bool possiblyInProgressRecordingsOnly, const QMap< QString, uint32_t > &inUseMap, const QMap< QString, bool > &isJobRunning, const QMap< QString, ProgramInfo * > &recMap, int sort, const QString &sortBy, bool ignoreLiveTV, bool ignoreDeleted)
Definition: programinfo.cpp:6130
ProgramInfo::m_bookmarkUpdate
QDateTime m_bookmarkUpdate
Definition: programinfo.h:838
RemoteCheckFile
bool RemoteCheckFile(ProgramInfo *pginfo, bool checkSlaves)
Definition: remoteutil.cpp:93
ProgramInfo::UpdateInUseMark
void UpdateInUseMark(bool force=false)
Definition: programinfo.cpp:4994
ProgramInfo::m_pathname
QString m_pathname
Definition: programinfo.h:791
kPIPPlayerInUseID
const QString kPIPPlayerInUseID
Definition: programtypes.cpp:17
mythsorthelper.h
force
bool force
Definition: mythcommflag.cpp:61
force_init
int force_init
Definition: programinfo.cpp:43
ProgramInfoType
ProgramInfoType
Definition: programtypes.h:166
ProgramInfo::m_endTs
QDateTime m_endTs
Definition: programinfo.h:804
ProgramInfo::GetRecordingGroup
QString GetRecordingGroup(void) const
Definition: programinfo.h:420
remoteutil.h
ProgramInfo::m_positionMapDBReplacement
PMapDBReplacement * m_positionMapDBReplacement
Definition: programinfo.h:856
StorageGroup::FileExists
bool FileExists(const QString &filename)
Definition: storagegroup.cpp:354
MythDate::kDateTimeShort
@ kDateTimeShort
Default local time.
Definition: mythdate.h:24
ProgramInfo::QueryTuningInfo
bool QueryTuningInfo(QString &channum, QString &input) const
Returns the channel and input needed to record the program.
Definition: programinfo.cpp:5378
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
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
ProgramInfo::SendAddedEvent
void SendAddedEvent(void) const
Sends event out that the ProgramInfo should be added to lists.
Definition: programinfo.cpp:2774
ProgramInfo::SaveCommBreakList
void SaveCommBreakList(frm_dir_map_t &frames) const
Definition: programinfo.cpp:3564
ProgramInfo::IsWatched
bool IsWatched(void) const
Definition: programinfo.h:487
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
kDupCheckDesc
@ kDupCheckDesc
Definition: recordingtypes.h:66
ProgramInfo::UpdateLastPlayTimeStamp
void UpdateLastPlayTimeStamp(bool lastplay) const
Definition: programinfo.cpp:2749
RecStatus::toString
static QString toString(RecStatus::Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
Definition: recordingstatus.cpp:40
programtypeflags.h
ProgramInfo::m_sourceId
uint32_t m_sourceId
Definition: programinfo.h:819
ProgramInfo::m_watchedPercent
int8_t m_watchedPercent
Definition: programinfo.h:843
InfoMap
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
ProgramInfo::kCategoryNone
@ kCategoryNone
Definition: programinfo.h:77
ProgGroupBy::ProgramId
@ ProgramId
Definition: programinfo.h:878
ProgramInfo::ensureSortFields
void ensureSortFields(void)
Ensure that the sortTitle and sortSubtitle fields are set.
Definition: programinfo.cpp:1152
string_to_myth_category_type
ProgramInfo::CategoryType string_to_myth_category_type(const QString &category_type)
Definition: programinfo.cpp:138
ProgramInfo::QueryBookmark
uint64_t QueryBookmark(void) const
Gets any bookmark position in database, unless the ignore bookmark flag is set.
Definition: programinfo.cpp:2814
ProgramInfo::Reload
bool Reload(void)
Definition: programinfo.cpp:1992
NEXT_STR
#define NEXT_STR()
Definition: programinfo.cpp:1341
ProgramInfo::IsRepeat
bool IsRepeat(void) const
Definition: programinfo.h:492
ProgGroupBy::None
@ None
Definition: programinfo.h:875
RecStatus::WillRecord
@ WillRecord
Definition: recordingstatus.h:31
AutoDeleteDeque::back
T back(void)
Definition: autodeletedeque.h:63
kDupCheckNone
@ kDupCheckNone
Definition: recordingtypes.h:64
ProgramInfo::GetVideoPropertyNames
QString GetVideoPropertyNames(void) const
Definition: programinfo.cpp:1555
ProgramInfo::m_hostname
QString m_hostname
Definition: programinfo.h:793
ProgramInfo::SetSubtitle
void SetSubtitle(const QString &st, const QString &sst=nullptr)
Definition: programinfo.cpp:1169
ProgramInfo::kFromRecordedQuery
static const QString kFromRecordedQuery
Definition: programinfo.h:852
MARK_ASPECT_CUSTOM
@ MARK_ASPECT_CUSTOM
Definition: programtypes.h:68
ProgramInfo::IsVideoBD
bool IsVideoBD(void) const
Definition: programinfo.h:350
ProgramInfo::MarkAsInUse
void MarkAsInUse(bool inuse, const QString &usedFor="")
Tracks a recording's in use status, to prevent deletion and to allow the storage scheduler to perform...
Definition: programinfo.cpp:5185
ProgramInfo::m_chanName
QString m_chanName
Definition: programinfo.h:785
ProgramInfo::MarkupEntry::type
int type
Definition: programinfo.h:699
ProgramInfo::SetPathname
void SetPathname(const QString &pn)
Definition: programinfo.cpp:2459
ProgramInfo::SaveVideoProperties
void SaveVideoProperties(uint mask, uint video_property_flags)
Definition: programinfo.cpp:4944
ProgramInfo::SaveResolution
void SaveResolution(uint64_t frame, uint width, uint height)
Store the Resolution at frame in the recordedmarkup table.
Definition: programinfo.cpp:4450
MythDate::fromSecsSinceEpoch
MBASE_PUBLIC QDateTime fromSecsSinceEpoch(int64_t seconds)
This function takes the number of seconds since the start of the epoch and returns a QDateTime with t...
Definition: mythdate.cpp:81
programinfo.h
ProgramInfo::UpdateMarkTimeStamp
void UpdateMarkTimeStamp(bool bookmarked) const
Definition: programinfo.cpp:2708
ProgramInfo::QueryMarkupFlag
bool QueryMarkupFlag(MarkTypes type) const
Returns true iff the speficied mark type is set on frame 0.
Definition: programinfo.cpp:3776
ProgramInfo::GetScheduledStartTime
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:391
ProgramInfo::QueryRecordedIdFromPathname
static bool QueryRecordedIdFromPathname(const QString &pathname, uint &recordedid)
Definition: programinfo.cpp:1242
kOneRecord
@ kOneRecord
Definition: recordingtypes.h:27
mythlogging.h
ProgramInfo::GetRecordingStatus
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:451
MythSortHelper::doTitle
QString doTitle(const QString &title) const
Create the sortable form of an title string.
Definition: mythsorthelper.cpp:164
ProgramInfo::QueryAverageAspectRatio
MarkTypes QueryAverageAspectRatio(void) const
Definition: programinfo.cpp:4581
MythCoreContext::GenMythURL
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
Definition: mythcorecontext.cpp:766
kPBPPlayerInUseID
const QString kPBPPlayerInUseID
Definition: programtypes.cpp:18
ProgramInfo::QueryBDBookmark
QStringList QueryBDBookmark(const QString &serialid) const
Queries "bdbookmark" table for bookmarking BD serial number.
Definition: programinfo.cpp:2985
MythCoreContext::GetQLocale
QLocale GetQLocale(void)
Definition: mythcorecontext.cpp:1884
kProgramInfoTypeVideoStreamingRTSP
@ kProgramInfoTypeVideoStreamingRTSP
Definition: programtypes.h:171
ProgramInfo::operator=
ProgramInfo & operator=(const ProgramInfo &other)
Copies important fields from other ProgramInfo.
Definition: programinfo.cpp:835
ProgramInfo::m_recType
uint8_t m_recType
Definition: programinfo.h:832
ProgramInfo::m_category
QString m_category
Definition: programinfo.h:777
remotefile.h
ProgramInfo::ClearMarkupMap
void ClearMarkupMap(MarkTypes type=MARK_ALL, int64_t min_frame=-1, int64_t max_frame=-1) const
Definition: programinfo.cpp:3577
ProgramInfo::QueryAverageWidth
uint QueryAverageWidth(void) const
If present in recording this loads average width of the main video stream from database's stream mark...
Definition: programinfo.cpp:4567
ProgramInfo::GetProgramFlagNames
QString GetProgramFlagNames(void) const
Definition: programinfo.cpp:1509
MythDate::kFilename
@ kFilename
Default UTC, "yyyyMMddhhmmss".
Definition: mythdate.h:18
ProgramInfo::MarkupEntry::data
uint64_t data
Definition: programinfo.h:701
programinfoupdater.h
MythCDROM::kBluray
@ kBluray
Definition: mythcdrom.h:29
ProgramInfo::GetSubtitleTypeNames
QString GetSubtitleTypeNames(void) const
Definition: programinfo.cpp:1549
MythCoreContext::GetBackendServerPort
int GetBackendServerPort(void)
Returns the locally defined backend control port.
Definition: mythcorecontext.cpp:1070
ProgramInfo::m_playGroup
QString m_playGroup
Definition: programinfo.h:789
ProgramInfo::kSchedulingKey
@ kSchedulingKey
Definition: programinfo.h:516
RecStatus::toDescription
static QString toDescription(Type recstatus, RecordingType rectype, const QDateTime &recstartts)
Converts "recstatus" into a long human readable description.
Definition: recordingstatus.cpp:188
ProgramInfo::GetShortInputName
QString GetShortInputName(void) const
Definition: programinfo.cpp:1934
hardwareprofile.i18n.t
t
Definition: i18n.py:36
ProgramInfo::SaveAspect
void SaveAspect(uint64_t frame, MarkTypes type, uint customAspect)
Store aspect ratio of a frame in the recordedmark table.
Definition: programinfo.cpp:4261
ProgramInfo::clear
virtual void clear(void)
Definition: programinfo.cpp:941
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
ProgramInfo::m_seriesId
QString m_seriesId
Definition: programinfo.h:796
compat.h
MARK_UTIL_PROGSTART
@ MARK_UTIL_PROGSTART
Definition: programtypes.h:75
kDupCheckSub
@ kDupCheckSub
Definition: recordingtypes.h:65
ProgramInfo::QueryDVDBookmark
QStringList QueryDVDBookmark(const QString &serialid) const
Queries "dvdbookmark" table for bookmarking DVD serial number.
Definition: programinfo.cpp:2908
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
kTranscoderInUseID
const QString kTranscoderInUseID
Definition: programtypes.cpp:24
ProgramInfoUpdater
Definition: programinfoupdater.h:48
LOC
#define LOC
Definition: programinfo.cpp:34
RecStatus::Failing
@ Failing
Definition: recordingstatus.h:18
TRANSCODING_COMPLETE
@ TRANSCODING_COMPLETE
Definition: programtypes.h:158
discover_program_info_type
static ProgramInfoType discover_program_info_type(uint chanid, const QString &pathname, bool use_remote)
Definition: programinfo.cpp:2423
ProgramInfo::m_lastModified
QDateTime m_lastModified
Definition: programinfo.h:810
ProgramInfo::HasCutlist
bool HasCutlist(void) const
Definition: programinfo.h:484
ProgramInfo::m_episode
uint m_episode
Definition: programinfo.h:774
ProgramInfo::kRecordingKey
@ kRecordingKey
Definition: programinfo.h:515
ProgramInfo::QueryCutList
bool QueryCutList(frm_dir_map_t &delMap, bool loadAutosave=false) const
Definition: programinfo.cpp:3485
kProgramInfoTypeVideoStreamingHTML
@ kProgramInfoTypeVideoStreamingHTML
Definition: programtypes.h:170
stringutil.h
ProgramInfo::m_sortTitle
QString m_sortTitle
Definition: programinfo.h:769
kProgramInfoTypeVideoBD
@ kProgramInfoTypeVideoBD
Definition: programtypes.h:172
AutoExpireType
AutoExpireType
Definition: programtypes.h:192
ProgramInfo::SaveTotalDuration
void SaveTotalDuration(std::chrono::milliseconds duration)
Store the Total Duration at frame 0 in the recordedmarkup table.
Definition: programinfo.cpp:4416
MythDate::kDateShort
@ kDateShort
Default local time.
Definition: mythdate.h:20
ProgramInfo::m_recGroup
QString m_recGroup
Definition: programinfo.h:788
ProgramInfo::SaveSeasonEpisode
void SaveSeasonEpisode(uint seas, uint ep)
Definition: programinfo.cpp:5011
MARK_CUT_START
@ MARK_CUT_START
Definition: programtypes.h:55
RecStatus::Conflict
@ Conflict
Definition: recordingstatus.h:39
kDupCheckSubThenDesc
@ kDupCheckSubThenDesc
Definition: recordingtypes.h:68
ProgramInfo::QueryTranscodeStatus
TranscodingStatus QueryTranscodeStatus(void) const
Returns the "transcoded" field in "recorded" table.
Definition: programinfo.cpp:3318
ProgramInfo::toString
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
Definition: programinfo.cpp:1955
ProgramInfo::ChannelText
QString ChannelText(const QString &format) const
Returns channel info using "format".
Definition: programinfo.cpp:4985
COMM_DETECT_COMMFREE
@ COMM_DETECT_COMMFREE
Definition: programtypes.h:128
ProgramInfoUpdater::insert
void insert(uint recordedid, PIAction action, uint64_t filesize=0ULL)
Definition: programinfoupdater.cpp:11
ProgramInfo::ToMap
virtual void ToMap(InfoMap &progMap, bool showrerecord=false, uint star_range=10, uint date_format=0) const
Converts ProgramInfo into QString QHash containing each field in ProgramInfo converted into localized...
Definition: programinfo.cpp:1588
MARK_TOTAL_FRAMES
@ MARK_TOTAL_FRAMES
Definition: programtypes.h:74
ProgramInfo::MarkupEntry
Definition: programinfo.h:697
MSqlQuery::setForwardOnly
void setForwardOnly(bool f)
Definition: mythdbcon.h:218
ProgramInfo::QueryCategoryType
CategoryType QueryCategoryType(void) const
Queries recordedprogram to get the category_type of the recording.
Definition: programinfo.cpp:3049
kLastUpdateOffset
static constexpr int64_t kLastUpdateOffset
Definition: programinfo.cpp:47
ProgramInfo::SetTitle
void SetTitle(const QString &t, const QString &st=nullptr)
Definition: programinfo.cpp:1162
storagegroup.h
StringUtil::intToPaddedString
QString intToPaddedString(int n, int width=2)
Creates a zero padded string representation of an integer.
Definition: stringutil.h:27
kDupCheckUnset
@ kDupCheckUnset
Definition: recordingtypes.h:63
MARK_ASPECT_4_3
@ MARK_ASPECT_4_3
Definition: programtypes.h:65
MARK_COMM_START
@ MARK_COMM_START
Definition: programtypes.h:58
from_recordedseek_offset_desc
static const char * from_recordedseek_offset_desc
Definition: programinfo.cpp:4108
propsValueToString
QString propsValueToString(const QString &name, QMap< T, QString > propNames, T props)
Definition: programinfo.cpp:1458
PMapDBReplacement::lock
QMutex * lock
Definition: programinfo.h:994
ProgramInfo::SaveBasename
bool SaveBasename(const QString &basename)
Sets a recording's basename in the database.
Definition: programinfo.cpp:2489
ProgramInfo::SaveInetRef
void SaveInetRef(const QString &inet)
Definition: programinfo.cpp:5035
kPIDelete
@ kPIDelete
Definition: programinfoupdater.h:20
MARK_ALL
@ MARK_ALL
Definition: programtypes.h:48
ProgramInfo::m_originalAirDate
QDate m_originalAirDate
Definition: programinfo.h:809
ProgramInfo::QueryTranscoderID
uint QueryTranscoderID(void) const
Definition: programinfo.cpp:5110
ProgramInfo::m_lastInUseTime
QDateTime m_lastInUseTime
Definition: programinfo.h:812
ProgramInfo::GetSecondsInRecording
std::chrono::seconds GetSecondsInRecording(void) const
Returns length of program/recording in seconds.
Definition: programinfo.cpp:1900
ProgramInfo::SetFilesize
virtual void SetFilesize(uint64_t sz)
Definition: programinfo.cpp:6438
ProgramInfo::ProgramFlagsFromNames
void ProgramFlagsFromNames(const QString &names)
Definition: programinfo.cpp:1580
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
ProgramInfo::IsSameTitleTimeslotAndChannel
bool IsSameTitleTimeslotAndChannel(const ProgramInfo &other) const
Checks title, chanid or chansign, start/end times, cardid, inputid for fully inclusive overlap.
Definition: programinfo.cpp:2360
ProgGroupBy::Type
Type
Definition: programinfo.h:874
clamp
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
ProgramInfo::LoadFromScheduler
static QStringList LoadFromScheduler(const QString &tmptable, int recordid)
Definition: programinfo.cpp:5619
MARK_VIDEO_RATE
@ MARK_VIDEO_RATE
Definition: programtypes.h:72
COMM_FLAG_DONE
@ COMM_FLAG_DONE
Definition: programtypes.h:121
TRANSCODING_NOT_TRANSCODED
@ TRANSCODING_NOT_TRANSCODED
Definition: programtypes.h:157
kDisableAutoExpire
@ kDisableAutoExpire
Definition: programtypes.h:193
ProgramInfo::i18n
static QString i18n(const QString &msg)
Translations for play,recording, & storage groups +.
Definition: programinfo.cpp:5487
from_filemarkup_offset_asc
static const char * from_filemarkup_offset_asc
Definition: programinfo.cpp:4089
ProgramInfo::QueryTotalFrames
int64_t QueryTotalFrames(void) const
If present in recording this loads total frames of the main video stream from database's stream marku...
Definition: programinfo.cpp:4659
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:918
kInvalidDateTime
static constexpr uint kInvalidDateTime
Definition: programinfo.cpp:46
kCatName
static const std::array< const QString, ProgramInfo::kNumCatTypes > kCatName
Definition: programinfo.cpp:127
ProgramInfo::m_recordedPercent
int8_t m_recordedPercent
Definition: programinfo.h:842
ProgramInfo::m_recStatus
int8_t m_recStatus
Definition: programinfo.h:831
ProgramInfo::AudioPropertiesFromNames
static uint AudioPropertiesFromNames(const QString &names)
Definition: programinfo.cpp:1575
kPreviewGeneratorInUseID
const QString kPreviewGeneratorInUseID
Definition: programtypes.cpp:25
MARK_UNSET
@ MARK_UNSET
Definition: programtypes.h:49
ProgramInfo::DiscoverProgramInfoType
ProgramInfoType DiscoverProgramInfoType(void) const
Definition: programinfo.cpp:2467
ProgramInfo::SaveAutoExpire
void SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete=false)
Set "autoexpire" field in "recorded" table to "autoExpire".
Definition: programinfo.cpp:3414
MythCDROM::kUnknown
@ kUnknown
Definition: mythcdrom.h:28
ProgramInfo::ProgramInfo
ProgramInfo(void)
Null constructor.
Definition: programinfo.h:81
ProgramInfo::m_spread
int8_t m_spread
Definition: programinfo.h:849
MARK_GOP_BYFRAME
@ MARK_GOP_BYFRAME
Definition: programtypes.h:63
frm_pos_map_t
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:44
myth_category_type_to_string
QString myth_category_type_to_string(ProgramInfo::CategoryType category_type)
Definition: programinfo.cpp:129
ProgramInfo::SubtitleTypesFromNames
static uint SubtitleTypesFromNames(const QString &names)
Definition: programinfo.cpp:1565
ProgramInfo::SaveFrameRate
void SaveFrameRate(uint64_t frame, uint framerate)
Store the Frame Rate at frame in the recordedmarkup table.
Definition: programinfo.cpp:4298
MythDate::fromString
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
ProgramInfo::CalculateProgress
void CalculateProgress(uint64_t pos)
Definition: programinfo.cpp:6590
StorageGroup::GetRelativePathname
static QString GetRelativePathname(const QString &filename)
Returns the relative pathname of a file by comparing the filename against all Storage Group directori...
Definition: storagegroup.cpp:420
RecStatus::Pending
@ Pending
Definition: recordingstatus.h:17
ProgramInfo::m_director
QString m_director
Definition: programinfo.h:778
MythDate::kSimplify
@ kSimplify
Do Today/Yesterday/Tomorrow transform.
Definition: mythdate.h:26
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:912
AutoDeleteDeque< ProgramInfo * >
ProgramInfo::m_parentId
uint32_t m_parentId
Definition: programinfo.h:817
MythCDROM::inspectImage
static ImageType inspectImage(const QString &path)
Definition: mythcdrom.cpp:188
ProgramInfo::m_chanPlaybackFilters
QString m_chanPlaybackFilters
Definition: programinfo.h:786
MARK_DURATION_MS
@ MARK_DURATION_MS
Definition: programtypes.h:73
MythCoreContext::GetMasterServerPort
static int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
Definition: mythcorecontext.cpp:986
FromProgramQuery
static bool FromProgramQuery(const QString &sql, const MSqlBindings &bindings, MSqlQuery &query, const uint start, const uint limit, uint &count, ProgGroupBy::Type groupBy)
Definition: programinfo.cpp:5659
set_flag
static void set_flag(uint32_t &flags, int flag_to_set, bool is_set)
Definition: programinfo.cpp:83
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
ProgramInfo::IsSameProgramAndStartTime
bool IsSameProgramAndStartTime(const ProgramInfo &other) const
Match same program, with same starttime (channel may be different)
Definition: programinfo.cpp:2330
ProgramInfo::s_updater
static ProgramInfoUpdater * s_updater
Definition: programinfo.h:859
ProgramInfo::QueryAutoExpire
AutoExpireType QueryAutoExpire(void) const
Returns "autoexpire" field from "recorded" table.
Definition: programinfo.cpp:3469
from_filemarkup_mark_asc
static const char * from_filemarkup_mark_asc
Definition: programinfo.cpp:4115
AutoDeleteDeque::push_back
void push_back(T info)
Definition: autodeletedeque.h:69
kTruncatingDeleteInUseID
const QString kTruncatingDeleteInUseID
Definition: programtypes.cpp:22
ProgramInfo::kTitleSubtitle
@ kTitleSubtitle
Definition: programinfo.h:514
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:67
ProgramInfo::GetProgramInfoType
ProgramInfoType GetProgramInfoType(void) const
Definition: programinfo.h:476
COMM_FLAG_NOT_FLAGGED
@ COMM_FLAG_NOT_FLAGGED
Definition: programtypes.h:120
ProgramInfo::GetAudioPropertyNames
QString GetAudioPropertyNames(void) const
Definition: programinfo.cpp:1560
ProgramInfo::m_inetRef
QString m_inetRef
Definition: programinfo.h:798
ProgramInfo::SaveBDBookmark
static void SaveBDBookmark(const QStringList &fields)
Definition: programinfo.cpp:3003
mythmiscutil.h
MythDate::secsInPast
std::chrono::seconds secsInPast(const QDateTime &past)
Definition: mythdate.cpp:212
ProgramInfo::QueryKeyFramePosition
bool QueryKeyFramePosition(uint64_t *position, uint64_t keyframe, bool backwards) const
Definition: programinfo.cpp:4230
RecStatus::Recording
@ Recording
Definition: recordingstatus.h:30
asAvailable
@ asAvailable
Definition: programtypes.h:176
load_markup_datum
static uint load_markup_datum(MarkTypes type, uint chanid, const QDateTime &recstartts)
Definition: programinfo.cpp:4484
ProgramInfo::QueryMarkupMap
void QueryMarkupMap(frm_dir_map_t &marks, MarkTypes type, bool merge=false) const
Definition: programinfo.cpp:3684
MARK_CUT_END
@ MARK_CUT_END
Definition: programtypes.h:54
ProgramInfo::m_subtitle
QString m_subtitle
Definition: programinfo.h:770
MythDate::kAddYear
@ kAddYear
Add year to string if not included.
Definition: mythdate.h:25
ProgramInfo::ToStringList
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
Definition: programinfo.cpp:1276
mythscheduler.h
ProgramInfo::kLongDescription
@ kLongDescription
Definition: programinfo.h:513
ProgramInfo::QueryLastFrameInPosMap
uint64_t QueryLastFrameInPosMap(void) const
Returns last frame in position map or 0.
Definition: programinfo.cpp:1914
ProgramInfo::GetSeason
uint GetSeason(void) const
Definition: programinfo.h:367
mythcorecontext.h
from_recordedseek_offset_asc
static const char * from_recordedseek_offset_asc
Definition: programinfo.cpp:4101
MythCoreContext::IsBackend
bool IsBackend(void) const
is this process a backend process
Definition: mythcorecontext.cpp:650
GetNextRecordingList
bool GetNextRecordingList(QDateTime &nextRecordingStart, bool *hasConflicts, std::vector< ProgramInfo > *list)
Definition: programinfo.cpp:6383
ProgramInfo::IsLocal
bool IsLocal(void) const
Definition: programinfo.h:352
ProgramInfo::SaveDeletePendingFlag
void SaveDeletePendingFlag(bool deleteFlag)
Set "deletepending" field in "recorded" table to "deleteFlag".
Definition: programinfo.cpp:3175
ProgramInfo::QueryIsEditing
bool QueryIsEditing(void) const
Queries "recorded" table for its "editing" field and returns true if it is set to true.
Definition: programinfo.cpp:3124
kPlayerInUseID
const QString kPlayerInUseID
Definition: programtypes.cpp:16
COMM_FLAG_PROCESSING
@ COMM_FLAG_PROCESSING
Definition: programtypes.h:122
ProgramInfo::ClearPositionMap
void ClearPositionMap(MarkTypes type) const
Definition: programinfo.cpp:3840
MythDate
Definition: mythdate.cpp:12
ProgramInfo::IsFileReadable
bool IsFileReadable(void)
Attempts to ascertain if the main file for this ProgramInfo is readable.
Definition: programinfo.cpp:5060
ProgramInfo::QueryAverageHeight
uint QueryAverageHeight(void) const
If present in recording this loads average height of the main video stream from database's stream mar...
Definition: programinfo.cpp:4558
ProgramInfo::GetCategoryTypeString
QString GetCategoryTypeString(void) const
Returns catType as a string.
Definition: programinfo.cpp:1908
MythCDROM::kDVD
@ kDVD
Definition: mythcdrom.h:30
ProgramInfo::m_syndicatedEpisode
QString m_syndicatedEpisode
Definition: programinfo.h:776
ProgramInfo::GetPlaybackURL
QString GetPlaybackURL(bool checkMaster=false, bool forceCheckLocal=false)
Returns filename or URL to be used to play back this recording.
Definition: programinfo.cpp:2567
ProgramInfo::IsSameChannel
bool IsSameChannel(const ProgramInfo &other) const
Checks whether channel id or callsign are identical.
Definition: programinfo.cpp:2375
ProgGroupBy::CallSign
@ CallSign
Definition: programinfo.h:877
TranscodingStatus
TranscodingStatus
Definition: programtypes.h:156
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
kPIUpdateFileSize
@ kPIUpdateFileSize
Definition: programinfoupdater.h:22
MythDate::ISODate
@ ISODate
Default UTC.
Definition: mythdate.h:17
ProgramInfo::m_findId
uint32_t m_findId
Definition: programinfo.h:821
MARK_SCAN_PROGRESSIVE
@ MARK_SCAN_PROGRESSIVE
Definition: programtypes.h:69
LoadFromProgram
bool LoadFromProgram(ProgramList &destination, const QString &where, const QString &groupBy, const QString &orderBy, const MSqlBindings &bindings, const ProgramList &schedList)
Definition: programinfo.cpp:5784
ProgGroupBy::toString
static QString toString(ProgGroupBy::Type groupBy)
Definition: programinfo.cpp:6596
RecordingDupMethodType
RecordingDupMethodType
Definition: recordingtypes.h:61
ProgramInfo::IsVideo
bool IsVideo(void) const
Definition: programinfo.h:490
ProgramInfo::m_chanSign
QString m_chanSign
Definition: programinfo.h:784
ProgramInfo::QueryInUseMap
static QMap< QString, uint32_t > QueryInUseMap(void)
Definition: programinfo.cpp:5555
ProgramInfo::kLastInUseOffset
static constexpr int64_t kLastInUseOffset
Definition: programinfo.h:811
ProgramInfo::SaveWatched
void SaveWatched(bool watchedFlag)
Set "watched" field in recorded/videometadata to "watchedFlag".
Definition: programinfo.cpp:3072
ProgramInfo::QueryTotalDuration
std::chrono::milliseconds QueryTotalDuration(void) const
If present this loads the total duration in milliseconds of the main video stream from recordedmarkup...
Definition: programinfo.cpp:4642
ProgramInfo::SubstituteMatches
virtual void SubstituteMatches(QString &str)
Subsitute MATCH% type variable names in the given string.
Definition: programinfo.cpp:5502
ProgramInfo::m_programFlags
uint32_t m_programFlags
ProgramFlag.
Definition: programinfo.h:823
ProgramInfo::ExtractKeyFromPathname
static bool ExtractKeyFromPathname(const QString &pathname, uint &chanid, QDateTime &recstartts)
Definition: programinfo.cpp:1197
ProgramInfo::IsVideoDVD
bool IsVideoDVD(void) const
Definition: programinfo.h:348
RecordingType
RecordingType
Definition: recordingtypes.h:19
ProgramInfo::m_partTotal
uint16_t m_partTotal
Definition: programinfo.h:829
ProgramInfo::SaveCutList
void SaveCutList(frm_dir_map_t &delMap, bool isAutoSave=false) const
Definition: programinfo.cpp:3517
ProgramInfo::CalculateRecordedProgress
void CalculateRecordedProgress()
Definition: programinfo.cpp:6511
ProgramInfo::m_chanStr
QString m_chanStr
Definition: programinfo.h:783
ProgramInfo::SendUpdateEvent
void SendUpdateEvent(void) const
Sends event out that the ProgramInfo should be reloaded.
Definition: programinfo.cpp:2768
StorageGroup::kSpecialGroups
static const QStringList kSpecialGroups
Definition: storagegroup.h:46
ProgramInfo::QueryKeyFrameInfo
bool QueryKeyFrameInfo(uint64_t *result, uint64_t position_or_keyframe, bool backwards, MarkTypes type, const char *from_filemarkup_asc, const char *from_filemarkup_desc, const char *from_recordedseek_asc, const char *from_recordedseek_desc) const
Definition: programinfo.cpp:4142
ProgramInfo::IsVideoFile
bool IsVideoFile(void) const
Definition: programinfo.h:346
MARK_UTIL_LASTPLAYPOS
@ MARK_UTIL_LASTPLAYPOS
Definition: programtypes.h:76
StorageGroup
Definition: storagegroup.h:11
ProgramInfo::SavePreserve
void SavePreserve(bool preserveEpisode)
Set "preserve" field in "recorded" table to "preserveEpisode".
Definition: programinfo.cpp:3387
toQChar
QChar toQChar(RecordingType rectype)
Converts "rectype" into a human readable character.
Definition: recordingtypes.cpp:126
insert_markup_datum
static void insert_markup_datum(MarkTypes type, uint mark, uint offset, const QString &videoPath)
Definition: programinfo.cpp:4377
ProgramInfo::Verbosity
Verbosity
Definition: programinfo.h:511
ProgramInfo::m_sortSubtitle
QString m_sortSubtitle
Definition: programinfo.h:771
AvailableStatusType
AvailableStatusType
Definition: programtypes.h:175
from_filemarkup_mark_desc
static const char * from_filemarkup_mark_desc
Definition: programinfo.cpp:4121
ProgramInfo::m_inputName
QString m_inputName
Definition: programinfo.h:837
LoadFromOldRecorded
bool LoadFromOldRecorded(ProgramList &destination, const QString &sql, const MSqlBindings &bindings)
Definition: programinfo.cpp:5965
MythDate::kDateFull
@ kDateFull
Default local time.
Definition: mythdate.h:19
ProgramInfo::SaveVideoScanType
void SaveVideoScanType(uint64_t frame, bool progressive)
Store the Progressive/Interlaced state in the recordedmarkup table.
Definition: programinfo.cpp:4323
ProgramInfo::MarkupEntry::frame
uint64_t frame
Definition: programinfo.h:700
ProgramInfo::QueryCommBreakList
void QueryCommBreakList(frm_dir_map_t &frames) const
Definition: programinfo.cpp:3571
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:844
Scheduler::GetAllPending
bool GetAllPending(RecList &retList, int recRuleId=0) const
Definition: scheduler.cpp:1745
ProgramInfo::SaveMarkupFlag
void SaveMarkupFlag(MarkTypes type) const
Clears the specified flag, then if sets it.
Definition: programinfo.cpp:3786
ProgramInfo::SaveMarkup
void SaveMarkup(const QVector< MarkupEntry > &mapMark, const QVector< MarkupEntry > &mapSeek) const
Definition: programinfo.cpp:4755
ProgramInfo::QueryIsInUse
bool QueryIsInUse(QStringList &byWho) const
Returns true if Program is in use.
Definition: programinfo.cpp:3203
ProgramInfo::QueryProgStart
uint64_t QueryProgStart(void) const
Gets any progstart position in database, unless the ignore progstart flag is set.
Definition: programinfo.cpp:2858
ProgramInfo::QueryLastPlayPos
uint64_t QueryLastPlayPos(void) const
Gets any lastplaypos position in database, unless the ignore lastplaypos flag is set.
Definition: programinfo.cpp:2841
ProgramInfo::m_partNumber
uint16_t m_partNumber
Definition: programinfo.h:828
ProgramInfo::IsSameProgram
bool IsSameProgram(const ProgramInfo &other) const
Checks whether this is the same program as "other", which may or may not be a repeat or on another ch...
Definition: programinfo.cpp:2272
DateFromListItem
static QDate DateFromListItem(const QString &str)
Definition: programinfo.cpp:1356
PMapDBReplacement::PMapDBReplacement
PMapDBReplacement()
Definition: programinfo.cpp:6425
musicbrainzngs.caa.hostname
string hostname
Definition: caa.py:17
MythDate::kDateTimeFull
@ kDateTimeFull
Default local time.
Definition: mythdate.h:23
kFileTransferInUseID
const QString kFileTransferInUseID
Definition: programtypes.cpp:21
MythDate::kTime
@ kTime
Default local time.
Definition: mythdate.h:22
ProgramInfo::m_recPriority2
int32_t m_recPriority2
Definition: programinfo.h:814
ProgramInfo::LoadProgramFromRecorded
bool LoadProgramFromRecorded(uint chanid, const QDateTime &recstartts)
Loads ProgramInfo for an existing recording.
Definition: programinfo.cpp:2006
ProgramInfo::m_programId
QString m_programId
Definition: programinfo.h:797
ProgramInfo::CheckProgramIDAuthorities
static void CheckProgramIDAuthorities(void)
Definition: programinfo.cpp:2382
kFlaggerInUseID
const QString kFlaggerInUseID
Definition: programtypes.cpp:23
ProgramInfo::m_stars
float m_stars
Rating, range [0..1].
Definition: programinfo.h:808
ProgramInfo::IsRecording
bool IsRecording(void) const
Definition: programinfo.h:491
ProgramInfo::QueryDurationKeyFrame
bool QueryDurationKeyFrame(uint64_t *keyframe, uint64_t duration, bool backwards) const
Definition: programinfo.cpp:4239
ProgramInfo::m_year
uint16_t m_year
Definition: programinfo.h:827
ProgramInfo::GetBasename
QString GetBasename(void) const
Definition: programinfo.h:345
kPIAdd
@ kPIAdd
Definition: programinfoupdater.h:19
ProgramInfo::QueryRecordingGroup
QString QueryRecordingGroup(void) const
Query recgroup from recorded.
Definition: programinfo.cpp:5094
ProgramInfo::IsGeneric
bool IsGeneric(void) const
Definition: programinfo.cpp:1946
RecordingDupInType
RecordingDupInType
Definition: recordingtypes.h:44
ProgramInfo::CreateRecordBasename
QString CreateRecordBasename(const QString &ext) const
Returns a filename for a recording based on the recording channel and date.
Definition: programinfo.cpp:2413
ProgramInfo::m_recordedId
uint m_recordedId
Definition: programinfo.h:836
ProgramInfo::m_videoProperties
VideoPropsType m_videoProperties
Definition: programinfo.h:824
PMapDBReplacement::map
QMap< MarkTypes, frm_pos_map_t > map
Definition: programinfo.h:995
MARK_VIDEO_WIDTH
@ MARK_VIDEO_WIDTH
Definition: programtypes.h:70
ProgramInfo::IsSameProgramWeakCheck
bool IsSameProgramWeakCheck(const ProgramInfo &other) const
Checks for duplicate using only title, chanid and startts.
Definition: programinfo.cpp:2192
ProgramInfo::SendDeletedEvent
void SendDeletedEvent(void) const
Sends event out that the ProgramInfo should be delete from lists.
Definition: programinfo.cpp:2779
PMapDBReplacement::~PMapDBReplacement
~PMapDBReplacement()
Definition: programinfo.cpp:6429
ProgramInfo::VideoPropertiesFromNames
static uint VideoPropertiesFromNames(const QString &names)
Definition: programinfo.cpp:1570
DateTimeToListInt
static QString DateTimeToListInt(const QDateTime &x)
Definition: programinfo.cpp:1264
ProgramInfo::SavePositionMap
void SavePositionMap(frm_pos_map_t &posMap, MarkTypes type, int64_t min_frame=-1, int64_t max_frame=-1) const
Definition: programinfo.cpp:3878
pginfo_init_statics
int pginfo_init_statics()
Definition: programinfo.cpp:40
kProgramInfoTypeRecording
@ kProgramInfoTypeRecording
Definition: programtypes.h:167
ProgramInfo::SaveLastPlayPos
void SaveLastPlayPos(uint64_t frame)
TODO Move to RecordingInfo.
Definition: programinfo.cpp:2727
kImportRecorderInUseID
const QString kImportRecorderInUseID
Definition: programtypes.cpp:19
MARK_TMP_CUT_START
@ MARK_TMP_CUT_START
Definition: programtypes.h:51
uint
unsigned int uint
Definition: freesurround.h:24
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:904
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
ProgramInfo::FromStringList
bool FromStringList(QStringList::const_iterator &it, const QStringList::const_iterator &end)
Uses a QStringList to initialize this ProgramInfo instance.
Definition: programinfo.cpp:1375
ProgramInfo::InitStatics
static int InitStatics(void)
Definition: programinfo.cpp:5478
ProgramInfo::GetStars
float GetStars(void) const
Definition: programinfo.h:446
ProgramInfo::QueryKeyFromPathname
static bool QueryKeyFromPathname(const QString &pathname, uint &chanid, QDateTime &recstartts)
Definition: programinfo.cpp:1219