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