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