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