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