MythTV  master
dvr.cpp
Go to the documentation of this file.
1 // Program Name: dvr.cpp
3 // Created : Mar. 7, 2011
4 //
5 // Copyright (c) 2011 David Blain <dblain@mythtv.org>
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 //
25 
26 #include <QMap>
27 #include <QRegExp>
28 
29 #include "dvr.h"
30 
31 #include "compat.h"
32 #include "mythversion.h"
33 #include "mythcorecontext.h"
34 #include "mythevent.h"
35 #include "scheduler.h"
36 #include "autoexpire.h"
37 #include "jobqueue.h"
38 #include "encoderlink.h"
39 #include "remoteutil.h"
40 #include "mythdate.h"
41 #include "recordinginfo.h"
42 #include "cardutil.h"
43 #include "inputinfo.h"
44 #include "programtypes.h"
45 #include "recordingtypes.h"
46 
47 #include "serviceUtil.h"
48 #include "mythscheduler.h"
49 #include "storagegroup.h"
50 #include "playgroup.h"
51 #include "recordingprofile.h"
52 
53 #include "scheduler.h"
54 #include "tv_rec.h"
55 
56 extern QMap<int, EncoderLink *> tvList;
57 extern AutoExpire *expirer;
58 
60 //
62 
64  int nStartIndex,
65  int nCount,
66  const QString &sTitleRegEx,
67  const QString &sRecGroup,
68  const QString &sStorageGroup,
69  const QString &sCategory,
70  const QString &sSort
71  )
72 {
73  QMap< QString, ProgramInfo* > recMap;
74 
76  recMap = gCoreContext->GetScheduler()->GetRecording();
77 
78  QMap< QString, uint32_t > inUseMap = ProgramInfo::QueryInUseMap();
79  QMap< QString, bool > isJobRunning= ProgramInfo::QueryJobsRunning(JOB_COMMFLAG);
80 
81  ProgramList progList;
82 
83  int desc = 1;
84  if (bDescending)
85  desc = -1;
86 
87  LoadFromRecorded( progList, false, inUseMap, isJobRunning, recMap, desc, sSort );
88 
89  QMap< QString, ProgramInfo* >::iterator mit = recMap.begin();
90 
91  for (; mit != recMap.end(); mit = recMap.erase(mit))
92  delete *mit;
93 
94  // ----------------------------------------------------------------------
95  // Build Response
96  // ----------------------------------------------------------------------
97 
98  auto *pPrograms = new DTC::ProgramList();
99  int nAvailable = 0;
100 
101  int nMax = (nCount > 0) ? nCount : progList.size();
102 
103  nAvailable = 0;
104  nCount = 0;
105 
106  QRegExp rTitleRegEx = QRegExp(sTitleRegEx, Qt::CaseInsensitive);
107 
108  for (auto pInfo : progList)
109  {
110  if (pInfo->IsDeletePending() ||
111  (!sTitleRegEx.isEmpty() && !pInfo->GetTitle().contains(rTitleRegEx)) ||
112  (!sRecGroup.isEmpty() && sRecGroup != pInfo->GetRecordingGroup()) ||
113  (!sStorageGroup.isEmpty() && sStorageGroup != pInfo->GetStorageGroup()) ||
114  (!sCategory.isEmpty() && sCategory != pInfo->GetCategory()))
115  continue;
116 
117  if ((nAvailable < nStartIndex) ||
118  (nCount >= nMax))
119  {
120  ++nAvailable;
121  continue;
122  }
123 
124  ++nAvailable;
125  ++nCount;
126 
127  DTC::Program *pProgram = pPrograms->AddNewProgram();
128 
129  FillProgramInfo( pProgram, pInfo, true );
130  }
131 
132  // ----------------------------------------------------------------------
133 
134  pPrograms->setStartIndex ( nStartIndex );
135  pPrograms->setCount ( nCount );
136  pPrograms->setTotalAvailable( nAvailable );
137  pPrograms->setAsOf ( MythDate::current() );
138  pPrograms->setVersion ( MYTH_BINARY_VERSION );
139  pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
140 
141  return pPrograms;
142 }
143 
145 //
147 
149  int nStartIndex,
150  int nCount,
151  const QDateTime &sStartTime,
152  const QDateTime &sEndTime,
153  const QString &sTitle,
154  const QString &sSeriesId,
155  int nRecordId,
156  const QString &sSort)
157 {
158  if (!sStartTime.isNull() && !sStartTime.isValid())
159  throw QString("StartTime is invalid");
160 
161  if (!sEndTime.isNull() && !sEndTime.isValid())
162  throw QString("EndTime is invalid");
163 
164  const QDateTime& dtStartTime = sStartTime;
165  const QDateTime& dtEndTime = sEndTime;
166 
167  if (!sEndTime.isNull() && dtEndTime < dtStartTime)
168  throw QString("EndTime is before StartTime");
169 
170  // ----------------------------------------------------------------------
171  // Build SQL statement for Program Listing
172  // ----------------------------------------------------------------------
173 
174  ProgramList progList;
175  MSqlBindings bindings;
176  QString sSQL;
177 
178  if (!dtStartTime.isNull())
179  {
180  sSQL += " AND endtime >= :StartDate ";
181  bindings[":StartDate"] = dtStartTime;
182  }
183 
184  if (!dtEndTime.isNull())
185  {
186  sSQL += " AND starttime <= :EndDate ";
187  bindings[":EndDate"] = dtEndTime;
188  }
189 
190  QStringList clause;
191 
192  if (nRecordId > 0)
193  {
194  clause << "recordid = :RecordId";
195  bindings[":RecordId"] = nRecordId;
196  }
197 
198  if (!sTitle.isEmpty())
199  {
200  clause << "title = :Title";
201  bindings[":Title"] = sTitle;
202  }
203 
204  if (!sSeriesId.isEmpty())
205  {
206  clause << "seriesid = :SeriesId";
207  bindings[":SeriesId"] = sSeriesId;
208  }
209 
210  if (!clause.isEmpty())
211  {
212  sSQL += QString(" AND (%1) ").arg(clause.join(" OR "));
213  }
214 
215  if (sSort == "starttime")
216  sSQL += "ORDER BY starttime "; // NOLINT(bugprone-branch-clone)
217  else if (sSort == "title")
218  sSQL += "ORDER BY title ";
219  else
220  sSQL += "ORDER BY starttime ";
221 
222  if (bDescending)
223  sSQL += "DESC ";
224  else
225  sSQL += "ASC ";
226 
227  uint nTotalAvailable = (nStartIndex == 0) ? 1 : 0;
228  LoadFromOldRecorded( progList, sSQL, bindings,
229  (uint)nStartIndex, (uint)nCount, nTotalAvailable );
230 
231  // ----------------------------------------------------------------------
232  // Build Response
233  // ----------------------------------------------------------------------
234 
235  auto *pPrograms = new DTC::ProgramList();
236 
237  nCount = (int)progList.size();
238  int nEndIndex = (int)progList.size();
239 
240  for( int n = 0; n < nEndIndex; n++)
241  {
242  ProgramInfo *pInfo = progList[ n ];
243 
244  DTC::Program *pProgram = pPrograms->AddNewProgram();
245 
246  FillProgramInfo( pProgram, pInfo, true );
247  }
248 
249  // ----------------------------------------------------------------------
250 
251  pPrograms->setStartIndex ( nStartIndex );
252  pPrograms->setCount ( nCount );
253  pPrograms->setTotalAvailable( nTotalAvailable );
254  pPrograms->setAsOf ( MythDate::current() );
255  pPrograms->setVersion ( MYTH_BINARY_VERSION );
256  pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
257 
258  return pPrograms;
259 }
260 
262 //
264 
266  int chanid, const QDateTime &recstarttsRaw)
267 {
268  if ((RecordedId <= 0) &&
269  (chanid <= 0 || !recstarttsRaw.isValid()))
270  throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
271 
272  // TODO Should use RecordingInfo
273  ProgramInfo pi;
274  if (RecordedId > 0)
275  pi = ProgramInfo(RecordedId);
276  else
277  pi = ProgramInfo(chanid, recstarttsRaw.toUTC());
278 
279  auto *pProgram = new DTC::Program();
280  FillProgramInfo( pProgram, &pi, true );
281 
282  return pProgram;
283 }
284 
286 //
288 
289 bool Dvr::RemoveRecorded(int RecordedId,
290  int chanid, const QDateTime &recstarttsRaw,
291  bool forceDelete, bool allowRerecord)
292 {
293  return DeleteRecording(RecordedId, chanid, recstarttsRaw, forceDelete,
294  allowRerecord);
295 }
296 
297 
298 bool Dvr::DeleteRecording(int RecordedId,
299  int chanid, const QDateTime &recstarttsRaw,
300  bool forceDelete, bool allowRerecord)
301 {
302  if ((RecordedId <= 0) &&
303  (chanid <= 0 || !recstarttsRaw.isValid()))
304  throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
305 
306  // TODO Should use RecordingInfo
307  ProgramInfo pi;
308  if (RecordedId > 0)
309  pi = ProgramInfo(RecordedId);
310  else
311  pi = ProgramInfo(chanid, recstarttsRaw.toUTC());
312 
313  if (pi.GetChanID() && pi.HasPathname())
314  {
315  QString cmd = QString("DELETE_RECORDING %1 %2 %3 %4")
316  .arg(pi.GetChanID())
318  .arg(forceDelete ? "FORCE" : "NO_FORCE")
319  .arg(allowRerecord ? "FORGET" : "NO_FORGET");
320  MythEvent me(cmd);
321 
322  gCoreContext->dispatch(me);
323  return true;
324  }
325 
326  return false;
327 }
328 
330 //
332 
333 bool Dvr::UnDeleteRecording(int RecordedId,
334  int chanid, const QDateTime &recstarttsRaw)
335 {
336  if ((RecordedId <= 0) &&
337  (chanid <= 0 || !recstarttsRaw.isValid()))
338  throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
339 
340  RecordingInfo ri;
341  if (RecordedId > 0)
342  ri = RecordingInfo(RecordedId);
343  else
344  ri = RecordingInfo(chanid, recstarttsRaw.toUTC());
345 
346  if (ri.GetChanID() && ri.HasPathname())
347  {
348  QString cmd = QString("UNDELETE_RECORDING %1 %2")
349  .arg(ri.GetChanID())
351  MythEvent me(cmd);
352 
353  gCoreContext->dispatch(me);
354  return true;
355  }
356 
357  return false;
358 }
359 
361 //
363 
364 bool Dvr::StopRecording(int RecordedId)
365 {
366  if (RecordedId <= 0)
367  throw QString("RecordedId param is invalid.");
368 
369  RecordingInfo ri = RecordingInfo(RecordedId);
370 
371  if (ri.GetChanID())
372  {
373  QString cmd = QString("STOP_RECORDING %1 %2")
374  .arg(ri.GetChanID())
376  MythEvent me(cmd);
377 
378  gCoreContext->dispatch(me);
379  return true;
380  }
381  throw QString("RecordedId %1 not found").arg(RecordedId);
382 
383  return false;
384 }
385 
387 //
389 
390 bool Dvr::ReactivateRecording(int RecordedId,
391  int chanid, const QDateTime &recstarttsRaw)
392 {
393  if ((RecordedId <= 0) &&
394  (chanid <= 0 || !recstarttsRaw.isValid()))
395  throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
396 
397  RecordingInfo ri;
398  if (RecordedId > 0)
399  ri = RecordingInfo(RecordedId);
400  else
401  ri = RecordingInfo(chanid, recstarttsRaw.toUTC());
402 
403  if (ri.GetChanID() && ri.HasPathname())
404  {
405  ri.ReactivateRecording();
406  return true;
407  }
408 
409  return false;
410 }
411 
413 //
415 
417 {
418  ScheduledRecording::RescheduleMatch(0, 0, 0, QDateTime(),
419  "RescheduleRecordings");
420  return true;
421 }
422 
424 //
426 
427 bool Dvr::UpdateRecordedWatchedStatus ( int RecordedId,
428  int chanid,
429  const QDateTime &recstarttsRaw,
430  bool watched)
431 {
432  if ((RecordedId <= 0) &&
433  (chanid <= 0 || !recstarttsRaw.isValid()))
434  throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
435 
436  // TODO Should use RecordingInfo
437  ProgramInfo pi;
438  if (RecordedId > 0)
439  pi = ProgramInfo(RecordedId);
440  else
441  pi = ProgramInfo(chanid, recstarttsRaw.toUTC());
442 
443  if (pi.GetChanID() && pi.HasPathname())
444  {
445  pi.SaveWatched(watched);
446  return true;
447  }
448 
449  return false;
450 }
451 
453 //
455 
456 long Dvr::GetSavedBookmark( int RecordedId,
457  int chanid,
458  const QDateTime &recstarttsRaw,
459  const QString &offsettype )
460 {
461  if ((RecordedId <= 0) &&
462  (chanid <= 0 || !recstarttsRaw.isValid()))
463  throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
464 
465  RecordingInfo ri;
466  if (RecordedId > 0)
467  ri = RecordingInfo(RecordedId);
468  else
469  ri = RecordingInfo(chanid, recstarttsRaw.toUTC());
470  uint64_t offset = 0;
471  bool isend=true;
472  uint64_t position = ri.QueryBookmark();
473  // if no bookmark return 0
474  if (position == 0)
475  return 0;
476  if (offsettype.toLower() == "position"){
477  // if bookmark cannot be converted to a keyframe we will
478  // just return the actual frame saved as the bookmark
479  if (ri.QueryKeyFramePosition(&offset, position, isend))
480  return offset;
481  }
482  if (offsettype.toLower() == "duration"){
483  if (ri.QueryKeyFrameDuration(&offset, position, isend))
484  return offset;
485  // If bookmark cannot be converted to a duration return -1
486  return -1;
487  }
488  return position;
489 }
490 
492 //
494 
495 bool Dvr::SetSavedBookmark( int RecordedId,
496  int chanid,
497  const QDateTime &recstarttsRaw,
498  const QString &offsettype,
499  long Offset )
500 {
501  if ((RecordedId <= 0) &&
502  (chanid <= 0 || !recstarttsRaw.isValid()))
503  throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
504 
505  if (Offset < 0)
506  throw QString("Offset must be >= 0.");
507 
508  RecordingInfo ri;
509  if (RecordedId > 0)
510  ri = RecordingInfo(RecordedId);
511  else
512  ri = RecordingInfo(chanid, recstarttsRaw.toUTC());
513  uint64_t position = 0;
514  bool isend=true;
515  if (offsettype.toLower() == "position"){
516  if (!ri.QueryPositionKeyFrame(&position, Offset, isend))
517  return false;
518  }
519  else if (offsettype.toLower() == "duration"){
520  if (!ri.QueryDurationKeyFrame(&position, Offset, isend))
521  return false;
522  }
523  else
524  position = Offset;
525  ri.SaveBookmark(position);
526  return true;
527 }
528 
530 //
532 
534  int chanid,
535  const QDateTime &recstarttsRaw,
536  const QString &offsettype )
537 {
538  int marktype = 0;
539  if ((RecordedId <= 0) &&
540  (chanid <= 0 || !recstarttsRaw.isValid()))
541  throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
542 
543  RecordingInfo ri;
544  if (RecordedId > 0)
545  ri = RecordingInfo(RecordedId);
546  else
547  ri = RecordingInfo(chanid, recstarttsRaw.toUTC());
548 
549  auto* pCutList = new DTC::CutList();
550  if (offsettype == "Position")
551  marktype = 1;
552  else if (offsettype == "Duration")
553  marktype = 2;
554  else
555  marktype = 0;
556 
557  FillCutList(pCutList, &ri, marktype);
558 
559  return pCutList;
560 }
561 
563 //
565 
567  int chanid,
568  const QDateTime &recstarttsRaw,
569  const QString &offsettype )
570 {
571  int marktype = 0;
572  if ((RecordedId <= 0) &&
573  (chanid <= 0 || !recstarttsRaw.isValid()))
574  throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
575 
576  RecordingInfo ri;
577  if (RecordedId > 0)
578  ri = RecordingInfo(RecordedId);
579  else
580  ri = RecordingInfo(chanid, recstarttsRaw.toUTC());
581 
582  auto* pCutList = new DTC::CutList();
583  if (offsettype == "Position")
584  marktype = 1;
585  else if (offsettype == "Duration")
586  marktype = 2;
587  else
588  marktype = 0;
589 
590  FillCommBreak(pCutList, &ri, marktype);
591 
592  return pCutList;
593 }
594 
596 //
598 
600  const QString &offsettype )
601 {
602  MarkTypes marktype = MARK_UNSET;
603  if (RecordedId <= 0)
604  throw QString("Recorded ID appears invalid.");
605 
606  RecordingInfo ri;
607  ri = RecordingInfo(RecordedId);
608 
609  auto* pCutList = new DTC::CutList();
610  if (offsettype == "BYTES")
611  marktype = MARK_GOP_BYFRAME;
612  else if (offsettype == "DURATION")
613  marktype = MARK_DURATION_MS;
614  else
615  {
616  delete pCutList;
617  throw QString("Type must be 'BYTES' or 'DURATION'.");
618  }
619 
620  FillSeek(pCutList, &ri, marktype);
621 
622  return pCutList;
623 }
624 
626 //
628 
630  int nCount )
631 {
632  pginfolist_t infoList;
633 
634  if (expirer)
635  expirer->GetAllExpiring( infoList );
636 
637  // ----------------------------------------------------------------------
638  // Build Response
639  // ----------------------------------------------------------------------
640 
641  auto *pPrograms = new DTC::ProgramList();
642 
643  nStartIndex = (nStartIndex > 0) ? min( nStartIndex, (int)infoList.size() ) : 0;
644  nCount = (nCount > 0) ? min( nCount, (int)infoList.size() ) : infoList.size();
645  int nEndIndex = min((nStartIndex + nCount), (int)infoList.size() );
646 
647  for( int n = nStartIndex; n < nEndIndex; n++)
648  {
649  ProgramInfo *pInfo = infoList[ n ];
650 
651  if (pInfo != nullptr)
652  {
653  DTC::Program *pProgram = pPrograms->AddNewProgram();
654 
655  FillProgramInfo( pProgram, pInfo, true );
656 
657  delete pInfo;
658  }
659  }
660 
661  // ----------------------------------------------------------------------
662 
663  pPrograms->setStartIndex ( nStartIndex );
664  pPrograms->setCount ( nCount );
665  pPrograms->setTotalAvailable( infoList.size() );
666  pPrograms->setAsOf ( MythDate::current() );
667  pPrograms->setVersion ( MYTH_BINARY_VERSION );
668  pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
669 
670  return pPrograms;
671 }
672 
674 //
676 
678 {
679  auto* pList = new DTC::EncoderList();
680 
681  QReadLocker tvlocker(&TVRec::s_inputsLock);
682  QList<InputInfo> inputInfoList = CardUtil::GetAllInputInfo();
683  foreach (auto elink, tvList)
684  {
685  if (elink != nullptr)
686  {
687  DTC::Encoder *pEncoder = pList->AddNewEncoder();
688 
689  pEncoder->setId ( elink->GetInputID() );
690  pEncoder->setState ( elink->GetState() );
691  pEncoder->setLocal ( elink->IsLocal() );
692  pEncoder->setConnected ( elink->IsConnected() );
693  pEncoder->setSleepStatus ( elink->GetSleepStatus() );
694  // pEncoder->setLowOnFreeSpace( elink->isLowOnFreeSpace());
695 
696  if (pEncoder->Local())
697  pEncoder->setHostName( gCoreContext->GetHostName() );
698  else
699  pEncoder->setHostName( elink->GetHostName() );
700 
701  foreach (auto inputInfo, inputInfoList)
702  {
703  if (inputInfo.m_inputId == static_cast<uint>(elink->GetInputID()))
704  {
705  DTC::Input *input = pEncoder->AddNewInput();
706  FillInputInfo(input, inputInfo);
707  }
708  }
709 
710  switch ( pEncoder->State() )
711  {
715  {
716  ProgramInfo *pInfo = elink->GetRecording();
717 
718  if (pInfo)
719  {
720  DTC::Program *pProgram = pEncoder->Recording();
721 
722  FillProgramInfo( pProgram, pInfo, true, true );
723 
724  delete pInfo;
725  }
726 
727  break;
728  }
729 
730  default:
731  break;
732  }
733  }
734  }
735  return pList;
736 }
737 
739 //
741 
743 {
744  auto *pList = new DTC::InputList();
745 
746  QList<InputInfo> inputInfoList = CardUtil::GetAllInputInfo();
747  foreach (auto inputInfo, inputInfoList)
748  {
749  DTC::Input *input = pList->AddNewInput();
750  FillInputInfo(input, inputInfo);
751  }
752 
753  return pList;
754 }
755 
757 //
759 
760 QStringList Dvr::GetRecGroupList()
761 {
762  MSqlQuery query(MSqlQuery::InitCon());
763  query.prepare("SELECT recgroup FROM recgroups WHERE recgroup <> 'Deleted' "
764  "ORDER BY recgroup");
765 
766  QStringList result;
767  if (!query.exec())
768  {
769  MythDB::DBError("GetRecGroupList", query);
770  return result;
771  }
772 
773  while (query.next())
774  result << query.value(0).toString();
775 
776  return result;
777 }
778 
780 //
782 
783 QStringList Dvr::GetProgramCategories( bool OnlyRecorded )
784 {
785  MSqlQuery query(MSqlQuery::InitCon());
786 
787  if (OnlyRecorded)
788  query.prepare("SELECT DISTINCT category FROM recorded ORDER BY category");
789  else
790  query.prepare("SELECT DISTINCT category FROM program ORDER BY category");
791 
792  QStringList result;
793  if (!query.exec())
794  {
795  MythDB::DBError("GetProgramCategories", query);
796  return result;
797  }
798 
799  while (query.next())
800  result << query.value(0).toString();
801 
802  return result;
803 }
804 
806 //
808 
810 {
812 }
813 
815 //
817 
819 {
820  return PlayGroup::GetNames();
821 }
822 
824 //
826 
828 {
829  auto* filterList = new DTC::RecRuleFilterList();
830 
831  MSqlQuery query(MSqlQuery::InitCon());
832 
833  query.prepare("SELECT filterid, description, newruledefault "
834  "FROM recordfilter ORDER BY filterid");
835 
836  if (query.exec())
837  {
838  while (query.next())
839  {
840  DTC::RecRuleFilter* ruleFilter = filterList->AddNewRecRuleFilter();
841  ruleFilter->setId(query.value(0).toInt());
842  ruleFilter->setDescription(QObject::tr(query.value(1).toString()
843  .toUtf8().constData()));
844  }
845  }
846 
847  return filterList;
848 }
849 
851 //
853 
854 QStringList Dvr::GetTitleList(const QString& RecGroup)
855 {
856  MSqlQuery query(MSqlQuery::InitCon());
857 
858  QString querystr = "SELECT DISTINCT title FROM recorded "
859  "WHERE deletepending = 0";
860 
861  if (!RecGroup.isEmpty())
862  querystr += " AND recgroup = :RECGROUP";
863  else
864  querystr += " AND recgroup != 'Deleted'";
865 
866  querystr += " ORDER BY title";
867 
868  query.prepare(querystr);
869 
870  if (!RecGroup.isEmpty())
871  query.bindValue(":RECGROUP", RecGroup);
872 
873  QStringList result;
874  if (!query.exec())
875  {
876  MythDB::DBError("GetTitleList recorded", query);
877  return result;
878  }
879 
880  while (query.next())
881  result << query.value(0).toString();
882 
883  return result;
884 }
885 
887 //
889 
891 {
892  MSqlQuery query(MSqlQuery::InitCon());
893 
894  QString querystr = QString(
895  "SELECT title, inetref, count(title) as count "
896  " FROM recorded AS r "
897  " JOIN recgroups AS g ON r.recgroupid = g.recgroupid "
898  " WHERE g.recgroup NOT IN ('Deleted', 'LiveTV') "
899  " AND r.deletepending = 0 "
900  " GROUP BY title, inetref "
901  " ORDER BY title");
902 
903  query.prepare(querystr);
904 
905  auto *pTitleInfos = new DTC::TitleInfoList();
906  if (!query.exec())
907  {
908  MythDB::DBError("GetTitleList recorded", query);
909  return pTitleInfos;
910  }
911 
912  while (query.next())
913  {
914  DTC::TitleInfo *pTitleInfo = pTitleInfos->AddNewTitleInfo();
915 
916  pTitleInfo->setTitle(query.value(0).toString());
917  pTitleInfo->setInetref(query.value(1).toString());
918  pTitleInfo->setCount(query.value(2).toInt());
919  }
920 
921  return pTitleInfos;
922 }
923 
925 //
927 
929  int nCount,
930  bool bShowAll,
931  int nRecordId,
932  int nRecStatus )
933 {
934  RecordingList recordingList; // Auto-delete deque
935  RecList tmpList; // Standard deque, objects must be deleted
936 
937  if (nRecordId <= 0)
938  nRecordId = -1;
939 
940  // NOTE: Fetching this information directly from the schedule is
941  // significantly faster than using ProgramInfo::LoadFromScheduler()
942  auto *scheduler = dynamic_cast<Scheduler*>(gCoreContext->GetScheduler());
943  if (scheduler)
944  scheduler->GetAllPending(tmpList, nRecordId);
945 
946  // Sort the upcoming into only those which will record
947  // NOLINTNEXTLINE(modernize-loop-convert)
948  for (auto it = tmpList.begin(); it < tmpList.end(); ++it)
949  {
950  if ((nRecStatus != 0) &&
951  ((*it)->GetRecordingStatus() != nRecStatus))
952  {
953  delete *it;
954  *it = nullptr;
955  continue;
956  }
957 
958  if (!bShowAll && ((((*it)->GetRecordingStatus() >= RecStatus::Pending) &&
959  ((*it)->GetRecordingStatus() <= RecStatus::WillRecord)) ||
960  ((*it)->GetRecordingStatus() == RecStatus::Recorded) ||
961  ((*it)->GetRecordingStatus() == RecStatus::Conflict)) &&
962  ((*it)->GetRecordingEndTime() > MythDate::current()))
963  { // NOLINT(bugprone-branch-clone)
964  recordingList.push_back(new RecordingInfo(**it));
965  }
966  else if (bShowAll &&
967  ((*it)->GetRecordingEndTime() > MythDate::current()))
968  {
969  recordingList.push_back(new RecordingInfo(**it));
970  }
971 
972  delete *it;
973  *it = nullptr;
974  }
975 
976  // ----------------------------------------------------------------------
977  // Build Response
978  // ----------------------------------------------------------------------
979 
980  auto *pPrograms = new DTC::ProgramList();
981 
982  nStartIndex = (nStartIndex > 0) ? min( nStartIndex, (int)recordingList.size() ) : 0;
983  nCount = (nCount > 0) ? min( nCount, (int)recordingList.size() ) : recordingList.size();
984  int nEndIndex = min((nStartIndex + nCount), (int)recordingList.size() );
985 
986  for( int n = nStartIndex; n < nEndIndex; n++)
987  {
988  ProgramInfo *pInfo = recordingList[ n ];
989 
990  DTC::Program *pProgram = pPrograms->AddNewProgram();
991 
992  FillProgramInfo( pProgram, pInfo, true );
993  }
994 
995  // ----------------------------------------------------------------------
996 
997  pPrograms->setStartIndex ( nStartIndex );
998  pPrograms->setCount ( nCount );
999  pPrograms->setTotalAvailable( recordingList.size() );
1000  pPrograms->setAsOf ( MythDate::current() );
1001  pPrograms->setVersion ( MYTH_BINARY_VERSION );
1002  pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
1003 
1004  return pPrograms;
1005 }
1006 
1008 //
1010 
1012  int nCount,
1013  int nRecordId )
1014 {
1015  RecordingList recordingList; // Auto-delete deque
1016  RecList tmpList; // Standard deque, objects must be deleted
1017 
1018  if (nRecordId <= 0)
1019  nRecordId = -1;
1020 
1021  // NOTE: Fetching this information directly from the schedule is
1022  // significantly faster than using ProgramInfo::LoadFromScheduler()
1023  auto *scheduler = dynamic_cast<Scheduler*>(gCoreContext->GetScheduler());
1024  if (scheduler)
1025  scheduler->GetAllPending(tmpList, nRecordId);
1026 
1027  // Sort the upcoming into only those which are conflicts
1028  // NOLINTNEXTLINE(modernize-loop-convert)
1029  for (auto it = tmpList.begin(); it < tmpList.end(); ++it)
1030  {
1031  if (((*it)->GetRecordingStatus() == RecStatus::Conflict) &&
1032  ((*it)->GetRecordingStartTime() >= MythDate::current()))
1033  {
1034  recordingList.push_back(new RecordingInfo(**it));
1035  }
1036  delete *it;
1037  *it = nullptr;
1038  }
1039 
1040  // ----------------------------------------------------------------------
1041  // Build Response
1042  // ----------------------------------------------------------------------
1043 
1044  auto *pPrograms = new DTC::ProgramList();
1045 
1046  nStartIndex = (nStartIndex > 0) ? min( nStartIndex, (int)recordingList.size() ) : 0;
1047  nCount = (nCount > 0) ? min( nCount, (int)recordingList.size() ) : recordingList.size();
1048  int nEndIndex = min((nStartIndex + nCount), (int)recordingList.size() );
1049 
1050  for( int n = nStartIndex; n < nEndIndex; n++)
1051  {
1052  ProgramInfo *pInfo = recordingList[ n ];
1053 
1054  DTC::Program *pProgram = pPrograms->AddNewProgram();
1055 
1056  FillProgramInfo( pProgram, pInfo, true );
1057  }
1058 
1059  // ----------------------------------------------------------------------
1060 
1061  pPrograms->setStartIndex ( nStartIndex );
1062  pPrograms->setCount ( nCount );
1063  pPrograms->setTotalAvailable( recordingList.size() );
1064  pPrograms->setAsOf ( MythDate::current() );
1065  pPrograms->setVersion ( MYTH_BINARY_VERSION );
1066  pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
1067 
1068  return pPrograms;
1069 }
1070 
1072  const QString& sTitle,
1073  const QString& sSubtitle,
1074  const QString& sDescription,
1075  const QString& sCategory,
1076  QDateTime recstarttsRaw,
1077  QDateTime recendtsRaw,
1078  const QString& sSeriesId,
1079  const QString& sProgramId,
1080  int nChanId,
1081  const QString& sStation,
1082  int nFindDay,
1083  QTime tFindTime,
1084  int nParentId,
1085  bool bInactive,
1086  uint nSeason,
1087  uint nEpisode,
1088  const QString& sInetref,
1089  QString sType,
1090  QString sSearchType,
1091  int nRecPriority,
1092  uint nPreferredInput,
1093  int nStartOffset,
1094  int nEndOffset,
1095  QString sDupMethod,
1096  QString sDupIn,
1097  uint nFilter,
1098  QString sRecProfile,
1099  QString sRecGroup,
1100  QString sStorageGroup,
1101  QString sPlayGroup,
1102  bool bAutoExpire,
1103  int nMaxEpisodes,
1104  bool bMaxNewest,
1105  bool bAutoCommflag,
1106  bool bAutoTranscode,
1107  bool bAutoMetaLookup,
1108  bool bAutoUserJob1,
1109  bool bAutoUserJob2,
1110  bool bAutoUserJob3,
1111  bool bAutoUserJob4,
1112  int nTranscoder)
1113 {
1114  QDateTime recstartts = recstarttsRaw.toUTC();
1115  QDateTime recendts = recendtsRaw.toUTC();
1116  RecordingRule rule;
1117  rule.LoadTemplate("Default");
1118 
1119  if (sType.isEmpty())
1120  sType = "single";
1121 
1122  if (sSearchType.isEmpty())
1123  sSearchType = "none";
1124 
1125  if (sDupMethod.isEmpty())
1126  sDupMethod = "subtitleanddescription";
1127 
1128  if (sDupIn.isEmpty())
1129  sDupIn = "all";
1130 
1131  rule.m_title = sTitle;
1132  rule.m_subtitle = sSubtitle;
1133  rule.m_description = sDescription;
1134 
1135  rule.m_startdate = recstartts.date();
1136  rule.m_starttime = recstartts.time();
1137  rule.m_enddate = recendts.date();
1138  rule.m_endtime = recendts.time();
1139 
1140  rule.m_type = recTypeFromString(sType);
1141  rule.m_searchType = searchTypeFromString(sSearchType);
1142  rule.m_dupMethod = dupMethodFromString(sDupMethod);
1143  rule.m_dupIn = dupInFromString(sDupIn);
1144 
1145  if (sRecProfile.isEmpty())
1146  sRecProfile = "Default";
1147 
1148  if (sRecGroup.isEmpty())
1149  sRecGroup = "Default";
1150 
1151  if (sStorageGroup.isEmpty())
1152  sStorageGroup = "Default";
1153 
1154  if (sPlayGroup.isEmpty())
1155  sPlayGroup = "Default";
1156 
1157  rule.m_category = sCategory;
1158  rule.m_seriesid = sSeriesId;
1159  rule.m_programid = sProgramId;
1160 
1161  rule.m_channelid = nChanId;
1162  rule.m_station = sStation;
1163 
1164  rule.m_findday = nFindDay;
1165  rule.m_findtime = tFindTime;
1166 
1167  rule.m_recProfile = sRecProfile;
1168  rule.m_recGroupID = RecordingInfo::GetRecgroupID(sRecGroup);
1169  if (rule.m_recGroupID == 0)
1171  rule.m_storageGroup = sStorageGroup;
1172  rule.m_playGroup = sPlayGroup;
1173 
1174  rule.m_parentRecID = nParentId;
1175  rule.m_isInactive = bInactive;
1176 
1177  rule.m_season = nSeason;
1178  rule.m_episode = nEpisode;
1179  rule.m_inetref = sInetref;
1180 
1181  rule.m_recPriority = nRecPriority;
1182  rule.m_prefInput = nPreferredInput;
1183  rule.m_startOffset = nStartOffset;
1184  rule.m_endOffset = nEndOffset;
1185  rule.m_filter = nFilter;
1186 
1187  rule.m_autoExpire = bAutoExpire;
1188  rule.m_maxEpisodes = nMaxEpisodes;
1189  rule.m_maxNewest = bMaxNewest;
1190 
1191  rule.m_autoCommFlag = bAutoCommflag;
1192  rule.m_autoTranscode = bAutoTranscode;
1193  rule.m_autoMetadataLookup = bAutoMetaLookup;
1194 
1195  rule.m_autoUserJob1 = bAutoUserJob1;
1196  rule.m_autoUserJob2 = bAutoUserJob2;
1197  rule.m_autoUserJob3 = bAutoUserJob3;
1198  rule.m_autoUserJob4 = bAutoUserJob4;
1199 
1200  rule.m_transcoder = nTranscoder;
1201 
1202  QString msg;
1203  if (!rule.IsValid(msg))
1204  throw msg;
1205 
1206  rule.Save();
1207 
1208  uint recid = rule.m_recordID;
1209 
1210  return recid;
1211 }
1212 
1214  QString sTitle,
1215  QString sSubtitle,
1216  QString sDescription,
1217  QString sCategory,
1218  QDateTime dStartTimeRaw,
1219  QDateTime dEndTimeRaw,
1220  QString sSeriesId,
1221  QString sProgramId,
1222  int nChanId,
1223  QString sStation,
1224  int nFindDay,
1225  QTime tFindTime,
1226  bool bInactive,
1227  uint nSeason,
1228  uint nEpisode,
1229  const QString& sInetref,
1230  QString sType,
1231  QString sSearchType,
1232  int nRecPriority,
1233  uint nPreferredInput,
1234  int nStartOffset,
1235  int nEndOffset,
1236  QString sDupMethod,
1237  QString sDupIn,
1238  uint nFilter,
1239  QString sRecProfile,
1240  QString sRecGroup,
1241  QString sStorageGroup,
1242  QString sPlayGroup,
1243  bool bAutoExpire,
1244  int nMaxEpisodes,
1245  bool bMaxNewest,
1246  bool bAutoCommflag,
1247  bool bAutoTranscode,
1248  bool bAutoMetaLookup,
1249  bool bAutoUserJob1,
1250  bool bAutoUserJob2,
1251  bool bAutoUserJob3,
1252  bool bAutoUserJob4,
1253  int nTranscoder)
1254 {
1255  if (nRecordId == 0 )
1256  throw QString("Record ID is invalid.");
1257 
1258  RecordingRule pRule;
1259  pRule.m_recordID = nRecordId;
1260  pRule.Load();
1261 
1262  if (!pRule.IsLoaded())
1263  throw QString("Record ID does not exist.");
1264 
1265  QDateTime recstartts = dStartTimeRaw.toUTC();
1266  QDateTime recendts = dEndTimeRaw.toUTC();
1267 
1268  pRule.m_isInactive = bInactive;
1269  if (sType.isEmpty())
1270  sType = "single";
1271 
1272  if (sSearchType.isEmpty())
1273  sSearchType = "none";
1274 
1275  if (sDupMethod.isEmpty())
1276  sDupMethod = "subtitleanddescription";
1277 
1278  if (sDupIn.isEmpty())
1279  sDupIn = "all";
1280 
1281  pRule.m_type = recTypeFromString(sType);
1282  pRule.m_searchType = searchTypeFromString(sSearchType);
1283  pRule.m_dupMethod = dupMethodFromString(sDupMethod);
1284  pRule.m_dupIn = dupInFromString(sDupIn);
1285 
1286  if (sRecProfile.isEmpty())
1287  sRecProfile = "Default";
1288 
1289  if (sRecGroup.isEmpty())
1290  sRecGroup = "Default";
1291 
1292  if (sStorageGroup.isEmpty())
1293  sStorageGroup = "Default";
1294 
1295  if (sPlayGroup.isEmpty())
1296  sPlayGroup = "Default";
1297 
1298  if (!sTitle.isEmpty())
1299  pRule.m_title = sTitle;
1300 
1301  if (!sSubtitle.isEmpty())
1302  pRule.m_subtitle = sSubtitle;
1303 
1304  if(!sDescription.isEmpty())
1305  pRule.m_description = sDescription;
1306 
1307  if (!sCategory.isEmpty())
1308  pRule.m_category = sCategory;
1309 
1310  if (!sSeriesId.isEmpty())
1311  pRule.m_seriesid = sSeriesId;
1312 
1313  if (!sProgramId.isEmpty())
1314  pRule.m_programid = sProgramId;
1315 
1316  if (nChanId)
1317  pRule.m_channelid = nChanId;
1318  if (!sStation.isEmpty())
1319  pRule.m_station = sStation;
1320 
1321  pRule.m_startdate = recstartts.date();
1322  pRule.m_starttime = recstartts.time();
1323  pRule.m_enddate = recendts.date();
1324  pRule.m_endtime = recendts.time();
1325 
1326  pRule.m_findday = nFindDay;
1327  pRule.m_findtime = tFindTime;
1328 
1329  pRule.m_recProfile = sRecProfile;
1330  pRule.m_recGroupID = RecordingInfo::GetRecgroupID(sRecGroup);
1331  if (pRule.m_recGroupID == 0)
1333  pRule.m_storageGroup = sStorageGroup;
1334  pRule.m_playGroup = sPlayGroup;
1335 
1336  pRule.m_isInactive = bInactive;
1337 
1338  pRule.m_season = nSeason;
1339  pRule.m_episode = nEpisode;
1340  pRule.m_inetref = sInetref;
1341 
1342  pRule.m_recPriority = nRecPriority;
1343  pRule.m_prefInput = nPreferredInput;
1344  pRule.m_startOffset = nStartOffset;
1345  pRule.m_endOffset = nEndOffset;
1346  pRule.m_filter = nFilter;
1347 
1348  pRule.m_autoExpire = bAutoExpire;
1349  pRule.m_maxEpisodes = nMaxEpisodes;
1350  pRule.m_maxNewest = bMaxNewest;
1351 
1352  pRule.m_autoCommFlag = bAutoCommflag;
1353  pRule.m_autoTranscode = bAutoTranscode;
1354  pRule.m_autoMetadataLookup = bAutoMetaLookup;
1355 
1356  pRule.m_autoUserJob1 = bAutoUserJob1;
1357  pRule.m_autoUserJob2 = bAutoUserJob2;
1358  pRule.m_autoUserJob3 = bAutoUserJob3;
1359  pRule.m_autoUserJob4 = bAutoUserJob4;
1360 
1361  pRule.m_transcoder = nTranscoder;
1362 
1363  QString msg;
1364  if (!pRule.IsValid(msg))
1365  throw msg;
1366 
1367  bool bResult = pRule.Save();
1368 
1369  return bResult;
1370 }
1371 
1373 {
1374  bool bResult = false;
1375 
1376  if (nRecordId == 0 )
1377  throw QString("Record ID does not exist.");
1378 
1379  RecordingRule pRule;
1380  pRule.m_recordID = nRecordId;
1381 
1382  bResult = pRule.Delete();
1383 
1384  return bResult;
1385 }
1386 
1387 bool Dvr::AddDontRecordSchedule(int nChanId, const QDateTime &dStartTime,
1388  bool bNeverRecord)
1389 {
1390  bool bResult = true;
1391 
1392  if (nChanId <= 0 || !dStartTime.isValid())
1393  throw QString("Program does not exist.");
1394 
1395  ProgramInfo *pi = LoadProgramFromProgram(nChanId, dStartTime.toUTC());
1396 
1397  if (!pi)
1398  throw QString("Program does not exist.");
1399 
1400  // Why RecordingInfo instead of ProgramInfo? Good question ...
1401  RecordingInfo recInfo = RecordingInfo(*pi);
1402 
1403  delete pi;
1404 
1405  if (bNeverRecord)
1406  {
1407  recInfo.ApplyNeverRecord();
1408  }
1409  else
1411 
1412  return bResult;
1413 }
1414 
1416  int nCount,
1417  const QString &Sort,
1418  bool Descending )
1419 {
1421  if (Sort.toLower() == "lastrecorded")
1422  sortingColumn = Scheduler::kSortLastRecorded;
1423  else if (Sort.toLower() == "nextrecording")
1424  sortingColumn = Scheduler::kSortNextRecording;
1425  else if (Sort.toLower() == "title")
1426  sortingColumn = Scheduler::kSortTitle; // NOLINT(bugprone-branch-clone)
1427  else if (Sort.toLower() == "priority")
1428  sortingColumn = Scheduler::kSortPriority;
1429  else if (Sort.toLower() == "type")
1430  sortingColumn = Scheduler::kSortType;
1431  else
1432  sortingColumn = Scheduler::kSortTitle;
1433 
1434  RecList recList;
1435  Scheduler::GetAllScheduled(recList, sortingColumn, !Descending);
1436 
1437  // ----------------------------------------------------------------------
1438  // Build Response
1439  // ----------------------------------------------------------------------
1440 
1441  auto *pRecRules = new DTC::RecRuleList();
1442 
1443  nStartIndex = (nStartIndex > 0) ? min( nStartIndex, (int)recList.size() ) : 0;
1444  nCount = (nCount > 0) ? min( nCount, (int)recList.size() ) : recList.size();
1445  int nEndIndex = min((nStartIndex + nCount), (int)recList.size() );
1446 
1447  for( int n = nStartIndex; n < nEndIndex; n++)
1448  {
1449  RecordingInfo *info = recList[n];
1450 
1451  if (info != nullptr)
1452  {
1453  DTC::RecRule *pRecRule = pRecRules->AddNewRecRule();
1454 
1455  FillRecRuleInfo( pRecRule, info->GetRecordingRule() );
1456  }
1457  }
1458 
1459  // ----------------------------------------------------------------------
1460 
1461  pRecRules->setStartIndex ( nStartIndex );
1462  pRecRules->setCount ( nCount );
1463  pRecRules->setTotalAvailable( recList.size() );
1464  pRecRules->setAsOf ( MythDate::current() );
1465  pRecRules->setVersion ( MYTH_BINARY_VERSION );
1466  pRecRules->setProtoVer ( MYTH_PROTO_VERSION );
1467 
1468  while (!recList.empty())
1469  {
1470  delete recList.back();
1471  recList.pop_back();
1472  }
1473 
1474  return pRecRules;
1475 }
1476 
1478  QString sTemplate,
1479  int nRecordedId,
1480  int nChanId,
1481  QDateTime dStartTimeRaw,
1482  bool bMakeOverride )
1483 {
1484  RecordingRule rule;
1485  QDateTime dStartTime = dStartTimeRaw.toUTC();
1486 
1487  if (nRecordId > 0)
1488  {
1489  rule.m_recordID = nRecordId;
1490  if (!rule.Load())
1491  throw QString("Record ID does not exist.");
1492  }
1493  else if (!sTemplate.isEmpty())
1494  {
1495  if (!rule.LoadTemplate(sTemplate))
1496  throw QString("Template does not exist.");
1497  }
1498  else if (nRecordedId > 0) // Loads from the Recorded/Recorded Program Table
1499  {
1500  // Despite the use of ProgramInfo, this only applies to Recordings.
1501  ProgramInfo recInfo(nRecordedId);
1502  if (!rule.LoadByProgram(&recInfo))
1503  throw QString("Recording does not exist");
1504  }
1505  else if (nChanId > 0 && dStartTime.isValid()) // Loads from Program Table, should NOT be used with recordings
1506  {
1507  // Despite the use of RecordingInfo, this only applies to programs in the
1508  // present or future, not to recordings? Confused yet?
1510  RecordingInfo info(nChanId, dStartTime, false, 0, &status);
1511  if (status != RecordingInfo::kFoundProgram)
1512  throw QString("Program does not exist.");
1513  RecordingRule *pRule = info.GetRecordingRule();
1514  if (bMakeOverride && rule.m_type != kSingleRecord &&
1515  rule.m_type != kOverrideRecord && rule.m_type != kDontRecord)
1516  pRule->MakeOverride();
1517  rule = *pRule;
1518  }
1519  else
1520  {
1521  throw QString("Invalid request.");
1522  }
1523 
1524  auto *pRecRule = new DTC::RecRule();
1525  FillRecRuleInfo( pRecRule, &rule );
1526 
1527  return pRecRule;
1528 }
1529 
1531 {
1532  bool bResult = false;
1533 
1534  if (nRecordId == 0 )
1535  throw QString("Record ID appears invalid.");
1536 
1537  RecordingRule pRule;
1538  pRule.m_recordID = nRecordId;
1539  pRule.Load();
1540 
1541  if (pRule.IsLoaded())
1542  {
1543  pRule.m_isInactive = false;
1544  bResult = pRule.Save();
1545  }
1546 
1547  return bResult;
1548 }
1549 
1551 {
1552  bool bResult = false;
1553 
1554  if (nRecordId == 0 )
1555  throw QString("Record ID appears invalid.");
1556 
1557  RecordingRule pRule;
1558  pRule.m_recordID = nRecordId;
1559  pRule.Load();
1560 
1561  if (pRule.IsLoaded())
1562  {
1563  pRule.m_isInactive = true;
1564  bResult = pRule.Save();
1565  }
1566 
1567  return bResult;
1568 }
1569 
1570 int Dvr::RecordedIdForKey(int chanid, const QDateTime &recstarttsRaw)
1571 {
1572  int recordedid = 0;
1573 
1574  if (!RecordingInfo::QueryRecordedIdForKey(recordedid, chanid,
1575  recstarttsRaw))
1576  return -1;
1577 
1578  return recordedid;
1579 }
1580 
1581 int Dvr::RecordedIdForPathname(const QString & pathname)
1582 {
1583  uint recordedid = 0;
1584 
1585  if (!ProgramInfo::QueryRecordedIdFromPathname(pathname, recordedid))
1586  return -1;
1587 
1588  return recordedid;
1589 }
1590 
1592 {
1593  auto type = static_cast<RecStatus::Type>(RecStatus);
1594  return RecStatus::toString(type);
1595 }
1596 
1597 QString Dvr::RecStatusToDescription(int RecStatus, int recType,
1598  const QDateTime &StartTime)
1599 {
1600  //if (!StartTime.isValid())
1601  // throw QString("StartTime appears invalid.");
1602  auto rsType = static_cast<RecStatus::Type>(RecStatus);
1603  auto recordingType = static_cast<RecordingType>(recType);
1604  return RecStatus::toDescription(rsType, recordingType, StartTime);
1605 }
1606 
1607 QString Dvr::RecTypeToString(QString recType)
1608 {
1609  bool ok = false;
1610  auto enumType = static_cast<RecordingType>(recType.toInt(&ok, 10));
1611  if (ok)
1612  return toString(enumType);
1613  // RecordingType type = static_cast<RecordingType>(recType);
1614  return toString(recTypeFromString(recType));
1615 }
1616 
1617 QString Dvr::RecTypeToDescription(QString recType)
1618 {
1619  bool ok = false;
1620  auto enumType = static_cast<RecordingType>(recType.toInt(&ok, 10));
1621  if (ok)
1622  return toDescription(enumType);
1623  // RecordingType type = static_cast<RecordingType>(recType);
1624  return toDescription(recTypeFromString(recType));
1625 }
1626 
1627 QString Dvr::DupInToString(QString DupIn)
1628 {
1629  // RecordingDupInType type= static_cast<RecordingDupInType>(DupIn);
1630  // return toString(type);
1631  return toString(dupInFromString(DupIn));
1632 }
1633 
1634 QString Dvr::DupInToDescription(QString DupIn)
1635 {
1636  // RecordingDupInType type= static_cast<RecordingDupInType>(DupIn);
1637  //return toDescription(type);
1638  return toDescription(dupInFromString(DupIn));
1639 }
1640 
1641 QString Dvr::DupMethodToString(QString DupMethod)
1642 {
1643  // RecordingDupMethodType method = static_cast<RecordingDupMethodType>(DupMethod);
1644  return toString(dupMethodFromString(DupMethod));
1645 }
1646 
1647 QString Dvr::DupMethodToDescription(QString DupMethod)
1648 {
1649  // RecordingDupMethodType method = static_cast<RecordingDupMethodType>(DupMethod);
1650  return toDescription(dupMethodFromString(DupMethod));
1651 }
1652 
1654 //
1656 
1657 int Dvr::ManageJobQueue( const QString &sAction,
1658  const QString &sJobName,
1659  int nJobId,
1660  int nRecordedId,
1661  QDateTime jobstarttsRaw,
1662  QString sRemoteHost,
1663  QString sJobArgs )
1664 {
1665  int nReturn = -1;
1666 
1667  if (!m_parsedParams.contains("jobname") &&
1668  !m_parsedParams.contains("recordedid") )
1669  {
1670  LOG(VB_GENERAL, LOG_ERR, "JobName and RecordedId are required.");
1671  return nReturn;
1672  }
1673 
1674  if (sRemoteHost.isEmpty())
1675  sRemoteHost = gCoreContext->GetHostName();
1676 
1677  int jobType = JobQueue::GetJobTypeFromName(sJobName);
1678 
1679  if (jobType == JOB_NONE)
1680  return nReturn;
1681 
1682  RecordingInfo ri = RecordingInfo(nRecordedId);
1683 
1684  if (!ri.GetChanID())
1685  return nReturn;
1686 
1687  if ( sAction == "Remove")
1688  {
1689  if (!m_parsedParams.contains("jobid") || nJobId < 0)
1690  {
1691  LOG(VB_GENERAL, LOG_ERR, "For Remove, a valid JobId is required.");
1692  return nReturn;
1693  }
1694 
1695  if (!JobQueue::SafeDeleteJob(nJobId, jobType, ri.GetChanID(),
1696  ri.GetRecordingStartTime()))
1697  return nReturn;
1698 
1699  return nJobId;
1700  }
1701 
1702  if ( sAction != "Add")
1703  {
1704  LOG(VB_GENERAL, LOG_ERR, QString("Illegal Action name '%1'. Use: Add, "
1705  "or Remove").arg(sAction));
1706  return nReturn;
1707  }
1708 
1709  if (((jobType & JOB_USERJOB) != 0) &&
1710  gCoreContext->GetSetting(sJobName, "").isEmpty())
1711  {
1712  LOG(VB_GENERAL, LOG_ERR, QString("%1 hasn't been defined.")
1713  .arg(sJobName));
1714  return nReturn;
1715  }
1716 
1717  if (!gCoreContext->GetBoolSettingOnHost(QString("JobAllow%1").arg(sJobName),
1718  sRemoteHost, false))
1719  {
1720  LOG(VB_GENERAL, LOG_ERR, QString("%1 hasn't been allowed on host %2.")
1721  .arg(sJobName).arg(sRemoteHost));
1722  return nReturn;
1723  }
1724 
1725  if (!jobstarttsRaw.isValid())
1726  jobstarttsRaw = QDateTime::currentDateTime();
1727 
1728  if (!JobQueue::InJobRunWindow(jobstarttsRaw))
1729  return nReturn;
1730 
1731  if (sJobArgs.isNull())
1732  sJobArgs = "";
1733 
1734  bool bReturn = JobQueue::QueueJob(jobType,
1735  ri.GetChanID(),
1736  ri.GetRecordingStartTime(),
1737  sJobArgs,
1738  QString("Dvr/ManageJobQueue"), // comment col.
1739  sRemoteHost,
1740  JOB_NO_FLAGS,
1741  JOB_QUEUED,
1742  jobstarttsRaw.toUTC());
1743 
1744  if (!bReturn)
1745  {
1746  LOG(VB_GENERAL, LOG_ERR, QString("%1 job wasn't queued because of a "
1747  "database error or because it was "
1748  "already running/stopping etc.")
1749  .arg(sJobName));
1750 
1751  return nReturn;
1752  }
1753 
1754  return JobQueue::GetJobID(jobType, ri.GetChanID(),
1755  ri.GetRecordingStartTime());
1756 }
bool QueryKeyFrameDuration(uint64_t *duration, uint64_t keyframe, bool backwards) const
QString RecStatusToDescription(int RecStatus, int RecType, const QDateTime &StartTime) override
Definition: dvr.cpp:1597
QString m_subtitle
Definition: recordingrule.h:81
QTime m_findtime
Time for timeslot rules.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
bool UnDeleteRecording(int RecordedId, int ChanId, const QDateTime &recstarttsRaw) override
Definition: dvr.cpp:333
MythScheduler * GetScheduler(void)
bool m_isInactive
Recording rule is enabled?
Definition: recordingrule.h:76
DTC::ProgramList * GetUpcomingList(int StartIndex, int Count, bool ShowAll, int RecordId, int RecStatus) override
Definition: dvr.cpp:928
QString DupInToString(QString DupIn) override
Definition: dvr.cpp:1627
QString RecStatusToString(int RecStatus) override
Definition: dvr.cpp:1591
bool RescheduleRecordings(void) override
Definition: dvr.cpp:416
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
AutoExpire * expirer
void ReactivateRecording(void)
Asks the scheduler to restart this recording if possible.
QString m_programid
Definition: recordingrule.h:94
SchedSortColumn
Definition: scheduler.h:85
ProgramInfo * LoadProgramFromProgram(const uint chanid, const QDateTime &starttime)
Watching LiveTV is the state for when we are watching a recording and the user has control over the c...
Definition: tv.h:63
void push_back(T info)
QString toDescription(RecordingType rectype)
Converts "rectype" into a human readable description.
bool LoadByProgram(const ProgramInfo *proginfo)
void FillProgramInfo(DTC::Program *pProgram, ProgramInfo *pInfo, bool bIncChannel, bool bDetails, bool bIncCast)
Definition: serviceUtil.cpp:44
bool SetSavedBookmark(int RecordedId, int ChanId, const QDateTime &recstarttsRaw, const QString &OffsetType, long Offset) override
Definition: dvr.cpp:495
DTC::CutList * GetRecordedSeek(int RecordedId, const QString &OffsetType) override
Definition: dvr.cpp:599
bool Delete(bool sendSig=true)
bool UpdateRecordSchedule(uint RecordId, QString Title, QString Subtitle, QString Description, QString Category, QDateTime dStartTimeRaw, QDateTime dEndTimeRaw, QString SeriesId, QString ProgramId, int ChanId, QString Station, int FindDay, QTime FindTime, bool Inactive, uint Season, uint Episode, const QString &Inetref, QString Type, QString SearchType, int RecPriority, uint PreferredInput, int StartOffset, int EndOffset, QString DupMethod, QString DupIn, uint Filter, QString RecProfile, QString RecGroup, QString StorageGroup, QString PlayGroup, bool AutoExpire, int MaxEpisodes, bool MaxNewest, bool AutoCommflag, bool AutoTranscode, bool AutoMetaLookup, bool AutoUserJob1, bool AutoUserJob2, bool AutoUserJob3, bool AutoUserJob4, int Transcoder) override
Definition: dvr.cpp:1213
bool DisableRecordSchedule(uint RecordId) override
Definition: dvr.cpp:1550
QString toString(MarkTypes type)
QStringList GetProgramCategories(bool OnlyRecorded) override
Definition: dvr.cpp:783
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
bool QueryPositionKeyFrame(uint64_t *keyframe, uint64_t position, bool backwards) const
bool DeleteRecording(int RecordedId, int ChanId, const QDateTime &recstarttsRaw, bool ForceDelete, bool AllowRerecord) override
Definition: dvr.cpp:298
void FillRecRuleInfo(DTC::RecRule *pRecRule, RecordingRule *pRule)
RecordingRule * GetRecordingRule(void)
Returns the "record" field, creating it if necessary.
RecordingDupInType dupInFromString(const QString &type)
DTC::EncoderList * GetEncoderList() override
Definition: dvr.cpp:677
QString m_storageGroup
static QStringList getRecordingsGroups(void)
bool StopRecording(int RecordedId) override
Definition: dvr.cpp:364
QString m_station
QString DupMethodToDescription(QString DupMethod) override
Definition: dvr.cpp:1647
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
RecSearchType searchTypeFromString(const QString &type)
AutoDeleteDeque< ProgramInfo * > ProgramList
Definition: programinfo.h:31
QStringList GetRecGroupList() override
Definition: dvr.cpp:760
DTC::ProgramList * GetExpiringList(int StartIndex, int Count) override
Definition: dvr.cpp:629
QList< QString > m_parsedParams
Definition: service.h:67
Watching Recording is the state for when we are watching an in progress recording,...
Definition: tv.h:80
int m_recordID
Unique Recording Rule ID.
Definition: recordingrule.h:72
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
size_t size(void) const
DTC::ProgramList * GetRecordedList(bool Descending, int StartIndex, int Count, const QString &TitleRegEx, const QString &RecGroup, const QString &StorageGroup, const QString &Category, const QString &Sort) override
Definition: dvr.cpp:63
RecordingType m_type
RecordingDupMethodType m_dupMethod
long GetSavedBookmark(int RecordedId, int ChanId, const QDateTime &recstarttsRaw, const QString &OffsetType) override
Definition: dvr.cpp:456
QStringList GetRecStorageGroupList() override
Definition: dvr.cpp:809
int RecordedIdForPathname(const QString &pathname) override
Definition: dvr.cpp:1581
static uint GetRecgroupID(const QString &recGroup)
Temporary helper during transition from string to ID.
bool IsValid(QString &msg)
DTC::Program * GetRecorded(int RecordedId, int ChanId, const QDateTime &recstarttsRaw) override
Definition: dvr.cpp:265
bool GetBoolSettingOnHost(const QString &key, const QString &host, bool defaultval=false)
static bool SafeDeleteJob(int jobID, int jobType, int chanid, const QDateTime &recstartts)
Definition: jobqueue.cpp:880
QVariant value(int i) const
Definition: mythdbcon.h:198
bool Save(bool sendSig=true)
MarkTypes
Definition: programtypes.h:48
Holds information on recordings and videos.
Definition: programinfo.h:67
void ApplyRecordStateChange(RecordingType newstate, bool save=true)
Sets RecordingType of "record", creating "record" if it does not exist.
Recording Only is a TVRec only state for when we are recording a program, but there is no one current...
Definition: tv.h:84
QString DupMethodToString(QString DupMethod) override
Definition: dvr.cpp:1641
static QReadWriteLock s_inputsLock
Definition: tv_rec.h:426
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:98
This class is used as a container for messages.
Definition: mythevent.h:16
DTC::RecRule * GetRecordSchedule(uint RecordId, QString Template, int nRecordedId, int ChanId, QDateTime dStartTimeRaw, bool MakeOverride) override
Definition: dvr.cpp:1477
QString DupInToDescription(QString DupIn) override
Definition: dvr.cpp:1634
static void GetAllScheduled(QStringList &strList, SchedSortColumn sortBy=kSortTitle, bool ascending=true)
Returns all scheduled programs serialized into a QStringList.
Definition: scheduler.cpp:1817
bool EnableRecordSchedule(uint RecordId) override
Definition: dvr.cpp:1530
static QMap< QString, uint32_t > QueryInUseMap(void)
bool m_autoMetadataLookup
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QStringList GetPlayGroupList() override
Definition: dvr.cpp:818
QString GetSetting(const QString &key, const QString &defaultval="")
bool RemoveRecordSchedule(uint RecordId) override
Definition: dvr.cpp:1372
int m_findday
callsign?
static QString toDescription(Type recstatus, RecordingType rectype, const QDateTime &recstartts)
Converts "recstatus" into a long human readable description.
Definition: recStatus.cpp:187
QMap< int, EncoderLink * > tvList
int RecordedIdForKey(int ChanId, const QDateTime &recstarttsRaw) override
Definition: dvr.cpp:1570
QString m_playGroup
void FillCutList(DTC::CutList *pCutList, RecordingInfo *rInfo, int marktype)
DTC::InputList * GetInputList() override
Definition: dvr.cpp:742
#define MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:48
QString m_description
Definition: recordingrule.h:83
DTC::TitleInfoList * GetTitleInfoList() override
Definition: dvr.cpp:890
bool LoadTemplate(const QString &category, const QString &categoryType="Default")
void SaveBookmark(uint64_t frame)
TODO Move to RecordingInfo.
unsigned int uint
Definition: compat.h:140
Internal representation of a recording rule, mirrors the record table.
Definition: recordingrule.h:32
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
static QList< InputInfo > GetAllInputInfo()
Definition: cardutil.cpp:1664
DTC::CutList * GetRecordedCutList(int RecordedId, int ChanId, const QDateTime &recstarttsRaw, const QString &OffsetType) override
Definition: dvr.cpp:533
DTC::ProgramList * GetConflictList(int StartIndex, int Count, int RecordId) override
Definition: dvr.cpp:1011
static void RescheduleMatch(uint recordid, uint sourceid, uint mplexid, const QDateTime &maxstarttime, const QString &why)
uint64_t QueryBookmark(void) const
Gets any bookmark position in database, unless the ignore bookmark flag is set.
void GetAllExpiring(QStringList &strList)
Gets the full list of programs that can expire in expiration order.
Definition: autoexpire.cpp:831
bool UpdateRecordedWatchedStatus(int RecordedId, int ChanId, const QDateTime &recstarttsRaw, bool Watched) override
Definition: dvr.cpp:427
QString m_seriesid
Definition: recordingrule.h:93
DTC::ProgramList * GetOldRecordedList(bool Descending, int StartIndex, int Count, const QDateTime &StartTime, const QDateTime &EndTime, const QString &Title, const QString &SeriesId, int RecordId, const QString &Sort) override
Definition: dvr.cpp:148
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)
QString RecTypeToString(QString RecType) override
Definition: dvr.cpp:1607
RecordingType recTypeFromString(const QString &type)
RecordingDupInType m_dupIn
void dispatch(const MythEvent &event)
DTC::CutList * GetRecordedCommBreak(int RecordedId, int ChanId, const QDateTime &recstarttsRaw, const QString &OffsetType) override
Definition: dvr.cpp:566
QString m_inetref
Definition: recordingrule.h:96
void FillSeek(DTC::CutList *pCutList, RecordingInfo *rInfo, MarkTypes marktype)
QStringList GetTitleList(const QString &RecGroup) override
Definition: dvr.cpp:854
static bool QueryRecordedIdFromPathname(const QString &pathname, uint &recordedid)
void FillCommBreak(DTC::CutList *pCutList, RecordingInfo *rInfo, int marktype)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
bool IsLoaded() const
Definition: recordingrule.h:57
bool QueryDurationKeyFrame(uint64_t *keyframe, uint64_t duration, bool backwards) const
QString m_title
Definition: recordingrule.h:79
vector< ProgramInfo * > pginfolist_t
Definition: autoexpire.h:25
int ManageJobQueue(const QString &Action, const QString &JobName, int JobId, int RecordedId, QDateTime jobstarttsRaw, QString RemoteHost, QString JobArgs) override
Definition: dvr.cpp:1657
Used to expire recordings to make space for new recordings.
Definition: autoexpire.h:61
std::deque< RecordingInfo * > RecList
Definition: mythscheduler.h:12
bool ReactivateRecording(int RecordedId, int ChanId, const QDateTime &recstarttsRaw) override
Definition: dvr.cpp:390
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:366
void ApplyNeverRecord(void)
Set this program to never be recorded by inserting 'history' for it into the database with a status o...
static QMap< QString, bool > QueryJobsRunning(int type)
QString m_recProfile
static int GetJobTypeFromName(const QString &name)
Definition: jobqueue.cpp:707
DTC::RecRuleFilterList * GetRecRuleFilterList() override
Definition: dvr.cpp:827
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:16
void SaveWatched(bool watchedFlag)
Set "watched" field in recorded/videometadata to "watchedFlag".
static QStringList GetNames(void)
Definition: playgroup.cpp:206
virtual QMap< QString, ProgramInfo * > GetRecording(void) const =0
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:398
bool LoadFromOldRecorded(ProgramList &destination, const QString &sql, const MSqlBindings &bindings)
bool Load(bool asTemplate=false)
Load a single rule from the recorded table.
unsigned m_filter
QString m_category
Definition: recordingrule.h:86
uint AddRecordSchedule(const QString &Title, const QString &Subtitle, const QString &Description, const QString &Category, QDateTime recstarttsRaw, QDateTime recendtsRaw, const QString &SeriesId, const QString &ProgramId, int ChanId, const QString &Station, int FindDay, QTime FindTime, int ParentId, bool Inactive, uint Season, uint Episode, const QString &Inetref, QString Type, QString SearchType, int RecPriority, uint PreferredInput, int StartOffset, int EndOffset, QString DupMethod, QString DupIn, uint Filter, QString RecProfile, QString RecGroup, QString StorageGroup, QString PlayGroup, bool AutoExpire, int MaxEpisodes, bool MaxNewest, bool AutoCommflag, bool AutoTranscode, bool AutoMetaLookup, bool AutoUserJob1, bool AutoUserJob2, bool AutoUserJob3, bool AutoUserJob4, int Transcoder) override
Definition: dvr.cpp:1071
RecordingDupMethodType dupMethodFromString(const QString &type)
void FillInputInfo(DTC::Input *input, const InputInfo &inputInfo)
bool RemoveRecorded(int RecordedId, int ChanId, const QDateTime &recstarttsRaw, bool ForceDelete, bool AllowRerecord) override
Definition: dvr.cpp:289
Default UTC.
Definition: mythdate.h:14
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
QString RecTypeToDescription(QString RecType) override
Definition: dvr.cpp:1617
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
DTC::RecRuleList * GetRecordScheduleList(int StartIndex, int Count, const QString &Sort, bool Descending) override
Definition: dvr.cpp:1415
bool MakeOverride(void)
static int GetJobID(int jobType, uint chanid, const QDateTime &recstartts)
Definition: jobqueue.cpp:647
static bool InJobRunWindow(QDateTime jobstarttsRaw)
Definition: jobqueue.cpp:1654
QString GetHostName(void)
static QString toString(Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
Definition: recStatus.cpp:39
static bool QueueJob(int jobType, uint chanid, const QDateTime &recstartts, const QString &args="", const QString &comment="", QString host="", int flags=0, int status=JOB_QUEUED, QDateTime schedruntime=QDateTime())
Definition: jobqueue.cpp:517
static bool QueryRecordedIdForKey(int &recordedid, uint chanid, const QDateTime &recstartts)
bool HasPathname(void) const
Definition: programinfo.h:352
bool QueryKeyFramePosition(uint64_t *position, uint64_t keyframe, bool backwards) const
RecSearchType m_searchType
bool AddDontRecordSchedule(int ChanId, const QDateTime &StartTime, bool NeverRecord) override
Definition: dvr.cpp:1387