MythTV  master
recordinginfo.cpp
Go to the documentation of this file.
1 // POSIX headers
2 #include <sys/types.h>
3 
4 // C headers
5 #include <cstdlib>
6 
7 // Qt headers
8 #include <QMap>
9 
10 // MythTV headers
11 #include "recordinginfo.h"
12 #include "recordingrule.h"
13 #include "scheduledrecording.h"
14 #include "mythmiscutil.h"
15 #include "mythdate.h"
16 #include "mythcorecontext.h"
17 #include "programinfoupdater.h"
18 #include "jobqueue.h"
19 #include "mythdb.h"
20 #include "mythlogging.h"
21 
22 #define LOC QString("RecordingInfo(%1): ").arg(GetBasename())
23 
24 static inline QString null_to_empty(const QString &str)
25 {
26  return str.isEmpty() ? "" : str;
27 }
28 
30 // works only for integer divisors of 60
31 static const uint kUnknownProgramLength = 30;
32 
34  const QString &_title,
35  const QString &_sortTitle,
36  const QString &_subtitle,
37  const QString &_sortSubtitle,
38  const QString &_description,
39  uint _season,
40  uint _episode,
41  uint _totalepisodes,
42  const QString &_syndicatedepisode,
43  const QString &_category,
44 
45  uint _chanid,
46  const QString &_chanstr,
47  const QString &_chansign,
48  const QString &_channame,
49 
50  const QString &_recgroup,
51  const QString &_playgroup,
52 
53  const QString &_hostname,
54  const QString &_storagegroup,
55 
56  uint _year,
57  uint _partnumber,
58  uint _parttotal,
59 
60  const QString &_seriesid,
61  const QString &_programid,
62  const QString &_inetref,
63  const CategoryType _catType,
64 
65  int _recpriority,
66 
67  const QDateTime &_startts,
68  const QDateTime &_endts,
69  const QDateTime &_recstartts,
70  const QDateTime &_recendts,
71 
72  float _stars,
73  const QDate &_originalAirDate,
74 
75  bool _repeat,
76 
77  RecStatus::Type _oldrecstatus,
78  bool _reactivate,
79 
80  uint _recordid,
81  uint _parentid,
82  RecordingType _rectype,
83  RecordingDupInType _dupin,
84  RecordingDupMethodType _dupmethod,
85 
86  uint _sourceid,
87  uint _inputid,
88 
89  uint _findid,
90 
91  bool _commfree,
92  uint _subtitleType,
93  uint _videoproperties,
94  uint _audioproperties,
95  bool _future,
96  int _schedorder,
97  uint _mplexid,
98  uint _sgroupid,
99  const QString &_inputname) :
100  ProgramInfo(
101  _title, _sortTitle, _subtitle, _sortSubtitle,
102  _description, _season, _episode, _totalepisodes,
103  _category, _chanid, _chanstr, _chansign, _channame,
104  QString(), _recgroup, _playgroup,
105  _startts, _endts, _recstartts, _recendts,
106  _seriesid, _programid, _inetref, _inputname),
107  m_oldrecstatus(_oldrecstatus),
108  m_future(_future),
109  m_schedorder(_schedorder),
110  m_mplexid(_mplexid),
111  m_sgroupid(_sgroupid),
112  m_desiredrecstartts(_startts),
113  m_desiredrecendts(_endts)
114 {
115  m_hostname = _hostname;
116  m_storagegroup = _storagegroup;
117 
118  m_syndicatedepisode = _syndicatedepisode;
119 
120  m_year = _year;
121  m_partnumber = _partnumber;
122  m_parttotal = _parttotal;
123  m_catType = _catType;
124 
125  m_recpriority = _recpriority;
126 
127  m_stars = clamp(_stars, 0.0F, 1.0F);
128  m_originalAirDate = _originalAirDate;
129  if (m_originalAirDate.isValid() && m_originalAirDate < QDate(1940, 1, 1))
130  m_originalAirDate = QDate();
131 
133  m_programflags |= _repeat ? FL_REPEAT : 0;
135  m_programflags |= _reactivate ? FL_REACTIVATE : 0;
137  m_programflags |= _commfree ? FL_CHANCOMMFREE : 0;
138 
139  m_recordid = _recordid;
140  m_parentid = _parentid;
141  m_rectype = _rectype;
142  m_dupin = _dupin;
143  m_dupmethod = _dupmethod;
144 
145  m_sourceid = _sourceid;
146  m_inputid = _inputid;
147  m_findid = _findid;
148 
149  m_properties = ((_subtitleType << kSubtitlePropertyOffset) |
150  (_videoproperties << kVideoPropertyOffset) |
151  _audioproperties);
152 
153  if (m_recstartts >= m_recendts)
154  {
155  // start/end-offsets are invalid so ignore
158  }
159 
161 }
162 
164  const QString &_title,
165  const QString &_sortTitle,
166  const QString &_subtitle,
167  const QString &_sortSubtitle,
168  const QString &_description,
169  uint _season,
170  uint _episode,
171  const QString &_category,
172 
173  uint _chanid,
174  const QString &_chanstr,
175  const QString &_chansign,
176  const QString &_channame,
177 
178  const QString &_recgroup,
179  const QString &_playgroup,
180 
181  const QString &_seriesid,
182  const QString &_programid,
183  const QString &_inetref,
184 
185  int _recpriority,
186 
187  const QDateTime &_startts,
188  const QDateTime &_endts,
189  const QDateTime &_recstartts,
190  const QDateTime &_recendts,
191 
192  RecStatus::Type _recstatus,
193 
194  uint _recordid,
195  RecordingType _rectype,
196  RecordingDupInType _dupin,
197  RecordingDupMethodType _dupmethod,
198 
199  uint _findid,
200 
201  bool _commfree) :
202  ProgramInfo(
203  _title, _sortTitle, _subtitle, _sortSubtitle,
204  _description, _season, _episode, 0,
205  _category, _chanid, _chanstr, _chansign, _channame,
206  QString(), _recgroup, _playgroup,
207  _startts, _endts, _recstartts, _recendts,
208  _seriesid, _programid, _inetref, ""),
209  m_desiredrecstartts(_startts),
210  m_desiredrecendts(_endts)
211 {
212  m_recpriority = _recpriority;
213 
214  m_recstatus = _recstatus,
215 
216  m_recordid = _recordid;
217  m_rectype = _rectype;
218  m_dupin = _dupin;
219  m_dupmethod = _dupmethod;
220 
221  m_findid = _findid;
222 
224  m_programflags |= _commfree ? FL_CHANCOMMFREE : 0;
225 
227 }
228 
238  uint _chanid, const QDateTime &desiredts,
239  bool genUnknown, uint maxHours, LoadStatus *status)
240 {
241  ProgramList schedList;
242  ProgramList progList;
243 
244  MSqlBindings bindings;
245  QString querystr = "WHERE program.chanid = :CHANID AND "
246  " program.starttime < :STARTTS1 AND "
247  " program.endtime > :STARTTS2 ";
248  bindings[":CHANID"] = QString::number(_chanid);
249  QDateTime query_startts = desiredts.addSecs(50 - desiredts.time().second());
250  bindings[":STARTTS1"] = query_startts;
251  bindings[":STARTTS2"] = query_startts;
252 
253  ::LoadFromScheduler(schedList);
254  LoadFromProgram(progList, querystr, bindings, schedList);
255 
256  if (!progList.empty())
257  {
258  ProgramInfo *pginfo = progList[0];
259 
260  if (maxHours > 0)
261  {
262  if (desiredts.secsTo(
263  pginfo->GetScheduledEndTime()) > (int)maxHours * 3600)
264  {
265  pginfo->SetScheduledEndTime(desiredts.addSecs(maxHours * 3600));
266  pginfo->SetRecordingEndTime(pginfo->GetScheduledEndTime());
267  }
268  }
269 
270  *this = *pginfo;
271  if (status)
272  *status = kFoundProgram;
273  return;
274  }
275 
276  m_recstartts = m_startts = desiredts;
277  m_recendts = m_endts = desiredts;
278  m_lastmodified = desiredts;
279 
280  MSqlQuery query(MSqlQuery::InitCon());
281  query.prepare("SELECT chanid, channum, callsign, name, "
282  "commmethod, outputfilters "
283  "FROM channel "
284  "WHERE chanid = :CHANID");
285  query.bindValue(":CHANID", _chanid);
286 
287  if (!query.exec())
288  {
289  MythDB::DBError("Loading Program overlapping a datetime", query);
290  if (status)
291  *status = kNoProgram;
292  return;
293  }
294 
295  if (!query.next())
296  {
297  if (status)
298  *status = kNoProgram;
299  return;
300  }
301 
302  m_chanid = query.value(0).toUInt();
303  m_chanstr = query.value(1).toString();
304  m_chansign = query.value(2).toString();
305  m_channame = query.value(3).toString();
307  m_programflags |= (query.value(4).toInt() == COMM_DETECT_COMMFREE) ?
308  FL_CHANCOMMFREE : 0;
309  m_chanplaybackfilters = query.value(5).toString();
310 
311  {
312  QMutexLocker locker(&s_staticDataLock);
313  if (s_unknownTitle.isEmpty())
314  s_unknownTitle = gCoreContext->GetSetting("UnknownTitle");
316  }
317 
318  if (!genUnknown)
319  {
320  if (status)
321  *status = kFakedZeroMinProgram;
322  return;
323  }
324 
325  // Round endtime up to the next half-hour.
326  m_endts = QDateTime(
327  m_endts.date(),
328  QTime(m_endts.time().hour(),
329  m_endts.time().minute() / kUnknownProgramLength
330  * kUnknownProgramLength), Qt::UTC);
331  m_endts = m_endts.addSecs(kUnknownProgramLength * 60);
332 
333  // if under a minute, bump it up to the next half hour
334  if (m_startts.secsTo(m_endts) < 60)
335  m_endts = m_endts.addSecs(kUnknownProgramLength * 60);
336 
338 
339  // Find next program starttime
340  bindings.clear();
341  QDateTime nextstart = m_startts;
342  querystr = "WHERE program.chanid = :CHANID AND "
343  " program.starttime > :STARTTS "
344  "GROUP BY program.starttime ORDER BY program.starttime LIMIT 1 ";
345  bindings[":CHANID"] = QString::number(_chanid);
346  bindings[":STARTTS"] = desiredts.addSecs(50 - desiredts.time().second());
347 
348  LoadFromProgram(progList, querystr, bindings, schedList);
349 
350  if (!progList.empty())
351  nextstart = (*progList.begin())->GetScheduledStartTime();
352 
353  if (nextstart > m_startts && nextstart < m_recendts)
354  m_recendts = m_endts = nextstart;
355 
356  if (status)
357  *status = kFakedLiveTVProgram;
358 
361 
363 }
364 
367  bool ignore_non_serialized_data)
368 {
369  bool is_same =
370  (m_chanid && m_recstartts.isValid() && m_startts.isValid() &&
371  m_chanid == other.GetChanID() &&
372  m_recstartts == other.GetRecordingStartTime() &&
373  m_startts == other.GetScheduledStartTime());
374 
375  ProgramInfo::clone(other, ignore_non_serialized_data);
376 
377  if (!is_same)
378  {
379  delete m_record;
380  m_record = nullptr;
381  }
382 
383  if (!ignore_non_serialized_data)
384  {
387  m_future = other.m_future;
388  m_schedorder = other.m_schedorder;
389  m_mplexid = other.m_mplexid;
390  m_sgroupid = other.m_sgroupid;
393  }
394 
395  delete m_recordingFile;
396  m_recordingFile = nullptr;
398 }
399 
402  bool ignore_non_serialized_data)
403 {
404  bool is_same =
405  (m_chanid && m_recstartts.isValid() && m_startts.isValid() &&
406  m_chanid == other.GetChanID() &&
407  m_recstartts == other.GetRecordingStartTime() &&
408  m_startts == other.GetScheduledStartTime());
409 
410  ProgramInfo::clone(other, ignore_non_serialized_data);
411 
412  if (!is_same)
413  {
414  delete m_record;
415  m_record = nullptr;
416  }
417 
420  m_future = false;
421  m_schedorder = 0;
422  m_mplexid = 0;
423  m_sgroupid = 0;
424  m_desiredrecstartts = QDateTime();
425  m_desiredrecendts = QDateTime();
426 
427  delete m_recordingFile;
428  m_recordingFile = nullptr;
430 }
431 
433 {
435 
436  delete m_record;
437  m_record = nullptr;
438 
441  m_future = false;
442  m_schedorder = 0;
443  m_mplexid = 0;
444  m_sgroupid = 0;
445  m_desiredrecstartts = QDateTime();
446  m_desiredrecendts = QDateTime();
447 
448  delete m_recordingFile;
449  m_recordingFile = nullptr;
450 }
451 
452 
457 {
458  delete m_record;
459  m_record = nullptr;
460 
461  delete m_recordingFile;
462  m_recordingFile = nullptr;
463 }
464 
471 {
472  if (m_record == nullptr)
473  {
474  m_record = new RecordingRule();
475  m_record->LoadByProgram(this);
476  }
477 
478  return m_record->m_type;
479 }
480 
487 {
488  if (m_record == nullptr)
489  {
490  m_record = new RecordingRule();
491  m_record->LoadByProgram(this);
492  }
493 
494  return m_record->m_recProfile;
495 }
496 
501 {
502  if (m_record == nullptr)
503  {
504  m_record = new RecordingRule();
505  m_record->LoadByProgram(this);
506  }
507 
508  int result = 0;
509 
511  result |= JOB_TRANSCODE;
513  result |= JOB_COMMFLAG;
515  result |= JOB_METADATA;
517  result |= JOB_USERJOB1;
519  result |= JOB_USERJOB2;
521  result |= JOB_USERJOB3;
523  result |= JOB_USERJOB4;
524 
525 
526  return result;
527 }
528 
533 {
534  MSqlQuery query(MSqlQuery::InitCon());
535 
536  if (getRecordID() < 0)
537  {
538  LOG(VB_GENERAL, LOG_ERR,
539  "ProgInfo Error: ApplyRecordRecID(void) needs recordid");
540  return;
541  }
542 
543  query.prepare("UPDATE recorded "
544  "SET recordid = :RECID "
545  "WHERE chanid = :CHANID AND starttime = :START");
546 
547  if (m_rectype == kOverrideRecord && m_parentid > 0)
548  query.bindValue(":RECID", m_parentid);
549  else
550  query.bindValue(":RECID", getRecordID());
551  query.bindValue(":CHANID", m_chanid);
552  query.bindValue(":START", m_recstartts);
553 
554  if (!query.exec())
555  MythDB::DBError(LOC + "RecordID update", query);
556 }
557 
568 {
570  if (newstate == kOverrideRecord || newstate == kDontRecord)
572  m_record->m_type = newstate;
573 
574  if (save)
575  {
576  if (newstate == kNotRecording)
577  m_record->Delete();
578  else
579  m_record->Save();
580  }
581 }
582 
589 {
591  m_record->m_recPriority = newrecpriority;
592  m_record->Save();
593 }
594 
600 void RecordingInfo::ApplyRecordRecGroupChange(const QString &newrecgroup)
601 {
602  MSqlQuery query(MSqlQuery::InitCon());
603 
604  int newrecgroupid = GetRecgroupID(newrecgroup);
605 
606  // Catchall - in the event that the group doesn't exist, then to avoid
607  // breakage, we need to create it
608  if (newrecgroupid == 0)
609  {
610  query.prepare("INSERT INTO recgroups SET recgroup = :NAME, "
611  "displayname = :DISPLAYNAME");
612  query.bindValue(":NAME", newrecgroup);
613  query.bindValue(":DISPLAYNAME", newrecgroup);
614 
615  if (query.exec())
616  newrecgroupid = query.lastInsertId().toInt();
617 
618  if (newrecgroupid <= 0)
619  {
620  LOG(VB_GENERAL, LOG_ERR, QString("Could not create recording group (%1). "
621  "Does it already exist?").arg(newrecgroup));
622  return;
623  }
624  }
625 
626  LOG(VB_GENERAL, LOG_NOTICE,
627  QString("ApplyRecordRecGroupChange: %1 to %2 (%3)").arg(m_recgroup)
628  .arg(newrecgroup)
629  .arg(newrecgroupid));
630 
631  query.prepare("UPDATE recorded"
632  " SET recgroup = :RECGROUP, "
633  " recgroupid = :RECGROUPID "
634  " WHERE chanid = :CHANID"
635  " AND starttime = :START ;");
636  query.bindValue(":RECGROUP", null_to_empty(newrecgroup));
637  query.bindValue(":RECGROUPID", newrecgroupid);
638  query.bindValue(":START", m_recstartts);
639  query.bindValue(":CHANID", m_chanid);
640 
641  if (!query.exec())
642  MythDB::DBError("RecGroup update", query);
643 
644  m_recgroup = newrecgroup; // Deprecate in favour of recgroupid
645  //recgroupid = newrecgroupid;
646 
647  SendUpdateEvent();
648 }
649 
651 {
652  MSqlQuery query(MSqlQuery::InitCon());
653 
654  QString newrecgroup;
655  if (newrecgroupid > 0)
656  {
657  newrecgroup = GetRecgroupString(newrecgroupid);
658 
659  query.prepare("UPDATE recorded"
660  " SET recgroup = :RECGROUP, "
661  " recgroupid = :RECGROUPID "
662  " WHERE chanid = :CHANID"
663  " AND starttime = :START ;");
664  query.bindValue(":RECGROUP", null_to_empty(newrecgroup));
665  query.bindValue(":RECGROUPID", newrecgroupid);
666  query.bindValue(":START", m_recstartts);
667  query.bindValue(":CHANID", m_chanid);
668 
669  if (!query.exec())
670  MythDB::DBError("RecGroup update", query);
671 
672  m_recgroup = newrecgroup; // Deprecate in favour of recgroupid
673  //recgroupid = newrecgroupid;
674 
675  SendUpdateEvent();
676  }
677 
678  LOG(VB_GENERAL, LOG_NOTICE,
679  QString("ApplyRecordRecGroupChange: %1 to %2 (%3)").arg(m_recgroup)
680  .arg(newrecgroup)
681  .arg(newrecgroupid));
682 }
683 
689 void RecordingInfo::ApplyRecordPlayGroupChange(const QString &newplaygroup)
690 {
691  MSqlQuery query(MSqlQuery::InitCon());
692 
693  query.prepare("UPDATE recorded"
694  " SET playgroup = :PLAYGROUP"
695  " WHERE chanid = :CHANID"
696  " AND starttime = :START ;");
697  query.bindValue(":PLAYGROUP", null_to_empty(newplaygroup));
698  query.bindValue(":START", m_recstartts);
699  query.bindValue(":CHANID", m_chanid);
700 
701  if (!query.exec())
702  MythDB::DBError("PlayGroup update", query);
703 
704  m_playgroup = newplaygroup;
705 
706  SendUpdateEvent();
707 }
708 
714 void RecordingInfo::ApplyStorageGroupChange(const QString &newstoragegroup)
715 {
716  MSqlQuery query(MSqlQuery::InitCon());
717 
718  query.prepare("UPDATE recorded"
719  " SET storagegroup = :STORAGEGROUP"
720  " WHERE chanid = :CHANID"
721  " AND starttime = :START ;");
722  query.bindValue(":STORAGEGROUP", null_to_empty(newstoragegroup));
723  query.bindValue(":START", m_recstartts);
724  query.bindValue(":CHANID", m_chanid);
725 
726  if (!query.exec())
727  MythDB::DBError("StorageGroup update", query);
728 
729  m_storagegroup = newstoragegroup;
730 
731  SendUpdateEvent();
732 }
733 
741 void RecordingInfo::ApplyRecordRecTitleChange(const QString &newTitle,
742  const QString &newSubtitle, const QString &newDescription)
743 {
744  MSqlQuery query(MSqlQuery::InitCon());
745  QString sql = "UPDATE recorded SET title = :TITLE, subtitle = :SUBTITLE ";
746  if (!newDescription.isNull())
747  sql += ", description = :DESCRIPTION ";
748  sql += " WHERE chanid = :CHANID AND starttime = :START ;";
749 
750  query.prepare(sql);
751  query.bindValue(":TITLE", newTitle);
752  query.bindValue(":SUBTITLE", null_to_empty(newSubtitle));
753  if (!newDescription.isNull())
754  query.bindValue(":DESCRIPTION", newDescription);
755  query.bindValue(":CHANID", m_chanid);
756  query.bindValue(":START", m_recstartts);
757 
758  if (!query.exec())
759  MythDB::DBError("RecTitle update", query);
760 
761  m_title = newTitle;
762  m_subtitle = newSubtitle;
763  if (!newDescription.isNull())
764  m_description = newDescription;
765 
766  SendUpdateEvent();
767 }
768 
769 /* \fn RecordingInfo::ApplyTranscoderProfileChangeById(int id)
770  * \brief Sets the transcoder profile for a recording
771  * \param profileid is the 'id' field from recordingprofiles table.
772  */
774 {
775  MSqlQuery query(MSqlQuery::InitCon());
776 
777  query.prepare("UPDATE recorded "
778  "SET transcoder = :PROFILEID "
779  "WHERE chanid = :CHANID "
780  "AND starttime = :START");
781  query.bindValue(":PROFILEID", id);
782  query.bindValue(":CHANID", m_chanid);
783  query.bindValue(":START", m_recstartts);
784 
785  if (!query.exec())
786  MythDB::DBError(LOC + "unable to update transcoder "
787  "in recorded table", query);
788 }
789 
794 {
795  if (profile == "Default") // use whatever is already in the transcoder
796  return;
797 
798  MSqlQuery query(MSqlQuery::InitCon());
799 
800  if (profile == "Autodetect")
801  {
802  query.prepare("UPDATE recorded "
803  "SET transcoder = 0 "
804  "WHERE chanid = :CHANID "
805  "AND starttime = :START");
806  query.bindValue(":CHANID", m_chanid);
807  query.bindValue(":START", m_recstartts);
808 
809  if (!query.exec())
810  MythDB::DBError(LOC + "unable to update transcoder "
811  "in recorded table", query);
812  }
813  else
814  {
815  MSqlQuery pidquery(MSqlQuery::InitCon());
816  pidquery.prepare("SELECT r.id "
817  "FROM recordingprofiles r, profilegroups p "
818  "WHERE r.profilegroup = p.id "
819  "AND p.name = 'Transcoders' "
820  "AND r.name = :PROFILE ");
821  pidquery.bindValue(":PROFILE", profile);
822 
823  if (!pidquery.exec())
824  {
825  MythDB::DBError("ProgramInfo: unable to query transcoder "
826  "profile ID", query);
827  }
828  else if (pidquery.next())
829  {
830  query.prepare("UPDATE recorded "
831  "SET transcoder = :TRANSCODER "
832  "WHERE chanid = :CHANID "
833  "AND starttime = :START");
834  query.bindValue(":TRANSCODER", pidquery.value(0).toInt());
835  query.bindValue(":CHANID", m_chanid);
836  query.bindValue(":START", m_recstartts);
837 
838  if (!query.exec())
839  MythDB::DBError(LOC + "unable to update transcoder "
840  "in recorded table", query);
841  }
842  else
843  {
844  LOG(VB_GENERAL, LOG_ERR,
845  "ProgramInfo: unable to query transcoder profile ID");
846  }
847  }
848 }
849 
855 {
859  AddHistory(true, true);
860 }
861 
866 {
868  if (curType == kNotRecording)
870 }
871 
876 {
878  return m_record;
879 }
880 
885 {
888  return m_recordid;
889 }
890 
892  uint chanid, const QDateTime& recstartts)
893 {
894  if (chanid < 1)
895  {
896  LOG(VB_RECORD, LOG_WARNING,
897  QString("QueryRecordedIdFromKey: Invalid chanid %1").arg(chanid));
898  return false;
899  }
900  if (!recstartts.isValid())
901  {
902  LOG(VB_RECORD, LOG_WARNING,
903  QString("QueryRecordedIdFromKey: Invalid start ts %1")
904  .arg(recstartts.toString()));
905  return false;
906  }
907 
908  MSqlQuery query(MSqlQuery::InitCon());
909  query.prepare(
910  "SELECT recordedid FROM recorded "
911  "WHERE chanid = :CHANID AND starttime = :RECSTARTTS");
912  query.bindValue(":CHANID", chanid);
913  query.bindValue(":RECSTARTTS", recstartts);
914  if (query.exec() && query.next())
915  {
916  recordedid = query.value(0).toUInt();
917  return true;
918  }
919 
920  return false;
921 }
922 
931 void RecordingInfo::StartedRecording(const QString& ext)
932 {
933  QString dirname = m_pathname;
934 
935  if (!m_record)
936  {
937  m_record = new RecordingRule();
938  m_record->LoadByProgram(this);
939  }
940 
943 
944  int count = 0;
945  while (!InsertProgram(this, m_record) && count < 50)
946  {
947  m_recstartts = m_recstartts.addSecs(1);
949  count++;
950  }
951 
952  if (count >= 50)
953  {
954  LOG(VB_GENERAL, LOG_ERR, "Couldn't insert program");
955  return;
956  }
957 
958  m_pathname = dirname + "/" + m_pathname;
959 
960  LOG(VB_FILE, LOG_INFO, LOC + QString("StartedRecording: Recording to '%1'")
961  .arg(m_pathname));
962 
963 
964  MSqlQuery query(MSqlQuery::InitCon());
965 
966  query.prepare("DELETE FROM recordedseek WHERE chanid = :CHANID"
967  " AND starttime = :START;");
968  query.bindValue(":CHANID", m_chanid);
969  query.bindValue(":START", m_recstartts);
970 
971  if (!query.exec() || !query.isActive())
972  MythDB::DBError("Clear seek info on record", query);
973 
974  query.prepare("DELETE FROM recordedmarkup WHERE chanid = :CHANID"
975  " AND starttime = :START;");
976  query.bindValue(":CHANID", m_chanid);
977  query.bindValue(":START", m_recstartts);
978 
979  if (!query.exec() || !query.isActive())
980  MythDB::DBError("Clear markup on record", query);
981 
982  query.prepare("REPLACE INTO recordedcredits"
983  " SELECT * FROM credits"
984  " WHERE chanid = :CHANID AND starttime = :START;");
985  query.bindValue(":CHANID", m_chanid);
986  query.bindValue(":START", m_startts);
987  if (!query.exec() || !query.isActive())
988  MythDB::DBError("Copy program credits on record", query);
989 
990  query.prepare("REPLACE INTO recordedprogram"
991  " SELECT * from program"
992  " WHERE chanid = :CHANID AND starttime = :START"
993  " AND title = :TITLE;");
994  query.bindValue(":CHANID", m_chanid);
995  query.bindValue(":START", m_startts);
996  query.bindValue(":TITLE", m_title);
997  if (!query.exec() || !query.isActive())
998  MythDB::DBError("Copy program data on record", query);
999 
1000  query.prepare("REPLACE INTO recordedrating"
1001  " SELECT * from programrating"
1002  " WHERE chanid = :CHANID AND starttime = :START;");
1003  query.bindValue(":CHANID", m_chanid);
1004  query.bindValue(":START", m_startts);
1005  if (!query.exec() || !query.isActive())
1006  MythDB::DBError("Copy program ratings on record", query);
1007 
1008  // File
1009  if (!GetRecordingFile())
1011  RecordingFile *recFile = GetRecordingFile();
1012  recFile->m_fileName = GetBasename();
1013  recFile->m_storageDeviceID = GetHostname();
1014  recFile->m_storageGroup = GetStorageGroup();
1015  recFile->Save();
1016 
1017  SendAddedEvent();
1018 }
1019 
1021  const RecordingRule *rule)
1022 {
1023  QString inputname = pg->GetInputName();
1024  int recgroupid = GetRecgroupID(pg->m_recgroup);
1025 
1026  MSqlQuery query(MSqlQuery::InitCon());
1027 
1028  if (!query.exec("LOCK TABLES recorded WRITE"))
1029  {
1030  MythDB::DBError("InsertProgram -- lock", query);
1031  return false;
1032  }
1033 
1034  query.prepare(
1035  "SELECT recordid "
1036  " FROM recorded "
1037  " WHERE chanid = :CHANID AND "
1038  " starttime = :STARTS");
1039  query.bindValue(":CHANID", pg->m_chanid);
1040  query.bindValue(":STARTS", pg->m_recstartts);
1041 
1042  bool err = true;
1043  if (!query.exec())
1044  {
1045  MythDB::DBError("InsertProgram -- select", query);
1046  }
1047  else if (query.next())
1048  {
1049  LOG(VB_GENERAL, LOG_ERR,
1050  QString("RecordingInfo::InsertProgram(%1): ")
1051  .arg(pg->toString()) + "recording already exists...");
1052  }
1053  else
1054  {
1055  err = false;
1056  }
1057 
1058  if (err)
1059  {
1060  if (!query.exec("UNLOCK TABLES"))
1061  MythDB::DBError("InsertProgram -- unlock tables", query);
1062  return false;
1063  }
1064 
1065  query.prepare(
1066  "INSERT INTO recorded "
1067  " (chanid, starttime, endtime, title, "
1068  " subtitle, description, season, episode, "
1069  " hostname, category, recgroup, autoexpire, "
1070  " recordid, seriesid, programid, inetref, "
1071  " stars, previouslyshown, originalairdate, "
1072  " findid, transcoder, playgroup, recpriority, "
1073  " basename, progstart, progend, profile, "
1074  " duplicate, storagegroup, inputname, recgroupid) "
1075  "VALUES"
1076  " (:CHANID, :STARTS, :ENDS, :TITLE, "
1077  " :SUBTITLE, :DESC, :SEASON, :EPISODE, "
1078  " :HOSTNAME, :CATEGORY, :RECGROUP, :AUTOEXP, "
1079  " :RECORDID, :SERIESID, :PROGRAMID, :INETREF, "
1080  " :STARS, :REPEAT, :ORIGAIRDATE, "
1081  " :FINDID, :TRANSCODER, :PLAYGROUP, :RECPRIORITY, "
1082  " :BASENAME, :PROGSTART, :PROGEND, :PROFILE, "
1083  " 0, :STORGROUP, :INPUTNAME, :RECGROUPID) "
1084  );
1085 
1086  if (pg->m_rectype == kOverrideRecord)
1087  query.bindValue(":RECORDID", pg->m_parentid);
1088  else
1089  query.bindValue(":RECORDID", pg->m_recordid);
1090 
1091  if (pg->m_originalAirDate.isValid())
1092  query.bindValue(":ORIGAIRDATE", pg->m_originalAirDate);
1093  // If there is no originalairdate use "year"
1094  else if (pg->m_year >= 1940)
1095  query.bindValue(":ORIGAIRDATE", QDate(pg->m_year,1,1));
1096  else
1097  query.bindValue(":ORIGAIRDATE", "0000-00-00");
1098 
1099  query.bindValue(":CHANID", pg->m_chanid);
1100  query.bindValue(":STARTS", pg->m_recstartts);
1101  query.bindValue(":ENDS", pg->m_recendts);
1102  query.bindValue(":TITLE", pg->m_title);
1103  query.bindValue(":SUBTITLE", null_to_empty(pg->m_subtitle));
1104  query.bindValue(":DESC", null_to_empty(pg->m_description));
1105  query.bindValue(":SEASON", pg->m_season);
1106  query.bindValue(":EPISODE", pg->m_episode);
1107  query.bindValue(":HOSTNAME", pg->m_hostname);
1108  query.bindValue(":CATEGORY", null_to_empty(pg->m_category));
1109  query.bindValue(":RECGROUP", null_to_empty(pg->m_recgroup));
1110  query.bindValue(":AUTOEXP", rule->m_autoExpire);
1111  query.bindValue(":SERIESID", null_to_empty(pg->m_seriesid));
1112  query.bindValue(":PROGRAMID", null_to_empty(pg->m_programid));
1113  query.bindValue(":INETREF", null_to_empty(pg->m_inetref));
1114  query.bindValue(":FINDID", pg->m_findid);
1115  query.bindValue(":STARS", pg->m_stars);
1116  query.bindValue(":REPEAT", pg->IsRepeat());
1117  query.bindValue(":TRANSCODER", rule->m_transcoder);
1118  query.bindValue(":PLAYGROUP", pg->m_playgroup);
1119  query.bindValue(":RECPRIORITY", rule->m_recPriority);
1120  query.bindValue(":BASENAME", pg->m_pathname);
1121  query.bindValue(":STORGROUP", null_to_empty(pg->m_storagegroup));
1122  query.bindValue(":PROGSTART", pg->m_startts);
1123  query.bindValue(":PROGEND", pg->m_endts);
1124  query.bindValue(":PROFILE", null_to_empty(rule->m_recProfile));
1125  query.bindValue(":INPUTNAME", inputname);
1126  query.bindValue(":RECGROUPID", recgroupid);
1127 
1128  bool ok = query.exec() && (query.numRowsAffected() > 0);
1129  if (ok)
1130  {
1131  pg->SetRecordingID(query.lastInsertId().toUInt());
1132  }
1133  bool active = query.isActive();
1134 
1135  if (!query.exec("UNLOCK TABLES"))
1136  MythDB::DBError("InsertProgram -- unlock tables", query);
1137 
1138  if (!ok && !active)
1139  MythDB::DBError("InsertProgram -- insert", query);
1140 
1141  else if (pg->m_recordid > 0)
1142  {
1143  query.prepare("UPDATE channel SET last_record = NOW() "
1144  "WHERE chanid = :CHANID");
1145  query.bindValue(":CHANID", pg->GetChanID());
1146  if (!query.exec())
1147  MythDB::DBError("InsertProgram -- channel last_record", query);
1148 
1149  query.prepare("UPDATE record SET last_record = NOW() "
1150  "WHERE recordid = :RECORDID");
1151  query.bindValue(":RECORDID", pg->m_recordid);
1152  if (!query.exec())
1153  MythDB::DBError("InsertProgram -- record last_record", query);
1154 
1155  if (pg->m_rectype == kOverrideRecord && pg->m_parentid > 0)
1156  {
1157  query.prepare("UPDATE record SET last_record = NOW() "
1158  "WHERE recordid = :PARENTID");
1159  query.bindValue(":PARENTID", pg->m_parentid);
1160  if (!query.exec())
1161  MythDB::DBError("InsertProgram -- record last_record override",
1162  query);
1163  }
1164  }
1165 
1166  return ok;
1167 }
1168 
1177 void RecordingInfo::FinishedRecording(bool allowReRecord)
1178 {
1179  MSqlQuery query(MSqlQuery::InitCon());
1180  query.prepare("UPDATE recorded SET endtime = :ENDTIME, "
1181  " duplicate = :DUPLICATE "
1182  "WHERE chanid = :CHANID AND "
1183  " starttime = :STARTTIME ");
1184  query.bindValue(":ENDTIME", m_recendts);
1185  query.bindValue(":CHANID", m_chanid);
1186  query.bindValue(":STARTTIME", m_recstartts);
1187  query.bindValue(":DUPLICATE", !allowReRecord);
1188 
1189  if (!query.exec())
1190  MythDB::DBError("FinishedRecording update", query);
1191 
1193  if (!allowReRecord)
1194  {
1196 
1197 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
1198  uint starttime = m_recstartts.toTime_t();
1199  uint endtime = m_recendts.toTime_t();
1200  int64_t duration = ((int64_t)endtime - (int64_t)starttime) * 1000000;
1201 #else
1202  qint64 starttime = m_recstartts.toSecsSinceEpoch();
1203  qint64 endtime = m_recendts.toSecsSinceEpoch();
1204  int64_t duration = (endtime - starttime) * 1000000;
1205 #endif
1206  SaveTotalDuration(duration);
1207 
1208  QString msg = "Finished recording";
1209  QString msg_subtitle = m_subtitle.isEmpty() ? "" :
1210  QString(" \"%1\"").arg(m_subtitle);
1211  QString details = QString("%1%2: channel %3")
1212  .arg(m_title)
1213  .arg(msg_subtitle)
1214  .arg(m_chanid);
1215 
1216  LOG(VB_GENERAL, LOG_INFO, QString("%1 %2").arg(msg).arg(details));
1217  }
1218 
1219  SendUpdateEvent();
1220 }
1221 
1227 {
1228  MSqlQuery query(MSqlQuery::InitCon());
1229  query.prepare("UPDATE recorded SET endtime = :ENDTIME "
1230  "WHERE chanid = :CHANID AND "
1231  " starttime = :STARTTIME ");
1232  query.bindValue(":ENDTIME", m_recendts);
1233 
1234  query.bindValue(":CHANID", m_chanid);
1235  query.bindValue(":STARTTIME", m_recstartts);
1236 
1237  if (!query.exec())
1238  MythDB::DBError("UpdateRecordingEnd update", query);
1239 
1240  SendUpdateEvent();
1241 }
1242 
1247 {
1248  MSqlQuery result(MSqlQuery::InitCon());
1249 
1250  result.prepare("UPDATE oldrecorded SET reactivate = 1 "
1251  "WHERE station = :STATION AND "
1252  " starttime = :STARTTIME AND "
1253  " title = :TITLE;");
1254  result.bindValue(":STARTTIME", m_startts);
1255  result.bindValue(":TITLE", m_title);
1256  result.bindValue(":STATION", m_chansign);
1257 
1258  if (!result.exec())
1259  MythDB::DBError("ReactivateRecording", result);
1260 
1262 }
1263 
1267 void RecordingInfo::AddHistory(bool resched, bool forcedup, bool future)
1268 {
1269  bool dup = (GetRecordingStatus() == RecStatus::Recorded || forcedup);
1272  LOG(VB_SCHEDULE, LOG_INFO, QString("AddHistory: %1/%2, %3, %4, %5/%6")
1273  .arg(int(rs)).arg(int(m_oldrecstatus)).arg(future).arg(dup)
1275  if (!future)
1277  if (dup)
1278  SetReactivated(false);
1279  uint erecid = m_parentid ? m_parentid : m_recordid;
1280 
1281  MSqlQuery result(MSqlQuery::InitCon());
1282 
1283  result.prepare("REPLACE INTO oldrecorded (chanid,starttime,"
1284  "endtime,title,subtitle,description,season,episode,"
1285  "category,seriesid,programid,inetref,findid,recordid,"
1286  "station,rectype,recstatus,duplicate,reactivate,generic,"
1287  "future) "
1288  "VALUES(:CHANID,:START,:END,:TITLE,:SUBTITLE,:DESC,:SEASON,"
1289  ":EPISODE,:CATEGORY,:SERIESID,:PROGRAMID,:INETREF,"
1290  ":FINDID,:RECORDID,:STATION,:RECTYPE,:RECSTATUS,:DUPLICATE,"
1291  ":REACTIVATE,:GENERIC,:FUTURE);");
1292  result.bindValue(":CHANID", m_chanid);
1293  result.bindValue(":START", m_startts);
1294  result.bindValue(":END", m_endts);
1295  result.bindValue(":TITLE", m_title);
1296  result.bindValue(":SUBTITLE", null_to_empty(m_subtitle));
1297  result.bindValue(":DESC", null_to_empty(m_description));
1298  result.bindValue(":SEASON", m_season);
1299  result.bindValue(":EPISODE", m_episode);
1300  result.bindValue(":CATEGORY", null_to_empty(m_category));
1301  result.bindValue(":SERIESID", null_to_empty(m_seriesid));
1302  result.bindValue(":PROGRAMID", null_to_empty(m_programid));
1303  result.bindValue(":INETREF", null_to_empty(m_inetref));
1304  result.bindValue(":FINDID", m_findid);
1305  result.bindValue(":RECORDID", erecid);
1306  result.bindValue(":STATION", null_to_empty(m_chansign));
1307  result.bindValue(":RECTYPE", m_rectype);
1308  result.bindValue(":RECSTATUS", rs);
1309  result.bindValue(":DUPLICATE", dup);
1310  result.bindValue(":REACTIVATE", 0);
1311  result.bindValue(":GENERIC", IsGeneric());
1312  result.bindValue(":FUTURE", future);
1313 
1314  if (!result.exec())
1315  MythDB::DBError("addHistory", result);
1316 
1317  if (dup && m_findid)
1318  {
1319  result.prepare("REPLACE INTO oldfind (recordid, findid) "
1320  "VALUES(:RECORDID,:FINDID);");
1321  result.bindValue(":RECORDID", erecid);
1322  result.bindValue(":FINDID", m_findid);
1323 
1324  if (!result.exec())
1325  MythDB::DBError("addFindHistory", result);
1326  }
1327 
1328  // The adding of an entry to oldrecorded may affect near-future
1329  // scheduling decisions, so recalculate if told
1330  if (resched)
1331  ScheduledRecording::RescheduleCheck(*this, "AddHistory");
1332 }
1333 
1338 {
1339  uint erecid = m_parentid ? m_parentid : m_recordid;
1340 
1341  MSqlQuery result(MSqlQuery::InitCon());
1342 
1343  result.prepare("DELETE FROM oldrecorded WHERE title = :TITLE AND "
1344  "starttime = :START AND station = :STATION");
1345  result.bindValue(":TITLE", m_title);
1346  result.bindValue(":START", m_recstartts);
1347  result.bindValue(":STATION", m_chansign);
1348 
1349  if (!result.exec())
1350  MythDB::DBError("deleteHistory", result);
1351 
1352  if (/*m_duplicate &&*/ m_findid)
1353  {
1354  result.prepare("DELETE FROM oldfind WHERE "
1355  "recordid = :RECORDID AND findid = :FINDID");
1356  result.bindValue(":RECORDID", erecid);
1357  result.bindValue(":FINDID", m_findid);
1358 
1359  if (!result.exec())
1360  MythDB::DBError("deleteFindHistory", result);
1361  }
1362 
1363  // The removal of an entry from oldrecorded may affect near-future
1364  // scheduling decisions, so recalculate
1365  ScheduledRecording::RescheduleCheck(*this, "DeleteHistory");
1366 }
1367 
1377 {
1378  uint erecid = m_parentid ? m_parentid : m_recordid;
1379  uint din = m_dupin;
1380  uint dmeth = m_dupmethod;
1381 
1382  if (din == kDupsUnset)
1383  din = kDupsInAll;
1384  if (dmeth == kDupCheckUnset)
1385  dmeth = kDupCheckSubThenDesc;
1386 
1387  MSqlQuery result(MSqlQuery::InitCon());
1388 
1389  // Handle this specific entry in recorded.
1390  result.prepare("UPDATE recorded SET duplicate = 0 "
1391  "WHERE chanid = :CHANID "
1392  "AND starttime = :STARTTIME "
1393  "AND title = :TITLE;");
1394  result.bindValue(":STARTTIME", m_recstartts);
1395  result.bindValue(":TITLE", m_title);
1396  result.bindValue(":CHANID", m_chanid);
1397 
1398  if (!result.exec())
1399  MythDB::DBError("forgetRecorded1", result);
1400 
1401  // Handle other matching entries in recorded.
1402  if (din & kDupsInRecorded)
1403  {
1404  result.prepare(
1405  "UPDATE recorded SET duplicate = 0 "
1406  "WHERE duplicate = 1 AND "
1407  " title = :TITLE AND "
1408  " ( "
1409  " (:PROGRAMID1 <> '' AND "
1410  " :PROGRAMID2 = recorded.programid) "
1411  " OR "
1412  " ( "
1413  " (:PROGRAMID3 = '' OR recorded.programid = '' OR "
1414  " LEFT(:PROGRAMID4, LOCATE('/', :PROGRAMID5)) <> "
1415  " LEFT(recorded.programid, "
1416  " LOCATE('/', recorded.programid))) "
1417  " AND "
1418  " (((:DUPMETHOD1 & 0x02) = 0) OR (:SUBTITLE1 <> '' "
1419  " AND :SUBTITLE2 = recorded.subtitle)) "
1420  " AND "
1421  " (((:DUPMETHOD2 & 0x04) = 0) OR (:DESCRIPTION1 <> '' "
1422  " AND :DESCRIPTION2 = recorded.description)) "
1423  " AND "
1424  " (((:DUPMETHOD3 & 0x08) = 0) OR "
1425  " (:SUBTITLE3 <> '' AND "
1426  " (:SUBTITLE4 = recorded.subtitle OR "
1427  " (recorded.subtitle = '' AND "
1428  " :SUBTITLE5 = recorded.description))) OR "
1429  " (:SUBTITLE6 = '' AND :DESCRIPTION3 <> '' AND "
1430  " (:DESCRIPTION4 = recorded.subtitle OR "
1431  " (recorded.subtitle = '' AND "
1432  " :DESCRIPTION5 = recorded.description)))) "
1433  " ) "
1434  " )" );
1435  result.bindValue(":TITLE", m_title);
1436  result.bindValue(":SUBTITLE1", null_to_empty(m_subtitle));
1437  result.bindValue(":SUBTITLE2", null_to_empty(m_subtitle));
1438  result.bindValue(":SUBTITLE3", null_to_empty(m_subtitle));
1439  result.bindValue(":SUBTITLE4", null_to_empty(m_subtitle));
1440  result.bindValue(":SUBTITLE5", null_to_empty(m_subtitle));
1441  result.bindValue(":SUBTITLE6", null_to_empty(m_subtitle));
1442  result.bindValue(":DESCRIPTION1", null_to_empty(m_description));
1443  result.bindValue(":DESCRIPTION2", null_to_empty(m_description));
1444  result.bindValue(":DESCRIPTION3", null_to_empty(m_description));
1445  result.bindValue(":DESCRIPTION4", null_to_empty(m_description));
1446  result.bindValue(":DESCRIPTION5", null_to_empty(m_description));
1447  result.bindValue(":PROGRAMID1", null_to_empty(m_programid));
1448  result.bindValue(":PROGRAMID2", null_to_empty(m_programid));
1449  result.bindValue(":PROGRAMID3", null_to_empty(m_programid));
1450  result.bindValue(":PROGRAMID4", null_to_empty(m_programid));
1451  result.bindValue(":PROGRAMID5", null_to_empty(m_programid));
1452  result.bindValue(":DUPMETHOD1", dmeth);
1453  result.bindValue(":DUPMETHOD2", dmeth);
1454  result.bindValue(":DUPMETHOD3", dmeth);
1455 
1456  if (!result.exec())
1457  MythDB::DBError("forgetRecorded2", result);
1458  }
1459 
1460  // Handle this specific entry in oldrecorded.
1461  result.prepare("UPDATE oldrecorded SET duplicate = 0 "
1462  "WHERE station = :STATION "
1463  "AND starttime = :STARTTIME "
1464  "AND title = :TITLE;");
1465  result.bindValue(":STARTTIME", m_startts);
1466  result.bindValue(":TITLE", m_title);
1467  result.bindValue(":STATION", m_chansign);
1468 
1469  if (!result.exec())
1470  MythDB::DBError("forgetOldRecorded1", result);
1471 
1472  // Handle other matching entries in oldrecorded.
1473  if (din & kDupsInOldRecorded)
1474  {
1475  result.prepare(
1476  "UPDATE oldrecorded SET duplicate = 0 "
1477  "WHERE duplicate = 1 AND "
1478  " title = :TITLE AND "
1479  " ( "
1480  " (:PROGRAMID1 <> '' AND "
1481  " :PROGRAMID2 = oldrecorded.programid) "
1482  " OR "
1483  " ( "
1484  " (:PROGRAMID3 = '' OR oldrecorded.programid = '' OR "
1485  " LEFT(:PROGRAMID4, LOCATE('/', :PROGRAMID5)) <> "
1486  " LEFT(oldrecorded.programid, "
1487  " LOCATE('/', oldrecorded.programid))) "
1488  " AND "
1489  " (((:DUPMETHOD1 & 0x02) = 0) OR (:SUBTITLE1 <> '' "
1490  " AND :SUBTITLE2 = oldrecorded.subtitle)) "
1491  " AND "
1492  " (((:DUPMETHOD2 & 0x04) = 0) OR (:DESCRIPTION1 <> '' "
1493  " AND :DESCRIPTION2 = oldrecorded.description)) "
1494  " AND "
1495  " (((:DUPMETHOD3 & 0x08) = 0) OR "
1496  " (:SUBTITLE3 <> '' AND "
1497  " (:SUBTITLE4 = oldrecorded.subtitle OR "
1498  " (oldrecorded.subtitle = '' AND "
1499  " :SUBTITLE5 = oldrecorded.description))) OR "
1500  " (:SUBTITLE6 = '' AND :DESCRIPTION3 <> '' AND "
1501  " (:DESCRIPTION4 = oldrecorded.subtitle OR "
1502  " (oldrecorded.subtitle = '' AND "
1503  " :DESCRIPTION5 = oldrecorded.description)))) "
1504  " ) "
1505  " )" );
1506  result.bindValue(":TITLE", m_title);
1507  result.bindValue(":SUBTITLE1", null_to_empty(m_subtitle));
1508  result.bindValue(":SUBTITLE2", null_to_empty(m_subtitle));
1509  result.bindValue(":SUBTITLE3", null_to_empty(m_subtitle));
1510  result.bindValue(":SUBTITLE4", null_to_empty(m_subtitle));
1511  result.bindValue(":SUBTITLE5", null_to_empty(m_subtitle));
1512  result.bindValue(":SUBTITLE6", null_to_empty(m_subtitle));
1513  result.bindValue(":DESCRIPTION1", null_to_empty(m_description));
1514  result.bindValue(":DESCRIPTION2", null_to_empty(m_description));
1515  result.bindValue(":DESCRIPTION3", null_to_empty(m_description));
1516  result.bindValue(":DESCRIPTION4", null_to_empty(m_description));
1517  result.bindValue(":DESCRIPTION5", null_to_empty(m_description));
1518  result.bindValue(":PROGRAMID1", null_to_empty(m_programid));
1519  result.bindValue(":PROGRAMID2", null_to_empty(m_programid));
1520  result.bindValue(":PROGRAMID3", null_to_empty(m_programid));
1521  result.bindValue(":PROGRAMID4", null_to_empty(m_programid));
1522  result.bindValue(":PROGRAMID5", null_to_empty(m_programid));
1523  result.bindValue(":DUPMETHOD1", dmeth);
1524  result.bindValue(":DUPMETHOD2", dmeth);
1525  result.bindValue(":DUPMETHOD3", dmeth);
1526 
1527  if (!result.exec())
1528  MythDB::DBError("forgetOldRecorded2", result);
1529  }
1530 
1531  // Remove any never records which aren't need anymore.
1532  result.prepare("DELETE FROM oldrecorded "
1533  "WHERE recstatus = :NEVER AND duplicate = 0");
1534  result.bindValue(":NEVER", RecStatus::NeverRecord);
1535 
1536  if (!result.exec())
1537  MythDB::DBError("forgetNeverHistory", result);
1538 
1539  // Handle matching entries in oldfind.
1540  if (m_findid)
1541  {
1542  result.prepare("DELETE FROM oldfind WHERE "
1543  "recordid = :RECORDID AND findid = :FINDID");
1544  result.bindValue(":RECORDID", erecid);
1545  result.bindValue(":FINDID", m_findid);
1546 
1547  if (!result.exec())
1548  MythDB::DBError("forgetFindHistory", result);
1549  }
1550 
1551  // The removal of an entry from oldrecorded may affect near-future
1552  // scheduling decisions, so recalculate
1553  ScheduledRecording::RescheduleCheck(*this, "ForgetHistory");
1554 }
1555 
1560 {
1561  MSqlQuery result(MSqlQuery::InitCon());
1562 
1563  result.prepare("UPDATE oldrecorded SET duplicate = 1 "
1564  "WHERE future = 0 AND duplicate = 0 "
1565  "AND title = :TITLE AND "
1566  "((programid = '' AND subtitle = :SUBTITLE"
1567  " AND description = :DESC) OR "
1568  " (programid <> '' AND programid = :PROGRAMID) OR "
1569  " (findid <> 0 AND findid = :FINDID))");
1570  result.bindValue(":TITLE", m_title);
1571  result.bindValue(":SUBTITLE", null_to_empty(m_subtitle));
1572  result.bindValue(":DESC", null_to_empty(m_description));
1573  result.bindValue(":PROGRAMID", null_to_empty(m_programid));
1574  result.bindValue(":FINDID", m_findid);
1575 
1576  if (!result.exec())
1577  MythDB::DBError("setDupHistory", result);
1578 
1579  ScheduledRecording::RescheduleCheck(*this, "SetHistory");
1580 }
1581 
1587 {
1588  str.replace("%RECID%", QString::number(getRecordID()));
1589  str.replace("%PARENTID%", QString::number(m_parentid));
1590  str.replace("%FINDID%", QString::number(m_findid));
1591  str.replace("%RECSTATUS%", QString::number(m_recstatus));
1592  str.replace("%RECTYPE%", QString::number(m_rectype));
1593  str.replace("%REACTIVATE%", IsReactivated() ? "1" : "0");
1594  str.replace("%INPUTNAME%", GetInputName());
1595  str.replace("%CHANNUM%", GetChanNum());
1596 
1598 }
1599 
1603 uint RecordingInfo::GetRecgroupID(const QString& recGroup)
1604 {
1605  MSqlQuery query(MSqlQuery::InitCon());
1606 
1607  query.prepare("SELECT recgroupid FROM recgroups WHERE recgroup = :RECGROUP");
1608  query.bindValue(":RECGROUP", null_to_empty(recGroup));
1609 
1610  if (!query.exec())
1611  MythDB::DBError("RecGroup update", query);
1612 
1613  if (!query.next())
1614  return 0;
1615 
1616  return query.value(0).toUInt();
1617 }
1618 
1623 {
1624  MSqlQuery query(MSqlQuery::InitCon());
1625 
1626  query.prepare("SELECT recgroup FROM recgroups WHERE recgroupid = :RECGROUPID");
1627  query.bindValue(":RECGROUPID", recGroupID);
1628  if (!query.exec() || !query.next())
1629  {
1630  MythDB::DBError("GetRecgroupString()", query);
1631  return QString();
1632  }
1633  return query.value(0).toString();
1634 }
1635 
1637 {
1638  if (!m_recordingFile)
1639  {
1641  if (m_recordedid > 0)
1642  {
1644  m_recordingFile->Load();
1645  }
1646  }
1647 }
1648 
1649 void RecordingInfo::SaveFilesize(uint64_t fsize)
1650 {
1651  if (!GetRecordingFile())
1653  GetRecordingFile()->m_fileSize = fsize;
1654  GetRecordingFile()->Save(); // Ideally this would be called just the once when all metadata is gathered
1655 
1657 
1658  ProgramInfo::SaveFilesize(fsize); // Temporary
1659 }
1660 
1661 void RecordingInfo::SetFilesize(uint64_t fsize)
1662 {
1663  if (!GetRecordingFile())
1665  GetRecordingFile()->m_fileSize = fsize;
1667 
1668  // Make sure the old storage location is updated for now
1669  ProgramInfo::SetFilesize(fsize);
1670 }
1671 
1672 uint64_t RecordingInfo::GetFilesize(void) const
1673 {
1674  if (GetRecordingFile() && GetRecordingFile()->m_fileSize > 0)
1675  return GetRecordingFile()->m_fileSize;
1676 
1677  // Temporary fallback to reading from old storage location
1678  return ProgramInfo::GetFilesize();
1679 }
1680 
1681 
1682 /* vim: set expandtab tabstop=4 shiftwidth=4: */
QString m_fileName
Definition: recordingfile.h:44
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
uint32_t m_recordid
Definition: programinfo.h:783
QString m_channame
Definition: programinfo.h:753
QString m_syndicatedepisode
Definition: programinfo.h:744
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
void ReactivateRecording(void)
Asks the scheduler to restart this recording if possible.
QDateTime m_lastmodified
Definition: programinfo.h:778
uint16_t m_partnumber
Definition: programinfo.h:794
uint32_t m_inputid
Definition: programinfo.h:787
void DeleteHistory(void)
Deletes recording history, creating "record" it if necessary.
bool LoadByProgram(const ProgramInfo *proginfo)
QDateTime m_endts
Definition: programinfo.h:772
QString m_description
Definition: programinfo.h:740
bool Delete(bool sendSig=true)
uint32_t m_parentid
Definition: programinfo.h:784
virtual uint64_t GetFilesize(void) const
bool IsReactivated(void) const
Definition: programinfo.h:480
void SetScheduledStartTime(const QDateTime &dt)
Definition: programinfo.h:508
void SetRecordingID(uint _recordedid) override
virtual void SubstituteMatches(QString &str)
Subsitute MATCH% type variable names in the given string.
QString m_chansign
Definition: programinfo.h:752
QDate m_originalAirDate
Definition: programinfo.h:777
enum RecordingDupInTypes RecordingDupInType
RecordingInfo(void)
Definition: recordinginfo.h:37
uint16_t m_properties
SubtitleType,VideoProperty,AudioProperty.
Definition: programinfo.h:792
QString m_storageGroup
Definition: recordingfile.h:41
static QString s_unknownTitle
RecordingFile * m_recordingFile
void ApplyRecordRecTitleChange(const QString &newTitle, const QString &newSubtitle, const QString &newDescription)
Sets the recording title, subtitle, and description both in this RecordingInfo and in the database.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
void SubstituteMatches(QString &str) override
Replace MATCH% vars in the specified string.
RecordingRule * GetRecordingRule(void)
Returns the "record" field, creating it if necessary.
QString GetTitle(void) const
Definition: programinfo.h:353
QDateTime m_recstartts
Definition: programinfo.h:773
uint32_t m_chanid
Definition: programinfo.h:750
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
CategoryType m_catType
Definition: programinfo.h:767
void SendUpdateEvent(void)
Sends event out that the ProgramInfo should be reloaded.
void ApplyStorageGroupChange(const QString &newstoragegroup)
Sets the storage group, both in this RecordingInfo and in the database.
int m_recordID
Unique Recording Rule ID.
Definition: recordingrule.h:72
uint8_t m_dupin
Definition: programinfo.h:799
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void SendAddedEvent(void) const
Sends event out that the ProgramInfo should be added to lists.
RecordingType m_type
void AddHistory(bool resched=true, bool forcedup=false, bool future=false)
Adds recording history, creating "record" it if necessary.
void SetReactivated(bool reactivate)
Definition: programinfo.h:526
int32_t m_recpriority
Definition: programinfo.h:748
uint m_recordedid
Definition: programinfo.h:802
RecordingFile * GetRecordingFile() const
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:368
void SetDupHistory(void)
Set the duplicate flag in oldrecorded.
static uint GetRecgroupID(const QString &recGroup)
Temporary helper during transition from string to ID.
iterator begin(void)
void LoadRecordingFile()
void SaveFilesize(uint64_t fsize) override
Sets recording file size in database, and sets "filesize" field.
static QStringList LoadFromScheduler(const QString &tmptable, int recordid)
static QMutex s_staticDataLock
Definition: programinfo.h:821
uint8_t m_dupmethod
Definition: programinfo.h:800
QString GetStorageGroup(void) const
Definition: programinfo.h:414
QString m_chanstr
Definition: programinfo.h:751
void FinishedRecording(bool allowReRecord)
If not a premature stop, adds program to history of recorded programs.
RecStatus::Type m_oldrecstatus
int8_t m_recstatus
Definition: programinfo.h:797
RecordingDupMethodType
int GetAutoRunJobs(void) const
Returns a bitmap of which jobs are attached to this RecordingInfo.
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:382
QVariant value(int i) const
Definition: mythdbcon.h:198
bool Save(bool sendSig=true)
virtual void clone(const RecordingInfo &other, bool ignore_non_serialized_data=false)
Copies important fields from other RecordingInfo.
Holds information on recordings and videos.
Definition: programinfo.h:66
void ApplyTranscoderProfileChangeById(int)
void ApplyRecordStateChange(RecordingType newstate, bool save=true)
Sets RecordingType of "record", creating "record" if it does not exist.
void SetScheduledEndTime(const QDateTime &dt)
Definition: programinfo.h:509
virtual void SaveFilesize(uint64_t fsize)
Sets recording file size in database, and sets "filesize" field.
float m_stars
Rating, range [0..1].
Definition: programinfo.h:776
virtual ~RecordingInfo()
Destructor deletes "record" if it exists.
QDateTime m_desiredrecendts
QVariant lastInsertId()
Return the id of the last inserted row.
Definition: mythdbcon.cpp:887
QString m_recgroup
Definition: programinfo.h:756
bool empty(void) const
void insert(uint recordedid, PIAction action, uint64_t filesize=0ULL)
QString m_storageDeviceID
Definition: recordingfile.h:40
void ForgetHistory(void)
Forget the recording of a program so it will be recorded again.
QString GetBasename(void) const
Definition: programinfo.h:336
bool m_autoMetadataLookup
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
static void RescheduleCheck(const RecordingInfo &recinfo, const QString &why)
bool isActive(void) const
Definition: mythdbcon.h:204
bool IsRepeat(void) const
Definition: programinfo.h:478
QString m_programid
Definition: programinfo.h:765
bool IsGeneric(void) const
QString m_hostname
Definition: programinfo.h:761
static void ReschedulePlace(const QString &why)
QString m_chanplaybackfilters
Definition: programinfo.h:754
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:565
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
Internal representation of a recording rule, mirrors the record table.
Definition: recordingrule.h:32
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:389
QString m_storagegroup
Definition: programinfo.h:762
void UpdateRecordingEnd(void)
Update information in the recorded table when the end-time of a recording is changed.
Holds information on a recording file and it's video and audio streams.
Definition: recordingfile.h:29
void ApplyRecordRecGroupChange(const QString &newrecgroup)
Sets the recording group, both in this RecordingInfo and in the database.
uint32_t m_programflags
ProgramFlag.
Definition: programinfo.h:790
float clamp(float val, float minimum, float maximum)
Definition: mythmiscutil.h:41
void QuickRecord(void)
Create a kSingleRecord if not already scheduled.
uint32_t m_findid
Definition: programinfo.h:788
uint16_t m_parttotal
Definition: programinfo.h:795
void clear(void) override
#define kVideoPropertyOffset
Definition: programtypes.h:195
void ApplyTranscoderProfileChange(const QString &profile) const
Sets the transcoder profile for a recording.
QString GetProgramRecordingProfile(void) const
Returns recording profile name that will be, or was used, for this program, creating "record" field i...
static ProgramInfoUpdater * s_updater
Definition: programinfo.h:822
uint64_t GetFilesize(void) const override
uint32_t m_sourceid
Definition: programinfo.h:786
void StartedRecording(const QString &ext)
Inserts this RecordingInfo into the database as an existing recording.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:807
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
uint16_t m_year
Definition: programinfo.h:793
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:439
static bool InsertProgram(RecordingInfo *pg, const RecordingRule *rule)
bool LoadFromProgram(ProgramList &destination, const QString &where, const QString &groupBy, const QString &orderBy, const MSqlBindings &bindings, const ProgramList &schedList)
virtual void clear(void)
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...
QDateTime m_desiredrecstartts
static QString GetRecgroupString(uint recGroupID)
Temporary helper during transition from string to ID.
QDateTime m_startts
Definition: programinfo.h:771
QString m_recProfile
QString CreateRecordBasename(const QString &ext) const
Returns a filename for a recording based on the recording channel and date.
QDateTime m_recendts
Definition: programinfo.h:774
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:396
int getRecordID(void)
Returns a record id, creating "record" it if necessary.
int numRowsAffected() const
Definition: mythdbcon.h:206
void SetRecordingEndTime(const QDateTime &dt)
Definition: programinfo.h:511
QString m_playgroup
Definition: programinfo.h:757
void ApplyRecordRecID(void)
Sets recordid to match RecordingRule recordid.
virtual void SetFilesize(uint64_t sz)
QString m_subtitle
Definition: programinfo.h:738
QString m_title
Definition: programinfo.h:736
virtual void clone(const ProgramInfo &other, bool ignore_non_serialized_data=false)
Copies important fields from other ProgramInfo.
QString m_seriesid
Definition: programinfo.h:764
QString GetHostname(void) const
Definition: programinfo.h:413
QString GetInputName(void) const
Definition: programinfo.h:456
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:98
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
class RecordingRule * m_record
QString m_inetref
Definition: programinfo.h:766
QString m_pathname
Definition: programinfo.h:759
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
void ApplyRecordRecPriorityChange(int)
Sets recording priority of "record", creating "record" if it does not exist.
uint8_t m_rectype
Definition: programinfo.h:798
void SetFilesize(uint64_t fsize) override
static QString null_to_empty(const QString &str)
uint m_episode
Definition: programinfo.h:742
void ApplyRecordPlayGroupChange(const QString &newplaygroup)
Sets the recording group, both in this RecordingInfo and in the database.
#define LOC
#define kSubtitlePropertyOffset
Definition: programtypes.h:209
static const uint kUnknownProgramLength
bool MakeOverride(void)
RecStatus::Type m_savedrecstatus
RecordingType GetProgramRecordingStatus(void)
Returns the recording type for this RecordingInfo, creating "record" field if necessary.
QString GetHostName(void)
static bool QueryRecordedIdForKey(int &recordedid, uint chanid, const QDateTime &recstartts)
Default UTC.
Definition: mythdate.h:14
QString m_category
Definition: programinfo.h:745
uint64_t m_fileSize
Definition: recordingfile.h:45
void SaveTotalDuration(int64_t duration)
Store the Total Duration at frame 0 in the recordedmarkup table.