MythTV master
remoteutil.cpp
Go to the documentation of this file.
1#include "remoteutil.h"
2
3#include <unistd.h>
4
5#include <QFileInfo>
6#include <QFile>
7#include <QDir>
8#include <QList>
9
10#include "libmythbase/compat.h"
16
17#include "programinfo.h"
18
19std::vector<ProgramInfo *> *RemoteGetRecordedList(int sort)
20{
21 QString str = "QUERY_RECORDINGS ";
22 if (sort < 0)
23 str += "Descending";
24 else if (sort > 0)
25 str += "Ascending";
26 else
27 str += "Unsorted";
28
29 QStringList strlist(str);
30
31 auto *info = new std::vector<ProgramInfo *>;
32
33 if (!RemoteGetRecordingList(*info, strlist))
34 {
35 delete info;
36 return nullptr;
37 }
38
39 return info;
40}
41
43{
44 QStringList strlist(QString("QUERY_LOAD"));
45
46 if (gCoreContext->SendReceiveStringList(strlist) && strlist.size() >= 3)
47 {
48 load[0] = strlist[0].toDouble();
49 load[1] = strlist[1].toDouble();
50 load[2] = strlist[2].toDouble();
51 return true;
52 }
53
54 return false;
55}
56
57bool RemoteGetUptime(std::chrono::seconds &uptime)
58{
59 QStringList strlist(QString("QUERY_UPTIME"));
60
61 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.isEmpty())
62 return false;
63
64 if (strlist[0].isEmpty() || !strlist[0].at(0).isNumber())
65 return false;
66
67 if (sizeof(std::chrono::seconds::rep) == sizeof(long long))
68 uptime = std::chrono::seconds(strlist[0].toLongLong());
69 else if (sizeof(std::chrono::seconds::rep) == sizeof(long))
70 uptime = std::chrono::seconds(strlist[0].toLong());
71 else if (sizeof(std::chrono::seconds::rep) == sizeof(int))
72 uptime = std::chrono::seconds(strlist[0].toInt());
73
74 return true;
75}
76
77bool RemoteGetMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
78{
79 QStringList strlist(QString("QUERY_MEMSTATS"));
80
81 if (gCoreContext->SendReceiveStringList(strlist) && strlist.size() >= 4)
82 {
83 totalMB = strlist[0].toInt();
84 freeMB = strlist[1].toInt();
85 totalVM = strlist[2].toInt();
86 freeVM = strlist[3].toInt();
87 return true;
88 }
89
90 return false;
91}
92
93bool RemoteCheckFile(ProgramInfo *pginfo, bool checkSlaves)
94{
95 QStringList strlist("QUERY_CHECKFILE");
96 strlist << QString::number((int)checkSlaves);
97 pginfo->ToStringList(strlist);
98
99 if (!gCoreContext->SendReceiveStringList(strlist) ||
100 (strlist.size() < 2) || !strlist[0].toInt())
101 return false;
102
103 // Only modify the pathname if the recording file is available locally on
104 // this host
105 QString localpath = strlist[1];
106 QFile checkFile(localpath);
107 if (checkFile.exists())
108 pginfo->SetPathname(localpath);
109
110 return true;
111}
112
113bool RemoteDeleteRecording(uint recordingID, bool forceMetadataDelete,
114 bool forgetHistory)
115{
116 // FIXME: Remove when DELETE_RECORDING has been updated to use recording id
117 ProgramInfo recInfo(recordingID);
118 bool result = true;
119 QString cmd =
120 QString("DELETE_RECORDING %1 %2 %3 %4")
121 .arg(QString::number(recInfo.GetChanID()),
122 recInfo.GetRecordingStartTime().toString(Qt::ISODate),
123 forceMetadataDelete ? "FORCE" : "NO_FORCE",
124 forgetHistory ? "FORGET" : "NO_FORGET");
125 QStringList strlist(cmd);
126
127 if ((!gCoreContext->SendReceiveStringList(strlist) || strlist.isEmpty()) ||
128 (strlist[0].toInt() == -2))
129 result = false;
130
131 if (!result)
132 {
133 LOG(VB_GENERAL, LOG_ALERT,
134 QString("Failed to delete recording %1:%2")
135 .arg(recInfo.GetChanID())
136 .arg(recInfo.GetRecordingStartTime().toString(Qt::ISODate)));
137 }
138
139 return result;
140}
141
143{
144 // FIXME: Remove when UNDELETE_RECORDING has been updated to use recording id
145 ProgramInfo recInfo(recordingID);
146 bool result = false;
147
148#if 0
149 if (!gCoreContext->GetNumSetting("AutoExpireInsteadOfDelete", 0))
150 return result;
151#endif
152
153 QStringList strlist(QString("UNDELETE_RECORDING"));
154 strlist.push_back(QString::number(recInfo.GetChanID()));
155 strlist.push_back(recInfo.GetRecordingStartTime().toString(Qt::ISODate));
156
158
159 if (!strlist.isEmpty() && strlist[0].toInt() == 0)
160 result = true;
161
162 return result;
163}
164
165void RemoteGetAllScheduledRecordings(std::vector<ProgramInfo *> &scheduledlist)
166{
167 QStringList strList(QString("QUERY_GETALLSCHEDULED"));
168 RemoteGetRecordingList(scheduledlist, strList);
169}
170
171void RemoteGetAllExpiringRecordings(std::vector<ProgramInfo *> &expiringlist)
172{
173 QStringList strList(QString("QUERY_GETEXPIRING"));
174 RemoteGetRecordingList(expiringlist, strList);
175}
176
178 std::vector<ProgramInfo *> &reclist, QStringList &strList)
179{
180 if (!gCoreContext->SendReceiveStringList(strList) || strList.isEmpty())
181 return 0;
182
183 int numrecordings = strList[0].toInt();
184 if (numrecordings <= 0)
185 return 0;
186
187 if ((numrecordings * NUMPROGRAMLINES) + 1 > strList.size())
188 {
189 LOG(VB_GENERAL, LOG_ERR,
190 "RemoteGetRecordingList() list size appears to be incorrect.");
191 return 0;
192 }
193
194 uint reclist_initial_size = (uint) reclist.size();
195 QStringList::const_iterator it = strList.cbegin() + 1;
196 for (int i = 0; i < numrecordings; i++)
197 {
198 auto *pginfo = new ProgramInfo(it, strList.cend());
199 reclist.push_back(pginfo);
200 }
201
202 return ((uint) reclist.size()) - reclist_initial_size;
203}
204
205std::vector<ProgramInfo *> *RemoteGetConflictList(const ProgramInfo *pginfo)
206{
207 QString cmd = QString("QUERY_GETCONFLICTING");
208 QStringList strlist( cmd );
209 pginfo->ToStringList(strlist);
210
211 auto *retlist = new std::vector<ProgramInfo *>;
212
213 RemoteGetRecordingList(*retlist, strlist);
214 return retlist;
215}
216
218{
219 QStringList strlist( "QUERY_PIXMAP_LASTMODIFIED" );
220 pginfo->ToStringList(strlist);
221
222 if (!gCoreContext->SendReceiveStringList(strlist))
223 return {};
224
225 if (!strlist.isEmpty() && !strlist[0].isEmpty() && (strlist[0] != "BAD"))
226 {
227 qint64 timet = strlist[0].toLongLong();
228 return MythDate::fromSecsSinceEpoch(timet);
229 }
230
231 return {};
232}
233
237 const ProgramInfo &pginfo, const QString &cachefile)
238{
239 QString loc("RemoteGetPreviewIfModified: ");
240 QDateTime cacheLastModified;
241 QFileInfo cachefileinfo(cachefile);
242 if (cachefileinfo.exists())
243 cacheLastModified = cachefileinfo.lastModified();
244
245 QStringList strlist("QUERY_PIXMAP_GET_IF_MODIFIED");
246 strlist << ((cacheLastModified.isValid()) ? // unix secs, UTC
247 QString::number(cacheLastModified.toSecsSinceEpoch()) :
248 QString("-1"));
249 strlist << QString::number(200 * 1024); // max size of preview file
250 pginfo.ToStringList(strlist);
251
252 if (!gCoreContext->SendReceiveStringList(strlist) ||
253 strlist.isEmpty() || strlist[0] == "ERROR")
254 {
255 LOG(VB_GENERAL, LOG_ERR, loc + "Remote error" +
256 ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : ""));
257
258 return {};
259 }
260
261 if (strlist[0] == "WARNING")
262 {
263 LOG(VB_NETWORK, LOG_WARNING, loc + "Remote warning" +
264 ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : ""));
265
266 return {};
267 }
268
269 QDateTime retdatetime;
270 qlonglong timet = strlist[0].toLongLong();
271 if (timet >= 0)
272 {
273 retdatetime = MythDate::fromSecsSinceEpoch(timet);
274 }
275
276 if (strlist.size() < 4)
277 {
278 return retdatetime;
279 }
280
281 size_t length = strlist[1].toULongLong();
282 quint16 checksum16 = strlist[2].toUInt();
283 QByteArray data = QByteArray::fromBase64(strlist[3].toLatin1());
284 if ((size_t) data.size() < length)
285 { // (note data.size() may be up to 3 bytes longer after decoding
286 LOG(VB_GENERAL, LOG_ERR, loc +
287 QString("Preview size check failed %1 < %2")
288 .arg(data.size()).arg(length));
289 return {};
290 }
291 data.resize(length);
292
293#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
294 quint16 calculated = qChecksum(data.constData(), data.size());
295#else
296 quint16 calculated = qChecksum(data);
297#endif
298 if (checksum16 != calculated)
299 {
300 LOG(VB_GENERAL, LOG_ERR, loc + "Preview checksum failed");
301 return {};
302 }
303
304 QString pdir(cachefile.section("/", 0, -2));
305 QDir cfd(pdir);
306 if (!cfd.exists() && !cfd.mkdir(pdir))
307 {
308 LOG(VB_GENERAL, LOG_ERR, loc +
309 QString("Unable to create remote cache directory '%1'")
310 .arg(pdir));
311
312 return {};
313 }
314
315 QFile file(cachefile);
316 if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
317 {
318 LOG(VB_GENERAL, LOG_ERR, loc +
319 QString("Unable to open cached preview file for writing '%1'")
320 .arg(cachefile));
321
322 return {};
323 }
324
325 off_t offset = 0;
326 size_t remaining = length;
327 uint failure_cnt = 0;
328 while ((remaining > 0) && (failure_cnt < 5))
329 {
330 ssize_t written = file.write(data.data() + offset, remaining);
331 if (written < 0)
332 {
333 failure_cnt++;
334 usleep(50000);
335 continue;
336 }
337
338 failure_cnt = 0;
339 offset += written;
340 remaining -= written;
341 }
342
343 if (remaining)
344 {
345 LOG(VB_GENERAL, LOG_ERR, loc +
346 QString("Failed to write cached preview file '%1'")
347 .arg(cachefile));
348
349 file.resize(0); // in case unlink fails..
350 file.remove(); // closes fd
351 return {};
352 }
353
354 file.close();
355
356 return retdatetime;
357}
358
359bool RemoteFillProgramInfo(ProgramInfo &pginfo, const QString &playbackhost)
360{
361 QStringList strlist( "FILL_PROGRAM_INFO" );
362 strlist << playbackhost;
363 pginfo.ToStringList(strlist);
364
366 {
367 ProgramInfo tmp(strlist);
368 if (tmp.HasPathname() || tmp.GetChanID())
369 {
370 pginfo = tmp;
371 return true;
372 }
373 }
374
375 return false;
376}
377
378QStringList RemoteRecordings(void)
379{
380 QStringList strlist("QUERY_ISRECORDING");
381
382 if (!gCoreContext->SendReceiveStringList(strlist, false, false))
383 {
384 QStringList empty;
385 empty << "0" << "0";
386 return empty;
387 }
388
389 return strlist;
390}
391
393{
394 int mask = 0;
395
396 QString cmd = "QUERY_ISRECORDING";
397
398 QStringList strlist( cmd );
399
400 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.isEmpty())
401 return mask;
402
403 int recCount = strlist[0].toInt();
404
405 for (int i = 0, j = 0; j < recCount; i++)
406 {
407 cmd = QString("QUERY_RECORDER %1").arg(i + 1);
408
409 strlist = QStringList( cmd );
410 strlist << "IS_RECORDING";
411
412 if (gCoreContext->SendReceiveStringList(strlist) && !strlist.isEmpty())
413 {
414 if (strlist[0].toInt())
415 {
416 mask |= 1<<i;
417 j++; // count active recorder
418 }
419 }
420 else
421 {
422 break;
423 }
424 }
425
426 return mask;
427}
428
429bool RemoteGetFileList(const QString& host, const QString& path, QStringList* list,
430 QString sgroup, bool fileNamesOnly)
431{
432
433 // Make sure the list is empty when we get started
434 list->clear();
435
436 if (sgroup.isEmpty())
437 sgroup = "Videos";
438
439 *list << "QUERY_SG_GETFILELIST";
440 *list << host;
441 *list << StorageGroup::GetGroupToUse(host, sgroup);
442 *list << path;
443 *list << QString::number(static_cast<int>(fileNamesOnly));
444
445 bool ok = false;
446
448 {
449 // since the master backend cannot connect back around to
450 // itself, and the libraries do not have access to the list
451 // of connected slave backends to query an existing connection
452 // start up a new temporary connection directly to the slave
453 // backend to query the file list
454 QString ann = QString("ANN Playback %1 0")
455 .arg(gCoreContext->GetHostName());
456 QString addr = gCoreContext->GetBackendServerIP(host);
457 int port = gCoreContext->GetBackendServerPort(host);
458 bool mismatch = false;
459
461 addr, port, ann, &mismatch);
462 if (sock)
463 {
464 ok = sock->SendReceiveStringList(*list);
465 sock->DecrRef();
466 }
467 else
468 {
469 list->clear();
470 }
471 }
472 else
473 {
475 }
476
477// Should the SLAVE UNREACH test be here ?
478 return ok;
479}
480
487{
488 QStringList strlist( QString("CHECK_RECORDING") );
489 pginfo->ToStringList(strlist);
490
491 if (gCoreContext->SendReceiveStringList(strlist) && !strlist.isEmpty())
492 return strlist[0].toInt();
493
494 return 0;
495}
496
506 const ProgramInfo *pginfo, int overrecsecs, int underrecsecs)
507{
508 QDateTime curtime = MythDate::current();
509
510 int retval = 0;
511
512 if (pginfo)
513 {
514 if (curtime >= pginfo->GetScheduledStartTime().addSecs(-underrecsecs) &&
515 curtime < pginfo->GetScheduledEndTime().addSecs(overrecsecs))
516 {
517 if (curtime >= pginfo->GetScheduledStartTime() &&
518 curtime < pginfo->GetScheduledEndTime())
519 retval = 1;
520 else if (curtime < pginfo->GetScheduledStartTime() &&
521 RemoteCheckForRecording(pginfo) > 0)
522 retval = 2;
523 else if (curtime > pginfo->GetScheduledEndTime() &&
524 RemoteCheckForRecording(pginfo) > 0)
525 retval = 3;
526 }
527 }
528
529 return retval;
530}
531
535std::vector<ProgramInfo *> *RemoteGetCurrentlyRecordingList(void)
536{
537 QString str = "QUERY_RECORDINGS ";
538 str += "Recording";
539 QStringList strlist( str );
540
541 auto *reclist = new std::vector<ProgramInfo *>;
542 auto *info = new std::vector<ProgramInfo *>;
543 if (!RemoteGetRecordingList(*info, strlist))
544 {
545 delete info;
546 return reclist;
547 }
548
549 // make sure whatever RemoteGetRecordingList() returned
550 // only has RecStatus::Recording shows
551 for (auto & p : *info)
552 {
553 if (p->GetRecordingStatus() == RecStatus::Recording ||
554 p->GetRecordingStatus() == RecStatus::Tuning ||
555 p->GetRecordingStatus() == RecStatus::Failing ||
556 (p->GetRecordingStatus() == RecStatus::Recorded &&
557 p->GetRecordingGroup() == "LiveTV"))
558 {
559 reclist->push_back(new ProgramInfo(*p));
560 }
561 }
562
563 while (!info->empty())
564 {
565 delete info->back();
566 info->pop_back();
567 }
568 delete info;
569
570 return reclist;
571}
572
576bool RemoteGetActiveBackends(QStringList *list)
577{
578 list->clear();
579 *list << "QUERY_ACTIVE_BACKENDS";
580
582 return false;
583
584 list->removeFirst();
585 return true;
586}
587
588static QString downloadRemoteFile(const QString &cmd, const QString &url,
589 const QString &storageGroup,
590 const QString &filename)
591{
592 QStringList strlist(cmd);
593 strlist << url;
594 strlist << storageGroup;
595 strlist << filename;
596
597 bool ok = gCoreContext->SendReceiveStringList(strlist);
598
599 if (!ok || strlist.size() < 2 || strlist[0] != "OK")
600 {
601 LOG(VB_GENERAL, LOG_ERR,
602 "downloadRemoteFile(): " + cmd + " returned ERROR!");
603 return {};
604 }
605
606 return strlist[1];
607}
608
609QString RemoteDownloadFile(const QString &url,
610 const QString &storageGroup,
611 const QString &filename)
612{
613 return downloadRemoteFile("DOWNLOAD_FILE", url, storageGroup, filename);
614}
615
616QString RemoteDownloadFileNow(const QString &url,
617 const QString &storageGroup,
618 const QString &filename)
619{
620 return downloadRemoteFile("DOWNLOAD_FILE_NOW", url, storageGroup, filename);
621}
622
623/* vim: set expandtab tabstop=4 shiftwidth=4: */
QString GetHostName(void)
MythSocket * ConnectCommandSocket(const QString &hostname, int port, const QString &announcement, bool *proto_mismatch=nullptr, int maxConnTry=-1, std::chrono::milliseconds setup_timeout=-1ms)
int GetBackendServerPort(void)
Returns the locally defined backend control port.
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
int GetNumSetting(const QString &key, int defaultval=0)
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
bool IsMasterBackend(void)
is this the actual MBE process
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:26
bool SendReceiveStringList(QStringList &list, uint min_reply_length=0, std::chrono::milliseconds timeoutMS=kLongTimeout)
Definition: mythsocket.cpp:326
Holds information on recordings and videos.
Definition: programinfo.h:68
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:373
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:398
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:405
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:391
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
void SetPathname(const QString &pn)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
static QString GetGroupToUse(const QString &host, const QString &sgroup)
unsigned int uint
Definition: freesurround.h:24
static guint32 * tmp
Definition: goom_core.cpp:26
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define off_t
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MBASE_PUBLIC QDateTime fromSecsSinceEpoch(int64_t seconds)
This function takes the number of seconds since the start of the epoch and returns a QDateTime with t...
Definition: mythdate.cpp:81
@ ISODate
Default UTC.
Definition: mythdate.h:17
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
dictionary info
Definition: azlyrics.py:7
static constexpr int8_t NUMPROGRAMLINES
Definition: programinfo.h:28
QStringList RemoteRecordings(void)
Definition: remoteutil.cpp:378
void RemoteGetAllScheduledRecordings(std::vector< ProgramInfo * > &scheduledlist)
Definition: remoteutil.cpp:165
bool RemoteGetUptime(std::chrono::seconds &uptime)
Definition: remoteutil.cpp:57
bool RemoteFillProgramInfo(ProgramInfo &pginfo, const QString &playbackhost)
Definition: remoteutil.cpp:359
QDateTime RemoteGetPreviewIfModified(const ProgramInfo &pginfo, const QString &cachefile)
Download preview & get timestamp if newer than cachefile's last modified time, otherwise just get the...
Definition: remoteutil.cpp:236
QString RemoteDownloadFile(const QString &url, const QString &storageGroup, const QString &filename)
Definition: remoteutil.cpp:609
std::vector< ProgramInfo * > * RemoteGetConflictList(const ProgramInfo *pginfo)
Definition: remoteutil.cpp:205
bool RemoteCheckFile(ProgramInfo *pginfo, bool checkSlaves)
Definition: remoteutil.cpp:93
bool RemoteGetActiveBackends(QStringList *list)
return list of backends currently connected to the master
Definition: remoteutil.cpp:576
bool RemoteGetFileList(const QString &host, const QString &path, QStringList *list, QString sgroup, bool fileNamesOnly)
Definition: remoteutil.cpp:429
bool RemoteDeleteRecording(uint recordingID, bool forceMetadataDelete, bool forgetHistory)
Definition: remoteutil.cpp:113
QString RemoteDownloadFileNow(const QString &url, const QString &storageGroup, const QString &filename)
Definition: remoteutil.cpp:616
int RemoteGetRecordingStatus(const ProgramInfo *pginfo, int overrecsecs, int underrecsecs)
Get status of an individual programme (with pre-post roll?).
Definition: remoteutil.cpp:505
std::vector< ProgramInfo * > * RemoteGetRecordedList(int sort)
Definition: remoteutil.cpp:19
int RemoteCheckForRecording(const ProgramInfo *pginfo)
Get recorder for a programme.
Definition: remoteutil.cpp:486
uint RemoteGetRecordingList(std::vector< ProgramInfo * > &reclist, QStringList &strList)
Definition: remoteutil.cpp:177
void RemoteGetAllExpiringRecordings(std::vector< ProgramInfo * > &expiringlist)
Definition: remoteutil.cpp:171
int RemoteGetRecordingMask(void)
Definition: remoteutil.cpp:392
bool RemoteGetMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
Definition: remoteutil.cpp:77
std::vector< ProgramInfo * > * RemoteGetCurrentlyRecordingList(void)
return list of currently recording shows
Definition: remoteutil.cpp:535
QDateTime RemoteGetPreviewLastModified(const ProgramInfo *pginfo)
Definition: remoteutil.cpp:217
bool RemoteGetLoad(system_load_array &load)
Definition: remoteutil.cpp:42
static QString downloadRemoteFile(const QString &cmd, const QString &url, const QString &storageGroup, const QString &filename)
Definition: remoteutil.cpp:588
bool RemoteUndeleteRecording(uint recordingID)
Definition: remoteutil.cpp:142
std::array< double, 3 > system_load_array
Definition: remoteutil.h:18