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