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"
39#include "libmythtv/cardutil.h"
41#include "libmythtv/jobqueue.h"
42#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 ri = RecordingInfo(RecordedId);
813 else if (ChanId > 0 && StartTime.isValid())
814 ri = RecordingInfo(ChanId, StartTime.toUTC());
815 else if (RecordId > 0)
816 {
817 // Find latest recording for that record id
819 query.prepare("SELECT recordedid FROM recorded "
820 " WHERE recordid = :RECORDID "
821 " ORDER BY starttime DESC LIMIT 1");
822 query.bindValue(":RECORDID", RecordId);
823 if (!query.exec())
824 {
825 MythDB::DBError("ReactivateRecording", query);
826 return false;
827 }
828 int recId {0};
829 if (query.next())
830 recId = query.value(0).toInt();
831 else
832 return false;
833 ri = RecordingInfo(recId);
834 }
835 else
836 {
837 throw QString("Recorded ID or Channel ID and StartTime or RecordId are invalid.");
838 }
839 if (ri.GetChanID() && ri.HasPathname())
840 {
842 return true;
843 }
844
845 return false;
846}
847
849//
851
853{
854 ScheduledRecording::RescheduleMatch(0, 0, 0, QDateTime(),
855 "RescheduleRecordings");
856 return true;
857}
858
860//
862
863bool V2Dvr::AllowReRecord ( int RecordedId, int ChanId, const QDateTime &StartTime)
864{
865 if (RecordedId > 0)
866 {
867 if (ChanId > 0 || StartTime.isValid())
868 throw QString("ERROR RecordedId param cannot be used with ChanId or StartTime.");
869 }
870 else if (ChanId > 0)
871 {
872 if (!StartTime.isValid())
873 throw QString("ERROR ChanId param requires a valid StartTime.");
874 }
875 else
876 {
877 throw QString("ERROR RecordedId or (ChanId and StartTime) required.");
878 }
879
880 if (RecordedId > 0)
881 {
882 RecordingInfo ri = RecordingInfo(RecordedId);
883 if (!ri.GetChanID())
884 throw QString("ERROR RecordedId %1 not found").arg(RecordedId);
885 ri.ForgetHistory();
886 }
887 else
888 {
889 ProgramInfo *progInfo = LoadProgramFromProgram(ChanId, StartTime);
890 if (progInfo == nullptr)
891 throw QString("ERROR Guide data for Chanid %1 at StartTime %2 not found")
892 .arg(ChanId).arg(StartTime.toString());
893 RecordingInfo recInfo(*progInfo);
894 recInfo.ForgetHistory();
895 delete progInfo;
896 }
897 return true;
898}
899
901// Prefer Dvr/UpdateRecordedMetadata. Some day, this should go away.
903
905 int chanid,
906 const QDateTime &StartTime,
907 bool watched)
908{
909 // LOG(VB_GENERAL, LOG_WARNING, "Deprecated, use Dvr/UpdateRecordedMetadata.");
910
911 if ((RecordedId <= 0) &&
912 (chanid <= 0 || !StartTime.isValid()))
913 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
914
915 // TODO Should use RecordingInfo
916 ProgramInfo pi;
917 if (RecordedId > 0)
918 pi = ProgramInfo(RecordedId);
919 else
920 pi = ProgramInfo(chanid, StartTime.toUTC());
921
922 if (pi.GetChanID() && pi.HasPathname())
923 {
924 pi.SaveWatched(watched);
925 return true;
926 }
927
928 return false;
929}
930
932//
934
935long V2Dvr::GetSavedBookmark( int RecordedId,
936 int chanid,
937 const QDateTime &StartTime,
938 const QString &offsettype )
939{
940 if ((RecordedId <= 0) &&
941 (chanid <= 0 || !StartTime.isValid()))
942 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
943
944 RecordingInfo ri;
945 if (RecordedId > 0)
946 ri = RecordingInfo(RecordedId);
947 else
948 ri = RecordingInfo(chanid, StartTime.toUTC());
949 uint64_t offset = 0;
950 bool isend=true;
951 uint64_t position = ri.QueryBookmark();
952 // if no bookmark return 0
953 if (position == 0)
954 return 0;
955 if (offsettype.toLower() == "position"){
956 // if bookmark cannot be converted to a keyframe we will
957 // just return the actual frame saved as the bookmark
958 if (ri.QueryKeyFramePosition(&offset, position, isend))
959 return offset;
960 }
961 if (offsettype.toLower() == "duration"){
962 if (ri.QueryKeyFrameDuration(&offset, position, isend))
963 return offset;
964 // If bookmark cannot be converted to a duration return -1
965 return -1;
966 }
967 return position;
968}
969
971// Get last play position
972// Providing -1 for the RecordedId will return response of -1.
973// This is a way to check if this api, and the other LastPlayPos APIs,
974// are supported
976
977long V2Dvr::GetLastPlayPos( int RecordedId,
978 int chanid,
979 const QDateTime &StartTime,
980 const QString &offsettype )
981{
982 if (RecordedId == -1)
983 return -1;
984
985 if ((RecordedId <= 0) &&
986 (chanid <= 0 || !StartTime.isValid()))
987 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
988
989 RecordingInfo ri;
990 if (RecordedId > 0)
991 ri = RecordingInfo(RecordedId);
992 else
993 ri = RecordingInfo(chanid, StartTime.toUTC());
994 uint64_t offset = 0;
995 bool isend=true;
996 uint64_t position = ri.QueryLastPlayPos();
997 // if no bookmark return 0
998 if (position == 0)
999 return 0;
1000 if (offsettype.toLower() == "position"){
1001 // if bookmark cannot be converted to a keyframe we will
1002 // just return the actual frame saved as the bookmark
1003 if (ri.QueryKeyFramePosition(&offset, position, isend))
1004 return offset;
1005 }
1006 if (offsettype.toLower() == "duration"){
1007 if (ri.QueryKeyFrameDuration(&offset, position, isend))
1008 return offset;
1009 // If bookmark cannot be converted to a duration return -1
1010 return -1;
1011 }
1012 return position;
1013}
1014
1016// Prefer Dvr/UpdateRecordedMetadata. Some day, this should go away.
1018
1019bool V2Dvr::SetSavedBookmark( int RecordedId,
1020 int chanid,
1021 const QDateTime &StartTime,
1022 const QString &offsettype,
1023 long Offset )
1024{
1025 // LOG(VB_GENERAL, LOG_WARNING, "Deprecated, use Dvr/UpdateRecordedMetadata.");
1026
1027 if ((RecordedId <= 0) &&
1028 (chanid <= 0 || !StartTime.isValid()))
1029 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
1030
1031 if (Offset < 0)
1032 throw QString("Offset must be >= 0.");
1033
1034 RecordingInfo ri;
1035 if (RecordedId > 0)
1036 ri = RecordingInfo(RecordedId);
1037 else
1038 ri = RecordingInfo(chanid, StartTime.toUTC());
1039 uint64_t position = 0;
1040 bool isend=true;
1041 if (offsettype.toLower() == "position"){
1042 if (!ri.QueryPositionKeyFrame(&position, Offset, isend))
1043 return false;
1044 }
1045 else if (offsettype.toLower() == "duration"){
1046 if (!ri.QueryDurationKeyFrame(&position, Offset, isend))
1047 return false;
1048 }
1049 else
1050 {
1051 position = Offset;
1052 }
1053 ri.SaveBookmark(position);
1054 return true;
1055}
1056
1058// Set last Play Position. Check if this is supported by first calling
1059// Get Last Play Position with -1.
1060// Prefer Dvr/UpdateRecordedMetadata. Some day, this should go away.
1062
1063bool V2Dvr::SetLastPlayPos( int RecordedId,
1064 int chanid,
1065 const QDateTime &StartTime,
1066 const QString &offsettype,
1067 long Offset )
1068{
1069 // LOG(VB_GENERAL, LOG_WARNING, "Deprecated, use Dvr/UpdateRecordedMetadata.");
1070
1071 if ((RecordedId <= 0) &&
1072 (chanid <= 0 || !StartTime.isValid()))
1073 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
1074
1075 if (Offset < 0)
1076 throw QString("Offset must be >= 0.");
1077
1078 RecordingInfo ri;
1079 if (RecordedId > 0)
1080 ri = RecordingInfo(RecordedId);
1081 else
1082 ri = RecordingInfo(chanid, StartTime.toUTC());
1083 uint64_t position = 0;
1084 bool isend=true;
1085 if (offsettype.toLower() == "position"){
1086 if (!ri.QueryPositionKeyFrame(&position, Offset, isend))
1087 return false;
1088 }
1089 else if (offsettype.toLower() == "duration"){
1090 if (!ri.QueryDurationKeyFrame(&position, Offset, isend))
1091 return false;
1092 }
1093 else
1094 {
1095 position = Offset;
1096 }
1097 ri.SaveLastPlayPos(position);
1098 return true;
1099}
1100
1102 int chanid,
1103 const QDateTime &StartTime,
1104 const QString &offsettype,
1105 bool IncludeFps )
1106{
1107 int marktype = 0;
1108 if ((RecordedId <= 0) &&
1109 (chanid <= 0 || !StartTime.isValid()))
1110 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
1111
1112 RecordingInfo ri;
1113 if (RecordedId > 0)
1114 ri = RecordingInfo(RecordedId);
1115 else
1116 ri = RecordingInfo(chanid, StartTime.toUTC());
1117
1118 auto* pCutList = new V2CutList();
1119 if (offsettype.toLower() == "position")
1120 marktype = 1;
1121 else if (offsettype.toLower() == "duration")
1122 marktype = 2;
1123 else
1124 marktype = 0;
1125
1126 V2FillCutList(pCutList, &ri, marktype, IncludeFps);
1127
1128 return pCutList;
1129}
1130
1132//
1134
1136 int chanid,
1137 const QDateTime &StartTime,
1138 const QString &offsettype,
1139 bool IncludeFps )
1140{
1141 int marktype = 0;
1142 if ((RecordedId <= 0) &&
1143 (chanid <= 0 || !StartTime.isValid()))
1144 throw QString("Recorded ID or Channel ID and StartTime appears invalid.");
1145
1146 RecordingInfo ri;
1147 if (RecordedId > 0)
1148 ri = RecordingInfo(RecordedId);
1149 else
1150 ri = RecordingInfo(chanid, StartTime.toUTC());
1151
1152 auto* pCutList = new V2CutList();
1153 if (offsettype.toLower() == "position")
1154 marktype = 1;
1155 else if (offsettype.toLower() == "duration")
1156 marktype = 2;
1157 else
1158 marktype = 0;
1159
1160 V2FillCommBreak(pCutList, &ri, marktype, IncludeFps);
1161
1162 return pCutList;
1163}
1164
1166//
1168
1170 const QString &offsettype )
1171{
1172 MarkTypes marktype = MARK_UNSET;
1173 if (RecordedId <= 0)
1174 throw QString("Recorded ID appears invalid.");
1175
1176 RecordingInfo ri;
1177 ri = RecordingInfo(RecordedId);
1178
1179 auto* pCutList = new V2CutList();
1180 if (offsettype.toLower() == "bytes")
1181 marktype = MARK_GOP_BYFRAME;
1182 else if (offsettype.toLower() == "duration")
1183 marktype = MARK_DURATION_MS;
1184 else
1185 {
1186 delete pCutList;
1187 throw QString("Type must be 'BYTES' or 'DURATION'.");
1188 }
1189
1190 V2FillSeek(pCutList, &ri, marktype);
1191
1192 return pCutList;
1193}
1194
1196//
1198
1200{
1201 RecordingInfo ri;
1202 ri = RecordingInfo(RecordedId);
1203
1204 if (!ri.HasPathname())
1205 throw QString("Invalid RecordedId %1").arg(RecordedId);
1206
1207 QVector<ProgramInfo::MarkupEntry> mapMark;
1208 QVector<ProgramInfo::MarkupEntry> mapSeek;
1209
1210 ri.QueryMarkup(mapMark, mapSeek);
1211
1212 auto* pMarkupList = new V2MarkupList();
1213 for (const auto& entry : std::as_const(mapMark))
1214 {
1215 V2Markup *pMarkup = pMarkupList->AddNewMarkup();
1216 QString typestr = toString(static_cast<MarkTypes>(entry.type));
1217 pMarkup->setType(typestr);
1218 pMarkup->setFrame(entry.frame);
1219 if (entry.isDataNull)
1220 pMarkup->setData("NULL");
1221 else
1222 pMarkup->setData(QString::number(entry.data));
1223 }
1224 for (const auto& entry : std::as_const(mapSeek))
1225 {
1226 V2Markup *pSeek = pMarkupList->AddNewSeek();
1227 QString typestr = toString(static_cast<MarkTypes>(entry.type));
1228 pSeek->setType(typestr);
1229 pSeek->setFrame(entry.frame);
1230 if (entry.isDataNull)
1231 pSeek->setData("NULL");
1232 else
1233 pSeek->setData(QString::number(entry.data));
1234 }
1235
1236
1237 return pMarkupList;
1238}
1239
1241//
1243
1244bool V2Dvr::SetRecordedMarkup(int RecordedId, const QString &MarkupList)
1245{
1246 RecordingInfo ri;
1247 ri = RecordingInfo(RecordedId);
1248
1249 if (!ri.HasPathname())
1250 throw QString("Invalid RecordedId %1").arg(RecordedId);
1251
1252 QVector<ProgramInfo::MarkupEntry> mapMark;
1253 QVector<ProgramInfo::MarkupEntry> mapSeek;
1254
1255 QJsonDocument doc = QJsonDocument::fromJson(MarkupList.toUtf8());
1256 QJsonObject markuplist = doc.object();
1257
1258 QJsonArray marks = markuplist["Mark"].toArray();
1259 for (const auto & m : std::as_const(marks))
1260 {
1261 QJsonObject markup = m.toObject();
1263
1264 QString typestr = markup.value("Type").toString("");
1265 entry.type = markTypeFromString(typestr);
1266 entry.frame = markup.value("Frame").toVariant()
1267 .toString().toLongLong();
1268 QString data = markup.value("Data").toString("NULL");
1269 entry.isDataNull = (data == "NULL");
1270 if (!entry.isDataNull)
1271 entry.data = data.toLongLong();
1272
1273 mapMark.append(entry);
1274 }
1275
1276 QJsonArray seeks = markuplist["Seek"].toArray();
1277 for (const auto & m : std::as_const(seeks))
1278 {
1279 QJsonObject markup = m.toObject();
1281
1282 QString typestr = markup.value("Type").toString("");
1283 entry.type = markTypeFromString(typestr);
1284 entry.frame = markup.value("Frame").toVariant().toString().toLongLong();
1285 QString data = markup.value("Data").toString("NULL");
1286 entry.isDataNull = (data == "NULL");
1287 if (!entry.isDataNull)
1288 entry.data = data.toLongLong();
1289
1290 mapSeek.append(entry);
1291 }
1292
1293 ri.SaveMarkup(mapMark, mapSeek);
1294
1295 return true;
1296}
1297
1299//
1301
1303{
1304 auto* pList = new V2EncoderList();
1305 FillEncoderList(pList->GetEncoders(), pList);
1306 return pList;
1307}
1308
1310//
1312
1314{
1315 auto *pList = new V2InputList();
1316
1317 QList<InputInfo> inputInfoList = CardUtil::GetAllInputInfo(false);
1318 for (const auto & inputInfo : std::as_const(inputInfoList))
1319 {
1320 V2Input *input = pList->AddNewInput();
1321 V2FillInputInfo(input, inputInfo);
1322 }
1323
1324 return pList;
1325}
1326
1328//
1330
1331QStringList V2Dvr::GetRecGroupList( const QString &UsedBy)
1332{
1334 if (UsedBy.compare("recorded",Qt::CaseInsensitive) == 0)
1335 query.prepare("SELECT DISTINCT recgroup FROM recorded "
1336 "ORDER BY recgroup");
1337 else if (UsedBy.compare("schedule",Qt::CaseInsensitive) == 0)
1338 query.prepare("SELECT DISTINCT recgroup FROM record "
1339 "ORDER BY recgroup");
1340 else
1341 query.prepare("SELECT recgroup FROM recgroups WHERE recgroup <> 'Deleted' "
1342 "ORDER BY recgroup");
1343
1344 QStringList result;
1345 if (!query.exec())
1346 {
1347 MythDB::DBError("GetRecGroupList", query);
1348 return result;
1349 }
1350
1351 while (query.next())
1352 result << query.value(0).toString();
1353
1354 return result;
1355}
1356
1358//
1360
1361QStringList V2Dvr::GetProgramCategories( bool OnlyRecorded )
1362{
1364
1365 if (OnlyRecorded)
1366 query.prepare("SELECT DISTINCT category FROM recorded ORDER BY category");
1367 else
1368 query.prepare("SELECT DISTINCT category FROM program ORDER BY category");
1369
1370 QStringList result;
1371 if (!query.exec())
1372 {
1373 MythDB::DBError("GetProgramCategories", query);
1374 return result;
1375 }
1376
1377 while (query.next())
1378 result << query.value(0).toString();
1379
1380 return result;
1381}
1382
1384//
1386
1388{
1390}
1391
1393//
1395
1397{
1398 return PlayGroup::GetNames();
1399}
1400
1402{
1403 auto* playGroup = new V2PlayGroup();
1404
1406
1407 query.prepare("SELECT name, titlematch, skipahead, skipback, timestretch, jump "
1408 "FROM playgroup WHERE name = :NAME ");
1409 query.bindValue(":NAME", Name);
1410
1411 if (query.exec())
1412 {
1413 if (query.next())
1414 {
1415 playGroup->setName(query.value(0).toString());
1416 playGroup->setTitleMatch(query.value(1).toString());
1417 playGroup->setSkipAhead(query.value(2).toInt());
1418 playGroup->setSkipBack(query.value(3).toInt());
1419 playGroup->setTimeStretch(query.value(4).toInt());
1420 playGroup->setJump(query.value(5).toInt());
1421 }
1422 else
1423 {
1424 throw QString("Play Group Not Found.");
1425 }
1426 }
1427 return playGroup;
1428}
1429
1430bool V2Dvr::RemovePlayGroup ( const QString & Name )
1431{
1432
1433 if (Name.compare("Default", Qt::CaseInsensitive) == 0)
1434 throw QString("ERROR: Cannot delete Default entry");
1436 query.prepare("DELETE FROM playgroup "
1437 "WHERE name = :NAME");
1438
1439 query.bindValue(":NAME", Name);
1440
1441 return query.exec();
1442}
1443
1444bool V2Dvr::AddPlayGroup ( const QString & Name,
1445 const QString & TitleMatch,
1446 int SkipAhead,
1447 int SkipBack,
1448 int TimeStretch,
1449 int Jump )
1450{
1452
1453 query.prepare("INSERT INTO playgroup "
1454 "(name, titlematch, skipahead, skipback, timestretch, jump) "
1455 " VALUES(:NAME, :TITLEMATCH, :SKIPAHEAD, :SKIPBACK, :TIMESTRETCH, :JUMP)");
1456 query.bindValue(":NAME", Name);
1457 query.bindValue(":TITLEMATCH", TitleMatch);
1458 query.bindValue(":SKIPAHEAD", SkipAhead);
1459 query.bindValue(":SKIPBACK", SkipBack);
1460 query.bindValue(":TIMESTRETCH", TimeStretch);
1461 query.bindValue(":JUMP", Jump);
1462
1463 return query.exec();
1464}
1465
1466bool V2Dvr::UpdatePlayGroup ( const QString & Name,
1467 const QString & TitleMatch,
1468 int SkipAhead,
1469 int SkipBack,
1470 int TimeStretch,
1471 int Jump )
1472{
1473 if (Name.isEmpty())
1474 throw QString("ERROR: Name is not specified");
1475
1476 bool ok = false;
1477
1478 QString sql = "UPDATE playgroup SET ";
1479 if (HAS_PARAMv2("TitleMatch"))
1480 {
1481 sql.append(" titlematch = :TITLEMATCH ");
1482 ok = true;
1483 }
1484 if (HAS_PARAMv2("SkipAhead"))
1485 {
1486 if (ok)
1487 sql.append(",");
1488 sql.append(" skipahead = :SKIPAHEAD ");
1489 ok = true;
1490 }
1491 if (HAS_PARAMv2("SkipBack"))
1492 {
1493 if (ok)
1494 sql.append(",");
1495 sql.append(" skipback = :SKIPBACK ");
1496 ok = true;
1497 }
1498 if (HAS_PARAMv2("TimeStretch"))
1499 {
1500 if (ok)
1501 sql.append(",");
1502 sql.append(" timestretch = :TIMESTRETCH ");
1503 ok = true;
1504 }
1505 if (HAS_PARAMv2("Jump"))
1506 {
1507 if (ok)
1508 sql.append(",");
1509 sql.append(" jump = :JUMP ");
1510 ok = true;
1511 }
1512 if (ok)
1513 {
1514 sql.append(" WHERE name = :NAME ");
1516 query.prepare(sql);
1517 query.bindValue(":NAME", Name);
1518 if (HAS_PARAMv2("TitleMatch"))
1519 query.bindValue(":TITLEMATCH", TitleMatch);
1520 if (HAS_PARAMv2("SkipAhead"))
1521 query.bindValue(":SKIPAHEAD", SkipAhead);
1522 if (HAS_PARAMv2("SkipBack"))
1523 query.bindValue(":SKIPBACK", SkipBack);
1524 if (HAS_PARAMv2("TimeStretch"))
1525 query.bindValue(":TIMESTRETCH", TimeStretch);
1526 if (HAS_PARAMv2("Jump"))
1527 query.bindValue(":JUMP", Jump);
1528 if (query.exec())
1529 return true;
1530 }
1531 return false;
1532}
1533
1535//
1537
1539{
1540 auto* filterList = new V2RecRuleFilterList();
1541
1543
1544 query.prepare("SELECT filterid, description, newruledefault "
1545 "FROM recordfilter ORDER BY filterid");
1546
1547 if (query.exec())
1548 {
1549 while (query.next())
1550 {
1551 V2RecRuleFilter* ruleFilter = filterList->AddNewRecRuleFilter();
1552 ruleFilter->setId(query.value(0).toInt());
1553 ruleFilter->setDescription(QObject::tr(query.value(1).toString()
1554 .toUtf8().constData()));
1555 }
1556 }
1557
1558 return filterList;
1559}
1560
1562//
1564
1565QStringList V2Dvr::GetTitleList(const QString& RecGroup)
1566{
1568
1569 QString querystr = "SELECT DISTINCT title FROM recorded "
1570 "WHERE deletepending = 0";
1571
1572 if (!RecGroup.isEmpty())
1573 querystr += " AND recgroup = :RECGROUP";
1574 else
1575 querystr += " AND recgroup != 'Deleted'";
1576
1577 querystr += " ORDER BY title";
1578
1579 query.prepare(querystr);
1580
1581 if (!RecGroup.isEmpty())
1582 query.bindValue(":RECGROUP", RecGroup);
1583
1584 QStringList result;
1585 if (!query.exec())
1586 {
1587 MythDB::DBError("GetTitleList recorded", query);
1588 return result;
1589 }
1590
1591 while (query.next())
1592 result << query.value(0).toString();
1593
1594 return result;
1595}
1596
1598//
1600
1602{
1604
1605 QString querystr = QString(
1606 "SELECT title, inetref, count(title) as count "
1607 " FROM recorded AS r "
1608 " JOIN recgroups AS g ON r.recgroupid = g.recgroupid "
1609 " WHERE g.recgroup NOT IN ('Deleted', 'LiveTV') "
1610 " AND r.deletepending = 0 "
1611 " GROUP BY title, inetref "
1612 " ORDER BY title");
1613
1614 query.prepare(querystr);
1615
1616 auto *pTitleInfos = new V2TitleInfoList();
1617 if (!query.exec())
1618 {
1619 MythDB::DBError("GetTitleList recorded", query);
1620 return pTitleInfos;
1621 }
1622
1623 while (query.next())
1624 {
1625 V2TitleInfo *pTitleInfo = pTitleInfos->AddNewTitleInfo();
1626
1627 pTitleInfo->setTitle(query.value(0).toString());
1628 pTitleInfo->setInetref(query.value(1).toString());
1629 pTitleInfo->setCount(query.value(2).toInt());
1630 }
1631
1632 return pTitleInfos;
1633}
1634
1635
1637 int nCount,
1638 int nRecordId,
1639 const QString &Sort )
1640{
1641 auto *pPrograms = new V2ProgramList();
1642 int size = FillUpcomingList(pPrograms->GetPrograms(), pPrograms,
1643 nStartIndex,
1644 nCount,
1645 true, // bShowAll,
1646 nRecordId,
1648 Sort);
1649
1650 pPrograms->setStartIndex ( nStartIndex );
1651 pPrograms->setCount ( nCount );
1652 pPrograms->setTotalAvailable( size );
1653 pPrograms->setAsOf ( MythDate::current() );
1654 pPrograms->setVersion ( MYTH_BINARY_VERSION );
1655 pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
1656
1657 return pPrograms;
1658}
1659
1661 int nCount,
1662 bool bShowAll,
1663 int nRecordId,
1664 const QString &RecStatus,
1665 const QString &Sort,
1666 const QString &RecGroup )
1667{
1668 int nRecStatus = 0;
1669 if (!RecStatus.isEmpty())
1670 {
1671 // Handle enum name
1672 QMetaEnum meta = QMetaEnum::fromType<RecStatus::Type>();
1673 bool ok {false};
1674 nRecStatus = meta.keyToValue(RecStatus.toLocal8Bit(), &ok);
1675 // if enum name not valid try for int nRecStatus
1676 if (!ok)
1677 nRecStatus = RecStatus.toInt(&ok);
1678 // if still not valid use 99999 to trigger an "unknown" response
1679 if (!ok)
1680 nRecStatus = 99999;
1681 }
1682 auto *pPrograms = new V2ProgramList();
1683 int size = FillUpcomingList(pPrograms->GetPrograms(), pPrograms,
1684 nStartIndex,
1685 nCount,
1686 bShowAll,
1687 nRecordId,
1688 nRecStatus,
1689 Sort,
1690 RecGroup );
1691
1692 pPrograms->setStartIndex ( nStartIndex );
1693 pPrograms->setCount ( nCount );
1694 pPrograms->setTotalAvailable( size );
1695 pPrograms->setAsOf ( MythDate::current() );
1696 pPrograms->setVersion ( MYTH_BINARY_VERSION );
1697 pPrograms->setProtoVer ( MYTH_PROTO_VERSION );
1698
1699 return pPrograms;
1700}
1701
1703 const QString& sTitle,
1704 const QString& sSubtitle,
1705 const QString& sDescription,
1706 const QString& sCategory,
1707 const QDateTime& StartTime,
1708 const QDateTime& EndTime,
1709 const QString& sSeriesId,
1710 const QString& sProgramId,
1711 int nChanId,
1712 const QString& sStation,
1713 int nFindDay,
1714 QTime tFindTime,
1715 int nParentId,
1716 bool bInactive,
1717 uint nSeason,
1718 uint nEpisode,
1719 const QString& sInetref,
1720 QString sType,
1721 QString sSearchType,
1722 int nRecPriority,
1723 uint nPreferredInput,
1724 int nStartOffset,
1725 int nEndOffset,
1726 const QDateTime& LastRecorded,
1727 QString sDupMethod,
1728 QString sDupIn,
1729 bool bNewEpisOnly,
1730 uint nFilter,
1731 QString sRecProfile,
1732 QString sRecGroup,
1733 QString sStorageGroup,
1734 QString sPlayGroup,
1735 bool bAutoExpire,
1736 int nMaxEpisodes,
1737 bool bMaxNewest,
1738 bool bAutoCommflag,
1739 bool bAutoTranscode,
1740 bool bAutoMetaLookup,
1741 bool bAutoUserJob1,
1742 bool bAutoUserJob2,
1743 bool bAutoUserJob3,
1744 bool bAutoUserJob4,
1745 int nTranscoder,
1746 const QString& AutoExtend)
1747{
1748 QDateTime recstartts = StartTime.toUTC();
1749 QDateTime recendts = EndTime.toUTC();
1750 QDateTime lastrects = LastRecorded.toUTC();
1751 RecordingRule rule;
1752 rule.LoadTemplate("Default");
1753
1754 if (sType.isEmpty())
1755 sType = "single";
1756
1757 if (sSearchType.isEmpty())
1758 sSearchType = "none";
1759
1760 if (sDupMethod.isEmpty())
1761 sDupMethod = "subtitleanddescription";
1762
1763 if (sDupIn.isEmpty())
1764 sDupIn = "all";
1765
1766 rule.m_title = sTitle;
1767 rule.m_subtitle = sSubtitle;
1768 rule.m_description = sDescription;
1769
1770 rule.m_startdate = recstartts.date();
1771 rule.m_starttime = recstartts.time();
1772 rule.m_enddate = recendts.date();
1773 rule.m_endtime = recendts.time();
1774
1775 rule.m_type = recTypeFromString(sType);
1776 rule.m_searchType = searchTypeFromString(sSearchType);
1777 if (rule.m_searchType == kManualSearch)
1779 else
1780 rule.m_dupMethod = dupMethodFromString(sDupMethod);
1781 rule.m_dupIn = dupInFromStringAndBool(sDupIn, bNewEpisOnly);
1782
1783 if (sRecProfile.isEmpty())
1784 sRecProfile = "Default";
1785
1786 if (sRecGroup.isEmpty())
1787 sRecGroup = "Default";
1788
1789 if (sStorageGroup.isEmpty())
1790 sStorageGroup = "Default";
1791
1792 if (sPlayGroup.isEmpty())
1793 sPlayGroup = "Default";
1794
1795 rule.m_category = sCategory;
1796 rule.m_seriesid = sSeriesId;
1797 rule.m_programid = sProgramId;
1798
1799 rule.m_channelid = nChanId;
1800 rule.m_station = sStation;
1801
1802 rule.m_findday = nFindDay;
1803 rule.m_findtime = tFindTime;
1804
1805 rule.m_recProfile = sRecProfile;
1807 if (rule.m_recGroupID == 0)
1808 {
1809 rule.m_recGroupID = V2CreateRecordingGroup(sRecGroup);
1810 if (rule.m_recGroupID <= 0)
1812 }
1813 rule.m_storageGroup = sStorageGroup;
1814 rule.m_playGroup = sPlayGroup;
1815
1816 rule.m_parentRecID = nParentId;
1817 rule.m_isInactive = bInactive;
1818
1819 rule.m_season = nSeason;
1820 rule.m_episode = nEpisode;
1821 rule.m_inetref = sInetref;
1822
1823 rule.m_recPriority = nRecPriority;
1824 rule.m_prefInput = nPreferredInput;
1825 rule.m_startOffset = nStartOffset;
1826 rule.m_endOffset = nEndOffset;
1827 rule.m_filter = nFilter;
1828
1829 rule.m_autoExpire = bAutoExpire;
1830 rule.m_maxEpisodes = nMaxEpisodes;
1831 rule.m_maxNewest = bMaxNewest;
1832
1833 rule.m_autoCommFlag = bAutoCommflag;
1834 rule.m_autoTranscode = bAutoTranscode;
1835 rule.m_autoMetadataLookup = bAutoMetaLookup;
1836
1837 rule.m_autoUserJob1 = bAutoUserJob1;
1838 rule.m_autoUserJob2 = bAutoUserJob2;
1839 rule.m_autoUserJob3 = bAutoUserJob3;
1840 rule.m_autoUserJob4 = bAutoUserJob4;
1841
1842 rule.m_transcoder = nTranscoder;
1843 rule.m_autoExtend = autoExtendTypeFromString(AutoExtend);
1844
1845 rule.m_lastRecorded = lastrects;
1846
1847 QString msg;
1848 if (!rule.IsValid(msg))
1849 throw QString(msg);
1850
1851 bool success = rule.Save();
1852 if (!success)
1853 throw QString("DATABASE ERROR: Check for duplicate recording rule");
1854
1855 uint recid = rule.m_recordID;
1856
1857 return recid;
1858}
1859
1861 const QString& sTitle,
1862 const QString& sSubtitle,
1863 const QString& sDescription,
1864 const QString& sCategory,
1865 const QDateTime& StartTime,
1866 const QDateTime& EndTime,
1867 const QString& sSeriesId,
1868 const QString& sProgramId,
1869 int nChanId,
1870 const QString& sStation,
1871 int nFindDay,
1872 QTime tFindTime,
1873 bool bInactive,
1874 uint nSeason,
1875 uint nEpisode,
1876 const QString& sInetref,
1877 QString sType,
1878 QString sSearchType,
1879 int nRecPriority,
1880 uint nPreferredInput,
1881 int nStartOffset,
1882 int nEndOffset,
1883 QString sDupMethod,
1884 QString sDupIn,
1885 bool bNewEpisOnly,
1886 uint nFilter,
1887 QString sRecProfile,
1888 QString sRecGroup,
1889 QString sStorageGroup,
1890 QString sPlayGroup,
1891 bool bAutoExpire,
1892 int nMaxEpisodes,
1893 bool bMaxNewest,
1894 bool bAutoCommflag,
1895 bool bAutoTranscode,
1896 bool bAutoMetaLookup,
1897 bool bAutoUserJob1,
1898 bool bAutoUserJob2,
1899 bool bAutoUserJob3,
1900 bool bAutoUserJob4,
1901 int nTranscoder,
1902 const QString& AutoExtend)
1903{
1904 if (nRecordId == 0 )
1905 throw QString("Record ID is invalid.");
1906
1907 RecordingRule pRule;
1908 pRule.m_recordID = nRecordId;
1909 pRule.Load();
1910
1911 if (!pRule.IsLoaded())
1912 throw QString("Record ID does not exist.");
1913
1914 QDateTime recstartts = StartTime.toUTC();
1915 QDateTime recendts = EndTime.toUTC();
1916
1917 pRule.m_isInactive = bInactive;
1918 if (sType.isEmpty())
1919 sType = "single";
1920
1921 if (sSearchType.isEmpty())
1922 sSearchType = "none";
1923
1924 if (sDupMethod.isEmpty())
1925 sDupMethod = "subtitleanddescription";
1926
1927 if (sDupIn.isEmpty())
1928 sDupIn = "all";
1929
1930 pRule.m_type = recTypeFromString(sType);
1931 pRule.m_searchType = searchTypeFromString(sSearchType);
1932 if (pRule.m_searchType == kManualSearch)
1933 pRule.m_dupMethod = kDupCheckNone;
1934 else
1935 pRule.m_dupMethod = dupMethodFromString(sDupMethod);
1936 pRule.m_dupIn = dupInFromStringAndBool(sDupIn, bNewEpisOnly);
1937
1938 if (sRecProfile.isEmpty())
1939 sRecProfile = "Default";
1940
1941 if (sRecGroup.isEmpty())
1942 sRecGroup = "Default";
1943
1944 if (sStorageGroup.isEmpty())
1945 sStorageGroup = "Default";
1946
1947 if (sPlayGroup.isEmpty())
1948 sPlayGroup = "Default";
1949
1950 if (!sTitle.isEmpty())
1951 pRule.m_title = sTitle;
1952
1953 if (!sSubtitle.isEmpty())
1954 pRule.m_subtitle = sSubtitle;
1955
1956 if(!sDescription.isEmpty())
1957 pRule.m_description = sDescription;
1958
1959 if (!sCategory.isEmpty())
1960 pRule.m_category = sCategory;
1961
1962 if (!sSeriesId.isEmpty())
1963 pRule.m_seriesid = sSeriesId;
1964
1965 if (!sProgramId.isEmpty())
1966 pRule.m_programid = sProgramId;
1967
1968 if (nChanId)
1969 pRule.m_channelid = nChanId;
1970 if (!sStation.isEmpty())
1971 pRule.m_station = sStation;
1972
1973 pRule.m_startdate = recstartts.date();
1974 pRule.m_starttime = recstartts.time();
1975 pRule.m_enddate = recendts.date();
1976 pRule.m_endtime = recendts.time();
1977
1978 pRule.m_findday = nFindDay;
1979 pRule.m_findtime = tFindTime;
1980
1981 pRule.m_recProfile = sRecProfile;
1982 pRule.m_recGroupID = RecordingInfo::GetRecgroupID(sRecGroup);
1983 if (pRule.m_recGroupID == 0)
1984 {
1985 pRule.m_recGroupID = V2CreateRecordingGroup(sRecGroup);
1986 if (pRule.m_recGroupID <= 0)
1988 }
1989 pRule.m_storageGroup = sStorageGroup;
1990 pRule.m_playGroup = sPlayGroup;
1991
1992 pRule.m_isInactive = bInactive;
1993
1994 pRule.m_season = nSeason;
1995 pRule.m_episode = nEpisode;
1996 pRule.m_inetref = sInetref;
1997
1998 pRule.m_recPriority = nRecPriority;
1999 pRule.m_prefInput = nPreferredInput;
2000 pRule.m_startOffset = nStartOffset;
2001 pRule.m_endOffset = nEndOffset;
2002 pRule.m_filter = nFilter;
2003
2004 pRule.m_autoExpire = bAutoExpire;
2005 pRule.m_maxEpisodes = nMaxEpisodes;
2006 pRule.m_maxNewest = bMaxNewest;
2007
2008 pRule.m_autoCommFlag = bAutoCommflag;
2009 pRule.m_autoTranscode = bAutoTranscode;
2010 pRule.m_autoMetadataLookup = bAutoMetaLookup;
2011
2012 pRule.m_autoUserJob1 = bAutoUserJob1;
2013 pRule.m_autoUserJob2 = bAutoUserJob2;
2014 pRule.m_autoUserJob3 = bAutoUserJob3;
2015 pRule.m_autoUserJob4 = bAutoUserJob4;
2016
2017 pRule.m_transcoder = nTranscoder;
2018
2019 if (!AutoExtend.isEmpty())
2020 pRule.m_autoExtend = autoExtendTypeFromString(AutoExtend);
2021
2022 QString msg;
2023 if (!pRule.IsValid(msg))
2024 throw QString(msg);
2025
2026 bool bResult = pRule.Save();
2027
2028 return bResult;
2029}
2030
2032{
2033 bool bResult = false;
2034
2035 if (nRecordId == 0 )
2036 throw QString("Record ID does not exist.");
2037
2038 RecordingRule pRule;
2039 pRule.m_recordID = nRecordId;
2040
2041 bResult = pRule.Delete();
2042
2043 return bResult;
2044}
2045
2046bool V2Dvr::AddDontRecordSchedule(int nChanId, const QDateTime &dStartTime,
2047 bool bNeverRecord)
2048{
2049 bool bResult = true;
2050
2051 if (nChanId <= 0 || !dStartTime.isValid())
2052 throw QString("Program does not exist.");
2053
2054 ProgramInfo *pi = LoadProgramFromProgram(nChanId, dStartTime.toUTC());
2055
2056 if (!pi)
2057 throw QString("Program does not exist.");
2058
2059 // Why RecordingInfo instead of ProgramInfo? Good question ...
2060 RecordingInfo recInfo = RecordingInfo(*pi);
2061
2062 delete pi;
2063
2064 if (bNeverRecord)
2065 {
2066 recInfo.ApplyNeverRecord();
2067 }
2068 else
2069 {
2071 }
2072
2073 return bResult;
2074}
2075
2077 int nCount,
2078 const QString &Sort,
2079 bool Descending )
2080{
2082 if (Sort.toLower() == "lastrecorded")
2083 sortingColumn = Scheduler::kSortLastRecorded;
2084 else if (Sort.toLower() == "nextrecording")
2085 sortingColumn = Scheduler::kSortNextRecording;
2086 else if (Sort.toLower() == "title")
2087 sortingColumn = Scheduler::kSortTitle; // NOLINT(bugprone-branch-clone)
2088 else if (Sort.toLower() == "priority")
2089 sortingColumn = Scheduler::kSortPriority;
2090 else if (Sort.toLower() == "type")
2091 sortingColumn = Scheduler::kSortType;
2092 else
2093 sortingColumn = Scheduler::kSortTitle;
2094
2095 RecList recList;
2096 Scheduler::GetAllScheduled(recList, sortingColumn, !Descending);
2097
2098 // ----------------------------------------------------------------------
2099 // Build Response
2100 // ----------------------------------------------------------------------
2101
2102 auto *pRecRules = new V2RecRuleList();
2103
2104 nStartIndex = (nStartIndex > 0) ? std::min( nStartIndex, (int)recList.size() ) : 0;
2105 nCount = (nCount > 0) ? std::min( nCount, (int)recList.size() ) : recList.size();
2106 int nEndIndex = std::min((nStartIndex + nCount), (int)recList.size() );
2107
2108 for( int n = nStartIndex; n < nEndIndex; n++)
2109 {
2110 RecordingInfo *info = recList[n];
2111
2112 if (info != nullptr)
2113 {
2114 V2RecRule *pRecRule = pRecRules->AddNewRecRule();
2115
2116 V2FillRecRuleInfo( pRecRule, info->GetRecordingRule() );
2117 }
2118 }
2119
2120 // ----------------------------------------------------------------------
2121
2122 pRecRules->setStartIndex ( nStartIndex );
2123 pRecRules->setCount ( nCount );
2124 pRecRules->setTotalAvailable( recList.size() );
2125 pRecRules->setAsOf ( MythDate::current() );
2126 pRecRules->setVersion ( MYTH_BINARY_VERSION );
2127 pRecRules->setProtoVer ( MYTH_PROTO_VERSION );
2128
2129 while (!recList.empty())
2130 {
2131 delete recList.back();
2132 recList.pop_back();
2133 }
2134
2135 return pRecRules;
2136}
2137
2139 const QString& sTemplate,
2140 int nRecordedId,
2141 int nChanId,
2142 const QDateTime& StartTime,
2143 bool bMakeOverride )
2144{
2145 RecordingRule rule;
2146 QDateTime dStartTime = StartTime.toUTC();
2147
2148 if (nRecordId > 0)
2149 {
2150 rule.m_recordID = nRecordId;
2151 if (!rule.Load())
2152 throw QString("Record ID does not exist.");
2153 }
2154 else if (!sTemplate.isEmpty())
2155 {
2156 if (!rule.LoadTemplate(sTemplate))
2157 throw QString("Template does not exist.");
2158 }
2159 else if (nRecordedId > 0) // Loads from the Recorded/Recorded Program Table
2160 {
2161 // Despite the use of ProgramInfo, this only applies to Recordings.
2162 ProgramInfo recInfo(nRecordedId);
2163 if (!rule.LoadByProgram(&recInfo))
2164 throw QString("Recording does not exist");
2165 }
2166 else if (nChanId > 0 && dStartTime.isValid()) // Loads from Program Table, should NOT be used with recordings
2167 {
2168 // Despite the use of RecordingInfo, this only applies to programs in the
2169 // present or future, not to recordings? Confused yet?
2171 RecordingInfo info(nChanId, dStartTime, false, 0h, &status);
2172 if (status != RecordingInfo::kFoundProgram)
2173 throw QString("Program does not exist.");
2174 RecordingRule *pRule = info.GetRecordingRule();
2175 if (bMakeOverride && rule.m_type != kSingleRecord &&
2176 rule.m_type != kOverrideRecord && rule.m_type != kDontRecord)
2177 pRule->MakeOverride();
2178 rule = *pRule;
2179 }
2180 else
2181 {
2182 throw QString("Invalid request.");
2183 }
2184
2185 auto *pRecRule = new V2RecRule();
2186 V2FillRecRuleInfo( pRecRule, &rule );
2187
2188 return pRecRule;
2189}
2190
2192{
2193 bool bResult = false;
2194
2195 if (nRecordId == 0 )
2196 throw QString("Record ID appears invalid.");
2197
2198 RecordingRule pRule;
2199 pRule.m_recordID = nRecordId;
2200 pRule.Load();
2201
2202 if (pRule.IsLoaded())
2203 {
2204 pRule.m_isInactive = false;
2205 bResult = pRule.Save();
2206 }
2207
2208 return bResult;
2209}
2210
2212{
2213 bool bResult = false;
2214
2215 if (nRecordId == 0 )
2216 throw QString("Record ID appears invalid.");
2217
2218 RecordingRule pRule;
2219 pRule.m_recordID = nRecordId;
2220 pRule.Load();
2221
2222 if (pRule.IsLoaded())
2223 {
2224 pRule.m_isInactive = true;
2225 bResult = pRule.Save();
2226 }
2227
2228 return bResult;
2229}
2230
2231int V2Dvr::RecordedIdForKey(int chanid, const QDateTime &StartTime)
2232{
2233 int recordedid = 0;
2234
2235 if (!RecordingInfo::QueryRecordedIdForKey(recordedid, chanid,
2236 StartTime))
2237 return -1;
2238
2239 return recordedid;
2240}
2241
2242int V2Dvr::RecordedIdForPathname(const QString & pathname)
2243{
2244 uint recordedid = 0;
2245
2246 if (!ProgramInfo::QueryRecordedIdFromPathname(pathname, recordedid))
2247 return -1;
2248
2249 return recordedid;
2250}
2251
2252QString V2Dvr::RecStatusToString(const QString & RecStatus)
2253{
2254 // Handle enum name
2255 QMetaEnum meta = QMetaEnum::fromType<RecStatus::Type>();
2256 bool ok {false};
2257 int value = meta.keyToValue(RecStatus.toLocal8Bit(), &ok);
2258 // if enum name not valid try for int value
2259 if (!ok)
2260 value = RecStatus.toInt(&ok);
2261 // if still not valid use 0 to trigger an "unknown" response
2262 if (!ok)
2263 value = 0;
2264 auto type = static_cast<RecStatus::Type>(value);
2265 return RecStatus::toString(type);
2266}
2267
2268QString V2Dvr::RecStatusToDescription(const QString & RecStatus, int recType,
2269 const QDateTime &StartTime)
2270{
2271 // Handle enum name
2272 QMetaEnum meta = QMetaEnum::fromType<RecStatus::Type>();
2273 bool ok {false};
2274 int value = meta.keyToValue(RecStatus.toLocal8Bit(), &ok);
2275 // if enum name not valid try for int value
2276 if (!ok)
2277 value = RecStatus.toInt(&ok);
2278 // if still not valid use 0 to trigger an "unknown" response
2279 if (!ok)
2280 value = 0;
2281 auto recstatusType = static_cast<RecStatus::Type>(value);
2282 auto recordingType = static_cast<RecordingType>(recType);
2283 return RecStatus::toDescription(recstatusType, recordingType, StartTime);
2284}
2285
2286QString V2Dvr::RecTypeToString(const QString& recType)
2287{
2288 bool ok = false;
2289 auto enumType = static_cast<RecordingType>(recType.toInt(&ok, 10));
2290 if (ok)
2291 return toString(enumType);
2292 // RecordingType type = static_cast<RecordingType>(recType);
2293 return toString(recTypeFromString(recType));
2294}
2295
2296QString V2Dvr::RecTypeToDescription(const QString& recType)
2297{
2298 bool ok = false;
2299 auto enumType = static_cast<RecordingType>(recType.toInt(&ok, 10));
2300 if (ok)
2301 return toDescription(enumType);
2302 // RecordingType type = static_cast<RecordingType>(recType);
2303 return toDescription(recTypeFromString(recType));
2304}
2305
2306QString V2Dvr::DupInToString(const QString& DupIn)
2307{
2308 // RecordingDupInType type= static_cast<RecordingDupInType>(DupIn);
2309 // return toString(type);
2310 return toString(dupInFromString(DupIn));
2311}
2312
2313QString V2Dvr::DupInToDescription(const QString& DupIn)
2314{
2315 // RecordingDupInType type= static_cast<RecordingDupInType>(DupIn);
2316 //return toDescription(type);
2317 return toDescription(dupInFromString(DupIn));
2318}
2319
2320QString V2Dvr::DupMethodToString(const QString& DupMethod)
2321{
2322 // RecordingDupMethodType method = static_cast<RecordingDupMethodType>(DupMethod);
2323 return toString(dupMethodFromString(DupMethod));
2324}
2325
2326QString V2Dvr::DupMethodToDescription(const QString& DupMethod)
2327{
2328 // RecordingDupMethodType method = static_cast<RecordingDupMethodType>(DupMethod);
2329 return toDescription(dupMethodFromString(DupMethod));
2330}
2331
2333//
2335
2336int V2Dvr::ManageJobQueue( const QString &sAction,
2337 const QString &sJobName,
2338 int nJobId,
2339 int nRecordedId,
2340 QDateTime JobStartTime,
2341 QString sRemoteHost,
2342 QString sJobArgs )
2343{
2344 int nReturn = -1;
2345
2346 if (!HAS_PARAMv2("JobName") ||
2347 !HAS_PARAMv2("RecordedId") )
2348 {
2349 LOG(VB_GENERAL, LOG_ERR, "JobName and RecordedId are required.");
2350 return nReturn;
2351 }
2352
2353 if (sRemoteHost.isEmpty())
2354 sRemoteHost = gCoreContext->GetHostName();
2355
2356 int jobType = JobQueue::GetJobTypeFromName(sJobName);
2357
2358 if (jobType == JOB_NONE)
2359 return nReturn;
2360
2361 RecordingInfo ri = RecordingInfo(nRecordedId);
2362
2363 if (!ri.GetChanID())
2364 return nReturn;
2365
2366 if ( sAction == "Remove")
2367 {
2368 if (!HAS_PARAMv2("JobId") || nJobId < 0)
2369 {
2370 LOG(VB_GENERAL, LOG_ERR, "For Remove, a valid JobId is required.");
2371 return nReturn;
2372 }
2373
2374 if (!JobQueue::SafeDeleteJob(nJobId, jobType, ri.GetChanID(),
2376 return nReturn;
2377
2378 return nJobId;
2379 }
2380
2381 if ( sAction != "Add")
2382 {
2383 LOG(VB_GENERAL, LOG_ERR, QString("Illegal Action name '%1'. Use: Add, "
2384 "or Remove").arg(sAction));
2385 return nReturn;
2386 }
2387
2388 if (((jobType & JOB_USERJOB) != 0) &&
2389 gCoreContext->GetSetting(sJobName, "").isEmpty())
2390 {
2391 LOG(VB_GENERAL, LOG_ERR, QString("%1 hasn't been defined.")
2392 .arg(sJobName));
2393 return nReturn;
2394 }
2395
2396 if (!gCoreContext->GetBoolSettingOnHost(QString("JobAllow%1").arg(sJobName),
2397 sRemoteHost, false))
2398 {
2399 LOG(VB_GENERAL, LOG_NOTICE, QString("Note: %1 hasn't been allowed on host %2.")
2400 .arg(sJobName, sRemoteHost));
2401 // return nReturn;
2402 }
2403
2404 if (!JobStartTime.isValid())
2405 JobStartTime = QDateTime::currentDateTime();
2406
2407 if (!JobQueue::InJobRunWindow(JobStartTime))
2408 return nReturn;
2409
2410 if (sJobArgs.isNull())
2411 sJobArgs = "";
2412
2413 bool bReturn = JobQueue::QueueJob(jobType,
2414 ri.GetChanID(),
2416 sJobArgs,
2417 QString("Dvr/ManageJobQueue"), // comment col.
2418 sRemoteHost,
2420 JOB_QUEUED,
2421 JobStartTime.toUTC());
2422
2423 if (!bReturn)
2424 {
2425 LOG(VB_GENERAL, LOG_ERR, QString("%1 job wasn't queued because of a "
2426 "database error or because it was "
2427 "already running/stopping etc.")
2428 .arg(sJobName));
2429
2430 return nReturn;
2431 }
2432
2433 return JobQueue::GetJobID(jobType, ri.GetChanID(),
2435}
2436
2438//
2440
2442 bool AutoExpire,
2443 long BookmarkOffset,
2444 const QString &BookmarkOffsetType,
2445 bool Damaged,
2446 const QString &Description,
2447 uint Episode,
2448 const QString &Inetref,
2449 long LastPlayOffset,
2450 const QString &LastPlayOffsetType,
2451 QDate OriginalAirDate,
2452 bool Preserve,
2453 uint Season,
2454 uint Stars,
2455 const QString &SubTitle,
2456 const QString &Title,
2457 bool Watched,
2458 const QString &RecGroup )
2459
2460{
2461 if (m_request->m_queries.size() < 2 || !HAS_PARAMv2("RecordedId"))
2462 {
2463 LOG(VB_GENERAL, LOG_ERR, "No RecordedId, or no parameters to change.");
2464 return false;
2465 }
2466
2467 auto pi = ProgramInfo(RecordedId);
2468 auto ri = RecordingInfo(RecordedId);
2469
2470 if (!ri.GetChanID())
2471 return false;
2472
2473 if (HAS_PARAMv2("AutoExpire"))
2474 pi.SaveAutoExpire(AutoExpire ? kNormalAutoExpire :
2475 kDisableAutoExpire, false);
2476
2477 if (HAS_PARAMv2("BookmarkOffset"))
2478 {
2479 uint64_t position =0;
2480
2481 if (BookmarkOffsetType.toLower() == "position")
2482 {
2483 if (!ri.QueryPositionKeyFrame(&position, BookmarkOffset, true))
2484 return false;
2485 }
2486 else if (BookmarkOffsetType.toLower() == "duration")
2487 {
2488 if (!ri.QueryDurationKeyFrame(&position, BookmarkOffset, true))
2489 return false;
2490 }
2491 else
2492 {
2493 position = BookmarkOffset;
2494 }
2495
2496 ri.SaveBookmark(position);
2497 }
2498
2499 if (HAS_PARAMv2("Damaged"))
2500 pi.SaveVideoProperties(VID_DAMAGED, Damaged ? VID_DAMAGED : 0);
2501
2502 if (HAS_PARAMv2("Description") ||
2503 HAS_PARAMv2("SubTitle") ||
2504 HAS_PARAMv2("Title"))
2505 {
2506
2507 QString tmp_description;
2508 QString tmp_subtitle;
2509 QString tmp_title;
2510
2511 if (HAS_PARAMv2("Description"))
2512 tmp_description = Description;
2513 else
2514 tmp_description = ri.GetDescription();
2515
2516 if (HAS_PARAMv2("SubTitle"))
2517 tmp_subtitle = SubTitle;
2518 else
2519 tmp_subtitle = ri.GetSubtitle();
2520
2521 if (HAS_PARAMv2("Title"))
2522 tmp_title = Title;
2523 else
2524 tmp_title = ri.GetTitle();
2525
2526 ri.ApplyRecordRecTitleChange(tmp_title, tmp_subtitle, tmp_description);
2527 }
2528
2529 if (HAS_PARAMv2("Episode") ||
2530 HAS_PARAMv2("Season"))
2531 {
2532 int tmp_episode = 0;
2533 int tmp_season = 0;
2534
2535 if (HAS_PARAMv2("Episode"))
2536 tmp_episode = Episode;
2537 else
2538 tmp_episode = ri.GetEpisode();
2539
2540 if (HAS_PARAMv2("Season"))
2541 tmp_season = Season;
2542 else
2543 tmp_season = ri.GetSeason();
2544
2545 pi.SaveSeasonEpisode(tmp_season, tmp_episode);
2546 }
2547
2548 if (HAS_PARAMv2("Inetref"))
2549 pi.SaveInetRef(Inetref);
2550
2551 if (HAS_PARAMv2("LastPlayOffset"))
2552 {
2553
2554 if (LastPlayOffset < 0)
2555 throw QString("LastPlayOffset must be >= 0.");
2556
2557 uint64_t position = LastPlayOffset;
2558 bool isend=true;
2559
2560 if (HAS_PARAMv2("LastPlayOffsetType"))
2561 {
2562 if (LastPlayOffsetType.toLower() == "position")
2563 {
2564 if (!ri.QueryPositionKeyFrame(&position, LastPlayOffset, isend))
2565 return false;
2566 }
2567 else if (LastPlayOffsetType.toLower() == "duration")
2568 {
2569 if (!ri.QueryDurationKeyFrame(&position, LastPlayOffset, isend))
2570 return false;
2571 }
2572 }
2573
2574 ri.SaveLastPlayPos(position);
2575
2576 return true;
2577
2578 }
2579
2580 if (HAS_PARAMv2("OriginalAirDate"))
2581 {
2582 // OriginalAirDate can be set to null by submitting value 'null' in json
2583 if (!OriginalAirDate.isValid() && !OriginalAirDate.isNull())
2584 {
2585 LOG(VB_GENERAL, LOG_ERR, "Need valid OriginalAirDate yyyy-mm-dd.");
2586 return false;
2587 }
2588 ri.ApplyOriginalAirDateChange(OriginalAirDate);
2589 }
2590
2591 if (HAS_PARAMv2("Preserve"))
2592 pi.SavePreserve(Preserve);
2593
2594 if (HAS_PARAMv2("Stars"))
2595 {
2596 if (Stars > 10)
2597 {
2598 LOG(VB_GENERAL, LOG_ERR, "Recording stars can be 0 to 10.");
2599 return false;
2600 }
2601 ri.ApplyStarsChange(Stars * 0.1F);
2602 }
2603
2604 if (HAS_PARAMv2("Watched"))
2605 pi.SaveWatched(Watched);
2606
2607 if (HAS_PARAMv2("RecGroup"))
2608 ri.ApplyRecordRecGroupChange(RecGroup);
2609
2610 return true;
2611}
2612
2613// Get a single record by filling PriorityName, otherwise all records
2615{
2616 auto *pList = new V2PowerPriorityList();
2617
2619
2620 QString sql("SELECT priorityname, recpriority, selectclause "
2621 "FROM powerpriority ");
2622
2623 if (!PriorityName.isEmpty())
2624 sql.append(" WHERE priorityname = :NAME ");
2625
2626 query.prepare(sql);
2627
2628 if (!PriorityName.isEmpty())
2629 query.bindValue(":NAME", PriorityName);
2630
2631 if (query.exec())
2632 {
2633 while (query.next())
2634 {
2635 V2PowerPriority * pRec = pList->AddNewPowerPriority();
2636 pRec->setPriorityName(query.value(0).toString());
2637 pRec->setRecPriority(query.value(1).toInt());
2638 pRec->setSelectClause(query.value(2).toString());
2639 }
2640 }
2641 else
2642 {
2643 throw (QString("Error accessing powerpriority table"));
2644 }
2645
2646 return pList;
2647}
2648
2649bool V2Dvr::RemovePowerPriority ( const QString & PriorityName )
2650{
2651 if (PriorityName.isEmpty())
2652 return false;
2653
2655 query.prepare("DELETE FROM powerpriority WHERE priorityname = :PRIORITYNAME");
2656 query.bindValue(":PRIORITYNAME", PriorityName);
2657
2658 return query.exec();
2659}
2660
2661bool V2Dvr::AddPowerPriority ( const QString & PriorityName,
2662 int RecPriority,
2663 const QString & SelectClause )
2664{
2665 if (PriorityName.isEmpty())
2666 throw QString("ERROR: PriorityName is not specified");
2667 if (SelectClause.isEmpty())
2668 throw QString("ERROR: SelectClause is required");
2669 QString msg = CheckPowerQuery(SelectClause);
2670 if (! msg.isEmpty() )
2671 throw(msg);
2673 query.prepare("INSERT INTO powerpriority "
2674 " (priorityname, recpriority, selectclause) "
2675 " VALUES(:PRIORITYNAME, :RECPRIORITY, :SELECTCLAUSE) ");
2676 query.bindValue(":PRIORITYNAME", PriorityName);
2677 query.bindValue(":RECPRIORITY", RecPriority);
2678 query.bindValue(":SELECTCLAUSE", SelectClause);
2679 if (!query.exec())
2680 throw(query.lastError().databaseText());
2681 return true;
2682}
2683
2684bool V2Dvr::UpdatePowerPriority ( const QString & PriorityName,
2685 int RecPriority,
2686 const QString & SelectClause )
2687{
2688 if (PriorityName.isEmpty())
2689 throw QString("ERROR: PriorityName is not specified");
2690 if (!HAS_PARAMv2("RecPriority") && !HAS_PARAMv2("SelectClause"))
2691 throw QString("ERROR: RecPriority or SelectClause is required");
2692
2693 if (HAS_PARAMv2("SelectClause"))
2694 {
2695 QString msg = CheckPowerQuery(SelectClause);
2696 if (! msg.isEmpty() )
2697 throw(msg);
2698 }
2700 bool comma = false;
2701 QString sql("UPDATE powerpriority SET ");
2702 if ( HAS_PARAMv2("RecPriority") )
2703 {
2704 sql.append(" recpriority = :RECPRIORITY ");
2705 comma = true;
2706 }
2707 if ( HAS_PARAMv2("SelectClause") )
2708 {
2709 if (comma)
2710 sql.append(" , ");
2711 sql.append(" selectclause = :SELECTCLAUSE ");
2712 }
2713 sql.append(" where priorityname = :PRIORITYNAME ");
2714 query.prepare(sql);
2715 query.bindValue(":PRIORITYNAME", PriorityName);
2716 if ( HAS_PARAMv2("RecPriority") )
2717 query.bindValue(":RECPRIORITY", RecPriority);
2718 if ( HAS_PARAMv2("SelectClause") )
2719 query.bindValue(":SELECTCLAUSE", SelectClause);
2720 if (!query.exec())
2721 throw(query.lastError().databaseText());
2722 return query.numRowsAffected() > 0;
2723}
2724
2725QString V2Dvr::CheckPowerQuery(const QString & SelectClause)
2726{
2727 QString msg;
2728 QString sql = QString("SELECT (%1) FROM (recordmatch, record, "
2729 "program, channel, capturecard, "
2730 "oldrecorded) WHERE NULL").arg(SelectClause);
2731 while (true)
2732 {
2733 int i = sql.indexOf("RECTABLE");
2734 if (i == -1) break;
2735 sql = sql.replace(i, strlen("RECTABLE"), "record");
2736 }
2737
2739 query.prepare(sql);
2740
2741 if (!query.exec())
2742 {
2743 msg = tr("An error was found when checking") + ":\n\n";
2744 msg += query.executedQuery();
2745 msg += "\n\n" + tr("The database error was") + ":\n";
2746 msg += query.lastError().databaseText();
2747 }
2748 return msg;
2749}
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:60
void GetAllExpiring(QStringList &strList)
Gets the full list of programs that can expire in expiration order.
Definition: autoexpire.cpp:842
static QList< InputInfo > GetAllInputInfo(bool virtTuners)
Definition: cardutil.cpp:1735
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:877
static bool InJobRunWindow(QDateTime jobstarttsRaw)
Definition: jobqueue.cpp:1660
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:508
static int GetJobID(int jobType, uint chanid, const QDateTime &recstartts)
Definition: jobqueue.cpp:644
static int GetJobTypeFromName(const QString &name)
Definition: jobqueue.cpp:704
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:837
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:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
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:226
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:235
Holds information on recordings and videos.
Definition: programinfo.h:68
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:373
static uint SubtitleTypesFromNames(const QString &names)
void QueryMarkup(QVector< MarkupEntry > &mapMark, QVector< MarkupEntry > &mapSeek) const
bool HasPathname(void) const
Definition: programinfo.h:359
void SavePreserve(bool preserveEpisode)
Set "preserve" field in "recorded" table to "preserveEpisode".
bool IsCommercialFlagged(void) const
Definition: programinfo.h:483
bool IsAutoExpirable(void) const
Definition: programinfo.h:488
bool IsPreserved(void) const
Definition: programinfo.h:489
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:450
void ProgramFlagsFromNames(const QString &names)
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:585
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:405
bool QueryPositionKeyFrame(uint64_t *keyframe, uint64_t position, bool backwards) const
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:391
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:487
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:87
@ kSortNextRecording
Definition: scheduler.h:87
@ kSortType
Definition: scheduler.h:88
@ kSortTitle
Definition: scheduler.h:87
@ kSortPriority
Definition: scheduler.h:88
@ kSortLastRecorded
Definition: scheduler.h:87
static void GetAllScheduled(QStringList &strList, SchedSortColumn sortBy=kSortTitle, bool ascending=true)
Returns all scheduled programs serialized into a QStringList.
Definition: scheduler.cpp:1848
static QStringList getRecordingsGroups(void)
static bool AddPlayGroup(const QString &Name, const QString &TitleMatch, int SkipAhead, int SkipBack, int TimeStretch, int Jump)
Definition: v2dvr.cpp:1444
static long GetSavedBookmark(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType)
Definition: v2dvr.cpp:935
static void RegisterCustomTypes()
static V2EncoderList * GetEncoderList()
Definition: v2dvr.cpp:1302
static bool RescheduleRecordings(void)
Definition: v2dvr.cpp:852
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:1702
static QStringList GetTitleList(const QString &RecGroup)
Definition: v2dvr.cpp:1565
V2Dvr()
Definition: v2dvr.cpp:88
static QString DupInToDescription(const QString &DupIn)
Definition: v2dvr.cpp:2313
static bool RemovePowerPriority(const QString &PriorityName)
Definition: v2dvr.cpp:2649
static bool RemoveRecordSchedule(uint RecordId)
Definition: v2dvr.cpp:2031
static bool StopRecording(int RecordedId)
Definition: v2dvr.cpp:775
static int RecordedIdForKey(int ChanId, const QDateTime &StartTime)
Definition: v2dvr.cpp:2231
static V2ProgramList * GetUpcomingList(int StartIndex, int Count, bool ShowAll, int RecordId, const QString &RecStatus, const QString &Sort, const QString &RecGroup)
Definition: v2dvr.cpp:1660
static int RecordedIdForPathname(const QString &Pathname)
Definition: v2dvr.cpp:2242
static V2MarkupList * GetRecordedMarkup(int RecordedId)
Definition: v2dvr.cpp:1199
static V2PowerPriorityList * GetPowerPriorityList(const QString &PriorityName)
Definition: v2dvr.cpp:2614
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:2076
static QString RecStatusToDescription(const QString &RecStatus, int RecType, const QDateTime &StartTime)
Definition: v2dvr.cpp:2268
static V2CutList * GetRecordedSeek(int RecordedId, const QString &OffsetType)
Definition: v2dvr.cpp:1169
static QStringList GetProgramCategories(bool OnlyRecorded)
Definition: v2dvr.cpp:1361
static QString CheckPowerQuery(const QString &SelectClause)
Definition: v2dvr.cpp:2725
static bool UnDeleteRecording(int RecordedId, int ChanId, const QDateTime &StartTime)
Definition: v2dvr.cpp:744
static V2PlayGroup * GetPlayGroup(const QString &Name)
Definition: v2dvr.cpp:1401
static bool AllowReRecord(int RecordedId, int ChanId, const QDateTime &StartTime)
Definition: v2dvr.cpp:863
bool UpdatePlayGroup(const QString &Name, const QString &TitleMatch, int SkipAhead, int SkipBack, int TimeStretch, int Jump)
Definition: v2dvr.cpp:1466
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:2441
static bool EnableRecordSchedule(uint RecordId)
Definition: v2dvr.cpp:2191
static QString DupMethodToDescription(const QString &DupMethod)
Definition: v2dvr.cpp:2326
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:1636
static bool SetRecordedMarkup(int RecordedId, const QString &MarkupList)
Definition: v2dvr.cpp:1244
static bool SetSavedBookmark(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType, long Offset)
Definition: v2dvr.cpp:1019
static V2InputList * GetInputList()
Definition: v2dvr.cpp:1313
static QStringList GetRecGroupList(const QString &UsedBy)
Definition: v2dvr.cpp:1331
static bool RemovePlayGroup(const QString &Name)
Definition: v2dvr.cpp:1430
static V2CutList * GetRecordedCommBreak(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType, bool IncludeFps)
Definition: v2dvr.cpp:1135
static QString DupMethodToString(const QString &DupMethod)
Definition: v2dvr.cpp:2320
static V2TitleInfoList * GetTitleInfoList()
Definition: v2dvr.cpp:1601
static long GetLastPlayPos(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType)
Definition: v2dvr.cpp:977
static bool DisableRecordSchedule(uint RecordId)
Definition: v2dvr.cpp:2211
static V2RecRule * GetRecordSchedule(uint RecordId, const QString &Template, int RecordedId, int ChanId, const QDateTime &StartTime, bool MakeOverride)
Definition: v2dvr.cpp:2138
static bool AddRecordedCredits(int RecordedId, const QString &Cast)
Definition: v2dvr.cpp:511
static QString DupInToString(const QString &DupIn)
Definition: v2dvr.cpp:2306
static QString RecStatusToString(const QString &RecStatus)
Definition: v2dvr.cpp:2252
static V2CutList * GetRecordedCutList(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType, bool IncludeFps)
Definition: v2dvr.cpp:1101
static QStringList GetRecStorageGroupList()
Definition: v2dvr.cpp:1387
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:1396
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:2286
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:904
static bool SetLastPlayPos(int RecordedId, int ChanId, const QDateTime &StartTime, const QString &OffsetType, long Offset)
Definition: v2dvr.cpp:1063
int ManageJobQueue(const QString &Action, const QString &JobName, int JobId, int RecordedId, QDateTime JobStartTime, QString RemoteHost, QString JobArgs)
Definition: v2dvr.cpp:2336
static QString RecTypeToDescription(const QString &RecType)
Definition: v2dvr.cpp:2296
bool UpdatePowerPriority(const QString &PriorityName, int RecPriority, const QString &SelectClause)
Definition: v2dvr.cpp:2684
static bool AddDontRecordSchedule(int ChanId, const QDateTime &StartTime, bool NeverRecord)
Definition: v2dvr.cpp:2046
static V2RecRuleFilterList * GetRecRuleFilterList()
Definition: v2dvr.cpp:1538
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:1860
static bool AddPowerPriority(const QString &PriorityName, int RecPriority, const QString &SelectClause)
Definition: v2dvr.cpp:2661
unsigned int uint
Definition: freesurround.h:24
@ 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:26
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)