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  QDateTime lastrectsRaw,
1096  QString sDupMethod,
1097  QString sDupIn,
1098  uint nFilter,
1099  QString sRecProfile,
1100  QString sRecGroup,
1101  QString sStorageGroup,
1102  QString sPlayGroup,
1103  bool bAutoExpire,
1104  int nMaxEpisodes,
1105  bool bMaxNewest,
1106  bool bAutoCommflag,
1107  bool bAutoTranscode,
1108  bool bAutoMetaLookup,
1109  bool bAutoUserJob1,
1110  bool bAutoUserJob2,
1111  bool bAutoUserJob3,
1112  bool bAutoUserJob4,
1113  int nTranscoder)
1114 {
1115  QDateTime recstartts = recstarttsRaw.toUTC();
1116  QDateTime recendts = recendtsRaw.toUTC();
1117  QDateTime lastrects = lastrectsRaw.toUTC();
1118  RecordingRule rule;
1119  rule.LoadTemplate("Default");
1120 
1121  if (sType.isEmpty())
1122  sType = "single";
1123 
1124  if (sSearchType.isEmpty())
1125  sSearchType = "none";
1126 
1127  if (sDupMethod.isEmpty())
1128  sDupMethod = "subtitleanddescription";
1129 
1130  if (sDupIn.isEmpty())
1131  sDupIn = "all";
1132 
1133  rule.m_title = sTitle;
1134  rule.m_subtitle = sSubtitle;
1135  rule.m_description = sDescription;
1136 
1137  rule.m_startdate = recstartts.date();
1138  rule.m_starttime = recstartts.time();
1139  rule.m_enddate = recendts.date();
1140  rule.m_endtime = recendts.time();
1141 
1142  rule.m_type = recTypeFromString(sType);
1143  rule.m_searchType = searchTypeFromString(sSearchType);
1144  rule.m_dupMethod = dupMethodFromString(sDupMethod);
1145  rule.m_dupIn = dupInFromString(sDupIn);
1146 
1147  if (sRecProfile.isEmpty())
1148  sRecProfile = "Default";
1149 
1150  if (sRecGroup.isEmpty())
1151  sRecGroup = "Default";
1152 
1153  if (sStorageGroup.isEmpty())
1154  sStorageGroup = "Default";
1155 
1156  if (sPlayGroup.isEmpty())
1157  sPlayGroup = "Default";
1158 
1159  rule.m_category = sCategory;
1160  rule.m_seriesid = sSeriesId;
1161  rule.m_programid = sProgramId;
1162 
1163  rule.m_channelid = nChanId;
1164  rule.m_station = sStation;
1165 
1166  rule.m_findday = nFindDay;
1167  rule.m_findtime = tFindTime;
1168 
1169  rule.m_recProfile = sRecProfile;
1170  rule.m_recGroupID = RecordingInfo::GetRecgroupID(sRecGroup);
1171  if (rule.m_recGroupID == 0)
1173  rule.m_storageGroup = sStorageGroup;
1174  rule.m_playGroup = sPlayGroup;
1175 
1176  rule.m_parentRecID = nParentId;
1177  rule.m_isInactive = bInactive;
1178 
1179  rule.m_season = nSeason;
1180  rule.m_episode = nEpisode;
1181  rule.m_inetref = sInetref;
1182 
1183  rule.m_recPriority = nRecPriority;
1184  rule.m_prefInput = nPreferredInput;
1185  rule.m_startOffset = nStartOffset;
1186  rule.m_endOffset = nEndOffset;
1187  rule.m_filter = nFilter;
1188 
1189  rule.m_autoExpire = bAutoExpire;
1190  rule.m_maxEpisodes = nMaxEpisodes;
1191  rule.m_maxNewest = bMaxNewest;
1192 
1193  rule.m_autoCommFlag = bAutoCommflag;
1194  rule.m_autoTranscode = bAutoTranscode;
1195  rule.m_autoMetadataLookup = bAutoMetaLookup;
1196 
1197  rule.m_autoUserJob1 = bAutoUserJob1;
1198  rule.m_autoUserJob2 = bAutoUserJob2;
1199  rule.m_autoUserJob3 = bAutoUserJob3;
1200  rule.m_autoUserJob4 = bAutoUserJob4;
1201 
1202  rule.m_transcoder = nTranscoder;
1203 
1204  rule.m_lastRecorded = lastrects;
1205 
1206  QString msg;
1207  if (!rule.IsValid(msg))
1208  throw msg;
1209 
1210  rule.Save();
1211 
1212  uint recid = rule.m_recordID;
1213 
1214  return recid;
1215 }
1216 
1218  QString sTitle,
1219  QString sSubtitle,
1220  QString sDescription,
1221  QString sCategory,
1222  QDateTime dStartTimeRaw,
1223  QDateTime dEndTimeRaw,
1224  QString sSeriesId,
1225  QString sProgramId,
1226  int nChanId,
1227  QString sStation,
1228  int nFindDay,
1229  QTime tFindTime,
1230  bool bInactive,
1231  uint nSeason,
1232  uint nEpisode,
1233  const QString& sInetref,
1234  QString sType,
1235  QString sSearchType,
1236  int nRecPriority,
1237  uint nPreferredInput,
1238  int nStartOffset,
1239  int nEndOffset,
1240  QString sDupMethod,
1241  QString sDupIn,
1242  uint nFilter,
1243  QString sRecProfile,
1244  QString sRecGroup,
1245  QString sStorageGroup,
1246  QString sPlayGroup,
1247  bool bAutoExpire,
1248  int nMaxEpisodes,
1249  bool bMaxNewest,
1250  bool bAutoCommflag,
1251  bool bAutoTranscode,
1252  bool bAutoMetaLookup,
1253  bool bAutoUserJob1,
1254  bool bAutoUserJob2,
1255  bool bAutoUserJob3,
1256  bool bAutoUserJob4,
1257  int nTranscoder)
1258 {
1259  if (nRecordId == 0 )
1260  throw QString("Record ID is invalid.");
1261 
1262  RecordingRule pRule;
1263  pRule.m_recordID = nRecordId;
1264  pRule.Load();
1265 
1266  if (!pRule.IsLoaded())
1267  throw QString("Record ID does not exist.");
1268 
1269  QDateTime recstartts = dStartTimeRaw.toUTC();
1270  QDateTime recendts = dEndTimeRaw.toUTC();
1271 
1272  pRule.m_isInactive = bInactive;
1273  if (sType.isEmpty())
1274  sType = "single";
1275 
1276  if (sSearchType.isEmpty())
1277  sSearchType = "none";
1278 
1279  if (sDupMethod.isEmpty())
1280  sDupMethod = "subtitleanddescription";
1281 
1282  if (sDupIn.isEmpty())
1283  sDupIn = "all";
1284 
1285  pRule.m_type = recTypeFromString(sType);
1286  pRule.m_searchType = searchTypeFromString(sSearchType);
1287  pRule.m_dupMethod = dupMethodFromString(sDupMethod);
1288  pRule.m_dupIn = dupInFromString(sDupIn);
1289 
1290  if (sRecProfile.isEmpty())
1291  sRecProfile = "Default";
1292 
1293  if (sRecGroup.isEmpty())
1294  sRecGroup = "Default";
1295 
1296  if (sStorageGroup.isEmpty())
1297  sStorageGroup = "Default";
1298 
1299  if (sPlayGroup.isEmpty())
1300  sPlayGroup = "Default";
1301 
1302  if (!sTitle.isEmpty())
1303  pRule.m_title = sTitle;
1304 
1305  if (!sSubtitle.isEmpty())
1306  pRule.m_subtitle = sSubtitle;
1307 
1308  if(!sDescription.isEmpty())
1309  pRule.m_description = sDescription;
1310 
1311  if (!sCategory.isEmpty())
1312  pRule.m_category = sCategory;
1313 
1314  if (!sSeriesId.isEmpty())
1315  pRule.m_seriesid = sSeriesId;
1316 
1317  if (!sProgramId.isEmpty())
1318  pRule.m_programid = sProgramId;
1319 
1320  if (nChanId)
1321  pRule.m_channelid = nChanId;
1322  if (!sStation.isEmpty())
1323  pRule.m_station = sStation;
1324 
1325  pRule.m_startdate = recstartts.date();
1326  pRule.m_starttime = recstartts.time();
1327  pRule.m_enddate = recendts.date();
1328  pRule.m_endtime = recendts.time();
1329 
1330  pRule.m_findday = nFindDay;
1331  pRule.m_findtime = tFindTime;
1332 
1333  pRule.m_recProfile = sRecProfile;
1334  pRule.m_recGroupID = RecordingInfo::GetRecgroupID(sRecGroup);
1335  if (pRule.m_recGroupID == 0)
1337  pRule.m_storageGroup = sStorageGroup;
1338  pRule.m_playGroup = sPlayGroup;
1339 
1340  pRule.m_isInactive = bInactive;
1341 
1342  pRule.m_season = nSeason;
1343  pRule.m_episode = nEpisode;
1344  pRule.m_inetref = sInetref;
1345 
1346  pRule.m_recPriority = nRecPriority;
1347  pRule.m_prefInput = nPreferredInput;
1348  pRule.m_startOffset = nStartOffset;
1349  pRule.m_endOffset = nEndOffset;
1350  pRule.m_filter = nFilter;
1351 
1352  pRule.m_autoExpire = bAutoExpire;
1353  pRule.m_maxEpisodes = nMaxEpisodes;
1354  pRule.m_maxNewest = bMaxNewest;
1355 
1356  pRule.m_autoCommFlag = bAutoCommflag;
1357  pRule.m_autoTranscode = bAutoTranscode;
1358  pRule.m_autoMetadataLookup = bAutoMetaLookup;
1359 
1360  pRule.m_autoUserJob1 = bAutoUserJob1;
1361  pRule.m_autoUserJob2 = bAutoUserJob2;
1362  pRule.m_autoUserJob3 = bAutoUserJob3;
1363  pRule.m_autoUserJob4 = bAutoUserJob4;
1364 
1365  pRule.m_transcoder = nTranscoder;
1366 
1367  QString msg;
1368  if (!pRule.IsValid(msg))
1369  throw msg;
1370 
1371  bool bResult = pRule.Save();
1372 
1373  return bResult;
1374 }
1375 
1377 {
1378  bool bResult = false;
1379 
1380  if (nRecordId == 0 )
1381  throw QString("Record ID does not exist.");
1382 
1383  RecordingRule pRule;
1384  pRule.m_recordID = nRecordId;
1385 
1386  bResult = pRule.Delete();
1387 
1388  return bResult;
1389 }
1390 
1391 bool Dvr::AddDontRecordSchedule(int nChanId, const QDateTime &dStartTime,
1392  bool bNeverRecord)
1393 {
1394  bool bResult = true;
1395 
1396  if (nChanId <= 0 || !dStartTime.isValid())
1397  throw QString("Program does not exist.");
1398 
1399  ProgramInfo *pi = LoadProgramFromProgram(nChanId, dStartTime.toUTC());
1400 
1401  if (!pi)
1402  throw QString("Program does not exist.");
1403 
1404  // Why RecordingInfo instead of ProgramInfo? Good question ...
1405  RecordingInfo recInfo = RecordingInfo(*pi);
1406 
1407  delete pi;
1408 
1409  if (bNeverRecord)
1410  {
1411  recInfo.ApplyNeverRecord();
1412  }
1413  else
1415 
1416  return bResult;
1417 }
1418 
1420  int nCount,
1421  const QString &Sort,
1422  bool Descending )
1423 {
1425  if (Sort.toLower() == "lastrecorded")
1426  sortingColumn = Scheduler::kSortLastRecorded;
1427  else if (Sort.toLower() == "nextrecording")
1428  sortingColumn = Scheduler::kSortNextRecording;
1429  else if (Sort.toLower() == "title")
1430  sortingColumn = Scheduler::kSortTitle; // NOLINT(bugprone-branch-clone)
1431  else if (Sort.toLower() == "priority")
1432  sortingColumn = Scheduler::kSortPriority;
1433  else if (Sort.toLower() == "type")
1434  sortingColumn = Scheduler::kSortType;
1435  else
1436  sortingColumn = Scheduler::kSortTitle;
1437 
1438  RecList recList;
1439  Scheduler::GetAllScheduled(recList, sortingColumn, !Descending);
1440 
1441  // ----------------------------------------------------------------------
1442  // Build Response
1443  // ----------------------------------------------------------------------
1444 
1445  auto *pRecRules = new DTC::RecRuleList();
1446 
1447  nStartIndex = (nStartIndex > 0) ? min( nStartIndex, (int)recList.size() ) : 0;
1448  nCount = (nCount > 0) ? min( nCount, (int)recList.size() ) : recList.size();
1449  int nEndIndex = min((nStartIndex + nCount), (int)recList.size() );
1450 
1451  for( int n = nStartIndex; n < nEndIndex; n++)
1452  {
1453  RecordingInfo *info = recList[n];
1454 
1455  if (info != nullptr)
1456  {
1457  DTC::RecRule *pRecRule = pRecRules->AddNewRecRule();
1458 
1459  FillRecRuleInfo( pRecRule, info->GetRecordingRule() );
1460  }
1461  }
1462 
1463  // ----------------------------------------------------------------------
1464 
1465  pRecRules->setStartIndex ( nStartIndex );
1466  pRecRules->setCount ( nCount );
1467  pRecRules->setTotalAvailable( recList.size() );
1468  pRecRules->setAsOf ( MythDate::current() );
1469  pRecRules->setVersion ( MYTH_BINARY_VERSION );
1470  pRecRules->setProtoVer ( MYTH_PROTO_VERSION );
1471 
1472  while (!recList.empty())
1473  {
1474  delete recList.back();
1475  recList.pop_back();
1476  }
1477 
1478  return pRecRules;
1479 }
1480 
1482  QString sTemplate,
1483  int nRecordedId,
1484  int nChanId,
1485  QDateTime dStartTimeRaw,
1486  bool bMakeOverride )
1487 {
1488  RecordingRule rule;
1489  QDateTime dStartTime = dStartTimeRaw.toUTC();
1490 
1491  if (nRecordId > 0)
1492  {
1493  rule.m_recordID = nRecordId;
1494  if (!rule.Load())
1495  throw QString("Record ID does not exist.");
1496  }
1497  else if (!sTemplate.isEmpty())
1498  {
1499  if (!rule.LoadTemplate(sTemplate))
1500  throw QString("Template does not exist.");
1501  }
1502  else if (nRecordedId > 0) // Loads from the Recorded/Recorded Program Table
1503  {
1504  // Despite the use of ProgramInfo, this only applies to Recordings.
1505  ProgramInfo recInfo(nRecordedId);
1506  if (!rule.LoadByProgram(&recInfo))
1507  throw QString("Recording does not exist");
1508  }
1509  else if (nChanId > 0 && dStartTime.isValid()) // Loads from Program Table, should NOT be used with recordings
1510  {
1511  // Despite the use of RecordingInfo, this only applies to programs in the
1512  // present or future, not to recordings? Confused yet?
1514  RecordingInfo info(nChanId, dStartTime, false, 0, &status);
1515  if (status != RecordingInfo::kFoundProgram)
1516  throw QString("Program does not exist.");
1517  RecordingRule *pRule = info.GetRecordingRule();
1518  if (bMakeOverride && rule.m_type != kSingleRecord &&
1519  rule.m_type != kOverrideRecord && rule.m_type != kDontRecord)
1520  pRule->MakeOverride();
1521  rule = *pRule;
1522  }
1523  else
1524  {
1525  throw QString("Invalid request.");
1526  }
1527 
1528  auto *pRecRule = new DTC::RecRule();
1529  FillRecRuleInfo( pRecRule, &rule );
1530 
1531  return pRecRule;
1532 }
1533 
1535 {
1536  bool bResult = false;
1537 
1538  if (nRecordId == 0 )
1539  throw QString("Record ID appears invalid.");
1540 
1541  RecordingRule pRule;
1542  pRule.m_recordID = nRecordId;
1543  pRule.Load();
1544 
1545  if (pRule.IsLoaded())
1546  {
1547  pRule.m_isInactive = false;
1548  bResult = pRule.Save();
1549  }
1550 
1551  return bResult;
1552 }
1553 
1555 {
1556  bool bResult = false;
1557 
1558  if (nRecordId == 0 )
1559  throw QString("Record ID appears invalid.");
1560 
1561  RecordingRule pRule;
1562  pRule.m_recordID = nRecordId;
1563  pRule.Load();
1564 
1565  if (pRule.IsLoaded())
1566  {
1567  pRule.m_isInactive = true;
1568  bResult = pRule.Save();
1569  }
1570 
1571  return bResult;
1572 }
1573 
1574 int Dvr::RecordedIdForKey(int chanid, const QDateTime &recstarttsRaw)
1575 {
1576  int recordedid = 0;
1577 
1578  if (!RecordingInfo::QueryRecordedIdForKey(recordedid, chanid,
1579  recstarttsRaw))
1580  return -1;
1581 
1582  return recordedid;
1583 }
1584 
1585 int Dvr::RecordedIdForPathname(const QString & pathname)
1586 {
1587  uint recordedid = 0;
1588 
1589  if (!ProgramInfo::QueryRecordedIdFromPathname(pathname, recordedid))
1590  return -1;
1591 
1592  return recordedid;
1593 }
1594 
1596 {
1597  auto type = static_cast<RecStatus::Type>(RecStatus);
1598  return RecStatus::toString(type);
1599 }
1600 
1601 QString Dvr::RecStatusToDescription(int RecStatus, int recType,
1602  const QDateTime &StartTime)
1603 {
1604  //if (!StartTime.isValid())
1605  // throw QString("StartTime appears invalid.");
1606  auto rsType = static_cast<RecStatus::Type>(RecStatus);
1607  auto recordingType = static_cast<RecordingType>(recType);
1608  return RecStatus::toDescription(rsType, recordingType, StartTime);
1609 }
1610 
1611 QString Dvr::RecTypeToString(QString recType)
1612 {
1613  bool ok = false;
1614  auto enumType = static_cast<RecordingType>(recType.toInt(&ok, 10));
1615  if (ok)
1616  return toString(enumType);
1617  // RecordingType type = static_cast<RecordingType>(recType);
1618  return toString(recTypeFromString(recType));
1619 }
1620 
1621 QString Dvr::RecTypeToDescription(QString recType)
1622 {
1623  bool ok = false;
1624  auto enumType = static_cast<RecordingType>(recType.toInt(&ok, 10));
1625  if (ok)
1626  return toDescription(enumType);
1627  // RecordingType type = static_cast<RecordingType>(recType);
1628  return toDescription(recTypeFromString(recType));
1629 }
1630 
1631 QString Dvr::DupInToString(QString DupIn)
1632 {
1633  // RecordingDupInType type= static_cast<RecordingDupInType>(DupIn);
1634  // return toString(type);
1635  return toString(dupInFromString(DupIn));
1636 }
1637 
1638 QString Dvr::DupInToDescription(QString DupIn)
1639 {
1640  // RecordingDupInType type= static_cast<RecordingDupInType>(DupIn);
1641  //return toDescription(type);
1642  return toDescription(dupInFromString(DupIn));
1643 }
1644 
1645 QString Dvr::DupMethodToString(QString DupMethod)
1646 {
1647  // RecordingDupMethodType method = static_cast<RecordingDupMethodType>(DupMethod);
1648  return toString(dupMethodFromString(DupMethod));
1649 }
1650 
1651 QString Dvr::DupMethodToDescription(QString DupMethod)
1652 {
1653  // RecordingDupMethodType method = static_cast<RecordingDupMethodType>(DupMethod);
1654  return toDescription(dupMethodFromString(DupMethod));
1655 }
1656 
1658 //
1660 
1661 int Dvr::ManageJobQueue( const QString &sAction,
1662  const QString &sJobName,
1663  int nJobId,
1664  int nRecordedId,
1665  QDateTime jobstarttsRaw,
1666  QString sRemoteHost,
1667  QString sJobArgs )
1668 {
1669  int nReturn = -1;
1670 
1671  if (!m_parsedParams.contains("jobname") &&
1672  !m_parsedParams.contains("recordedid") )
1673  {
1674  LOG(VB_GENERAL, LOG_ERR, "JobName and RecordedId are required.");
1675  return nReturn;
1676  }
1677 
1678  if (sRemoteHost.isEmpty())
1679  sRemoteHost = gCoreContext->GetHostName();
1680 
1681  int jobType = JobQueue::GetJobTypeFromName(sJobName);
1682 
1683  if (jobType == JOB_NONE)
1684  return nReturn;
1685 
1686  RecordingInfo ri = RecordingInfo(nRecordedId);
1687 
1688  if (!ri.GetChanID())
1689  return nReturn;
1690 
1691  if ( sAction == "Remove")
1692  {
1693  if (!m_parsedParams.contains("jobid") || nJobId < 0)
1694  {
1695  LOG(VB_GENERAL, LOG_ERR, "For Remove, a valid JobId is required.");
1696  return nReturn;
1697  }
1698 
1699  if (!JobQueue::SafeDeleteJob(nJobId, jobType, ri.GetChanID(),
1700  ri.GetRecordingStartTime()))
1701  return nReturn;
1702 
1703  return nJobId;
1704  }
1705 
1706  if ( sAction != "Add")
1707  {
1708  LOG(VB_GENERAL, LOG_ERR, QString("Illegal Action name '%1'. Use: Add, "
1709  "or Remove").arg(sAction));
1710  return nReturn;
1711  }
1712 
1713  if (((jobType & JOB_USERJOB) != 0) &&
1714  gCoreContext->GetSetting(sJobName, "").isEmpty())
1715  {
1716  LOG(VB_GENERAL, LOG_ERR, QString("%1 hasn't been defined.")
1717  .arg(sJobName));
1718  return nReturn;
1719  }
1720 
1721  if (!gCoreContext->GetBoolSettingOnHost(QString("JobAllow%1").arg(sJobName),
1722  sRemoteHost, false))
1723  {
1724  LOG(VB_GENERAL, LOG_ERR, QString("%1 hasn't been allowed on host %2.")
1725  .arg(sJobName).arg(sRemoteHost));
1726  return nReturn;
1727  }
1728 
1729  if (!jobstarttsRaw.isValid())
1730  jobstarttsRaw = QDateTime::currentDateTime();
1731 
1732  if (!JobQueue::InJobRunWindow(jobstarttsRaw))
1733  return nReturn;
1734 
1735  if (sJobArgs.isNull())
1736  sJobArgs = "";
1737 
1738  bool bReturn = JobQueue::QueueJob(jobType,
1739  ri.GetChanID(),
1740  ri.GetRecordingStartTime(),
1741  sJobArgs,
1742  QString("Dvr/ManageJobQueue"), // comment col.
1743  sRemoteHost,
1744  JOB_NO_FLAGS,
1745  JOB_QUEUED,
1746  jobstarttsRaw.toUTC());
1747 
1748  if (!bReturn)
1749  {
1750  LOG(VB_GENERAL, LOG_ERR, QString("%1 job wasn't queued because of a "
1751  "database error or because it was "
1752  "already running/stopping etc.")
1753  .arg(sJobName));
1754 
1755  return nReturn;
1756  }
1757 
1758  return JobQueue::GetJobID(jobType, ri.GetChanID(),
1759  ri.GetRecordingStartTime());
1760 }
bool QueryKeyFrameDuration(uint64_t *duration, uint64_t keyframe, bool backwards) const
QString RecStatusToDescription(int RecStatus, int RecType, const QDateTime &StartTime) override
Definition: dvr.cpp:1601
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:1631
QString RecStatusToString(int RecStatus) override
Definition: dvr.cpp:1595
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:1217
bool DisableRecordSchedule(uint RecordId) override
Definition: dvr.cpp:1554
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:1651
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:1585
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:1645
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
QDateTime m_lastRecorded
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:1481
QString DupInToDescription(QString DupIn) override
Definition: dvr.cpp:1638
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:1534
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:1376
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:1574
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:1611
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:1661
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)
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, QDateTime lastrectsRaw, 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
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
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:1621
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:1419
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:1391