MythTV  master
recordingrule.cpp
Go to the documentation of this file.
1 
2 #include "recordingrule.h"
3 
4 #include <utility>
5 
6 // libmythbase
7 #include "mythdb.h"
8 
9 // libmyth
10 #include "mythcorecontext.h"
11 
12 // libmythtv
13 #include "scheduledrecording.h" // For RescheduleMatch()
14 #include "playgroup.h" // For GetInitialName()
15 #include "recordingprofile.h" // For constants
16 #include "mythdate.h"
17 #include "mythsorthelper.h"
18 
19 static inline QString null_to_empty(const QString &str)
20 {
21  return str.isEmpty() ? "" : str;
22 }
23 
24 // If the GetNumSetting() calls here are ever removed, update schema
25 // upgrade 1302 in dbcheck.cpp to manually apply them to the Default
26 // template. Failing to do so will cause users upgrading from older
27 // versions to lose those settings.
28 
30  : m_findtime(QTime::fromString("00:00:00", Qt::ISODate)),
31  m_findid(QDate(1970, 1, 1).daysTo(MythDate::current().toLocalTime().date())
32  + 719528),
33  m_transcoder(RecordingProfile::TranscoderAutodetect)
34 {
35  QDateTime dt = MythDate::current();
36  m_enddate = m_startdate = dt.date();
37  m_endtime = m_starttime = dt.time();
38 
40 }
41 
46 {
47  std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
48 
49  if (m_sortTitle.isEmpty() and not m_title.isEmpty())
50  m_sortTitle = sh->doTitle(m_title);
51  if (m_sortSubtitle.isEmpty() and not m_subtitle.isEmpty())
52  m_sortSubtitle = sh->doTitle(m_subtitle);
53 }
54 
58 bool RecordingRule::Load(bool asTemplate)
59 {
60  if (m_recordID <= 0)
61  return false;
62 
64  query.prepare("SELECT type, search, "
65  "recpriority, prefinput, startoffset, endoffset, dupmethod, dupin, "
66  "inactive, profile, recgroup, storagegroup, playgroup, autoexpire, "
67  "maxepisodes, maxnewest, autocommflag, autotranscode, transcoder, "
68  "autouserjob1, autouserjob2, autouserjob3, autouserjob4, "
69  "autometadata, parentid, title, subtitle, description, season, episode, "
70  "category, starttime, startdate, endtime, enddate, seriesid, programid, "
71  "inetref, chanid, station, findday, findtime, findid, "
72  "next_record, last_record, last_delete, avg_delay, filter, recgroupid "
73  "FROM record WHERE recordid = :RECORDID ;");
74 
75  query.bindValue(":RECORDID", m_recordID);
76 
77  if (!query.exec())
78  {
79  MythDB::DBError("SELECT record", query);
80  return false;
81  }
82 
83  if (!query.next())
84  return false;
85 
86  // Schedule
87  if (!asTemplate)
88  {
89  m_type = static_cast<RecordingType>(query.value(0).toInt());
90  m_searchType = static_cast<RecSearchType>(query.value(1).toInt());
91  }
92  m_recPriority = query.value(2).toInt();
93  m_prefInput = query.value(3).toInt();
94  m_startOffset = query.value(4).toInt();
95  m_endOffset = query.value(5).toInt();
96  m_dupMethod = static_cast<RecordingDupMethodType>
97  (query.value(6).toInt());
98  m_dupIn = static_cast<RecordingDupInType>(query.value(7).toInt());
99  m_filter = query.value(47).toUInt();
100  m_isInactive = query.value(8).toBool();
101 
102  // Storage
103  m_recProfile = query.value(9).toString();
104  m_recGroupID = query.value(48).toUInt();
105  m_storageGroup = query.value(11).toString();
106  m_playGroup = query.value(12).toString();
107  m_autoExpire = query.value(13).toBool();
108  m_maxEpisodes = query.value(14).toInt();
109  m_maxNewest = query.value(15).toBool();
110 
111  // Post Process
112  m_autoCommFlag = query.value(16).toBool();
113  m_autoTranscode = query.value(17).toBool();
114  m_transcoder = query.value(18).toInt();
115  m_autoUserJob1 = query.value(19).toBool();
116  m_autoUserJob2 = query.value(20).toBool();
117  m_autoUserJob3 = query.value(21).toBool();
118  m_autoUserJob4 = query.value(22).toBool();
119  m_autoMetadataLookup = query.value(23).toBool();
120 
121  // Original rule id for override rule
122  if (!asTemplate)
123  m_parentRecID = query.value(24).toInt();
124 
125  // Recording metadata
126  if (!asTemplate)
127  {
128  m_title = query.value(25).toString();
129  m_subtitle = query.value(26).toString();
130  m_description = query.value(27).toString();
131  m_season = query.value(28).toUInt();
132  m_episode = query.value(29).toUInt();
133  m_category = query.value(30).toString();
134  m_starttime = query.value(31).toTime();
135  m_startdate = query.value(32).toDate();
136  m_endtime = query.value(33).toTime();
137  m_enddate = query.value(34).toDate();
138  m_seriesid = query.value(35).toString();
139  m_programid = query.value(36).toString();
140  m_inetref = query.value(37).toString();
141  }
142 
143  // Associated data for rule types
144  if (!asTemplate)
145  {
146  m_channelid = query.value(38).toInt();
147  m_station = query.value(39).toString();
148  m_findday = query.value(40).toInt();
149  m_findtime = query.value(41).toTime();
150  m_findid = query.value(42).toInt();
151  }
152 
153  // Statistic fields - Used to generate statistics about particular rules
154  // and influence watch list weighting
155  if (!asTemplate)
156  {
157  m_nextRecording = MythDate::as_utc(query.value(43).toDateTime());
158  m_lastRecorded = MythDate::as_utc(query.value(44).toDateTime());
159  m_lastDeleted = MythDate::as_utc(query.value(45).toDateTime());
160  m_averageDelay = query.value(46).toInt();
161  }
162 
165  m_template = (asTemplate || m_isTemplate) ?
166  query.value(30).toString() : "";
167 
168  if (!asTemplate)
169  m_loaded = true;
170 
172  return true;
173 }
174 
176 {
177  if (!proginfo)
178  return false;
179 
180  m_progInfo = proginfo;
181 
182  m_recordID = proginfo->GetRecordingRuleID();
183  if (m_recordID)
184  {
185  if (!Load())
186  return false;
187  }
188  else
189  LoadTemplate(proginfo->GetCategory(), proginfo->GetCategoryTypeString());
190 
191  if (m_type != kTemplateRecord &&
193  {
195  if (!proginfo->GetRecordingRuleID())
197  }
198 
200  m_loaded = true;
201  return true;
202 }
203 
220 bool RecordingRule::LoadBySearch(RecSearchType lsearch, const QString& textname,
221  const QString& forwhat, QString joininfo,
222  ProgramInfo *pginfo)
223 {
224  MSqlQuery query(MSqlQuery::InitCon());
225 
226  int rid = 0;
227  query.prepare("SELECT recordid FROM record WHERE "
228  "search = :SEARCH AND description LIKE :FORWHAT");
229  query.bindValue(":SEARCH", lsearch);
230  query.bindValue(":FORWHAT", forwhat);
231 
232  if (query.exec())
233  {
234  if (query.next())
235  rid = query.value(0).toInt();
236  // else rid is zero, which is valid, we're looking at a new rule
237  }
238  else
239  {
240  MythDB::DBError("loadBySearch", query);
241  return false;
242  }
243 
244  if (rid)
245  {
246  m_recordID = rid;
247  if (!Load())
248  return false;
249  }
250  else
251  {
252  LoadTemplate("Default");
253 
254  QString searchType;
255  m_searchType = lsearch;
256  searchType = SearchTypeToString(m_searchType);
257 
258  QString ltitle = QString("%1 (%2)").arg(textname).arg(searchType);
259  m_title = ltitle;
260  m_sortTitle = nullptr;
261  m_subtitle = m_sortSubtitle = std::move(joininfo);
262  m_description = forwhat;
263 
264  if (pginfo)
265  {
266  m_findday =
267  (pginfo->GetScheduledStartTime().toLocalTime().date()
268  .dayOfWeek() + 1) % 7;
269  m_findtime = pginfo->GetScheduledStartTime().toLocalTime().time();
270  m_findid = QDate(1970, 1, 1).daysTo(
271  pginfo->GetScheduledStartTime().toLocalTime().date()) + 719528;
272  }
273  }
274 
276  m_loaded = true;
277  return true;
278 }
279 
280 bool RecordingRule::LoadTemplate(const QString& category, const QString& categoryType)
281 {
282  MSqlQuery query(MSqlQuery::InitCon());
283  query.prepare("SELECT recordid, category, "
284  " (category = :CAT1) AS catmatch, "
285  " (category = :CATTYPE1) AS typematch "
286  "FROM record "
287  "WHERE type = :TEMPLATE AND "
288  " (category = :CAT2 OR category = :CATTYPE2 "
289  " OR category = 'Default') "
290  "ORDER BY catmatch DESC, typematch DESC"
291  );
292  query.bindValue(":TEMPLATE", kTemplateRecord);
293  query.bindValue(":CAT1", category);
294  query.bindValue(":CAT2", category);
295  query.bindValue(":CATTYPE1", categoryType);
296  query.bindValue(":CATTYPE2", categoryType);
297 
298  if (!query.exec())
299  {
300  MythDB::DBError("LoadByTemplate", query);
301  return false;
302  }
303 
304  if (!query.next())
305  return false;
306 
307  int savedRecordID = m_recordID;
308  m_recordID = query.value(0).toInt();
309  bool result = Load(true);
310  m_recordID = savedRecordID;
311 
312  return result;
313 }
314 
315 bool RecordingRule::MakeTemplate(QString category)
316 {
317  if (m_recordID > 0)
318  return false;
319 
320  if (category.compare(tr("Default"), Qt::CaseInsensitive) == 0)
321  {
322  category = "Default";
323  m_title = tr("Default (Template)");
325  }
326  else
327  {
328  //: %1 is the category
329  m_title = tr("%1 (Template)").arg(category);
331  }
332 
333  LoadTemplate(category);
334  m_recordID = 0;
336  m_category = category;
337  m_loaded = true;
338  m_isTemplate = true;
339 
340  return true;
341 }
342 
343 bool RecordingRule::ModifyPowerSearchByID(int rid, const QString& textname,
344  QString forwhat, QString joininfo)
345 {
346  if (rid <= 0)
347  return false;
348 
349  m_recordID = rid;
350  if (!Load() || m_searchType != kPowerSearch)
351  return false;
352 
353  QString ltitle = QString("%1 (%2)").arg(textname)
354  .arg(tr("Power Search"));
355  m_title = ltitle;
356  m_sortTitle = nullptr;
357  m_subtitle = m_sortSubtitle = std::move(joininfo);
358  m_description = std::move(forwhat);
359 
361  m_loaded = true;
362  return true;
363 }
364 
366 {
367  if (m_recordID <= 0)
368  return false;
369 
371  return false;
372 
373  m_isOverride = true;
375  m_recordID = 0;
377  m_isInactive = false;
378 
381 
383 
384  return true;
385 }
386 
387 bool RecordingRule::Save(bool sendSig)
388 {
389  QString sql = "SET type = :TYPE, search = :SEARCHTYPE, "
390  "recpriority = :RECPRIORITY, prefinput = :INPUT, "
391  "startoffset = :STARTOFFSET, endoffset = :ENDOFFSET, "
392  "dupmethod = :DUPMETHOD, dupin = :DUPIN, "
393  "filter = :FILTER, "
394  "inactive = :INACTIVE, profile = :RECPROFILE, "
395  "recgroup = :RECGROUP, "
396  "recgroupid = :RECGROUPID, "
397  "storagegroup = :STORAGEGROUP, "
398  "playgroup = :PLAYGROUP, autoexpire = :AUTOEXPIRE, "
399  "maxepisodes = :MAXEPISODES, maxnewest = :MAXNEWEST, "
400  "autocommflag = :AUTOCOMMFLAG, "
401  "autotranscode = :AUTOTRANSCODE, "
402  "transcoder = :TRANSCODER, autouserjob1 = :AUTOUSERJOB1, "
403  "autouserjob2 = :AUTOUSERJOB2, "
404  "autouserjob3 = :AUTOUSERJOB3, "
405  "autouserjob4 = :AUTOUSERJOB4, "
406  "autometadata = :AUTOMETADATA, "
407  "parentid = :PARENTID, title = :TITLE, "
408  "subtitle = :SUBTITLE, season = :SEASON, episode = :EPISODE, "
409  "description = :DESCRIPTION, category = :CATEGORY, "
410  "starttime = :STARTTIME, startdate = :STARTDATE, "
411  "endtime = :ENDTIME, enddate = :ENDDATE, seriesid = :SERIESID, "
412  "programid = :PROGRAMID, inetref = :INETREF, chanid = :CHANID, "
413  "station = :STATION, findday = :FINDDAY, "
414  "findtime = :FINDTIME, findid = :FINDID, "
415  "next_record = :NEXTREC, last_record = :LASTREC, "
416  "last_delete = :LASTDELETE, avg_delay = :AVGDELAY ";
417 
418  QString sqlquery;
419  if (m_recordID > 0 || (m_recordTable != "record" && m_tempID > 0))
420  sqlquery = QString("UPDATE %1 %2 WHERE recordid = :RECORDID;")
421  .arg(m_recordTable).arg(sql);
422  else
423  sqlquery = QString("INSERT INTO %1 %2;").arg(m_recordTable).arg(sql);
424 
425  MSqlQuery query(MSqlQuery::InitCon());
426  query.prepare(sqlquery);
427  query.bindValue(":TYPE", m_type);
428  query.bindValue(":SEARCHTYPE", m_searchType);
429  query.bindValue(":RECPRIORITY", m_recPriority);
430  query.bindValue(":INPUT", m_prefInput);
431  query.bindValue(":STARTOFFSET", m_startOffset);
432  query.bindValue(":ENDOFFSET", m_endOffset);
433  query.bindValue(":DUPMETHOD", m_dupMethod);
434  query.bindValue(":DUPIN", m_dupIn);
435  query.bindValue(":FILTER", m_filter);
436  query.bindValue(":INACTIVE", m_isInactive);
437  query.bindValue(":RECPROFILE", null_to_empty(m_recProfile));
438  // Temporary, remove once transition to recgroupid is complete
440  query.bindValue(":RECGROUPID", m_recGroupID);
441  query.bindValue(":STORAGEGROUP", null_to_empty(m_storageGroup));
442  query.bindValue(":PLAYGROUP", null_to_empty(m_playGroup));
443  query.bindValue(":AUTOEXPIRE", m_autoExpire);
444  query.bindValue(":MAXEPISODES", m_maxEpisodes);
445  query.bindValue(":MAXNEWEST", m_maxNewest);
446  query.bindValue(":AUTOCOMMFLAG", m_autoCommFlag);
447  query.bindValue(":AUTOTRANSCODE", m_autoTranscode);
448  query.bindValue(":TRANSCODER", m_transcoder);
449  query.bindValue(":AUTOUSERJOB1", m_autoUserJob1);
450  query.bindValue(":AUTOUSERJOB2", m_autoUserJob2);
451  query.bindValue(":AUTOUSERJOB3", m_autoUserJob3);
452  query.bindValue(":AUTOUSERJOB4", m_autoUserJob4);
453  query.bindValue(":AUTOMETADATA", m_autoMetadataLookup);
454  query.bindValue(":PARENTID", m_parentRecID);
455  query.bindValue(":TITLE", m_title);
456  query.bindValue(":SUBTITLE", null_to_empty(m_subtitle));
457  query.bindValue(":DESCRIPTION", null_to_empty(m_description));
458  query.bindValue(":SEASON", m_season);
459  query.bindValue(":EPISODE", m_episode);
460  query.bindValue(":CATEGORY", null_to_empty(m_category));
461  query.bindValue(":STARTTIME", m_starttime);
462  query.bindValue(":STARTDATE", m_startdate);
463  query.bindValue(":ENDTIME", m_endtime);
464  query.bindValue(":ENDDATE", m_enddate);
465  query.bindValue(":SERIESID", null_to_empty(m_seriesid));
466  query.bindValue(":PROGRAMID", null_to_empty(m_programid));
467  query.bindValue(":INETREF", null_to_empty(m_inetref));
468  query.bindValue(":CHANID", m_channelid);
469  query.bindValue(":STATION", null_to_empty(m_station));
470  query.bindValue(":FINDDAY", m_findday);
471  query.bindValue(":FINDTIME", m_findtime);
472  query.bindValue(":FINDID", m_findid);
473  query.bindValue(":NEXTREC", m_nextRecording);
474  query.bindValue(":LASTREC", m_lastRecorded);
475  query.bindValue(":LASTDELETE", m_lastDeleted);
476  query.bindValue(":AVGDELAY", m_averageDelay);
477 
478  if (m_recordTable != "record" && m_tempID > 0)
479  query.bindValue(":RECORDID", m_tempID);
480  else if (m_recordID > 0)
481  query.bindValue(":RECORDID", m_recordID);
482 
483  if (!query.exec())
484  {
485  MythDB::DBError("UPDATE/INSERT record", query);
486  return false;
487  }
488 
489  if (m_recordTable != "record" && m_tempID <= 0)
490  m_tempID = query.lastInsertId().toInt();
491  else if (m_recordID <= 0)
492  m_recordID = query.lastInsertId().toInt();
493 
494  if (sendSig)
496  QString("SaveRule %1").arg(m_title));
497 
498  return true;
499 }
500 
501 bool RecordingRule::Delete(bool sendSig)
502 {
503  if (m_recordID < 0)
504  return false;
505 
506  QString querystr;
507  MSqlQuery query(MSqlQuery::InitCon());
508  query.prepare("DELETE FROM record WHERE recordid = :RECORDID");
509  query.bindValue(":RECORDID", m_recordID);
510  if (!query.exec())
511  {
512  MythDB::DBError("ScheduledRecording::remove -- record", query);
513  return false;
514  }
515 
516  query.prepare("DELETE FROM oldfind WHERE recordid = :RECORDID");
517  query.bindValue(":RECORDID", m_recordID);
518  if (!query.exec())
519  {
520  MythDB::DBError("ScheduledRecording::remove -- oldfind", query);
521  }
522 
524  {
525  query.prepare("DELETE FROM program WHERE manualid = :RECORDID");
526  query.bindValue(":RECORDID", m_recordID);
527  if (!query.exec())
528  {
529  MythDB::DBError("ScheduledRecording::remove -- oldfind", query);
530  }
531  }
532 
533  if (sendSig)
535  QString("DeleteRule %1").arg(m_title));
536 
537  // Set m_recordID to zero, the rule is no longer in the database so it's
538  // not valid. Should you want, this allows a rule to be removed from the
539  // database and then re-inserted with Save()
540  m_recordID = 0;
541 
542  return true;
543 }
544 
545 void RecordingRule::ToMap(InfoMap &infoMap) const
546 {
547  if (m_title == "Default (Template)")
548  {
549  infoMap["title"] = tr("Default (Template)");
550  infoMap["sorttitle"] = tr("Default (Template)");
551  }
552  else
553  {
554  infoMap["title"] = m_title;
555  infoMap["sorttitle"] = m_sortTitle;
556  }
557  infoMap["subtitle"] = m_subtitle;
558  infoMap["sortsubtitle"] = m_sortSubtitle;
559  infoMap["description"] = m_description;
560  infoMap["season"] = QString::number(m_season);
561  infoMap["episode"] = QString::number(m_episode);
562 
563  if (m_category == "Default")
564  infoMap["category"] = tr("Default", "category");
565  else
566  infoMap["category"] = m_category;
567  infoMap["callsign"] = m_station;
568 
569  QDateTime starttm(m_startdate, m_starttime, Qt::UTC);
570  infoMap["starttime"] = MythDate::toString(starttm, MythDate::kTime);
571  infoMap["startdate"] = MythDate::toString(
573 
574  QDateTime endtm(m_enddate, m_endtime, Qt::UTC);
575  infoMap["endtime"] = MythDate::toString(endtm, MythDate::kTime);
576  infoMap["enddate"] = MythDate::toString(
578 
579  infoMap["inetref"] = m_inetref;
580  infoMap["chanid"] = m_channelid;
581  infoMap["channel"] = m_station;
582 
583  QDateTime startts(m_startdate, m_starttime, Qt::UTC);
584  QDateTime endts(m_enddate, m_endtime, Qt::UTC);
585 
586  QString length;
587  int hours, minutes, seconds;
588  seconds = startts.secsTo(endts);
589 
590  minutes = seconds / 60;
591  hours = minutes / 60;
592  minutes = minutes % 60;
593 
594  infoMap["lenmins"] = QCoreApplication::translate("(Common)", "%n minute(s)",
595  "", minutes);
596 
597  QString minstring = QCoreApplication::translate("(Common)", "%n minute(s)",
598  "", minutes);
599 
600  QString hourstring = QCoreApplication::translate("(Common)", "%n hour(s)",
601  "", hours);
602 
603  if (hours > 0)
604  {
605  //: Time duration, %1 is replaced by the hours, %2 by the minutes
606  infoMap["lentime"] = QCoreApplication::translate("(Common)", "%1 %2",
607  "Hours and minutes").arg(hourstring).arg(minstring);
608  }
609  else
610  infoMap["lentime"] = minstring;
611 
612 
613  infoMap["timedate"] = MythDate::toString(
614  startts, MythDate::kDateTimeFull | MythDate::kSimplify) + " - " +
616 
617  infoMap["shorttimedate"] =
619  startts, MythDate::kDateTimeShort | MythDate::kSimplify) + " - " +
621 
623  {
624  QDateTime ldt =
625  QDateTime(MythDate::current().toLocalTime().date(), m_findtime,
626  Qt::LocalTime);
627  QString findfrom = MythDate::toString(ldt, MythDate::kTime);
628  if (m_type == kWeeklyRecord)
629  {
630  int daynum = (m_findday + 5) % 7 + 1;
631  findfrom = QString("%1, %2")
632  .arg(gCoreContext->GetQLocale().dayName(daynum, QLocale::ShortFormat))
633  .arg(findfrom);
634  }
635  infoMap["subtitle"] = tr("(%1 or later) %3",
636  "e.g. (Sunday or later) program "
637  "subtitle").arg(findfrom)
638  .arg(m_subtitle);
639  }
640 
641  infoMap["searchtype"] = SearchTypeToString(m_searchType);
642  if (m_searchType != kNoSearch)
643  infoMap["searchforwhat"] = m_description;
644 
645  using namespace MythDate;
646  if (m_nextRecording.isValid())
647  {
648  infoMap["nextrecording"] = MythDate::toString(
650  }
651  if (m_lastRecorded.isValid())
652  {
653  infoMap["lastrecorded"] = MythDate::toString(
655  }
656  if (m_lastDeleted.isValid())
657  {
658  infoMap["lastdeleted"] = MythDate::toString(
660  }
661 
662  infoMap["ruletype"] = toString(m_type);
663  infoMap["rectype"] = toString(m_type);
664 
665  if (m_template == "Default")
666  infoMap["template"] = tr("Default", "Default template");
667  else
668  infoMap["template"] = m_template;
669 }
670 
671 void RecordingRule::UseTempTable(bool usetemp, const QString& table)
672 {
674 
675  if (usetemp)
676  {
677  m_recordTable = table;
678 
679  query.prepare("SELECT GET_LOCK(:LOCK, 2);");
680  query.bindValue(":LOCK", "DiffSchedule");
681  if (!query.exec())
682  {
683  MythDB::DBError("Obtaining lock in testRecording", query);
684  return;
685  }
686 
687  query.prepare(QString("DROP TABLE IF EXISTS %1;").arg(table));
688  if (!query.exec())
689  {
690  MythDB::DBError("Deleting old table in testRecording", query);
691  return;
692  }
693 
694  query.prepare(QString("CREATE TABLE %1 SELECT * FROM record;")
695  .arg(table));
696  if (!query.exec())
697  {
698  MythDB::DBError("Create new temp table", query);
699  return;
700  }
701 
702  query.prepare(QString("ALTER TABLE %1 MODIFY recordid int(10) "
703  "UNSIGNED NOT NULL AUTO_INCREMENT primary key;")
704  .arg(table));
705  if (!query.exec())
706  {
707  MythDB::DBError("Modify recordid column to include "
708  "auto-increment", query);
709  return;
710  }
711 
712  if (m_recordID > 0)
714 
715  Save(false);
716  }
717  else
718  {
719  query.prepare("SELECT RELEASE_LOCK(:LOCK);");
720  query.bindValue(":LOCK", "DiffSchedule");
721  if (!query.exec())
722  {
723  MythDB::DBError("Free lock", query);
724  return;
725  }
726  m_recordTable = "record";
727  m_tempID = 0;
728  }
729 }
730 
732 {
733  if (!m_progInfo)
734  return;
735 
749  if (m_recordID <= 0)
750  {
751  m_findday =
752  (m_progInfo->GetScheduledStartTime().toLocalTime().date()
753  .dayOfWeek() + 1) % 7;
754  m_findtime = m_progInfo->GetScheduledStartTime().toLocalTime().time();
755  m_findid = QDate(1970, 1, 1).daysTo(
756  m_progInfo->GetScheduledStartTime().toLocalTime().date()) + 719528;
757  }
758  else
759  {
760  if (m_findid > 0)
762  else
763  {
764  QDate epoch(1970, 1, 1);
765  m_findid = epoch.daysTo(
766  m_progInfo->GetScheduledStartTime().toLocalTime().date())
767  + 719528;
768  }
769  }
771 
772  if (m_inetref.isEmpty())
773  {
777  }
779 }
780 
782 {
783  MSqlQuery query(MSqlQuery::InitCon());
784  query.prepare("SELECT SUM(1 << filterid) FROM recordfilter "
785  "WHERE filterid >= 0 AND filterid < :NUMFILTERS AND "
786  " TRIM(clause) <> '' AND newruledefault <> 0");
787  query.bindValue(":NUMFILTERS", RecordingRule::kNumFilters);
788  if (!query.exec())
789  {
790  MythDB::DBError("GetDefaultFilter", query);
791  return 0;
792  }
793 
794  if (!query.next())
795  return 0;
796 
797  return query.value(0).toUInt();
798 }
799 
801 {
802  QString searchTypeString;
803 
804  switch (searchType)
805  {
806  case kNoSearch:
807  searchTypeString = ""; // Allow themers to decide what to display
808  break;
809  case kPowerSearch:
810  searchTypeString = tr("Power Search");
811  break;
812  case kTitleSearch:
813  searchTypeString = tr("Title Search");
814  break;
815  case kKeywordSearch:
816  searchTypeString = tr("Keyword Search");
817  break;
818  case kPeopleSearch:
819  searchTypeString = tr("People Search");
820  break;
821  default:
822  searchTypeString = tr("Unknown Search");
823  break;
824  }
825 
826  return searchTypeString;
827 }
828 
830 {
831  QStringList result;
832 
833  MSqlQuery query(MSqlQuery::InitCon());
834  query.prepare("SELECT category "
835  "FROM record "
836  "WHERE type = :TEMPLATE "
837  "ORDER BY category = 'Default' DESC, category"
838  );
839  query.bindValue(":TEMPLATE", kTemplateRecord);
840 
841  if (!query.exec())
842  {
843  MythDB::DBError("LoadByTemplate", query);
844  return result;
845  }
846 
847  while (query.next())
848  result << query.value(0).toString();
849 
850  return result;
851 }
852 
853 bool RecordingRule::IsValid(QString &msg)
854 {
855  bool isOverride = false;
856  switch (m_type)
857  {
858  case kSingleRecord:
859  case kDailyRecord:
860  case kAllRecord:
861  case kWeeklyRecord:
862  case kOneRecord:
863  case kTemplateRecord:
864  break;
865  case kOverrideRecord:
866  case kDontRecord:
867  isOverride = true;
868  break;
869  case kNotRecording:
870  default:
871  msg = QString("Invalid recording type.");
872  return false;
873  }
874 
875  bool isNormal = false;
876  bool isSearch = false;
877  bool isManual = false;
878  switch (m_searchType)
879  {
880  case kNoSearch:
881  isNormal = true;
882  break;
883  case kPowerSearch:
884  case kTitleSearch:
885  case kKeywordSearch:
886  case kPeopleSearch:
887  isSearch = true;
888  break;
889  case kManualSearch:
890  isManual = true;
891  break;
892  default:
893  msg = QString("Invalid search type.");
894  return false;
895  }
896 
897  if ((isNormal && (m_type == kDailyRecord || m_type == kWeeklyRecord)) ||
898  (isSearch && (m_type != kDailyRecord && m_type != kWeeklyRecord &&
899  m_type != kOneRecord && m_type != kAllRecord)) ||
900  (isManual && (m_type != kDailyRecord && m_type != kWeeklyRecord &&
902  {
903  msg = QString("Invalid recording type/search type.");
904  return false;
905  }
906 
907  if ((m_parentRecID && !isOverride) ||
908  (!m_parentRecID && isOverride))
909  {
910  msg = QString("Invalid parentID/override.");
911  return false;
912  }
913 
914  if (m_title.isEmpty())
915  {
916  msg = QString("Invalid title.");
917  return false;
918  }
919 
920  if (m_searchType == kPowerSearch)
921  {
922  MSqlQuery query(MSqlQuery::InitCon());
923  query.prepare(QString("SELECT NULL FROM (program, channel) "
924  "%1 WHERE %2")
925  .arg(m_subtitle).arg(m_description));
926  if (m_description.contains(';') || !query.exec())
927  {
928  msg = QString("Invalid custom search values.");
929  return false;
930  }
931  }
932  else if (isSearch)
933  {
934  if (m_description.isEmpty())
935  {
936  msg = QString("Invalid search value.");
937  return false;
938  }
939  }
940 
941  if (!isSearch)
942  {
943  if (!m_startdate.isValid() || !m_starttime.isValid() ||
944  !m_enddate.isValid() || !m_endtime.isValid())
945  {
946  msg = QString("Invalid start/end date/time.");
947  return false;
948  }
949  int secsto = QDateTime(m_startdate, m_starttime)
950  .secsTo(QDateTime(m_enddate, m_endtime));
951  if (secsto <= 0 || secsto > (8 * 3600))
952  {
953  msg = QString("Invalid duration.");
954  return false;
955  }
956 
957  if (!m_channelid || m_station.isEmpty())
958  {
959  msg = QString("Invalid channel.");
960  return false;
961  }
962  }
963 
964  if (m_findday < 0 || m_findday > 6 || !m_findtime.isValid())
965  {
966  msg = QString("Invalid find values.");
967  return false;
968  }
969 
970  if (m_recPriority < -99 || m_recPriority > 99)
971  {
972  msg = QString("Invalid priority.");
973  return false;
974  }
975 
976  if (m_startOffset < -480 || m_startOffset > 480 ||
977  m_endOffset < -480 || m_endOffset > 480)
978  {
979  msg = QString("Invalid start/end offset.");
980  return false;
981  }
982 
983  if (m_prefInput < 0)
984  {
985  msg = QString("Invalid preferred input.");
986  return false;
987  }
988 
989  unsigned valid_mask = (1 << kNumFilters) - 1;
990  if ((m_filter & valid_mask) != m_filter)
991  {
992  msg = QString("Invalid filter value.");
993  return false;
994  }
995 
996  if (m_recProfile.isEmpty() || (m_recGroupID == 0) ||
997  m_storageGroup.isEmpty() || m_playGroup.isEmpty())
998  {
999  msg = QString("Invalid group value.");
1000  return false;
1001  }
1002 
1003  if (m_maxEpisodes < 0)
1004  {
1005  msg = QString("Invalid max episodes value.");
1006  return false;
1007  }
1008 
1009  if (m_dupIn & ~(kDupsInAll | kDupsNewEpi))
1010  {
1011  msg = QString("Invalid duplicate scope.");
1012  return false;
1013  }
1014 
1017  {
1018  msg = QString("Invalid duplicate method.");
1019  return false;
1020  }
1021 
1022  if (m_transcoder < 0)
1023  {
1024  msg = QString("Invalid transcoder value.");
1025  return false;
1026  }
1027 
1028  return true;
1029 }
QString m_subtitle
Definition: recordingrule.h:81
enum RecSearchTypes RecSearchType
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 m_isInactive
Recording rule is enabled?
Definition: recordingrule.h:76
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
QString m_programid
Definition: recordingrule.h:94
bool LoadByProgram(const ProgramInfo *proginfo)
QString m_sortTitle
Definition: recordingrule.h:80
bool Delete(bool sendSig=true)
QString toString(MarkTypes type)
void ToMap(InfoMap &infoMap) const
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
static QString SearchTypeToString(const RecSearchType searchType)
QString GetTitle(void) const
Definition: programinfo.h:353
QString m_storageGroup
QString m_station
int m_recordID
Unique Recording Rule ID.
Definition: recordingrule.h:72
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
RecordingType m_type
Default local time.
Definition: mythdate.h:21
RecordingDupMethodType m_dupMethod
void ensureSortFields(void)
Ensure that the sortTitle and sortSubtitle fields are set.
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
Definition: mythdate.cpp:23
bool IsValid(QString &msg)
QString GetInetRef(void) const
Definition: programinfo.h:429
QDateTime m_nextRecording
static QString GetInitialName(const ProgramInfo *pi)
Definition: playgroup.cpp:224
static QString null_to_empty(const QString &str)
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)
Holds information on recordings and videos.
Definition: programinfo.h:66
QDateTime m_lastRecorded
QString GetChannelSchedulingID(void) const
This is the unique programming identifier of a channel.
Definition: programinfo.h:375
QVariant lastInsertId()
Return the id of the last inserted row.
Definition: mythdbcon.cpp:887
Default local time.
Definition: mythdate.h:19
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
QString GetDescription(void) const
Definition: programinfo.h:357
QString m_recordTable
bool m_autoMetadataLookup
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
int m_findday
callsign?
QString GetSortSubtitle(void) const
Definition: programinfo.h:356
QString m_playGroup
QString GetSubtitle(void) const
Definition: programinfo.h:355
Default local time.
Definition: mythdate.h:16
QString m_description
Definition: recordingrule.h:83
bool LoadTemplate(const QString &category, const QString &categoryType="Default")
static MSqlQueryInfo SchedCon()
Returns dedicated connection. (Required for using temporary SQL tables.)
Definition: mythdbcon.cpp:565
QString m_template
uint GetSeason(void) const
Definition: programinfo.h:358
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
Do Today/Yesterday/Tomorrow transform.
Definition: mythdate.h:23
QString GetCategoryTypeString(void) const
Returns catType as a string.
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:389
bool ModifyPowerSearchByID(int rid, const QString &textname, QString forwhat, QString joininfo="")
static void RescheduleMatch(uint recordid, uint sourceid, uint mplexid, const QDateTime &maxstarttime, const QString &why)
static unsigned GetDefaultFilter(void)
QString m_seriesid
Definition: recordingrule.h:93
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
RecordingDupInType m_dupIn
QString m_inetref
Definition: recordingrule.h:96
bool LoadBySearch(RecSearchType lsearch, const QString &textname, const QString &forwhat, QString joininfo="", ProgramInfo *pginfo=nullptr)
Load a recording rule based on search parameters.
QString GetSeriesID(void) const
Definition: programinfo.h:427
bool MakeTemplate(QString category)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:807
QString m_title
Definition: recordingrule.h:79
QLocale GetQLocale(void)
const ProgramInfo * m_progInfo
QString GetSortTitle(void) const
Definition: programinfo.h:354
QDateTime m_lastDeleted
void AssignProgramInfo()
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:364
QString m_sortSubtitle
Definition: recordingrule.h:82
static QString GetRecgroupString(uint recGroupID)
Temporary helper during transition from string to ID.
static const int kNumFilters
Definition: recordingrule.h:37
QString m_recProfile
uint GetRecordingRuleID(void) const
Definition: programinfo.h:441
Default local time.
Definition: mythdate.h:20
QString GetProgramID(void) const
Definition: programinfo.h:428
uint GetFindID(void) const
Definition: programinfo.h:462
void UseTempTable(bool usetemp, const QString &table="record_tmp")
bool Load(bool asTemplate=false)
Load a single rule from the recorded table.
unsigned m_filter
QString m_category
Definition: recordingrule.h:86
Add year to string if not included.
Definition: mythdate.h:22
static QStringList GetTemplateNames(void)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
QString GetCategory(void) const
Definition: programinfo.h:361
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
bool MakeOverride(void)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
Default UTC.
Definition: mythdate.h:14
RecSearchType m_searchType
uint GetEpisode(void) const
Definition: programinfo.h:359
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.