MythTV master
v2dvr.cpp
Go to the documentation of this file.
1
2// Program Name: v2dvr.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// Qt
27#include <QJsonArray>
28#include <QJsonDocument>
29
30// MythTV
36#include "libmythbase/mythversion.h"
38#include "libmythtv/cardutil.h"
40#include "libmythtv/jobqueue.h"
41#include "libmythtv/playgroup.h"
44#include "libmythtv/tv_rec.h"
45
46// MythBackend
47#include "autoexpire.h"
48#include "backendcontext.h"
49#include "encoderlink.h"
50#include "scheduler.h"
51#include "v2dvr.h"
52#include "v2serviceUtil.h"
53#include "v2titleInfoList.h"
54
55// This will be initialised in a thread safe manner on first use
57 (DVR_HANDLE, V2Dvr::staticMetaObject, &V2Dvr::RegisterCustomTypes))
58
60{
61 qRegisterMetaType<V2ProgramList*>("V2ProgramList");
62 qRegisterMetaType<V2Program*>("V2Program");
63 qRegisterMetaType<V2CutList*>("V2CutList");
64 qRegisterMetaType<V2Cutting*>("V2Cutting");
65 qRegisterMetaType<V2MarkupList*>("V2MarkupList");
66 qRegisterMetaType<V2Markup*>("V2Markup");
67 qRegisterMetaType<V2EncoderList*>("V2EncoderList");
68 qRegisterMetaType<V2Encoder*>("V2Encoder");
69 qRegisterMetaType<V2InputList*>("V2InputList");
70 qRegisterMetaType<V2Input*>("V2Input");
71 qRegisterMetaType<V2RecRuleFilterList*>("V2RecRuleFilterList");
72 qRegisterMetaType<V2RecRuleFilter*>("V2RecRuleFilter");
73 qRegisterMetaType<V2TitleInfoList*>("V2TitleInfoList");
74 qRegisterMetaType<V2TitleInfo*>("V2TitleInfo");
75 qRegisterMetaType<V2RecRule*>("V2RecRule");
76 qRegisterMetaType<V2RecRuleList*>("V2RecRuleList");
77 qRegisterMetaType<V2ChannelInfo*>("V2ChannelInfo");
78 qRegisterMetaType<V2RecordingInfo*>("V2RecordingInfo");
79 qRegisterMetaType<V2ArtworkInfoList*>("V2ArtworkInfoList");
80 qRegisterMetaType<V2ArtworkInfo*>("V2ArtworkInfo");
81 qRegisterMetaType<V2CastMemberList*>("V2CastMemberList");
82 qRegisterMetaType<V2CastMember*>("V2CastMember");
83 qRegisterMetaType<V2PlayGroup*>("V2PlayGroup");
84 qRegisterMetaType<V2PowerPriority*>("V2PowerPriority");
85 qRegisterMetaType<V2PowerPriorityList*>("V2PowerPriorityList");
86}
87
89 : MythHTTPService(s_service)
90{
91}
92
94 int nCount )
95{
96 pginfolist_t infoList;
97
98 if (gExpirer)
99 gExpirer->GetAllExpiring( infoList );
100
101 // ----------------------------------------------------------------------
102 // Build Response
103 // ----------------------------------------------------------------------
104
105 auto *pPrograms = new V2ProgramList();
106
107 nStartIndex = (nStartIndex > 0) ? std::min( nStartIndex, (int)infoList.size() ) : 0;
108 nCount = (nCount > 0) ? std::min( nCount, (int)infoList.size() ) : infoList.size();
109 int nEndIndex = std::min((nStartIndex + nCount), (int)infoList.size() );
110
111 for( int n = nStartIndex; n < nEndIndex; n++)
112 {
113 ProgramInfo *pInfo = infoList[ n ];
114
115 if (pInfo != nullptr)
116 {
117 V2Program *pProgram = pPrograms->AddNewProgram();
118
119 V2FillProgramInfo( pProgram, pInfo, true );
120
121 delete pInfo;
122 }
123 }
124
125 // ----------------------------------------------------------------------
126
127 pPrograms->setStartIndex ( nStartIndex );
128 pPrograms->setCount ( nCount );
129 pPrograms->setTotalAvailable( infoList.size() );
130 pPrograms->setAsOf ( MythDate::current() );
131 pPrograms->setVersion ( MYTH_BINARY_VERSION );
132 pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
133
134 return pPrograms;
135}
136
138 int nStartIndex,
139 int nCount,
140 const QString &sTitleRegEx,
141 const QString &sRecGroup,
142 const QString &sStorageGroup,
143 const QString &sCategory,
144 const QString &sSort,
145 bool bIgnoreLiveTV,
146 bool bIgnoreDeleted,
147 bool bIncChannel,
148 bool bDetails,
149 bool bIncCast,
150 bool bIncArtWork,
151 bool bIncRecording
152 )
153{
154 if (!HAS_PARAMv2("IncChannel"))
155 bIncChannel = true;
156
157 if (!HAS_PARAMv2("Details"))
158 bDetails = true;
159
160 if (!HAS_PARAMv2("IncCast"))
161 bIncCast = true;
162
163 if (!HAS_PARAMv2("IncArtwork"))
164 bIncArtWork = true;
165
166 if (!HAS_PARAMv2("IncRecording"))
167 bIncRecording = true;
168
169 QMap< QString, ProgramInfo* > recMap;
170
173
174 QMap< QString, uint32_t > inUseMap = ProgramInfo::QueryInUseMap();
175 QMap< QString, bool > isJobRunning= ProgramInfo::QueryJobsRunning(JOB_COMMFLAG);
176
177 ProgramList progList;
178
179 int desc = 1;
180 if (bDescending)
181 desc = -1;
182
183 if (bIgnoreLiveTV && (sRecGroup == "LiveTV"))
184 {
185 bIgnoreLiveTV = false;
186 LOG(VB_GENERAL, LOG_ERR, QString("Setting Ignore%1=false because RecGroup=%1")
187 .arg(sRecGroup));
188 }
189
190 if (bIgnoreDeleted && (sRecGroup == "Deleted"))
191 {
192 bIgnoreDeleted = false;
193 LOG(VB_GENERAL, LOG_ERR, QString("Setting Ignore%1=false because RecGroup=%1")
194 .arg(sRecGroup));
195 }
196
197 LoadFromRecorded( progList, false, inUseMap, isJobRunning, recMap, desc,
198 sSort, bIgnoreLiveTV, bIgnoreDeleted );
199
200 QMap< QString, ProgramInfo* >::iterator mit = recMap.begin();
201
202 for (; mit != recMap.end(); mit = recMap.erase(mit))
203 delete *mit;
204
205 // ----------------------------------------------------------------------
206 // Build Response
207 // ----------------------------------------------------------------------
208
209 auto *pPrograms = new V2ProgramList();
210
211 int nAvailable = 0;
212
213 int nMax = (nCount > 0) ? nCount : progList.size();
214
215 nAvailable = 0;
216 nCount = 0;
217
218 QRegularExpression rTitleRegEx
219 { sTitleRegEx, QRegularExpression::CaseInsensitiveOption };
220
221 for (auto *pInfo : progList)
222 {
223 if (pInfo->IsDeletePending() ||
224 (!sTitleRegEx.isEmpty() && !pInfo->GetTitle().contains(rTitleRegEx)) ||
225 (!sRecGroup.isEmpty() && sRecGroup != pInfo->GetRecordingGroup()) ||
226 (!sStorageGroup.isEmpty() && sStorageGroup != pInfo->GetStorageGroup()) ||
227 (!sCategory.isEmpty() && sCategory != pInfo->GetCategory()))
228 continue;
229
230 if ((nAvailable < nStartIndex) ||
231 (nCount >= nMax))
232 {
233 ++nAvailable;
234 continue;
235 }
236
237 ++nAvailable;
238 ++nCount;
239
240 V2Program *pProgram = pPrograms->AddNewProgram();
241 V2FillProgramInfo( pProgram, pInfo, bIncChannel, bDetails, bIncCast, bIncArtWork, bIncRecording );
242 }
243
244 // ----------------------------------------------------------------------
245
246 pPrograms->setStartIndex ( nStartIndex );
247 pPrograms->setCount ( nCount );
248 pPrograms->setTotalAvailable( nAvailable );
249 pPrograms->setAsOf ( MythDate::current() );
250 pPrograms->setVersion ( MYTH_BINARY_VERSION );
251 pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
252
253 return pPrograms;
254}
255
257// Note that you should not specify both Title and TitleRegEx, that is counter-
258// productive and would only work if the TitleRegEx matched the Title.
260
262 int nStartIndex,
263 int nCount,
264 const QDateTime &sStartTime,
265 const QDateTime &sEndTime,
266 const QString &sTitle,
267 const QString &TitleRegEx,
268 const QString &SubtitleRegEx,
269 const QString &sSeriesId,
270 int nRecordId,
271 const QString &sSort)
272{
273 if (!sStartTime.isNull() && !sStartTime.isValid())
274 throw QString("StartTime is invalid");
275
276 if (!sEndTime.isNull() && !sEndTime.isValid())
277 throw QString("EndTime is invalid");
278
279 const QDateTime& dtStartTime = sStartTime;
280 const QDateTime& dtEndTime = sEndTime;
281
282 if (!sEndTime.isNull() && dtEndTime < dtStartTime)
283 throw QString("EndTime is before StartTime");
284
285 // ----------------------------------------------------------------------
286 // Build SQL statement for Program Listing
287 // ----------------------------------------------------------------------
288
289 ProgramList progList;
290 MSqlBindings bindings;
291 QString sSQL;
292
293 if (!dtStartTime.isNull())
294 {
295 sSQL += " AND endtime >= :StartDate ";
296 bindings[":StartDate"] = dtStartTime;
297 }
298
299 if (!dtEndTime.isNull())
300 {
301 sSQL += " AND starttime <= :EndDate ";
302 bindings[":EndDate"] = dtEndTime;
303 }
304
305 QStringList clause;
306
307 if (nRecordId > 0)
308 {
309 clause << "recordid = :RecordId";
310 bindings[":RecordId"] = nRecordId;
311 }
312
313 if (!sTitle.isEmpty())
314 {
315 clause << "title = :Title";
316 bindings[":Title"] = sTitle;
317 }
318
319 if (!TitleRegEx.isEmpty())
320 {
321 clause << "title REGEXP :TitleRegEx";
322 bindings[":TitleRegEx"] = TitleRegEx;
323 }
324
325 if (!SubtitleRegEx.isEmpty())
326 {
327 clause << "subtitle REGEXP :SubtitleRegEx";
328 bindings[":SubtitleRegEx"] = SubtitleRegEx;
329 }
330
331 if (!sSeriesId.isEmpty())
332 {
333 clause << "seriesid = :SeriesId";
334 bindings[":SeriesId"] = sSeriesId;
335 }
336
337 if (!clause.isEmpty())
338 {
339 sSQL += QString(" AND (%1) ").arg(clause.join(" AND "));
340 }
341
342 QStringList sortByFields;
343 sortByFields << "starttime" << "title" << "subtitle" << "season" << "episode" << "category"
344 << "channum" << "rectype" << "recstatus" << "duration" ;
345 QStringList fields = sSort.split(",");
346 // Add starttime as last or only sort.
347 fields << "starttime";
348 sSQL += " ORDER BY ";
349 bool first = true;
350 for (const QString& oneField : std::as_const(fields))
351 {
352 QString field = oneField.simplified().toLower();
353 if (field.isEmpty())
354 continue;
355 if (sortByFields.contains(field))
356 {
357 if (first)
358 first = false;
359 else
360 sSQL += ", ";
361 if (field == "channum")
362 {
363 // this is to sort numerically rather than alphabetically
364 field = "channum*1000-ifnull(regexp_substr(channum,'-.*'),0)";
365 }
366 else if (field == "duration")
367 {
368 field = "timestampdiff(second,starttime,endtime)";
369 }
370 else if (field == "title")
371 {
372 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
373 QString prefixes = sh->getPrefixes();
374 field = "REGEXP_REPLACE(title,'" + prefixes + "','')";
375 }
376 else if (field == "subtitle")
377 {
378 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
379 QString prefixes = sh->getPrefixes();
380 field = "REGEXP_REPLACE(subtitle,'" + prefixes + "','')";
381 }
382 sSQL += field;
383 if (bDescending)
384 sSQL += " DESC ";
385 else
386 sSQL += " ASC ";
387 }
388 else
389 {
390 LOG(VB_GENERAL, LOG_WARNING, QString("V2Dvr::GetOldRecordedList() got an unknown sort field '%1' - ignoring").arg(oneField));
391 }
392 }
393
394 uint nTotalAvailable = (nStartIndex == 0) ? 1 : 0;
395 LoadFromOldRecorded( progList, sSQL, bindings,
396 (uint)nStartIndex, (uint)nCount, nTotalAvailable );
397
398 // ----------------------------------------------------------------------
399 // Build Response
400 // ----------------------------------------------------------------------
401
402 auto *pPrograms = new V2ProgramList();
403
404 nCount = (int)progList.size();
405 int nEndIndex = (int)progList.size();
406
407 for( int n = 0; n < nEndIndex; n++)
408 {
409 ProgramInfo *pInfo = progList[ n ];
410
411 V2Program *pProgram = pPrograms->AddNewProgram();
412
413 V2FillProgramInfo( pProgram, pInfo, true );
414 }
415
416 // ----------------------------------------------------------------------
417
418 pPrograms->setStartIndex ( nStartIndex );
419 pPrograms->setCount ( nCount );
420 pPrograms->setTotalAvailable( nTotalAvailable );
421 pPrograms->setAsOf ( MythDate::current() );
422 pPrograms->setVersion ( MYTH_BINARY_VERSION );
423 pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
424
425 return pPrograms;
426}
427
428bool V2Dvr::RemoveOldRecorded ( int ChanId,
429 const QDateTime &StartTime,
430 bool Reschedule )
431{
432 if (!HAS_PARAMv2("ChanId") || !HAS_PARAMv2("StartTime"))
433 throw QString("Channel ID and StartTime appears invalid.");
434 QString sql("DELETE FROM oldrecorded "
435 " WHERE chanid = :ChanId AND starttime = :StartTime" );
437 query.prepare(sql);
438 query.bindValue(":ChanId", ChanId);
439 query.bindValue(":StartTime", StartTime);
440 if (!query.exec())
441 {
442 MythDB::DBError("RemoveOldRecorded", query);
443 return false;
444 }
445 if (query.numRowsAffected() <= 0)
446 return false;
447 if (!HAS_PARAMv2("Reschedule"))
448 Reschedule = true;
449 if (Reschedule)
451 return true;
452}
453
454bool V2Dvr::UpdateOldRecorded ( int ChanId,
455 const QDateTime &StartTime,
456 bool Duplicate,
457 bool Reschedule )
458{
459 if (!HAS_PARAMv2("ChanId") || !HAS_PARAMv2("StartTime"))
460 throw QString("Channel ID and StartTime appears invalid.");
461 if (!HAS_PARAMv2("Duplicate"))
462 throw QString("Error: Nothing to change.");
463 QString sql("UPDATE oldrecorded "
464 " SET Duplicate = :Duplicate "
465 " WHERE chanid = :ChanId AND starttime = :StartTime" );
467 query.prepare(sql);
468 query.bindValue(":Duplicate", Duplicate);
469 query.bindValue(":ChanId", ChanId);
470 query.bindValue(":StartTime", StartTime);
471 if (!query.exec())
472 {
473 MythDB::DBError("UpdateOldRecorded", query);
474 return false;
475 }
476 if (!HAS_PARAMv2("Reschedule"))
477 Reschedule = true;
478 if (Reschedule)
480 return true;
481}
482
484//
486
488 int chanid, const QDateTime &StartTime)
489{
490 if ((RecordedId <= 0) &&
491 (chanid <= 0 || !StartTime.isValid()))
492 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
493
494 // TODO Should use RecordingInfo
495 ProgramInfo pi;
496 if (RecordedId > 0)
497 pi = ProgramInfo(RecordedId);
498 else
499 pi = ProgramInfo(chanid, StartTime.toUTC());
500
501 auto *pProgram = new V2Program();
502 V2FillProgramInfo( pProgram, &pi, true );
503
504 return pProgram;
505}
506
508//
510
511bool V2Dvr::AddRecordedCredits(int RecordedId, const QString & Cast)
512{
513 QJsonDocument jsonDoc = QJsonDocument::fromJson(Cast.toUtf8());
514 // Verify the corresponding recording exists
515 RecordingInfo ri(RecordedId);
516 if (!ri.HasPathname())
517 throw QString("AddRecordedCredits: recordedid %1 does "
518 "not exist.").arg(RecordedId);
519
520 DBCredits* credits = V2jsonCastToCredits(jsonDoc.object());
521 if (credits == nullptr)
522 throw QString("AddRecordedCredits: Failed to parse cast from json.");
523
525 for (auto & person : *credits)
526 {
527 if (!person.InsertDB(query, ri.GetChanID(),
528 ri.GetScheduledStartTime(), true))
529 throw QString("AddRecordedCredits: Failed to add credit "
530 "%1 to DB").arg(person.toString());
531 }
532
533 return true;
534}
535
537//
539
540int V2Dvr::AddRecordedProgram(const QString &Program)
541{
542 QJsonDocument doc = QJsonDocument::fromJson(Program.toUtf8());
543 QJsonObject program = doc.object();
544 QJsonObject channel = program["Channel"].toObject();
545 QJsonObject recording = program["Recording"].toObject();
546 QJsonObject cast = program["Cast"].toObject();
547
548 auto *pi = new ProgInfo();
549 int chanid = channel.value("ChanId").toVariant().toString().toUInt();
550
551 QString hostname = program["HostName"].toString("");
552
553 if (ChannelUtil::GetChanNum(chanid).isEmpty())
554 throw QString("AddRecordedProgram: chanid %1 does "
555 "not exist.").arg(chanid);
556
557 pi->m_title = program.value("Title").toString("");
558 pi->m_subtitle = program.value("SubTitle").toString("");
559 pi->m_description = program.value("Description").toString("");
560 pi->m_category = program.value("Category").toString("");
561 pi->m_starttime = QDateTime::fromString(program.value("StartTime")
562 .toString(""), Qt::ISODate);
563 pi->m_endtime = QDateTime::fromString(program.value("EndTime")
564 .toString(""), Qt::ISODate);
565 pi->m_originalairdate = QDate::fromString(program.value("Airdate").toString(),
567 pi->m_airdate = pi->m_originalairdate.year();
568 pi->m_partnumber = program.value("PartNumber").toString("0").toUInt();
569 pi->m_parttotal = program.value("PartTotal").toString("0").toUInt();
570 pi->m_syndicatedepisodenumber = "";
571 pi->m_subtitleType = ProgramInfo::SubtitleTypesFromNames
572 (program.value("SubPropNames").toString(""));
574 (program.value("AudioPropNames").toString(""));
576 (program.value("VideoPropNames").toString(""));
577 pi->m_stars = program.value("Stars").toVariant().toString().toFloat();
578 pi->m_categoryType = string_to_myth_category_type(program.value("CatType").toString(""));
579 pi->m_seriesId = program.value("SeriesId").toString("");
580 pi->m_programId = program.value("ProgramId").toString("");
581 pi->m_inetref = program.value("Inetref").toString("");
582 pi->m_previouslyshown = false;
583 pi->m_listingsource = 0;
584// pi->m_ratings =
585// pi->m_genres =
586 pi->m_season = program.value("Season").toVariant()
587 .toString().toUInt();
588 pi->m_episode = program.value("Episode").toVariant()
589 .toString().toUInt();
590 pi->m_totalepisodes = program.value("TotalEpisodes").toVariant()
591 .toString().toUInt();
592
593 pi->m_channel = channel.value("ChannelName").toString("");
594
595 pi->m_startts = recording.value("StartTs").toString("");
596 pi->m_endts = recording.value("EndTs").toString("");
597 QDateTime recstartts = QDateTime::fromString(pi->m_startts, Qt::ISODate);
598 QDateTime recendts = QDateTime::fromString(pi->m_endts, Qt::ISODate);
599
600 pi->m_title_pronounce = "";
601 pi->m_credits = V2jsonCastToCredits(cast);
602 pi->m_showtype = "";
603 pi->m_colorcode = "";
604 pi->m_clumpidx = "";
605 pi->m_clumpmax = "";
606
607 // pi->m_ratings =
608
609 /* Create a recordedprogram DB entry. */
611 if (!pi->InsertDB(query, chanid, true))
612 {
613 throw QString("AddRecordedProgram: "
614 "Failed to add recordedprogram entry.");
615 }
616
617 /* Create recorded DB entry */
618 RecordingInfo ri(pi->m_title, pi->m_title,
619 pi->m_subtitle, pi->m_subtitle,
620 pi->m_description,
621 pi->m_season, pi->m_episode,
622 pi->m_totalepisodes,
623 pi->m_syndicatedepisodenumber,
624 pi->m_category,
625 chanid,
626 channel.value("ChanNum").toString("0"),
627 channel.value("CallSign").toString(""),
628 pi->m_channel,
629 recording.value("RecGroup").toString(""),
630 recording.value("PlayGroup").toString(""),
631 hostname,
632 recording.value("StorageGroup").toString(""),
633 pi->m_airdate,
634 pi->m_partnumber,
635 pi->m_parttotal,
636 pi->m_seriesId,
637 pi->m_programId,
638 pi->m_inetref,
639 pi->m_categoryType,
640 recording.value("Priority").toString("0").toInt(),
641 pi->m_starttime,
642 pi->m_endtime,
643 recstartts,
644 recendts,
645 pi->m_stars,
646 pi->m_originalairdate,
647 program.value("Repeat").toString("false").toLower() == "true",
648 static_cast<RecStatus::Type>(recording.value("Status").toInt()),
649 false, // reactivate
650 recording.value("RecordedId").toString("0").toInt(),
651 0, // parentid
652 static_cast<RecordingType>(recording.value("RecType").toInt()),
653 static_cast<RecordingDupInType>(recording.value("DupInType").toInt()),
654 static_cast<RecordingDupMethodType>(recording.value("DupMethod").toInt()),
655 channel.value("SourceId").toVariant().toString().toUInt(),
656 channel.value("InputId").toVariant().toString().toUInt(),
657 0, // findid
658 channel.value("CommFree").toBool(),
659 pi->m_subtitleType,
660 pi->m_videoProps,
661 pi->m_audioProps,
662 false, // future
663 0, // schedorder
664 0, // mplexid
665 0, // sgroupid,
666 recording.value("EncoderName").toString(""));
667
668 ri.ProgramFlagsFromNames(program.value("ProgramFlagNames").toString(""));
669
670 QString filename = program.value("FileName").toString("");
671 QString ext("");
672 int idx = filename.lastIndexOf('.');
673 if (idx > 0)
674 ext = filename.right(filename.size() - idx - 1);
675 // Inserts this RecordingInfo into the database as an existing recording
676 if (!ri.InsertRecording(ext, true))
677 throw QString("Failed to create RecordingInfo database entry. "
678 "Non unique starttime?");
679
680 ri.InsertFile();
681
684 ri.SavePreserve(ri.IsPreserved());
687 ri.SaveWatched(ri.IsWatched());
688 // TODO: Cutlist
689
691 ri.SendUpdateEvent();
692
693 return ri.GetRecordingID();
694}
695
697//
699
700bool V2Dvr::RemoveRecorded(int RecordedId,
701 int chanid, const QDateTime &StartTime,
702 bool forceDelete, bool allowRerecord)
703{
704 return DeleteRecording(RecordedId, chanid, StartTime, forceDelete,
705 allowRerecord);
706}
707
708
709bool V2Dvr::DeleteRecording(int RecordedId,
710 int chanid, const QDateTime &StartTime,
711 bool forceDelete, bool allowRerecord)
712{
713 if ((RecordedId <= 0) &&
714 (chanid <= 0 || !StartTime.isValid()))
715 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
716
717 // TODO Should use RecordingInfo
718 ProgramInfo pi;
719 if (RecordedId > 0)
720 pi = ProgramInfo(RecordedId);
721 else
722 pi = ProgramInfo(chanid, StartTime.toUTC());
723
724 if (pi.GetChanID() && pi.HasPathname())
725 {
726 QString cmd = QString("DELETE_RECORDING %1 %2 %3 %4")
727 .arg(QString::number(pi.GetChanID()),
729 forceDelete ? "FORCE" : "NO_FORCE",
730 allowRerecord ? "FORGET" : "NO_FORGET");
731 MythEvent me(cmd);
732
734 return true;
735 }
736
737 return false;
738}
739
741//
743
744bool V2Dvr::UnDeleteRecording(int RecordedId,
745 int chanid, const QDateTime &StartTime)
746{
747 if ((RecordedId <= 0) &&
748 (chanid <= 0 || !StartTime.isValid()))
749 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
750
751 RecordingInfo ri;
752 if (RecordedId > 0)
753 ri = RecordingInfo(RecordedId);
754 else
755 ri = RecordingInfo(chanid, StartTime.toUTC());
756
757 if (ri.GetChanID() && ri.HasPathname())
758 {
759 QString cmd = QString("UNDELETE_RECORDING %1 %2")
760 .arg(ri.GetChanID())
762 MythEvent me(cmd);
763
765 return true;
766 }
767
768 return false;
769}
770
772//
774
775bool V2Dvr::StopRecording(int RecordedId)
776{
777 if (RecordedId <= 0)
778 throw QString("RecordedId param is invalid.");
779
780 RecordingInfo ri = RecordingInfo(RecordedId);
781
782 if (ri.GetChanID())
783 {
784 QString cmd = QString("STOP_RECORDING %1 %2")
785 .arg(ri.GetChanID())
787 MythEvent me(cmd);
788
790 return true;
791 }
792 throw QString("RecordedId %1 not found").arg(RecordedId);
793
794 return false;
795}
796
798// Supply one of the following
799// RecordedId
800// or
801// ChanId and StartTime
802// or
803// RecordId
805
806bool V2Dvr::ReactivateRecording(int RecordedId,
807 int ChanId, const QDateTime &StartTime,
808 int RecordId )
809{
810 RecordingInfo ri;
811 if (RecordedId > 0)
812 {
813 ri = RecordingInfo(RecordedId);
814 }
815 else if (ChanId > 0 && StartTime.isValid())
816 {
817 ri = RecordingInfo(ChanId, StartTime.toUTC());
818 }
819 else if (RecordId > 0)
820 {
821 // Find latest recording for that record id
823 query.prepare("SELECT recordedid FROM recorded "
824 " WHERE recordid = :RECORDID "
825 " ORDER BY starttime DESC LIMIT 1");
826 query.bindValue(":RECORDID", RecordId);
827 if (!query.exec())
828 {
829 MythDB::DBError("ReactivateRecording", query);
830 return false;
831 }
832 int recId {0};
833 if (query.next())
834 recId = query.value(0).toInt();
835 else
836 return false;
837 ri = RecordingInfo(recId);
838 }
839 else
840 {
841 throw QString("Recorded ID or Channel ID and StartTime or RecordId are invalid.");
842 }
843 if (ri.GetChanID() && ri.HasPathname())
844 {
846 return true;
847 }
848
849 return false;
850}
851
853//
855
857{
858 ScheduledRecording::RescheduleMatch(0, 0, 0, QDateTime(),
859 "RescheduleRecordings");
860 return true;
861}
862
864//
866
867bool V2Dvr::AllowReRecord ( int RecordedId, int ChanId, const QDateTime &StartTime)
868{
869 if (RecordedId > 0)
870 {
871 if (ChanId > 0 || StartTime.isValid())
872 throw QString("ERROR RecordedId param cannot be used with ChanId or StartTime.");
873 }
874 else if (ChanId > 0)
875 {
876 if (!StartTime.isValid())
877 throw QString("ERROR ChanId param requires a valid StartTime.");
878 }
879 else
880 {
881 throw QString("ERROR RecordedId or (ChanId and StartTime) required.");
882 }
883
884 if (RecordedId > 0)
885 {
886 RecordingInfo ri = RecordingInfo(RecordedId);
887 if (!ri.GetChanID())
888 throw QString("ERROR RecordedId %1 not found").arg(RecordedId);
889 ri.ForgetHistory();
890 }
891 else
892 {
893 ProgramInfo *progInfo = LoadProgramFromProgram(ChanId, StartTime);
894 if (progInfo == nullptr)
895 throw QString("ERROR Guide data for Chanid %1 at StartTime %2 not found")
896 .arg(ChanId).arg(StartTime.toString());
897 RecordingInfo recInfo(*progInfo);
898 recInfo.ForgetHistory();
899 delete progInfo;
900 }
901 return true;
902}
903
905// Prefer Dvr/UpdateRecordedMetadata. Some day, this should go away.
907
909 int chanid,
910 const QDateTime &StartTime,
911 bool watched)
912{
913 // LOG(VB_GENERAL, LOG_WARNING, "Deprecated, use Dvr/UpdateRecordedMetadata.");
914
915 if ((RecordedId <= 0) &&
916 (chanid <= 0 || !StartTime.isValid()))
917 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
918
919 // TODO Should use RecordingInfo
920 ProgramInfo pi;
921 if (RecordedId > 0)
922 pi = ProgramInfo(RecordedId);
923 else
924 pi = ProgramInfo(chanid, StartTime.toUTC());
925
926 if (pi.GetChanID() && pi.HasPathname())
927 {
928 pi.SaveWatched(watched);
929 return true;
930 }
931
932 return false;
933}
934
936//
938
939long V2Dvr::GetSavedBookmark( int RecordedId,
940 int chanid,
941 const QDateTime &StartTime,
942 const QString &offsettype )
943{
944 if ((RecordedId <= 0) &&
945 (chanid <= 0 || !StartTime.isValid()))
946 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
947
948 RecordingInfo ri;
949 if (RecordedId > 0)
950 ri = RecordingInfo(RecordedId);
951 else
952 ri = RecordingInfo(chanid, StartTime.toUTC());
953 uint64_t offset = 0;
954 bool isend=true;
955 uint64_t position = ri.QueryBookmark();
956 // if no bookmark return 0
957 if (position == 0)
958 return 0;
959 if (offsettype.toLower() == "position"){
960 // if bookmark cannot be converted to a keyframe we will
961 // just return the actual frame saved as the bookmark
962 if (ri.QueryKeyFramePosition(&offset, position, isend))
963 return offset;
964 }
965 if (offsettype.toLower() == "duration"){
966 if (ri.QueryKeyFrameDuration(&offset, position, isend))
967 return offset;
968 // If bookmark cannot be converted to a duration return -1
969 return -1;
970 }
971 return position;
972}
973
975// Get last play position
976// Providing -1 for the RecordedId will return response of -1.
977// This is a way to check if this api, and the other LastPlayPos APIs,
978// are supported
980
981long V2Dvr::GetLastPlayPos( int RecordedId,
982 int chanid,
983 const QDateTime &StartTime,
984 const QString &offsettype )
985{
986 if (RecordedId == -1)
987 return -1;
988
989 if ((RecordedId <= 0) &&
990 (chanid <= 0 || !StartTime.isValid()))
991 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
992
993 RecordingInfo ri;
994 if (RecordedId > 0)
995 ri = RecordingInfo(RecordedId);
996 else
997 ri = RecordingInfo(chanid, StartTime.toUTC());
998 uint64_t offset = 0;
999 bool isend=true;
1000 uint64_t position = ri.QueryLastPlayPos();
1001 // if no bookmark return 0
1002 if (position == 0)
1003 return 0;
1004 if (offsettype.toLower() == "position"){
1005 // if bookmark cannot be converted to a keyframe we will
1006 // just return the actual frame saved as the bookmark
1007 if (ri.QueryKeyFramePosition(&offset, position, isend))
1008 return offset;
1009 }
1010 if (offsettype.toLower() == "duration"){
1011 if (ri.QueryKeyFrameDuration(&offset, position, isend))
1012 return offset;
1013 // If bookmark cannot be converted to a duration return -1
1014 return -1;
1015 }
1016 return position;
1017}
1018
1020// Prefer Dvr/UpdateRecordedMetadata. Some day, this should go away.
1022
1023bool V2Dvr::SetSavedBookmark( int RecordedId,
1024 int chanid,
1025 const QDateTime &StartTime,
1026 const QString &offsettype,
1027 long Offset )
1028{
1029 // LOG(VB_GENERAL, LOG_WARNING, "Deprecated, use Dvr/UpdateRecordedMetadata.");
1030
1031 if ((RecordedId <= 0) &&
1032 (chanid <= 0 || !StartTime.isValid()))
1033 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
1034
1035 if (Offset < 0)
1036 throw QString("Offset must be >= 0.");
1037
1038 RecordingInfo ri;
1039 if (RecordedId > 0)
1040 ri = RecordingInfo(RecordedId);
1041 else
1042 ri = RecordingInfo(chanid, StartTime.toUTC());
1043 uint64_t position = 0;
1044 bool isend=true;
1045 if (offsettype.toLower() == "position"){
1046 if (!ri.QueryPositionKeyFrame(&position, Offset, isend))
1047 return false;
1048 }
1049 else if (offsettype.toLower() == "duration"){
1050 if (!ri.QueryDurationKeyFrame(&position, Offset, isend))
1051 return false;
1052 }
1053 else
1054 {
1055 position = Offset;
1056 }
1057 ri.SaveBookmark(position);
1058 return true;
1059}
1060
1062// Set last Play Position. Check if this is supported by first calling
1063// Get Last Play Position with -1.
1064// Prefer Dvr/UpdateRecordedMetadata. Some day, this should go away.
1066
1067bool V2Dvr::SetLastPlayPos( int RecordedId,
1068 int chanid,
1069 const QDateTime &StartTime,
1070 const QString &offsettype,
1071 long Offset )
1072{
1073 // LOG(VB_GENERAL, LOG_WARNING, "Deprecated, use Dvr/UpdateRecordedMetadata.");
1074
1075 if ((RecordedId <= 0) &&
1076 (chanid <= 0 || !StartTime.isValid()))
1077 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
1078
1079 if (Offset < 0)
1080 throw QString("Offset must be >= 0.");
1081
1082 RecordingInfo ri;
1083 if (RecordedId > 0)
1084 ri = RecordingInfo(RecordedId);
1085 else
1086 ri = RecordingInfo(chanid, StartTime.toUTC());
1087 uint64_t position = 0;
1088 bool isend=true;
1089 if (offsettype.toLower() == "position"){
1090 if (!ri.QueryPositionKeyFrame(&position, Offset, isend))
1091 return false;
1092 }
1093 else if (offsettype.toLower() == "duration"){
1094 if (!ri.QueryDurationKeyFrame(&position, Offset, isend))
1095 return false;
1096 }
1097 else
1098 {
1099 position = Offset;
1100 }
1101 ri.SaveLastPlayPos(position);
1102 return true;
1103}
1104
1106 int chanid,
1107 const QDateTime &StartTime,
1108 const QString &offsettype,
1109 bool IncludeFps )
1110{
1111 int marktype = 0;
1112 if ((RecordedId <= 0) &&
1113 (chanid <= 0 || !StartTime.isValid()))
1114 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
1115
1116 RecordingInfo ri;
1117 if (RecordedId > 0)
1118 ri = RecordingInfo(RecordedId);
1119 else
1120 ri = RecordingInfo(chanid, StartTime.toUTC());
1121
1122 auto* pCutList = new V2CutList();
1123 if (offsettype.toLower() == "position")
1124 marktype = 1;
1125 else if (offsettype.toLower() == "duration")
1126 marktype = 2;
1127 else
1128 marktype = 0;
1129
1130 V2FillCutList(pCutList, &ri, marktype, IncludeFps);
1131
1132 return pCutList;
1133}
1134
1136//
1138
1140 int chanid,
1141 const QDateTime &StartTime,
1142 const QString &offsettype,
1143 bool IncludeFps )
1144{
1145 int marktype = 0;
1146 if ((RecordedId <= 0) &&
1147 (chanid <= 0 || !StartTime.isValid()))
1148 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
1149
1150 RecordingInfo ri;
1151 if (RecordedId > 0)
1152 ri = RecordingInfo(RecordedId);
1153 else
1154 ri = RecordingInfo(chanid, StartTime.toUTC());
1155
1156 auto* pCutList = new V2CutList();
1157 if (offsettype.toLower() == "position")
1158 marktype = 1;
1159 else if (offsettype.toLower() == "duration")
1160 marktype = 2;
1161 else
1162 marktype = 0;
1163
1164 V2FillCommBreak(pCutList, &ri, marktype, IncludeFps);
1165
1166 return pCutList;
1167}
1168
1170//
1172
1174 const QString &offsettype )
1175{
1176 MarkTypes marktype = MARK_UNSET;
1177 if (RecordedId <= 0)
1178 throw QString("Recorded ID appears invalid.");
1179
1180 RecordingInfo ri;
1181 ri = RecordingInfo(RecordedId);
1182
1183 auto* pCutList = new V2CutList();
1184 if (offsettype.toLower() == "bytes")
1185 {
1186 marktype = MARK_GOP_BYFRAME;
1187 }
1188 else if (offsettype.toLower() == "duration")
1189 {
1190 marktype = MARK_DURATION_MS;
1191 }
1192 else
1193 {
1194 delete pCutList;
1195 throw QString("Type must be 'BYTES' or 'DURATION'.");
1196 }
1197
1198 V2FillSeek(pCutList, &ri, marktype);
1199
1200 return pCutList;
1201}
1202
1204//
1206
1208{
1209 RecordingInfo ri;
1210 ri = RecordingInfo(RecordedId);
1211
1212 if (!ri.HasPathname())
1213 throw QString("Invalid RecordedId %1").arg(RecordedId);
1214
1215 QVector<ProgramInfo::MarkupEntry> mapMark;
1216 QVector<ProgramInfo::MarkupEntry> mapSeek;
1217
1218 ri.QueryMarkup(mapMark, mapSeek);
1219
1220 auto* pMarkupList = new V2MarkupList();
1221 for (const auto& entry : std::as_const(mapMark))
1222 {
1223 V2Markup *pMarkup = pMarkupList->AddNewMarkup();
1224 QString typestr = toString(static_cast<MarkTypes>(entry.type));
1225 pMarkup->setType(typestr);
1226 pMarkup->setFrame(entry.frame);
1227 if (entry.isDataNull)
1228 pMarkup->setData("NULL");
1229 else
1230 pMarkup->setData(QString::number(entry.data));
1231 }
1232 for (const auto& entry : std::as_const(mapSeek))
1233 {
1234 V2Markup *pSeek = pMarkupList->AddNewSeek();
1235 QString typestr = toString(static_cast<MarkTypes>(entry.type));
1236 pSeek->setType(typestr);
1237 pSeek->setFrame(entry.frame);
1238 if (entry.isDataNull)
1239 pSeek->setData("NULL");
1240 else
1241 pSeek->setData(QString::number(entry.data));
1242 }
1243
1244
1245 return pMarkupList;
1246}
1247
1249//
1251
1252bool V2Dvr::SetRecordedMarkup(int RecordedId, const QString &MarkupList)
1253{
1254 RecordingInfo ri;
1255 ri = RecordingInfo(RecordedId);
1256
1257 if (!ri.HasPathname())
1258 throw QString("Invalid RecordedId %1").arg(RecordedId);
1259
1260 QVector<ProgramInfo::MarkupEntry> mapMark;
1261 QVector<ProgramInfo::MarkupEntry> mapSeek;
1262
1263 QJsonDocument doc = QJsonDocument::fromJson(MarkupList.toUtf8());
1264 QJsonObject markuplist = doc.object();
1265
1266 QJsonArray marks = markuplist["Mark"].toArray();
1267 for (const auto & m : std::as_const(marks))
1268 {
1269 QJsonObject markup = m.toObject();
1271
1272 QString typestr = markup.value("Type").toString("");
1273 entry.type = markTypeFromString(typestr);
1274 entry.frame = markup.value("Frame").toVariant()
1275 .toString().toLongLong();
1276 QString data = markup.value("Data").toString("NULL");
1277 entry.isDataNull = (data == "NULL");
1278 if (!entry.isDataNull)
1279 entry.data = data.toLongLong();
1280
1281 mapMark.append(entry);
1282 }
1283
1284 QJsonArray seeks = markuplist["Seek"].toArray();
1285 for (const auto & m : std::as_const(seeks))
1286 {
1287 QJsonObject markup = m.toObject();
1289
1290 QString typestr = markup.value("Type").toString("");
1291 entry.type = markTypeFromString(typestr);
1292 entry.frame = markup.value("Frame").toVariant().toString().toLongLong();
1293 QString data = markup.value("Data").toString("NULL");
1294 entry.isDataNull = (data == "NULL");
1295 if (!entry.isDataNull)
1296 entry.data = data.toLongLong();
1297
1298 mapSeek.append(entry);
1299 }
1300
1301 ri.SaveMarkup(mapMark, mapSeek);
1302
1303 return true;
1304}
1305
1307//
1309
1311{
1312 auto* pList = new V2EncoderList();
1313 FillEncoderList(pList->GetEncoders(), pList);
1314 return pList;
1315}
1316
1318//
1320
1322{
1323 auto *pList = new V2InputList();
1324
1325 QList<InputInfo> inputInfoList = CardUtil::GetAllInputInfo(false);
1326 for (const auto & inputInfo : std::as_const(inputInfoList))
1327 {
1328 V2Input *input = pList->AddNewInput();
1329 V2FillInputInfo(input, inputInfo);
1330 }
1331
1332 return pList;
1333}
1334
1336//
1338
1339QStringList V2Dvr::GetRecGroupList( const QString &UsedBy)
1340{
1342 if (UsedBy.compare("recorded",Qt::CaseInsensitive) == 0)
1343 query.prepare("SELECT DISTINCT recgroup FROM recorded "
1344 "ORDER BY recgroup");
1345 else if (UsedBy.compare("schedule",Qt::CaseInsensitive) == 0)
1346 query.prepare("SELECT DISTINCT recgroup FROM record "
1347 "ORDER BY recgroup");
1348 else
1349 query.prepare("SELECT recgroup FROM recgroups WHERE recgroup <> 'Deleted' "
1350 "ORDER BY recgroup");
1351
1352 QStringList result;
1353 if (!query.exec())
1354 {
1355 MythDB::DBError("GetRecGroupList", query);
1356 return result;
1357 }
1358
1359 while (query.next())
1360 result << query.value(0).toString();
1361
1362 return result;
1363}
1364
1366//
1368
1369QStringList V2Dvr::GetProgramCategories( bool OnlyRecorded )
1370{
1372
1373 if (OnlyRecorded)
1374 query.prepare("SELECT DISTINCT category FROM recorded ORDER BY category");
1375 else
1376 query.prepare("SELECT DISTINCT category FROM program ORDER BY category");
1377
1378 QStringList result;
1379 if (!query.exec())
1380 {
1381 MythDB::DBError("GetProgramCategories", query);
1382 return result;
1383 }
1384
1385 while (query.next())
1386 result << query.value(0).toString();
1387
1388 return result;
1389}
1390
1392//
1394
1396{
1398}
1399
1401//
1403
1405{
1406 return PlayGroup::GetNames();
1407}
1408
1410{
1411 auto* playGroup = new V2PlayGroup();
1412
1414
1415 query.prepare("SELECT name, titlematch, skipahead, skipback, timestretch, jump "
1416 "FROM playgroup WHERE name = :NAME ");
1417 query.bindValue(":NAME", Name);
1418
1419 if (query.exec())
1420 {
1421 if (query.next())
1422 {
1423 playGroup->setName(query.value(0).toString());
1424 playGroup->setTitleMatch(query.value(1).toString());
1425 playGroup->setSkipAhead(query.value(2).toInt());
1426 playGroup->setSkipBack(query.value(3).toInt());
1427 playGroup->setTimeStretch(query.value(4).toInt());
1428 playGroup->setJump(query.value(5).toInt());
1429 }
1430 else
1431 {
1432 throw QString("Play Group Not Found.");
1433 }
1434 }
1435 return playGroup;
1436}
1437
1438bool V2Dvr::RemovePlayGroup ( const QString & Name )
1439{
1440
1441 if (Name.compare("Default", Qt::CaseInsensitive) == 0)
1442 throw QString("ERROR: Cannot delete Default entry");
1444 query.prepare("DELETE FROM playgroup "
1445 "WHERE name = :NAME");
1446
1447 query.bindValue(":NAME", Name);
1448
1449 return query.exec();
1450}
1451
1452bool V2Dvr::AddPlayGroup ( const QString & Name,
1453 const QString & TitleMatch,
1454 int SkipAhead,
1455 int SkipBack,
1456 int TimeStretch,
1457 int Jump )
1458{
1460
1461 query.prepare("INSERT INTO playgroup "
1462 "(name, titlematch, skipahead, skipback, timestretch, jump) "
1463 " VALUES(:NAME, :TITLEMATCH, :SKIPAHEAD, :SKIPBACK, :TIMESTRETCH, :JUMP)");
1464 query.bindValue(":NAME", Name);
1465 query.bindValue(":TITLEMATCH", TitleMatch);
1466 query.bindValue(":SKIPAHEAD", SkipAhead);
1467 query.bindValue(":SKIPBACK", SkipBack);
1468 query.bindValue(":TIMESTRETCH", TimeStretch);
1469 query.bindValue(":JUMP", Jump);
1470
1471 return query.exec();
1472}
1473
1474bool V2Dvr::UpdatePlayGroup ( const QString & Name,
1475 const QString & TitleMatch,
1476 int SkipAhead,
1477 int SkipBack,
1478 int TimeStretch,
1479 int Jump )
1480{
1481 if (Name.isEmpty())
1482 throw QString("ERROR: Name is not specified");
1483
1484 bool ok = false;
1485
1486 QString sql = "UPDATE playgroup SET ";
1487 if (HAS_PARAMv2("TitleMatch"))
1488 {
1489 sql.append(" titlematch = :TITLEMATCH ");
1490 ok = true;
1491 }
1492 if (HAS_PARAMv2("SkipAhead"))
1493 {
1494 if (ok)
1495 sql.append(",");
1496 sql.append(" skipahead = :SKIPAHEAD ");
1497 ok = true;
1498 }
1499 if (HAS_PARAMv2("SkipBack"))
1500 {
1501 if (ok)
1502 sql.append(",");
1503 sql.append(" skipback = :SKIPBACK ");
1504 ok = true;
1505 }
1506 if (HAS_PARAMv2("TimeStretch"))
1507 {
1508 if (ok)
1509 sql.append(",");
1510 sql.append(" timestretch = :TIMESTRETCH ");
1511 ok = true;
1512 }
1513 if (HAS_PARAMv2("Jump"))
1514 {
1515 if (ok)
1516 sql.append(",");
1517 sql.append(" jump = :JUMP ");
1518 ok = true;
1519 }
1520 if (ok)
1521 {
1522 sql.append(" WHERE name = :NAME ");
1524 query.prepare(sql);
1525 query.bindValue(":NAME", Name);
1526 if (HAS_PARAMv2("TitleMatch"))
1527 query.bindValue(":TITLEMATCH", TitleMatch);
1528 if (HAS_PARAMv2("SkipAhead"))
1529 query.bindValue(":SKIPAHEAD", SkipAhead);
1530 if (HAS_PARAMv2("SkipBack"))
1531 query.bindValue(":SKIPBACK", SkipBack);
1532 if (HAS_PARAMv2("TimeStretch"))
1533 query.bindValue(":TIMESTRETCH", TimeStretch);
1534 if (HAS_PARAMv2("Jump"))
1535 query.bindValue(":JUMP", Jump);
1536 if (query.exec())
1537 return true;
1538 }
1539 return false;
1540}
1541
1543//
1545
1547{
1548 auto* filterList = new V2RecRuleFilterList();
1549
1551
1552 query.prepare("SELECT filterid, description, newruledefault "
1553 "FROM recordfilter ORDER BY filterid");
1554
1555 if (query.exec())
1556 {
1557 while (query.next())
1558 {
1559 V2RecRuleFilter* ruleFilter = filterList->AddNewRecRuleFilter();
1560 ruleFilter->setId(query.value(0).toInt());
1561 ruleFilter->setDescription(QObject::tr(query.value(1).toString()
1562 .toUtf8().constData()));
1563 }
1564 }
1565
1566 return filterList;
1567}
1568
1570//
1572
1573QStringList V2Dvr::GetTitleList(const QString& RecGroup)
1574{
1576
1577 QString querystr = "SELECT DISTINCT title FROM recorded "
1578 "WHERE deletepending = 0";
1579
1580 if (!RecGroup.isEmpty())
1581 querystr += " AND recgroup = :RECGROUP";
1582 else
1583 querystr += " AND recgroup != 'Deleted'";
1584
1585 querystr += " ORDER BY title";
1586
1587 query.prepare(querystr);
1588
1589 if (!RecGroup.isEmpty())
1590 query.bindValue(":RECGROUP", RecGroup);
1591
1592 QStringList result;
1593 if (!query.exec())
1594 {
1595 MythDB::DBError("GetTitleList recorded", query);
1596 return result;
1597 }
1598
1599 while (query.next())
1600 result << query.value(0).toString();
1601
1602 return result;
1603}
1604
1606//
1608
1610{
1612
1613 QString querystr = QString(
1614 "SELECT title, inetref, count(title) as count "
1615 " FROM recorded AS r "
1616 " JOIN recgroups AS g ON r.recgroupid = g.recgroupid "
1617 " WHERE g.recgroup NOT IN ('Deleted', 'LiveTV') "
1618 " AND r.deletepending = 0 "
1619 " GROUP BY title, inetref "
1620 " ORDER BY title");
1621
1622 query.prepare(querystr);
1623
1624 auto *pTitleInfos = new V2TitleInfoList();
1625 if (!query.exec())
1626 {
1627 MythDB::DBError("GetTitleList recorded", query);
1628 return pTitleInfos;
1629 }
1630
1631 while (query.next())
1632 {
1633 V2TitleInfo *pTitleInfo = pTitleInfos->AddNewTitleInfo();
1634
1635 pTitleInfo->setTitle(query.value(0).toString());
1636 pTitleInfo->setInetref(query.value(1).toString());
1637 pTitleInfo->setCount(query.value(2).toInt());
1638 }
1639
1640 return pTitleInfos;
1641}
1642
1643
1645 int nCount,
1646 int nRecordId,
1647 const QString &Sort )
1648{
1649 auto *pPrograms = new V2ProgramList();
1650 int size = FillUpcomingList(pPrograms->GetPrograms(), pPrograms,
1651 nStartIndex,
1652 nCount,
1653 true, // bShowAll,
1654 nRecordId,
1656 Sort);
1657
1658 pPrograms->setStartIndex ( nStartIndex );
1659 pPrograms->setCount ( nCount );
1660 pPrograms->setTotalAvailable( size );
1661 pPrograms->setAsOf ( MythDate::current() );
1662 pPrograms->setVersion ( MYTH_BINARY_VERSION );
1663 pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
1664
1665 return pPrograms;
1666}
1667
1669 int nCount,
1670 bool bShowAll,
1671 int nRecordId,
1672 const QString &RecStatus,
1673 const QString &Sort,
1674 const QString &RecGroup )
1675{
1676 int nRecStatus = 0;
1677 if (!RecStatus.isEmpty())
1678 {
1679 // Handle enum name
1680 QMetaEnum meta = QMetaEnum::fromType<RecStatus::Type>();
1681 bool ok {false};
1682 nRecStatus = meta.keyToValue(RecStatus.toLocal8Bit().constData(), &ok);
1683 // if enum name not valid try for int nRecStatus
1684 if (!ok)
1685 nRecStatus = RecStatus.toInt(&ok);
1686 // if still not valid use 99999 to trigger an "unknown" response
1687 if (!ok)
1688 nRecStatus = 99999;
1689 }
1690 auto *pPrograms = new V2ProgramList();
1691 int size = FillUpcomingList(pPrograms->GetPrograms(), pPrograms,
1692 nStartIndex,
1693 nCount,
1694 bShowAll,
1695 nRecordId,
1696 nRecStatus,
1697 Sort,
1698 RecGroup );
1699
1700 pPrograms->setStartIndex ( nStartIndex );
1701 pPrograms->setCount ( nCount );
1702 pPrograms->setTotalAvailable( size );
1703 pPrograms->setAsOf ( MythDate::current() );
1704 pPrograms->setVersion ( MYTH_BINARY_VERSION );
1705 pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
1706
1707 return pPrograms;
1708}
1709
1711 const QString& sTitle,
1712 const QString& sSubtitle,
1713 const QString& sDescription,
1714 const QString& sCategory,
1715 const QDateTime& StartTime,
1716 const QDateTime& EndTime,
1717 const QString& sSeriesId,
1718 const QString& sProgramId,
1719 int nChanId,
1720 const QString& sStation,
1721 int nFindDay,
1722 QTime tFindTime,
1723 int nParentId,
1724 bool bInactive,
1725 uint nSeason,
1726 uint nEpisode,
1727 const QString& sInetref,
1728 QString sType,
1729 QString sSearchType,
1730 int nRecPriority,
1731 uint nPreferredInput,
1732 int nStartOffset,
1733 int nEndOffset,
1734 const QDateTime& LastRecorded,
1735 QString sDupMethod,
1736 QString sDupIn,
1737 bool bNewEpisOnly,
1738 uint nFilter,
1739 QString sRecProfile,
1740 QString sRecGroup,
1741 QString sStorageGroup,
1742 QString sPlayGroup,
1743 bool bAutoExpire,
1744 int nMaxEpisodes,
1745 bool bMaxNewest,
1746 bool bAutoCommflag,
1747 bool bAutoTranscode,
1748 bool bAutoMetaLookup,
1749 bool bAutoUserJob1,
1750 bool bAutoUserJob2,
1751 bool bAutoUserJob3,
1752 bool bAutoUserJob4,
1753 int nTranscoder,
1754 const QString& AutoExtend)
1755{
1756 QDateTime recstartts = StartTime.toUTC();
1757 QDateTime recendts = EndTime.toUTC();
1758 QDateTime lastrects = LastRecorded.toUTC();
1759 RecordingRule rule;
1760 rule.LoadTemplate("Default");
1761
1762 if (sType.isEmpty())
1763 sType = "single";
1764
1765 if (sSearchType.isEmpty())
1766 sSearchType = "none";
1767
1768 if (sDupMethod.isEmpty())
1769 sDupMethod = "subtitleanddescription";
1770
1771 if (sDupIn.isEmpty())
1772 sDupIn = "all";
1773
1774 rule.m_title = sTitle;
1775 rule.m_subtitle = sSubtitle;
1776 rule.m_description = sDescription;
1777
1778 rule.m_startdate = recstartts.date();
1779 rule.m_starttime = recstartts.time();
1780 rule.m_enddate = recendts.date();
1781 rule.m_endtime = recendts.time();
1782
1783 rule.m_type = recTypeFromString(sType);
1784 rule.m_searchType = searchTypeFromString(sSearchType);
1785 if (rule.m_searchType == kManualSearch)
1787 else
1788 rule.m_dupMethod = dupMethodFromString(sDupMethod);
1789 rule.m_dupIn = dupInFromStringAndBool(sDupIn, bNewEpisOnly);
1790
1791 if (sRecProfile.isEmpty())
1792 sRecProfile = "Default";
1793
1794 if (sRecGroup.isEmpty())
1795 sRecGroup = "Default";
1796
1797 if (sStorageGroup.isEmpty())
1798 sStorageGroup = "Default";
1799
1800 if (sPlayGroup.isEmpty())
1801 sPlayGroup = "Default";
1802
1803 rule.m_category = sCategory;
1804 rule.m_seriesid = sSeriesId;
1805 rule.m_programid = sProgramId;
1806
1807 rule.m_channelid = nChanId;
1808 rule.m_station = sStation;
1809
1810 rule.m_findday = nFindDay;
1811 rule.m_findtime = tFindTime;
1812
1813 rule.m_recProfile = sRecProfile;
1815 if (rule.m_recGroupID == 0)
1816 {
1817 rule.m_recGroupID = V2CreateRecordingGroup(sRecGroup);
1818 if (rule.m_recGroupID <= 0)
1820 }
1821 rule.m_storageGroup = sStorageGroup;
1822 rule.m_playGroup = sPlayGroup;
1823
1824 rule.m_parentRecID = nParentId;
1825 rule.m_isInactive = bInactive;
1826
1827 rule.m_season = nSeason;
1828 rule.m_episode = nEpisode;
1829 rule.m_inetref = sInetref;
1830
1831 rule.m_recPriority = nRecPriority;
1832 rule.m_prefInput = nPreferredInput;
1833 rule.m_startOffset = nStartOffset;
1834 rule.m_endOffset = nEndOffset;
1835 rule.m_filter = nFilter;
1836
1837 rule.m_autoExpire = bAutoExpire;
1838 rule.m_maxEpisodes = nMaxEpisodes;
1839 rule.m_maxNewest = bMaxNewest;
1840
1841 rule.m_autoCommFlag = bAutoCommflag;
1842 rule.m_autoTranscode = bAutoTranscode;
1843 rule.m_autoMetadataLookup = bAutoMetaLookup;
1844
1845 rule.m_autoUserJob1 = bAutoUserJob1;
1846 rule.m_autoUserJob2 = bAutoUserJob2;
1847 rule.m_autoUserJob3 = bAutoUserJob3;
1848 rule.m_autoUserJob4 = bAutoUserJob4;
1849
1850 rule.m_transcoder = nTranscoder;
1851 rule.m_autoExtend = autoExtendTypeFromString(AutoExtend);
1852
1853 rule.m_lastRecorded = lastrects;
1854
1855 QString msg;
1856 if (!rule.IsValid(msg))
1857 throw QString(msg);
1858
1859 bool success = rule.Save();
1860 if (!success)
1861 throw QString("DATABASE ERROR: Check for duplicate recording rule");
1862
1863 uint recid = rule.m_recordID;
1864
1865 return recid;
1866}
1867
1869 const QString& sTitle,
1870 const QString& sSubtitle,
1871 const QString& sDescription,
1872 const QString& sCategory,
1873 const QDateTime& StartTime,
1874 const QDateTime& EndTime,
1875 const QString& sSeriesId,
1876 const QString& sProgramId,
1877 int nChanId,
1878 const QString& sStation,
1879 int nFindDay,
1880 QTime tFindTime,
1881 bool bInactive,
1882 uint nSeason,
1883 uint nEpisode,
1884 const QString& sInetref,
1885 QString sType,
1886 QString sSearchType,
1887 int nRecPriority,
1888 uint nPreferredInput,
1889 int nStartOffset,
1890 int nEndOffset,
1891 QString sDupMethod,
1892 QString sDupIn,
1893 bool bNewEpisOnly,
1894 uint nFilter,
1895 QString sRecProfile,
1896 QString sRecGroup,
1897 QString sStorageGroup,
1898 QString sPlayGroup,
1899 bool bAutoExpire,
1900 int nMaxEpisodes,
1901 bool bMaxNewest,
1902 bool bAutoCommflag,
1903 bool bAutoTranscode,
1904 bool bAutoMetaLookup,
1905 bool bAutoUserJob1,
1906 bool bAutoUserJob2,
1907 bool bAutoUserJob3,
1908 bool bAutoUserJob4,
1909 int nTranscoder,
1910 const QString& AutoExtend)
1911{
1912 if (nRecordId == 0 )
1913 throw QString("Record ID is invalid.");
1914
1915 RecordingRule pRule;
1916 pRule.m_recordID = nRecordId;
1917 pRule.Load();
1918
1919 if (!pRule.IsLoaded())
1920 throw QString("Record ID does not exist.");
1921
1922 QDateTime recstartts = StartTime.toUTC();
1923 QDateTime recendts = EndTime.toUTC();
1924
1925 pRule.m_isInactive = bInactive;
1926 if (sType.isEmpty())
1927 sType = "single";
1928
1929 if (sSearchType.isEmpty())
1930 sSearchType = "none";
1931
1932 if (sDupMethod.isEmpty())
1933 sDupMethod = "subtitleanddescription";
1934
1935 if (sDupIn.isEmpty())
1936 sDupIn = "all";
1937
1938 pRule.m_type = recTypeFromString(sType);
1939 pRule.m_searchType = searchTypeFromString(sSearchType);
1940 if (pRule.m_searchType == kManualSearch)
1941 pRule.m_dupMethod = kDupCheckNone;
1942 else
1943 pRule.m_dupMethod = dupMethodFromString(sDupMethod);
1944 pRule.m_dupIn = dupInFromStringAndBool(sDupIn, bNewEpisOnly);
1945
1946 if (sRecProfile.isEmpty())
1947 sRecProfile = "Default";
1948
1949 if (sRecGroup.isEmpty())
1950 sRecGroup = "Default";
1951
1952 if (sStorageGroup.isEmpty())
1953 sStorageGroup = "Default";
1954
1955 if (sPlayGroup.isEmpty())
1956 sPlayGroup = "Default";
1957
1958 if (!sTitle.isEmpty())
1959 pRule.m_title = sTitle;
1960
1961 if (!sSubtitle.isEmpty())
1962 pRule.m_subtitle = sSubtitle;
1963
1964 if(!sDescription.isEmpty())
1965 pRule.m_description = sDescription;
1966
1967 if (!sCategory.isEmpty())
1968 pRule.m_category = sCategory;
1969
1970 if (!sSeriesId.isEmpty())
1971 pRule.m_seriesid = sSeriesId;
1972
1973 if (!sProgramId.isEmpty())
1974 pRule.m_programid = sProgramId;
1975
1976 if (nChanId)
1977 pRule.m_channelid = nChanId;
1978 if (!sStation.isEmpty())
1979 pRule.m_station = sStation;
1980
1981 pRule.m_startdate = recstartts.date();
1982 pRule.m_starttime = recstartts.time();
1983 pRule.m_enddate = recendts.date();
1984 pRule.m_endtime = recendts.time();
1985
1986 pRule.m_findday = nFindDay;
1987 pRule.m_findtime = tFindTime;
1988
1989 pRule.m_recProfile = sRecProfile;
1990 pRule.m_recGroupID = RecordingInfo::GetRecgroupID(sRecGroup);
1991 if (pRule.m_recGroupID == 0)
1992 {
1993 pRule.m_recGroupID = V2CreateRecordingGroup(sRecGroup);
1994 if (pRule.m_recGroupID <= 0)
1996 }
1997 pRule.m_storageGroup = sStorageGroup;
1998 pRule.m_playGroup = sPlayGroup;
1999
2000 pRule.m_isInactive = bInactive;
2001
2002 pRule.m_season = nSeason;
2003 pRule.m_episode = nEpisode;
2004 pRule.m_inetref = sInetref;
2005
2006 pRule.m_recPriority = nRecPriority;
2007 pRule.m_prefInput = nPreferredInput;
2008 pRule.m_startOffset = nStartOffset;
2009 pRule.m_endOffset = nEndOffset;
2010 pRule.m_filter = nFilter;
2011
2012 pRule.m_autoExpire = bAutoExpire;
2013 pRule.m_maxEpisodes = nMaxEpisodes;
2014 pRule.m_maxNewest = bMaxNewest;
2015
2016 pRule.m_autoCommFlag = bAutoCommflag;
2017 pRule.m_autoTranscode = bAutoTranscode;
2018 pRule.m_autoMetadataLookup = bAutoMetaLookup;
2019
2020 pRule.m_autoUserJob1 = bAutoUserJob1;
2021 pRule.m_autoUserJob2 = bAutoUserJob2;
2022 pRule.m_autoUserJob3 = bAutoUserJob3;
2023 pRule.m_autoUserJob4 = bAutoUserJob4;
2024
2025 pRule.m_transcoder = nTranscoder;
2026
2027 if (!AutoExtend.isEmpty())
2028 pRule.m_autoExtend = autoExtendTypeFromString(AutoExtend);
2029
2030 QString msg;
2031 if (!pRule.IsValid(msg))
2032 throw QString(msg);
2033
2034 bool bResult = pRule.Save();
2035
2036 return bResult;
2037}
2038
2040{
2041 bool bResult = false;
2042
2043 if (nRecordId == 0 )
2044 throw QString("Record ID does not exist.");
2045
2046 RecordingRule pRule;
2047 pRule.m_recordID = nRecordId;
2048
2049 bResult = pRule.Delete();
2050
2051 return bResult;
2052}
2053
2054bool V2Dvr::AddDontRecordSchedule(int nChanId, const QDateTime &dStartTime,
2055 bool bNeverRecord)
2056{
2057 bool bResult = true;
2058
2059 if (nChanId <= 0 || !dStartTime.isValid())
2060 throw QString("Program does not exist.");
2061
2062 ProgramInfo *pi = LoadProgramFromProgram(nChanId, dStartTime.toUTC());
2063
2064 if (!pi)
2065 throw QString("Program does not exist.");
2066
2067 // Why RecordingInfo instead of ProgramInfo? Good question ...
2068 RecordingInfo recInfo = RecordingInfo(*pi);
2069
2070 delete pi;
2071
2072 if (bNeverRecord)
2073 {
2074 recInfo.ApplyNeverRecord();
2075 }
2076 else
2077 {
2079 }
2080
2081 return bResult;
2082}
2083
2085 int nCount,
2086 const QString &Sort,
2087 bool Descending )
2088{
2090 if (Sort.toLower() == "lastrecorded")
2091 sortingColumn = Scheduler::kSortLastRecorded;
2092 else if (Sort.toLower() == "nextrecording")
2093 sortingColumn = Scheduler::kSortNextRecording;
2094 else if (Sort.toLower() == "title")
2095 sortingColumn = Scheduler::kSortTitle; // NOLINT(bugprone-branch-clone)
2096 else if (Sort.toLower() == "priority")
2097 sortingColumn = Scheduler::kSortPriority;
2098 else if (Sort.toLower() == "type")
2099 sortingColumn = Scheduler::kSortType;
2100 else
2101 sortingColumn = Scheduler::kSortTitle;
2102
2103 RecList recList;
2104 Scheduler::GetAllScheduled(recList, sortingColumn, !Descending);
2105
2106 // ----------------------------------------------------------------------
2107 // Build Response
2108 // ----------------------------------------------------------------------
2109
2110 auto *pRecRules = new V2RecRuleList();
2111
2112 nStartIndex = (nStartIndex > 0) ? std::min( nStartIndex, (int)recList.size() ) : 0;
2113 nCount = (nCount > 0) ? std::min( nCount, (int)recList.size() ) : recList.size();
2114 int nEndIndex = std::min((nStartIndex + nCount), (int)recList.size() );
2115
2116 for( int n = nStartIndex; n < nEndIndex; n++)
2117 {
2118 RecordingInfo *info = recList[n];
2119
2120 if (info != nullptr)
2121 {
2122 V2RecRule *pRecRule = pRecRules->AddNewRecRule();
2123
2124 V2FillRecRuleInfo( pRecRule, info->GetRecordingRule() );
2125 }
2126 }
2127
2128 // ----------------------------------------------------------------------
2129
2130 pRecRules->setStartIndex ( nStartIndex );
2131 pRecRules->setCount ( nCount );
2132 pRecRules->setTotalAvailable( recList.size() );
2133 pRecRules->setAsOf ( MythDate::current() );
2134 pRecRules->setVersion ( MYTH_BINARY_VERSION );
2135 pRecRules->setProtoVer ( MYTH_PROTO_VERSION );
2136
2137 while (!recList.empty())
2138 {
2139 delete recList.back();
2140 recList.pop_back();
2141 }
2142
2143 return pRecRules;
2144}
2145
2147 const QString& sTemplate,
2148 int nRecordedId,
2149 int nChanId,
2150 const QDateTime& StartTime,
2151 bool bMakeOverride )
2152{
2153 RecordingRule rule;
2154 QDateTime dStartTime = StartTime.toUTC();
2155
2156 if (nRecordId > 0)
2157 {
2158 rule.m_recordID = nRecordId;
2159 if (!rule.Load())
2160 throw QString("Record ID does not exist.");
2161 }
2162 else if (!sTemplate.isEmpty())
2163 {
2164 if (!rule.LoadTemplate(sTemplate))
2165 throw QString("Template does not exist.");
2166 }
2167 else if (nRecordedId > 0) // Loads from the Recorded/Recorded Program Table
2168 {
2169 // Despite the use of ProgramInfo, this only applies to Recordings.
2170 ProgramInfo recInfo(nRecordedId);
2171 if (!rule.LoadByProgram(&recInfo))
2172 throw QString("Recording does not exist");
2173 }
2174 else if (nChanId > 0 && dStartTime.isValid()) // Loads from Program Table, should NOT be used with recordings
2175 {
2176 // Despite the use of RecordingInfo, this only applies to programs in the
2177 // present or future, not to recordings? Confused yet?
2179 RecordingInfo info(nChanId, dStartTime, false, 0h, &status);
2180 if (status != RecordingInfo::kFoundProgram)
2181 throw QString("Program does not exist.");
2182 RecordingRule *pRule = info.GetRecordingRule();
2183 if (bMakeOverride && rule.m_type != kSingleRecord &&
2184 rule.m_type != kOverrideRecord && rule.m_type != kDontRecord)
2185 pRule->MakeOverride();
2186 rule = *pRule;
2187 }
2188 else
2189 {
2190 throw QString("Invalid request.");
2191 }
2192
2193 auto *pRecRule = new V2RecRule();
2194 V2FillRecRuleInfo( pRecRule, &rule );
2195
2196 return pRecRule;
2197}
2198
2200{
2201 bool bResult = false;
2202
2203 if (nRecordId == 0 )
2204 throw QString("Record ID appears invalid.");
2205
2206 RecordingRule pRule;
2207 pRule.m_recordID = nRecordId;
2208 pRule.Load();
2209
2210 if (pRule.IsLoaded())
2211 {
2212 pRule.m_isInactive = false;
2213 bResult = pRule.Save();
2214 }
2215
2216 return bResult;
2217}
2218
2220{
2221 bool bResult = false;
2222
2223 if (nRecordId == 0 )
2224 throw QString("Record ID appears invalid.");
2225
2226 RecordingRule pRule;
2227 pRule.m_recordID = nRecordId;
2228 pRule.Load();
2229
2230 if (pRule.IsLoaded())
2231 {
2232 pRule.m_isInactive = true;
2233 bResult = pRule.Save();
2234 }
2235
2236 return bResult;
2237}
2238
2239int V2Dvr::RecordedIdForKey(int chanid, const QDateTime &StartTime)
2240{
2241 int recordedid = 0;
2242
2243 if (!RecordingInfo::QueryRecordedIdForKey(recordedid, chanid,
2244 StartTime))
2245 return -1;
2246
2247 return recordedid;
2248}
2249
2250int V2Dvr::RecordedIdForPathname(const QString & pathname)
2251{
2252 uint recordedid = 0;
2253
2254 if (!ProgramInfo::QueryRecordedIdFromPathname(pathname, recordedid))
2255 return -1;
2256
2257 return recordedid;
2258}
2259
2260QString V2Dvr::RecStatusToString(const QString & RecStatus)
2261{
2262 // Handle enum name
2263 QMetaEnum meta = QMetaEnum::fromType<RecStatus::Type>();
2264 bool ok {false};
2265 int value = meta.keyToValue(RecStatus.toLocal8Bit().constData(), &ok);
2266 // if enum name not valid try for int value
2267 if (!ok)
2268 value = RecStatus.toInt(&ok);
2269 // if still not valid use 0 to trigger an "unknown" response
2270 if (!ok)
2271 value = 0;
2272 auto type = static_cast<RecStatus::Type>(value);
2273 return RecStatus::toString(type);
2274}
2275
2276QString V2Dvr::RecStatusToDescription(const QString & RecStatus, int recType,
2277 const QDateTime &StartTime)
2278{
2279 // Handle enum name
2280 QMetaEnum meta = QMetaEnum::fromType<RecStatus::Type>();
2281 bool ok {false};
2282 int value = meta.keyToValue(RecStatus.toLocal8Bit().constData(), &ok);
2283 // if enum name not valid try for int value
2284 if (!ok)
2285 value = RecStatus.toInt(&ok);
2286 // if still not valid use 0 to trigger an "unknown" response
2287 if (!ok)
2288 value = 0;
2289 auto recstatusType = static_cast<RecStatus::Type>(value);
2290 auto recordingType = static_cast<RecordingType>(recType);
2291 return RecStatus::toDescription(recstatusType, recordingType, StartTime);
2292}
2293
2294QString V2Dvr::RecTypeToString(const QString& recType)
2295{
2296 bool ok = false;
2297 auto enumType = static_cast<RecordingType>(recType.toInt(&ok, 10));
2298 if (ok)
2299 return toString(enumType);
2300 // RecordingType type = static_cast<RecordingType>(recType);
2301 return toString(recTypeFromString(recType));
2302}
2303
2304QString V2Dvr::RecTypeToDescription(const QString& recType)
2305{
2306 bool ok = false;
2307 auto enumType = static_cast<RecordingType>(recType.toInt(&ok, 10));
2308 if (ok)
2309 return toDescription(enumType);
2310 // RecordingType type = static_cast<RecordingType>(recType);
2311 return toDescription(recTypeFromString(recType));
2312}
2313
2314QString V2Dvr::DupInToString(const QString& DupIn)
2315{
2316 // RecordingDupInType type= static_cast<RecordingDupInType>(DupIn);
2317 // return toString(type);
2318 return toString(dupInFromString(DupIn));
2319}
2320
2321QString V2Dvr::DupInToDescription(const QString& DupIn)
2322{
2323 // RecordingDupInType type= static_cast<RecordingDupInType>(DupIn);
2324 //return toDescription(type);
2325 return toDescription(dupInFromString(DupIn));
2326}
2327
2328QString V2Dvr::DupMethodToString(const QString& DupMethod)
2329{
2330 // RecordingDupMethodType method = static_cast<RecordingDupMethodType>(DupMethod);
2331 return toString(dupMethodFromString(DupMethod));
2332}
2333
2334QString V2Dvr::DupMethodToDescription(const QString& DupMethod)
2335{
2336 // RecordingDupMethodType method = static_cast<RecordingDupMethodType>(DupMethod);
2337 return toDescription(dupMethodFromString(DupMethod));
2338}
2339
2341//
2343
2344int V2Dvr::ManageJobQueue( const QString &sAction,
2345 const QString &sJobName,
2346 int nJobId,
2347 int nRecordedId,
2348 QDateTime JobStartTime,
2349 QString sRemoteHost,
2350 QString sJobArgs )
2351{
2352 int nReturn = -1;
2353
2354 if (!HAS_PARAMv2("JobName") ||
2355 !HAS_PARAMv2("RecordedId") )
2356 {
2357 LOG(VB_GENERAL, LOG_ERR, "JobName and RecordedId are required.");
2358 return nReturn;
2359 }
2360
2361 if (sRemoteHost.isEmpty())
2362 sRemoteHost = gCoreContext->GetHostName();
2363
2364 int jobType = JobQueue::GetJobTypeFromName(sJobName);
2365
2366 if (jobType == JOB_NONE)
2367 return nReturn;
2368
2369 RecordingInfo ri = RecordingInfo(nRecordedId);
2370
2371 if (!ri.GetChanID())
2372 return nReturn;
2373
2374 if ( sAction == "Remove")
2375 {
2376 if (!HAS_PARAMv2("JobId") || nJobId < 0)
2377 {
2378 LOG(VB_GENERAL, LOG_ERR, "For Remove, a valid JobId is required.");
2379 return nReturn;
2380 }
2381
2382 if (!JobQueue::SafeDeleteJob(nJobId, jobType, ri.GetChanID(),
2384 return nReturn;
2385
2386 return nJobId;
2387 }
2388
2389 if ( sAction != "Add")
2390 {
2391 LOG(VB_GENERAL, LOG_ERR, QString("Illegal Action name '%1'. Use: Add, "
2392 "or Remove").arg(sAction));
2393 return nReturn;
2394 }
2395
2396 if (((jobType & JOB_USERJOB) != 0) &&
2397 gCoreContext->GetSetting(sJobName, "").isEmpty())
2398 {
2399 LOG(VB_GENERAL, LOG_ERR, QString("%1 hasn't been defined.")
2400 .arg(sJobName));
2401 return nReturn;
2402 }
2403
2404 if (!gCoreContext->GetBoolSettingOnHost(QString("JobAllow%1").arg(sJobName),
2405 sRemoteHost, false))
2406 {
2407 LOG(VB_GENERAL, LOG_INFO, QString("JobAllow%1 hasn't been setup for "
2408 "host %2 (will be run by default.)")
2409 .arg(sJobName, sRemoteHost));
2410 }
2411
2412 if (!JobStartTime.isValid())
2413 JobStartTime = QDateTime::currentDateTime();
2414
2415 if (!JobQueue::InJobRunWindow(JobStartTime))
2416 return nReturn;
2417
2418 if (sJobArgs.isNull())
2419 sJobArgs = "";
2420
2421 bool bReturn = JobQueue::QueueJob(jobType,
2422 ri.GetChanID(),
2424 sJobArgs,
2425 QString("Dvr/ManageJobQueue"), // comment col.
2426 sRemoteHost,
2428 JOB_QUEUED,
2429 JobStartTime.toUTC());
2430
2431 if (!bReturn)
2432 {
2433 LOG(VB_GENERAL, LOG_ERR, QString("%1 job wasn't queued because of a "
2434 "database error or because it was "
2435 "already running/stopping etc.")
2436 .arg(sJobName));
2437
2438 return nReturn;
2439 }
2440
2441 return JobQueue::GetJobID(jobType, ri.GetChanID(),
2443}
2444
2446//
2448
2450 bool AutoExpire,
2451 long BookmarkOffset,
2452 const QString &BookmarkOffsetType,
2453 bool Damaged,
2454 const QString &Description,
2455 uint Episode,
2456 const QString &Inetref,
2457 long LastPlayOffset,
2458 const QString &LastPlayOffsetType,
2459 QDate OriginalAirDate,
2460 bool Preserve,
2461 uint Season,
2462 uint Stars,
2463 const QString &SubTitle,
2464 const QString &Title,
2465 bool Watched,
2466 const QString &RecGroup )
2467
2468{
2469 if (m_request->m_queries.size() < 2 || !HAS_PARAMv2("RecordedId"))
2470 {
2471 LOG(VB_GENERAL, LOG_ERR, "No RecordedId, or no parameters to change.");
2472 return false;
2473 }
2474
2475 auto pi = ProgramInfo(RecordedId);
2476 auto ri = RecordingInfo(RecordedId);
2477
2478 if (!ri.GetChanID())
2479 return false;
2480
2481 if (HAS_PARAMv2("AutoExpire"))
2482 pi.SaveAutoExpire(AutoExpire ? kNormalAutoExpire :
2483 kDisableAutoExpire, false);
2484
2485 if (HAS_PARAMv2("BookmarkOffset"))
2486 {
2487 uint64_t position =0;
2488
2489 if (BookmarkOffsetType.toLower() == "position")
2490 {
2491 if (!ri.QueryPositionKeyFrame(&position, BookmarkOffset, true))
2492 return false;
2493 }
2494 else if (BookmarkOffsetType.toLower() == "duration")
2495 {
2496 if (!ri.QueryDurationKeyFrame(&position, BookmarkOffset, true))
2497 return false;
2498 }
2499 else
2500 {
2501 position = BookmarkOffset;
2502 }
2503
2504 ri.SaveBookmark(position);
2505 }
2506
2507 if (HAS_PARAMv2("Damaged"))
2508 pi.SaveVideoProperties(VID_DAMAGED, Damaged ? VID_DAMAGED : 0);
2509
2510 if (HAS_PARAMv2("Description") ||
2511 HAS_PARAMv2("SubTitle") ||
2512 HAS_PARAMv2("Title"))
2513 {
2514
2515 QString tmp_description;
2516 QString tmp_subtitle;
2517 QString tmp_title;
2518
2519 if (HAS_PARAMv2("Description"))
2520 tmp_description = Description;
2521 else
2522 tmp_description = ri.GetDescription();
2523
2524 if (HAS_PARAMv2("SubTitle"))
2525 tmp_subtitle = SubTitle;
2526 else
2527 tmp_subtitle = ri.GetSubtitle();
2528
2529 if (HAS_PARAMv2("Title"))
2530 tmp_title = Title;
2531 else
2532 tmp_title = ri.GetTitle();
2533
2534 ri.ApplyRecordRecTitleChange(tmp_title, tmp_subtitle, tmp_description);
2535 }
2536
2537 if (HAS_PARAMv2("Episode") ||
2538 HAS_PARAMv2("Season"))
2539 {
2540 int tmp_episode = 0;
2541 int tmp_season = 0;
2542
2543 if (HAS_PARAMv2("Episode"))
2544 tmp_episode = Episode;
2545 else
2546 tmp_episode = ri.GetEpisode();
2547
2548 if (HAS_PARAMv2("Season"))
2549 tmp_season = Season;
2550 else
2551 tmp_season = ri.GetSeason();
2552
2553 pi.SaveSeasonEpisode(tmp_season, tmp_episode);
2554 }
2555
2556 if (HAS_PARAMv2("Inetref"))
2557 pi.SaveInetRef(Inetref);
2558
2559 if (HAS_PARAMv2("LastPlayOffset"))
2560 {
2561
2562 if (LastPlayOffset < 0)
2563 throw QString("LastPlayOffset must be >= 0.");
2564
2565 uint64_t position = LastPlayOffset;
2566 bool isend=true;
2567
2568 if (HAS_PARAMv2("LastPlayOffsetType"))
2569 {
2570 if (LastPlayOffsetType.toLower() == "position")
2571 {
2572 if (!ri.QueryPositionKeyFrame(&position, LastPlayOffset, isend))
2573 return false;
2574 }
2575 else if (LastPlayOffsetType.toLower() == "duration")
2576 {
2577 if (!ri.QueryDurationKeyFrame(&position, LastPlayOffset, isend))
2578 return false;
2579 }
2580 }
2581
2582 ri.SaveLastPlayPos(position);
2583
2584 return true;
2585
2586 }
2587
2588 if (HAS_PARAMv2("OriginalAirDate"))
2589 {
2590 // OriginalAirDate can be set to null by submitting value 'null' in json
2591 if (!OriginalAirDate.isValid() && !OriginalAirDate.isNull())
2592 {
2593 LOG(VB_GENERAL, LOG_ERR, "Need valid OriginalAirDate yyyy-mm-dd.");
2594 return false;
2595 }
2596 ri.ApplyOriginalAirDateChange(OriginalAirDate);
2597 }
2598
2599 if (HAS_PARAMv2("Preserve"))
2600 pi.SavePreserve(Preserve);
2601
2602 if (HAS_PARAMv2("Stars"))
2603 {
2604 if (Stars > 10)
2605 {
2606 LOG(VB_GENERAL, LOG_ERR, "Recording stars can be 0 to 10.");
2607 return false;
2608 }
2609 ri.ApplyStarsChange(Stars * 0.1F);
2610 }
2611
2612 if (HAS_PARAMv2("Watched"))
2613 pi.SaveWatched(Watched);
2614
2615 if (HAS_PARAMv2("RecGroup"))
2616 ri.ApplyRecordRecGroupChange(RecGroup);
2617
2618 return true;
2619}
2620
2621// Get a single record by filling PriorityName, otherwise all records
2623{
2624 auto *pList = new V2PowerPriorityList();
2625
2627
2628 QString sql("SELECT priorityname, recpriority, selectclause "
2629 "FROM powerpriority ");
2630
2631 if (!PriorityName.isEmpty())
2632 sql.append(" WHERE priorityname = :NAME ");
2633
2634 query.prepare(sql);
2635
2636 if (!PriorityName.isEmpty())
2637 query.bindValue(":NAME", PriorityName);
2638
2639 if (query.exec())
2640 {
2641 while (query.next())
2642 {
2643 V2PowerPriority * pRec = pList->AddNewPowerPriority();
2644 pRec->setPriorityName(query.value(0).toString());
2645 pRec->setRecPriority(query.value(1).toInt());
2646 pRec->setSelectClause(query.value(2).toString());
2647 }
2648 }
2649 else
2650 {
2651 throw (QString("Error accessing powerpriority table"));
2652 }
2653
2654 return pList;
2655}
2656
2657bool V2Dvr::RemovePowerPriority ( const QString & PriorityName )
2658{
2659 if (PriorityName.isEmpty())
2660 return false;
2661
2663 query.prepare("DELETE FROM powerpriority WHERE priorityname = :PRIORITYNAME");
2664 query.bindValue(":PRIORITYNAME", PriorityName);
2665
2666 return query.exec();
2667}
2668
2669bool V2Dvr::AddPowerPriority ( const QString & PriorityName,
2670 int RecPriority,
2671 const QString & SelectClause )
2672{
2673 if (PriorityName.isEmpty())
2674 throw QString("ERROR: PriorityName is not specified");
2675 if (SelectClause.isEmpty())
2676 throw QString("ERROR: SelectClause is required");
2677 QString msg = CheckPowerQuery(SelectClause);
2678 if (! msg.isEmpty() )
2679 throw std::move(msg);
2681 query.prepare("INSERT INTO powerpriority "
2682 " (priorityname, recpriority, selectclause) "
2683 " VALUES(:PRIORITYNAME, :RECPRIORITY, :SELECTCLAUSE) ");
2684 query.bindValue(":PRIORITYNAME", PriorityName);
2685 query.bindValue(":RECPRIORITY", RecPriority);
2686 query.bindValue(":SELECTCLAUSE", SelectClause);
2687 if (!query.exec())
2688 throw(query.lastError().databaseText());
2689 return true;
2690}
2691
2692bool V2Dvr::UpdatePowerPriority ( const QString & PriorityName,
2693 int RecPriority,
2694 const QString & SelectClause )
2695{
2696 if (PriorityName.isEmpty())
2697 throw QString("ERROR: PriorityName is not specified");
2698 if (!HAS_PARAMv2("RecPriority") && !HAS_PARAMv2("SelectClause"))
2699 throw QString("ERROR: RecPriority or SelectClause is required");
2700
2701 if (HAS_PARAMv2("SelectClause"))
2702 {
2703 QString msg = CheckPowerQuery(SelectClause);
2704 if (! msg.isEmpty() )
2705 throw std::move(msg);
2706 }
2708 bool comma = false;
2709 QString sql("UPDATE powerpriority SET ");
2710 if ( HAS_PARAMv2("RecPriority") )
2711 {
2712 sql.append(" recpriority = :RECPRIORITY ");
2713 comma = true;
2714 }
2715 if ( HAS_PARAMv2("SelectClause") )
2716 {
2717 if (comma)
2718 sql.append(" , ");
2719 sql.append(" selectclause = :SELECTCLAUSE ");
2720 }
2721 sql.append(" where priorityname = :PRIORITYNAME ");
2722 query.prepare(sql);
2723 query.bindValue(":PRIORITYNAME", PriorityName);
2724 if ( HAS_PARAMv2("RecPriority") )
2725 query.bindValue(":RECPRIORITY", RecPriority);
2726 if ( HAS_PARAMv2("SelectClause") )
2727 query.bindValue(":SELECTCLAUSE", SelectClause);
2728 if (!query.exec())
2729 throw(query.lastError().databaseText());
2730 return query.numRowsAffected() > 0;
2731}
2732
2733QString V2Dvr::CheckPowerQuery(const QString & SelectClause)
2734{
2735 QString msg;
2736 QString sql = QString("SELECT (%1) FROM (recordmatch, record, "
2737 "program, channel, capturecard, "
2738 "oldrecorded) WHERE NULL").arg(SelectClause);
2739 while (true)
2740 {
2741 int i = sql.indexOf("RECTABLE");
2742 if (i == -1) break;
2743 sql = sql.replace(i, strlen("RECTABLE"), "record");
2744 }
2745
2747 query.prepare(sql);
2748
2749 if (!query.exec())
2750 {
2751 msg = tr("An error was found when checking") + ":\n\n";
2752 msg += query.executedQuery();
2753 msg += "\n\n" + tr("The database error was") + ":\n";
2754 msg += query.lastError().databaseText();
2755 }
2756 return msg;
2757}
std::vector< ProgramInfo * > pginfolist_t
Definition: autoexpire.h:23
AutoExpire * gExpirer
static int Reschedule(const MythUtilCommandLineParser &)
size_t size(void) const
Used to expire recordings to make space for new recordings.
Definition: autoexpire.h:61
void GetAllExpiring(QStringList &strList)
Gets the full list of programs that can expire in expiration order.
Definition: autoexpire.cpp:846
static QList< InputInfo > GetAllInputInfo(bool virtTuners)
Definition: cardutil.cpp:1745
static QString GetChanNum(int chan_id)
Returns the channel-number string of the given channel.
static bool SafeDeleteJob(int jobID, int jobType, int chanid, const QDateTime &recstartts)
Definition: jobqueue.cpp:889
static bool InJobRunWindow(QDateTime jobstarttsRaw)
Definition: jobqueue.cpp:1672
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:520
static int GetJobID(int jobType, uint chanid, const QDateTime &recstartts)
Definition: jobqueue.cpp:656
static int GetJobTypeFromName(const QString &name)
Definition: jobqueue.cpp:716
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QString executedQuery(void) const
Definition: mythdbcon.h:205
QSqlError lastError(void) const
Definition: mythdbcon.h:213
QVariant value(int i) const
Definition: mythdbcon.h:204
int numRowsAffected() const
Definition: mythdbcon.h:217
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
QString GetHostName(void)
MythScheduler * GetScheduler(void)
QString GetSetting(const QString &key, const QString &defaultval="")
void dispatch(const MythEvent &event)
bool GetBoolSettingOnHost(const QString &key, const QString &host, bool defaultval=false)
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
This class is used as a container for messages.
Definition: mythevent.h:17
bool HAS_PARAMv2(const QString &p)
HTTPRequest2 m_request
virtual QMap< QString, ProgramInfo * > GetRecording(void) const =0
static QStringList GetNames(void)
Definition: playgroup.cpp:238
Holds information on recordings and videos.
Definition: programinfo.h:74
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:380
static uint SubtitleTypesFromNames(const QString &names)
void QueryMarkup(QVector< MarkupEntry > &mapMark, QVector< MarkupEntry > &mapSeek) const
bool HasPathname(void) const
Definition: programinfo.h:365
void SavePreserve(bool preserveEpisode)
Set "preserve" field in "recorded" table to "preserveEpisode".
bool IsCommercialFlagged(void) const
Definition: programinfo.h:490
bool IsAutoExpirable(void) const
Definition: programinfo.h:495
bool IsPreserved(void) const
Definition: programinfo.h:496
void SaveWatched(bool watchedFlag)
Set "watched" field in recorded/videometadata to "watchedFlag".
void SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete=false)
Set "autoexpire" field in "recorded" table to "autoExpire".
uint GetRecordingID(void) const
Definition: programinfo.h:457
void ProgramFlagsFromNames(const QString &names)
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:592
void SaveMarkup(const QVector< MarkupEntry > &mapMark, const QVector< MarkupEntry > &mapSeek) const
bool QueryKeyFrameDuration(uint64_t *duration, uint64_t keyframe, bool backwards) const
static uint AudioPropertiesFromNames(const QString &names)
static QMap< QString, bool > QueryJobsRunning(int type)
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:412
bool QueryPositionKeyFrame(uint64_t *keyframe, uint64_t position, bool backwards) const
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:398
void SaveLastPlayPos(uint64_t frame)
TODO Move to RecordingInfo.
bool QueryKeyFramePosition(uint64_t *position, uint64_t keyframe, bool backwards) const
static uint VideoPropertiesFromNames(const QString &names)
bool QueryDurationKeyFrame(uint64_t *keyframe, uint64_t duration, bool backwards) const
bool IsWatched(void) const
Definition: programinfo.h:494
static bool QueryRecordedIdFromPathname(const QString &pathname, uint &recordedid)
static QMap< QString, uint32_t > QueryInUseMap(void)
uint64_t QueryLastPlayPos(void) const
Gets any lastplaypos position in database, unless the ignore lastplaypos flag is set.
uint64_t QueryBookmark(void) const
Gets any bookmark position in database, unless the ignore bookmark flag is set.
void SaveCommFlagged(CommFlagStatus flag)
Set "commflagged" field in "recorded" table to "flag".
void SendUpdateEvent(void) const
Sends event out that the ProgramInfo should be reloaded.
void SaveBookmark(uint64_t frame)
Clears any existing bookmark in DB and if frame is greater than 0 sets a new bookmark.
static QString toDescription(Type recstatus, RecordingType rectype, const QDateTime &recstartts)
Converts "recstatus" into a long human readable description.
static QString toString(RecStatus::Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:36
void InsertFile(void)
void ApplyRecordStateChange(RecordingType newstate, bool save=true)
Sets RecordingType of "record", creating "record" if it does not exist.
void ApplyNeverRecord(void)
Set this program to never be recorded by inserting 'history' for it into the database with a status o...
void ForgetHistory(void)
Forget the recording of a program so it will be recorded again.
bool InsertRecording(const QString &ext, bool force_match=false)
static bool QueryRecordedIdForKey(int &recordedid, uint chanid, const QDateTime &recstartts)
void ReactivateRecording(void)
Asks the scheduler to restart this recording if possible.
static uint GetRecgroupID(const QString &recGroup)
Temporary helper during transition from string to ID.
Internal representation of a recording rule, mirrors the record table.
Definition: recordingrule.h:30
RecordingType m_type
QString m_station
Definition: recordingrule.h:99
QString m_programid
Definition: recordingrule.h:86
QString m_category
Definition: recordingrule.h:83
int m_findday
Day of the week for once per week etc.
QString m_inetref
Definition: recordingrule.h:88
bool LoadTemplate(const QString &title, const QString &category="Default", const QString &categoryType="Default")
RecSearchType m_searchType
bool LoadByProgram(const ProgramInfo *proginfo)
unsigned m_filter
bool IsValid(QString &msg) const
QTime m_findtime
Time for timeslot rules.
QString m_description
Definition: recordingrule.h:82
QString m_storageGroup
int m_recordID
Unique Recording Rule ID.
Definition: recordingrule.h:71
RecordingDupInType m_dupIn
QString m_title
Definition: recordingrule.h:78
int m_channelid
callsign?
QString m_recProfile
bool m_isInactive
Recording rule is enabled?
Definition: recordingrule.h:75
bool MakeOverride(void)
QString m_playGroup
bool Save(bool sendSig=true)
QString m_subtitle
Definition: recordingrule.h:80
RecordingDupMethodType m_dupMethod
bool Load(bool asTemplate=false)
Load a single rule from the recorded table.
bool Delete(bool sendSig=true)
QString m_seriesid
Definition: recordingrule.h:85
QDateTime m_lastRecorded
bool IsLoaded() const
Definition: recordingrule.h:56
bool m_autoMetadataLookup
AutoExtendType m_autoExtend
static void RescheduleMatch(uint recordid, uint sourceid, uint mplexid, const QDateTime &maxstarttime, const QString &why)
SchedSortColumn
Definition: scheduler.h:86
@ kSortNextRecording
Definition: scheduler.h:86
@ kSortType
Definition: scheduler.h:87
@ kSortTitle
Definition: scheduler.h:86
@ kSortPriority
Definition: scheduler.h:87
@ kSortLastRecorded
Definition: scheduler.h:86
static void GetAllScheduled(QStringList &strList, SchedSortColumn sortBy=kSortTitle, bool ascending=true)
Returns all scheduled programs serialized into a QStringList.
Definition: scheduler.cpp:1858
static QStringList getRecordingsGroups(void)
static bool AddPlayGroup(const QString &Name, const QString &TitleMatch, int SkipAhead, int SkipBack, int TimeStretch, int Jump)
Definition: v2dvr.cpp:1452
static long GetSavedBookmark(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType)
Definition: v2dvr.cpp:939
static void RegisterCustomTypes()
static V2EncoderList * GetEncoderList()
Definition: v2dvr.cpp:1310
static bool RescheduleRecordings(void)
Definition: v2dvr.cpp:856
static uint AddRecordSchedule(const QString &Title, const QString &Subtitle, const QString &Description, const QString &Category, const QDateTime &StartTime, const QDateTime &EndTime, 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, const QDateTime &LastRecorded, QString DupMethod, QString DupIn, bool NewEpisOnly, 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, const QString &AutoExtend)
Definition: v2dvr.cpp:1710
static QStringList GetTitleList(const QString &RecGroup)
Definition: v2dvr.cpp:1573
V2Dvr()
Definition: v2dvr.cpp:88
static QString DupInToDescription(const QString &DupIn)
Definition: v2dvr.cpp:2321
static bool RemovePowerPriority(const QString &PriorityName)
Definition: v2dvr.cpp:2657
static bool RemoveRecordSchedule(uint RecordId)
Definition: v2dvr.cpp:2039
static bool StopRecording(int RecordedId)
Definition: v2dvr.cpp:775
static int RecordedIdForKey(int ChanId, const QDateTime &StartTime)
Definition: v2dvr.cpp:2239
static V2ProgramList * GetUpcomingList(int StartIndex, int Count, bool ShowAll, int RecordId, const QString &RecStatus, const QString &Sort, const QString &RecGroup)
Definition: v2dvr.cpp:1668
static int RecordedIdForPathname(const QString &Pathname)
Definition: v2dvr.cpp:2250
static V2MarkupList * GetRecordedMarkup(int RecordedId)
Definition: v2dvr.cpp:1207
static V2PowerPriorityList * GetPowerPriorityList(const QString &PriorityName)
Definition: v2dvr.cpp:2622
static int AddRecordedProgram(const QString &Program)
Definition: v2dvr.cpp:540
static V2RecRuleList * GetRecordScheduleList(int StartIndex, int Count, const QString &Sort, bool Descending)
Definition: v2dvr.cpp:2084
static QString RecStatusToDescription(const QString &RecStatus, int RecType, const QDateTime &StartTime)
Definition: v2dvr.cpp:2276
static V2CutList * GetRecordedSeek(int RecordedId, const QString &OffsetType)
Definition: v2dvr.cpp:1173
static QStringList GetProgramCategories(bool OnlyRecorded)
Definition: v2dvr.cpp:1369
static QString CheckPowerQuery(const QString &SelectClause)
Definition: v2dvr.cpp:2733
static bool UnDeleteRecording(int RecordedId, int ChanId, const QDateTime &StartTime)
Definition: v2dvr.cpp:744
static V2PlayGroup * GetPlayGroup(const QString &Name)
Definition: v2dvr.cpp:1409
static bool AllowReRecord(int RecordedId, int ChanId, const QDateTime &StartTime)
Definition: v2dvr.cpp:867
bool UpdatePlayGroup(const QString &Name, const QString &TitleMatch, int SkipAhead, int SkipBack, int TimeStretch, int Jump)
Definition: v2dvr.cpp:1474
bool UpdateRecordedMetadata(uint RecordedId, bool AutoExpire, long BookmarkOffset, const QString &BookmarkOffsetType, bool Damaged, const QString &Description, uint Episode, const QString &Inetref, long LastPlayOffset, const QString &LastPlayOffsetType, QDate OriginalAirDate, bool Preserve, uint Season, uint Stars, const QString &SubTitle, const QString &Title, bool Watched, const QString &RecGroup)
Definition: v2dvr.cpp:2449
static bool EnableRecordSchedule(uint RecordId)
Definition: v2dvr.cpp:2199
static QString DupMethodToDescription(const QString &DupMethod)
Definition: v2dvr.cpp:2334
static bool DeleteRecording(int RecordedId, int ChanId, const QDateTime &StartTime, bool ForceDelete, bool AllowRerecord)
Definition: v2dvr.cpp:709
static V2ProgramList * GetConflictList(int StartIndex, int Count, int RecordId, const QString &Sort)
Definition: v2dvr.cpp:1644
static bool SetRecordedMarkup(int RecordedId, const QString &MarkupList)
Definition: v2dvr.cpp:1252
static bool SetSavedBookmark(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType, long Offset)
Definition: v2dvr.cpp:1023
static V2InputList * GetInputList()
Definition: v2dvr.cpp:1321
static QStringList GetRecGroupList(const QString &UsedBy)
Definition: v2dvr.cpp:1339
static bool RemovePlayGroup(const QString &Name)
Definition: v2dvr.cpp:1438
static V2CutList * GetRecordedCommBreak(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType, bool IncludeFps)
Definition: v2dvr.cpp:1139
static QString DupMethodToString(const QString &DupMethod)
Definition: v2dvr.cpp:2328
static V2TitleInfoList * GetTitleInfoList()
Definition: v2dvr.cpp:1609
static long GetLastPlayPos(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType)
Definition: v2dvr.cpp:981
static bool DisableRecordSchedule(uint RecordId)
Definition: v2dvr.cpp:2219
static V2RecRule * GetRecordSchedule(uint RecordId, const QString &Template, int RecordedId, int ChanId, const QDateTime &StartTime, bool MakeOverride)
Definition: v2dvr.cpp:2146
static bool AddRecordedCredits(int RecordedId, const QString &Cast)
Definition: v2dvr.cpp:511
static QString DupInToString(const QString &DupIn)
Definition: v2dvr.cpp:2314
static QString RecStatusToString(const QString &RecStatus)
Definition: v2dvr.cpp:2260
static V2CutList * GetRecordedCutList(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType, bool IncludeFps)
Definition: v2dvr.cpp:1105
static QStringList GetRecStorageGroupList()
Definition: v2dvr.cpp:1395
V2ProgramList * GetRecordedList(bool Descending, int StartIndex, int Count, const QString &TitleRegEx, const QString &RecGroup, const QString &StorageGroup, const QString &Category, const QString &Sort, bool IgnoreLiveTV, bool IgnoreDeleted, bool IncChannel, bool Details, bool IncCast, bool IncArtWork, bool IncRecording)
Definition: v2dvr.cpp:137
static QStringList GetPlayGroupList()
Definition: v2dvr.cpp:1404
static bool ReactivateRecording(int RecordedId, int ChanId, const QDateTime &StartTime, int RecordId)
Definition: v2dvr.cpp:806
static QString RecTypeToString(const QString &RecType)
Definition: v2dvr.cpp:2294
static V2ProgramList * GetOldRecordedList(bool Descending, int StartIndex, int Count, const QDateTime &StartTime, const QDateTime &EndTime, const QString &Title, const QString &TitleRegEx, const QString &SubtitleRegEx, const QString &SeriesId, int RecordId, const QString &Sort)
Definition: v2dvr.cpp:261
static V2Program * GetRecorded(int RecordedId, int ChanId, const QDateTime &StartTime)
Definition: v2dvr.cpp:487
static bool RemoveRecorded(int RecordedId, int ChanId, const QDateTime &StartTime, bool ForceDelete, bool AllowRerecord)
Definition: v2dvr.cpp:700
bool RemoveOldRecorded(int ChanId, const QDateTime &StartTime, bool Reschedule)
Definition: v2dvr.cpp:428
static bool UpdateRecordedWatchedStatus(int RecordedId, int ChanId, const QDateTime &StartTime, bool Watched)
Definition: v2dvr.cpp:908
static bool SetLastPlayPos(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType, long Offset)
Definition: v2dvr.cpp:1067
int ManageJobQueue(const QString &Action, const QString &JobName, int JobId, int RecordedId, QDateTime JobStartTime, QString RemoteHost, QString JobArgs)
Definition: v2dvr.cpp:2344
static QString RecTypeToDescription(const QString &RecType)
Definition: v2dvr.cpp:2304
bool UpdatePowerPriority(const QString &PriorityName, int RecPriority, const QString &SelectClause)
Definition: v2dvr.cpp:2692
static bool AddDontRecordSchedule(int ChanId, const QDateTime &StartTime, bool NeverRecord)
Definition: v2dvr.cpp:2054
static V2RecRuleFilterList * GetRecRuleFilterList()
Definition: v2dvr.cpp:1546
static V2ProgramList * GetExpiringList(int StartIndex, int Count)
Definition: v2dvr.cpp:93
bool UpdateOldRecorded(int ChanId, const QDateTime &StartTime, bool Duplicate, bool Reschedule)
Definition: v2dvr.cpp:454
static bool UpdateRecordSchedule(uint RecordId, const QString &Title, const QString &Subtitle, const QString &Description, const QString &Category, const QDateTime &StartTime, const QDateTime &EndTime, const QString &SeriesId, const QString &ProgramId, int ChanId, const 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, bool NewEpisOnly, 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, const QString &AutoExtend)
Definition: v2dvr.cpp:1868
static bool AddPowerPriority(const QString &PriorityName, int RecPriority, const QString &SelectClause)
Definition: v2dvr.cpp:2669
unsigned int uint
Definition: compat.h:60
@ JOB_USERJOB
Definition: jobqueue.h:83
@ JOB_NONE
Definition: jobqueue.h:75
@ JOB_COMMFLAG
Definition: jobqueue.h:79
@ JOB_NO_FLAGS
Definition: jobqueue.h:59
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:100
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
std::deque< RecordingInfo * > RecList
Definition: mythscheduler.h:12
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ ISODate
Default UTC.
Definition: mythdate.h:17
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
dictionary info
Definition: azlyrics.py:7
string hostname
Definition: caa.py:17
std::vector< DBPerson > DBCredits
Definition: programdata.h:73
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, bool ignoreLiveTV, bool ignoreDeleted)
ProgramInfo::CategoryType string_to_myth_category_type(const QString &category_type)
ProgramInfo * LoadProgramFromProgram(const uint chanid, const QDateTime &starttime)
bool LoadFromOldRecorded(ProgramList &destination, const QString &sql, const MSqlBindings &bindings)
MarkTypes markTypeFromString(const QString &str)
MarkTypes
Definition: programtypes.h:46
@ MARK_GOP_BYFRAME
Definition: programtypes.h:63
@ MARK_UNSET
Definition: programtypes.h:49
@ MARK_DURATION_MS
Definition: programtypes.h:73
@ kDisableAutoExpire
Definition: programtypes.h:193
@ kNormalAutoExpire
Definition: programtypes.h:194
@ COMM_FLAG_DONE
Definition: programtypes.h:121
@ COMM_FLAG_NOT_FLAGGED
Definition: programtypes.h:120
RecordingDupMethodType dupMethodFromString(const QString &type)
RecordingDupInType dupInFromString(const QString &type)
RecordingType recTypeFromString(const QString &type)
QString toDescription(RecordingType rectype)
Converts "rectype" into a human readable description.
RecSearchType searchTypeFromString(const QString &type)
AutoExtendType autoExtendTypeFromString(const QString &type)
RecordingDupInType dupInFromStringAndBool(const QString &type, bool newEpisodesOnly)
@ kManualSearch
RecordingDupInType
RecordingType
@ kOverrideRecord
@ kSingleRecord
@ kDontRecord
RecordingDupMethodType
@ kDupCheckNone
@ Stars
Definition: synaesthesia.h:23
Q_GLOBAL_STATIC_WITH_ARGS(MythHTTPMetaService, s_service,(DVR_HANDLE, V2Dvr::staticMetaObject, &V2Dvr::RegisterCustomTypes)) void V2Dvr
Definition: v2dvr.cpp:56
#define DVR_HANDLE
Definition: v2dvr.h:42
void FillEncoderList(QVariantList &list, QObject *parent)
void V2FillInputInfo(V2Input *input, const InputInfo &inputInfo)
void V2FillCommBreak(V2CutList *pCutList, ProgramInfo *rInfo, int marktype, bool includeFps)
DBCredits * V2jsonCastToCredits(const QJsonObject &cast)
void V2FillRecRuleInfo(V2RecRule *pRecRule, RecordingRule *pRule)
void V2FillProgramInfo(V2Program *pProgram, ProgramInfo *pInfo, bool bIncChannel, bool bDetails, bool bIncCast, bool bIncArtwork, bool bIncRecording)
int FillUpcomingList(QVariantList &list, QObject *parent, int &nStartIndex, int &nCount, bool bShowAll, int nRecordId, int nRecStatus, const QString &Sort, const QString &RecGroup)
void V2FillSeek(V2CutList *pCutList, RecordingInfo *rInfo, MarkTypes marktype)
int V2CreateRecordingGroup(const QString &groupName)
void V2FillCutList(V2CutList *pCutList, ProgramInfo *rInfo, int marktype, bool includeFps)