13 #include "libmythbase/mythconfig.h"
16 #include <sys/ioctl.h>
18 #if CONFIG_SYSTEMD_NOTIFY
19 #include <systemd/sd-daemon.h>
25 #else // if !__linux__
26 # include <sys/param.h>
28 # include <sys/mount.h>
34 #include <QCoreApplication>
38 #include <QWaitCondition>
39 #include <QWriteLocker>
41 #include <QRegularExpression>
45 #include <QNetworkInterface>
46 #include <QNetworkProxy>
47 #include <QHostAddress>
64 #include "libmythbase/mythversion.h"
95 static constexpr std::chrono::milliseconds
PRT_TIMEOUT { 10ms };
99 #define LOC QString("MainServer: ")
100 #define LOC_WARN QString("MainServer, Warning: ")
101 #define LOC_ERR QString("MainServer, Error: ")
105 bool delete_file_immediately(
const QString &
filename,
106 bool followLinks,
bool checkexists)
110 bool success1 =
true;
111 bool success2 =
true;
113 LOG(VB_FILE, LOG_INFO,
LOC +
114 QString(
"About to delete file: %1").arg(
filename));
118 if (finfo.isSymLink())
122 QFile target(linktext);
123 if (!(success1 = target.remove()))
125 LOG(VB_GENERAL, LOG_ERR,
LOC +
126 QString(
"Error deleting '%1' -> '%2'")
131 if ((!checkexists || checkFile.exists()) &&
132 !(success2 = checkFile.remove()))
134 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error deleting '%1': %2")
137 return success1 && success2;
202 QMutexLocker locker(&
m_lock);
212 m_wait.wait(locker.mutex(), left.count());
218 QMutexLocker locker(&
m_lock);
243 QMap<int, EncoderLink *> *_tvList,
245 m_encoderList(_tvList),
246 m_ismaster(master), m_threadPool(
"ProcessRequestPool"),
247 m_sched(
sched), m_expirer(_expirer)
270 bool v4IsSet = !config_v4.isNull();
275 bool v6IsSet = !config_v6.isNull();
277 if (v6IsSet && !listenAddrs.contains(config_v6))
278 LOG(VB_GENERAL, LOG_WARNING,
LOC +
279 "Unable to find IPv6 address to bind");
281 if (v4IsSet && !listenAddrs.contains(config_v4))
282 LOG(VB_GENERAL, LOG_WARNING,
LOC +
283 "Unable to find IPv4 address to bind");
285 if ((v4IsSet && !listenAddrs.contains(config_v4))
286 && (v6IsSet && !listenAddrs.contains(config_v6))
289 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to find either IPv4 or IPv6 "
290 "address we can bind to, exiting");
323 QList<FileSystemInfo> m_fsInfos;
440 auto *ms =
new MythSocket(socketDescriptor,
this);
441 if (ms->IsConnected())
453 QCoreApplication::processEvents();
461 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"No data on sock %1")
475 QStringList listline;
478 if (!
pbs->ReadStringList(listline) || listline.empty())
481 LOG(VB_GENERAL, LOG_INFO,
"No data in ProcessRequestWork()");
486 else if (!bIsControl)
493 LOG(VB_GENERAL, LOG_INFO,
LOC +
"No data in ProcessRequestWork()");
497 QString line = listline[0];
499 line = line.simplified();
500 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
501 QStringList tokens = line.split(
' ', QString::SkipEmptyParts);
503 QStringList tokens = line.split(
' ', Qt::SkipEmptyParts);
505 QString command = tokens[0];
507 if (command ==
"MYTH_PROTO_VERSION")
509 if (tokens.size() < 2)
515 if (command ==
"ANN")
520 if (command ==
"DONE")
531 LOG(VB_GENERAL, LOG_ERR,
LOC +
"ProcessRequest unknown socket");
537 if (command ==
"QUERY_FILETRANSFER")
539 if (tokens.size() != 2)
544 else if (command ==
"QUERY_RECORDINGS")
546 if (tokens.size() != 2)
551 else if (command ==
"QUERY_RECORDING")
555 else if (command ==
"GO_TO_SLEEP")
559 else if (command ==
"QUERY_FREE_SPACE")
563 else if (command ==
"QUERY_FREE_SPACE_LIST")
567 else if (command ==
"QUERY_FREE_SPACE_SUMMARY")
571 else if (command ==
"QUERY_LOAD")
575 else if (command ==
"QUERY_UPTIME")
579 else if (command ==
"QUERY_HOSTNAME")
583 else if (command ==
"QUERY_MEMSTATS")
587 else if (command ==
"QUERY_TIME_ZONE")
591 else if (command ==
"QUERY_CHECKFILE")
595 else if (command ==
"QUERY_FILE_EXISTS")
597 if (listline.size() < 2)
602 else if (command ==
"QUERY_FINDFILE")
604 if (listline.size() < 4)
609 else if (command ==
"QUERY_FILE_HASH")
611 if (listline.size() < 3)
616 else if (command ==
"QUERY_GUIDEDATATHROUGH")
620 else if (command ==
"DELETE_FILE")
622 if (listline.size() < 3)
627 else if (command ==
"MOVE_FILE")
629 if (listline.size() < 4)
634 else if (command ==
"STOP_RECORDING")
638 else if (command ==
"CHECK_RECORDING")
642 else if (command ==
"DELETE_RECORDING")
644 if (3 <= tokens.size() && tokens.size() <= 5)
646 bool force = (tokens.size() >= 4) && (tokens[3] ==
"FORCE");
647 bool forget = (tokens.size() >= 5) && (tokens[4] ==
"FORGET");
653 else if (command ==
"FORCE_DELETE_RECORDING")
657 else if (command ==
"UNDELETE_RECORDING")
661 else if (command ==
"ADD_CHILD_INPUT")
666 LOG(VB_GENERAL, LOG_ERR,
LOC +
667 "ADD_CHILD_INPUT command received in master context");
668 reslist << QString(
"ERROR: Called in master context");
670 else if (tokens.size() != 2)
671 reslist <<
"ERROR: Bad ADD_CHILD_INPUT request";
675 reslist << QString(
"ERROR: Failed to add child input");
678 else if (command ==
"RESCHEDULE_RECORDINGS")
680 listline.pop_front();
683 else if (command ==
"FORGET_RECORDING")
687 else if (command ==
"QUERY_GETALLPENDING")
689 if (tokens.size() == 1)
691 else if (tokens.size() == 2)
696 else if (command ==
"QUERY_GETALLSCHEDULED")
700 else if (command ==
"QUERY_GETCONFLICTING")
704 else if (command ==
"QUERY_GETEXPIRING")
708 else if (command ==
"QUERY_SG_GETFILELIST")
712 else if (command ==
"QUERY_SG_FILEQUERY")
716 else if (command ==
"GET_FREE_INPUT_INFO")
718 if (tokens.size() != 2)
723 else if (command ==
"QUERY_RECORDER")
725 if (tokens.size() != 2)
730 else if ((command ==
"QUERY_RECORDING_DEVICE") ||
731 (command ==
"QUERY_RECORDING_DEVICES"))
735 else if (command ==
"SET_NEXT_LIVETV_DIR")
737 if (tokens.size() != 3)
742 else if (command ==
"SET_CHANNEL_INFO")
746 else if (command ==
"QUERY_REMOTEENCODER")
748 if (tokens.size() != 2)
753 else if (command ==
"GET_RECORDER_FROM_NUM")
757 else if (command ==
"GET_RECORDER_NUM")
761 else if (command ==
"QUERY_GENPIXMAP2")
765 else if (command ==
"QUERY_PIXMAP_LASTMODIFIED")
769 else if (command ==
"QUERY_PIXMAP_GET_IF_MODIFIED")
773 else if (command ==
"QUERY_ISRECORDING")
777 else if (command ==
"MESSAGE")
779 if ((listline.size() >= 2) && (listline[1].startsWith(
"SET_VERBOSE")))
781 else if ((listline.size() >= 2) &&
782 (listline[1].startsWith(
"SET_LOG_LEVEL")))
787 else if (command ==
"FILL_PROGRAM_INFO")
791 else if (command ==
"LOCK_TUNER")
793 if (tokens.size() == 1)
795 else if (tokens.size() == 2)
800 else if (command ==
"FREE_TUNER")
802 if (tokens.size() != 2)
807 else if (command ==
"QUERY_ACTIVE_BACKENDS")
811 else if (command ==
"QUERY_IS_ACTIVE_BACKEND")
813 if (tokens.size() != 1)
818 else if (command ==
"QUERY_COMMBREAK")
820 if (tokens.size() != 3)
825 else if (command ==
"QUERY_CUTLIST")
827 if (tokens.size() != 3)
832 else if (command ==
"QUERY_BOOKMARK")
834 if (tokens.size() != 3)
839 else if (command ==
"SET_BOOKMARK")
841 if (tokens.size() != 4)
846 else if (command ==
"QUERY_SETTING")
848 if (tokens.size() != 3)
853 else if (command ==
"SET_SETTING")
855 if (tokens.size() != 4)
860 else if (command ==
"SCAN_VIDEOS")
864 else if (command ==
"SCAN_MUSIC")
868 else if (command ==
"MUSIC_TAG_UPDATE_VOLATILE")
870 if (listline.size() != 6)
875 else if (command ==
"MUSIC_CALC_TRACK_LENGTH")
877 if (listline.size() != 3)
882 else if (command ==
"MUSIC_TAG_UPDATE_METADATA")
884 if (listline.size() != 3)
889 else if (command ==
"MUSIC_FIND_ALBUMART")
891 if (listline.size() != 4)
896 else if (command ==
"MUSIC_TAG_GETIMAGE")
898 if (listline.size() < 4)
903 else if (command ==
"MUSIC_TAG_ADDIMAGE")
905 if (listline.size() < 5)
910 else if (command ==
"MUSIC_TAG_REMOVEIMAGE")
912 if (listline.size() < 4)
917 else if (command ==
"MUSIC_TAG_CHANGEIMAGE")
919 if (listline.size() < 5)
924 else if (command ==
"MUSIC_LYRICS_FIND")
926 if (listline.size() < 3)
931 else if (command ==
"MUSIC_LYRICS_GETGRABBERS")
935 else if (command ==
"MUSIC_LYRICS_SAVE")
937 if (listline.size() < 3)
942 else if (command ==
"IMAGE_SCAN")
945 QStringList reply = (listline.size() == 2)
947 : QStringList(
"ERROR") <<
"Bad: " << listline;
951 else if (command ==
"IMAGE_COPY")
954 QStringList reply = (listline.size() >= 2)
956 : QStringList(
"ERROR") <<
"Bad: " << listline;
960 else if (command ==
"IMAGE_MOVE")
963 QStringList reply = (listline.size() == 4)
965 HandleDbMove(listline[1], listline[2], listline[3])
966 : QStringList(
"ERROR") <<
"Bad: " << listline;
970 else if (command ==
"IMAGE_DELETE")
973 QStringList reply = (listline.size() == 2)
975 : QStringList(
"ERROR") <<
"Bad: " << listline;
979 else if (command ==
"IMAGE_HIDE")
982 QStringList reply = (listline.size() == 3)
984 HandleHide(listline[1].toInt() != 0, listline[2])
985 : QStringList(
"ERROR") <<
"Bad: " << listline;
989 else if (command ==
"IMAGE_TRANSFORM")
992 QStringList reply = (listline.size() == 3)
994 HandleTransform(listline[1].toInt(), listline[2])
995 : QStringList(
"ERROR") <<
"Bad: " << listline;
999 else if (command ==
"IMAGE_RENAME")
1002 QStringList reply = (listline.size() == 3)
1004 : QStringList(
"ERROR") <<
"Bad: " << listline;
1008 else if (command ==
"IMAGE_CREATE_DIRS")
1011 QStringList reply = (listline.size() >= 4)
1013 HandleDirs(listline[1], listline[2].toInt() != 0, listline.mid(3))
1014 : QStringList(
"ERROR") <<
"Bad: " << listline;
1018 else if (command ==
"IMAGE_COVER")
1021 QStringList reply = (listline.size() == 3)
1023 HandleCover(listline[1].toInt(), listline[2].toInt())
1024 : QStringList(
"ERROR") <<
"Bad: " << listline;
1028 else if (command ==
"IMAGE_IGNORE")
1031 QStringList reply = (listline.size() == 2)
1033 : QStringList(
"ERROR") <<
"Bad: " << listline;
1037 else if (command ==
"ALLOW_SHUTDOWN")
1039 if (tokens.size() != 1)
1044 else if (command ==
"BLOCK_SHUTDOWN")
1046 if (tokens.size() != 1)
1051 else if (command ==
"SHUTDOWN_NOW")
1053 if (tokens.size() != 1)
1058 if (listline.size() >= 2)
1059 halt_cmd = listline[1];
1061 if (!halt_cmd.isEmpty())
1063 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
1064 "Going down now as of Mainserver request!");
1071 else if (command ==
"BACKEND_MESSAGE")
1073 QString message = listline[1];
1074 QStringList extra( listline[2] );
1075 for (
int i = 3; i < listline.size(); i++)
1076 extra << listline[i];
1080 else if ((command ==
"DOWNLOAD_FILE") ||
1081 (command ==
"DOWNLOAD_FILE_NOW"))
1083 if (listline.size() != 4)
1088 else if (command ==
"REFRESH_BACKEND")
1090 LOG(VB_GENERAL, LOG_INFO ,
LOC +
"Reloading backend settings");
1093 else if (command ==
"OK")
1095 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Got 'OK' out of sequence.");
1097 else if (command ==
"UNKNOWN_COMMAND")
1099 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Got 'UNKNOWN_COMMAND' out of sequence.");
1103 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unknown command: " + command);
1107 QStringList strlist;
1108 strlist <<
"UNKNOWN_COMMAND";
1121 QStringList broadcast;
1122 QSet<QString> receivers;
1140 auto *me =
dynamic_cast<MythEvent *
>(e);
1144 QString message = me->
Message();
1146 if ((message ==
"PREVIEW_SUCCESS" || message ==
"PREVIEW_QUEUED") &&
1147 me->ExtraDataCount() >= 5)
1150 uint recordingID = me->ExtraData(0).toUInt();
1151 const QString&
filename = me->ExtraData(1);
1152 const QString& msg = me->ExtraData(2);
1153 const QString& datetime = me->ExtraData(3);
1155 if (message ==
"PREVIEW_QUEUED")
1157 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1158 QString(
"Preview Queued: '%1' '%2'")
1164 ok = ok &&
file.open(QIODevice::ReadOnly);
1168 QByteArray data =
file.readAll();
1169 QStringList extra(
"OK");
1170 extra.push_back(QString::number(recordingID));
1171 extra.push_back(msg);
1172 extra.push_back(datetime);
1173 extra.push_back(QString::number(data.size()));
1174 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1175 quint16 checksum = qChecksum(data.constData(), data.size());
1177 quint16 checksum = qChecksum(data);
1179 extra.push_back(QString::number(checksum));
1180 extra.push_back(QString(data.toBase64()));
1182 for (
uint i = 4 ; i < (
uint) me->ExtraDataCount(); i++)
1184 const QString& token = me->ExtraData(i);
1185 extra.push_back(token);
1189 receivers.insert(*it);
1194 if (receivers.empty())
1196 LOG(VB_GENERAL, LOG_ERR,
LOC +
1197 "PREVIEW_SUCCESS but no receivers.");
1201 broadcast.push_back(
"BACKEND_MESSAGE");
1202 broadcast.push_back(
"GENERATED_PIXMAP");
1207 message =
"PREVIEW_FAILED";
1213 if (message ==
"PREVIEW_FAILED" && me->ExtraDataCount() >= 5)
1215 const QString& pginfokey = me->ExtraData(0);
1216 const QString& msg = me->ExtraData(2);
1218 QStringList extra(
"ERROR");
1219 extra.push_back(pginfokey);
1220 extra.push_back(msg);
1221 for (
uint i = 4 ; i < (
uint) me->ExtraDataCount(); i++)
1223 const QString& token = me->ExtraData(i);
1224 extra.push_back(token);
1228 receivers.insert(*it);
1233 if (receivers.empty())
1235 LOG(VB_GENERAL, LOG_ERR,
LOC +
1236 "PREVIEW_FAILED but no receivers.");
1240 broadcast.push_back(
"BACKEND_MESSAGE");
1241 broadcast.push_back(
"GENERATED_PIXMAP");
1245 if (me->Message().startsWith(
"AUTO_EXPIRE"))
1247 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1248 QStringList tokens = me->Message()
1249 .split(
" ", QString::SkipEmptyParts);
1251 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1254 if (tokens.size() != 3)
1256 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad AUTO_EXPIRE message");
1280 QString msg = QString(
"Cannot find program info for '%1', "
1281 "while attempting to Auto-Expire.")
1282 .arg(me->Message());
1283 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
1289 if (me->Message().startsWith(
"QUERY_NEXT_LIVETV_DIR") &&
m_sched)
1291 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1292 QStringList tokens = me->Message()
1293 .split(
" ", QString::SkipEmptyParts);
1295 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1298 if (tokens.size() != 2)
1300 LOG(VB_GENERAL, LOG_ERR,
LOC +
1301 QString(
"Bad %1 message").arg(tokens[0]));
1309 if (me->Message().startsWith(
"STOP_RECORDING"))
1311 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1312 QStringList tokens = me->Message().split(
" ",
1313 QString::SkipEmptyParts);
1315 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1319 if (tokens.size() < 3 || tokens.size() > 3)
1321 LOG(VB_GENERAL, LOG_ERR,
LOC +
1322 QString(
"Bad STOP_RECORDING message: %1")
1323 .arg(me->Message()));
1336 LOG(VB_GENERAL, LOG_ERR,
LOC +
1337 QString(
"Cannot find program info for '%1' while "
1338 "attempting to stop recording.").arg(me->Message()));
1344 if ((me->Message().startsWith(
"DELETE_RECORDING")) ||
1345 (me->Message().startsWith(
"FORCE_DELETE_RECORDING")))
1347 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1348 QStringList tokens = me->Message()
1349 .split(
" ", QString::SkipEmptyParts);
1351 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1355 if (tokens.size() < 3 || tokens.size() > 5)
1357 LOG(VB_GENERAL, LOG_ERR,
LOC +
1358 QString(
"Bad %1 message").arg(tokens[0]));
1362 bool force = (tokens.size() >= 4) && (tokens[3] ==
"FORCE");
1363 bool forget = (tokens.size() >= 5) && (tokens[4] ==
"FORGET");
1370 if (tokens[0] ==
"FORCE_DELETE_RECORDING")
1377 LOG(VB_GENERAL, LOG_ERR,
LOC +
1378 QString(
"Cannot find program info for '%1' while "
1379 "attempting to delete.").arg(me->Message()));
1385 if (me->Message().startsWith(
"UNDELETE_RECORDING"))
1387 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1388 QStringList tokens = me->Message().split(
" ",
1389 QString::SkipEmptyParts);
1391 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1395 if (tokens.size() < 3 || tokens.size() > 3)
1397 LOG(VB_GENERAL, LOG_ERR,
LOC +
1398 QString(
"Bad UNDELETE_RECORDING message: %1")
1399 .arg(me->Message()));
1412 LOG(VB_GENERAL, LOG_ERR,
LOC +
1413 QString(
"Cannot find program info for '%1' while "
1414 "attempting to undelete.").arg(me->Message()));
1420 if (me->Message().startsWith(
"ADD_CHILD_INPUT"))
1422 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1423 QStringList tokens = me->Message()
1424 .split(
" ", QString::SkipEmptyParts);
1426 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1430 LOG(VB_GENERAL, LOG_ERR,
LOC +
1431 "ADD_CHILD_INPUT event received in slave context");
1433 else if (tokens.size() != 2)
1435 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad ADD_CHILD_INPUT message");
1444 if (me->Message().startsWith(
"RESCHEDULE_RECORDINGS") &&
m_sched)
1446 const QStringList& request = me->ExtraDataList();
1451 if (me->Message().startsWith(
"SCHEDULER_ADD_RECORDING") &&
m_sched)
1456 LOG(VB_GENERAL, LOG_ERR,
LOC +
1457 "Bad SCHEDULER_ADD_RECORDING message");
1465 if (me->Message().startsWith(
"UPDATE_RECORDING_STATUS") &&
m_sched)
1467 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1468 QStringList tokens = me->Message()
1469 .split(
" ", QString::SkipEmptyParts);
1471 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1474 if (tokens.size() != 6)
1476 LOG(VB_GENERAL, LOG_ERR,
LOC +
1477 "Bad UPDATE_RECORDING_STATUS message");
1481 uint cardid = tokens[1].toUInt();
1482 uint chanid = tokens[2].toUInt();
1487 recstatus, recendts);
1493 if (me->Message().startsWith(
"LIVETV_EXITED"))
1495 const QString& chainid = me->ExtraData();
1503 if (me->Message() ==
"CLEAR_SETTINGS_CACHE")
1506 if (me->Message().startsWith(
"RESET_IDLETIME") &&
m_sched)
1509 if (me->Message() ==
"LOCAL_RECONNECT_TO_MASTER")
1512 if (me->Message() ==
"LOCAL_SLAVE_BACKEND_ENCODERS_OFFLINE")
1515 if (me->Message().startsWith(
"LOCAL_"))
1518 if (me->Message() ==
"CREATE_THUMBNAILS")
1521 if (me->Message() ==
"IMAGE_GET_METADATA")
1524 std::unique_ptr<MythEvent> mod_me {
nullptr};
1525 if (me->Message().startsWith(
"MASTER_UPDATE_REC_INFO"))
1527 QStringList tokens = me->Message().simplified().split(
" ");
1528 uint recordedid = 0;
1529 if (tokens.size() >= 2)
1530 recordedid = tokens[1].toUInt();
1531 if (recordedid == 0)
1545 mod_me = std::make_unique<MythEvent>(
"RECORDING_LIST_CHANGE UPDATE", list);
1553 if (me->Message().startsWith(
"DOWNLOAD_FILE"))
1555 QStringList extraDataList = me->ExtraDataList();
1556 QString localFile = extraDataList[1];
1557 QFile
file(localFile);
1558 QStringList tokens = me->Message().simplified().split(
" ");
1566 if ((tokens.size() >= 2) && (tokens[1] ==
"FINISHED"))
1569 mod_me = std::make_unique<MythEvent>(me->Message(), extraDataList);
1572 if (broadcast.empty())
1574 broadcast.push_back(
"BACKEND_MESSAGE");
1575 if (mod_me !=
nullptr)
1577 broadcast.push_back(mod_me->Message());
1578 broadcast += mod_me->ExtraDataList();
1582 broadcast.push_back(me->Message());
1583 broadcast += me->ExtraDataList();
1588 if (!broadcast.empty())
1591 std::vector<PlaybackSock *> localPBSList;
1596 localPBSList.push_back(
pbs);
1600 bool sendGlobal =
false;
1601 if (
m_ismaster && broadcast[1].startsWith(
"GLOBAL_"))
1603 broadcast[1].replace(
"GLOBAL_",
"LOCAL_");
1604 MythEvent me(broadcast[1], broadcast[2]);
1610 QSet<PlaybackSock*> sentSet;
1612 bool isSystemEvent = broadcast[1].startsWith(
"SYSTEM_EVENT ");
1615 std::vector<PlaybackSock*>::const_iterator iter;
1616 for (iter = localPBSList.begin(); iter != localPBSList.end(); ++iter)
1620 if (sentSet.contains(
pbs) ||
pbs->IsDisconnected())
1623 if (!receivers.empty() && !receivers.contains(
pbs->getHostname()))
1626 sentSet.insert(
pbs);
1628 bool reallysendit =
false;
1630 if (broadcast[1] ==
"CLEAR_SETTINGS_CACHE")
1633 (
pbs->isSlaveBackend() ||
pbs->wantsEvents()))
1634 reallysendit =
true;
1636 else if (sendGlobal)
1638 if (
pbs->isSlaveBackend())
1639 reallysendit =
true;
1641 else if (
pbs->wantsEvents())
1643 reallysendit =
true;
1650 if (!
pbs->wantsSystemEvents())
1654 if (!
pbs->wantsOnlySystemEvents())
1656 if (sentSetSystemEvent.contains(
pbs->getHostname()))
1659 sentSetSystemEvent <<
pbs->getHostname();
1662 else if (
pbs->wantsOnlySystemEvents())
1672 for (iter = localPBSList.begin(); iter != localPBSList.end(); ++iter)
1690 QStringList retlist;
1691 const QString&
version = slist[1];
1692 if (
version != MYTH_PROTO_VERSION)
1694 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1695 "MainServer::HandleVersion - Client speaks protocol version " +
1696 version +
" but we speak " + MYTH_PROTO_VERSION +
'!');
1697 retlist <<
"REJECT" << MYTH_PROTO_VERSION;
1703 if (slist.size() < 3)
1705 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1706 "MainServer::HandleVersion - Client did not pass protocol "
1707 "token. Refusing connection!");
1708 retlist <<
"REJECT" << MYTH_PROTO_VERSION;
1714 const QString& token = slist[2];
1715 if (token != QString::fromUtf8(MYTH_PROTO_TOKEN))
1717 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1718 QString(
"MainServer::HandleVersion - Client sent incorrect "
1719 "protocol token \"%1\" for protocol version. Refusing "
1720 "connection!").arg(token));
1721 retlist <<
"REJECT" << MYTH_PROTO_VERSION;
1727 retlist <<
"ACCEPT" << MYTH_PROTO_VERSION;
1756 QStringList retlist(
"OK" );
1757 QStringList errlist(
"ERROR" );
1759 if (commands.size() < 3 || commands.size() > 6)
1762 if (commands.size() == 2)
1763 info = QString(
" %1").arg(commands[1]);
1765 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Received malformed ANN%1 query")
1768 errlist <<
"malformed_ann_query";
1776 if (
pbs->getSocket() == socket)
1778 LOG(VB_GENERAL, LOG_WARNING,
LOC +
1779 QString(
"Client %1 is trying to announce a socket "
1789 if (commands[1] ==
"Playback" || commands[1] ==
"Monitor" ||
1790 commands[1] ==
"Frontend")
1792 if (commands.size() < 4)
1794 LOG(VB_GENERAL, LOG_ERR,
LOC +
1795 QString(
"Received malformed ANN %1 query")
1798 errlist <<
"malformed_ann_query";
1815 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"MainServer::ANN %1")
1817 LOG(VB_GENERAL, LOG_INFO,
LOC +
1818 QString(
"adding: %1(%2) as a client (events: %3)")
1820 .arg(quintptr(socket),0,16)
1822 pbs->setBlockShutdown((commands[1] ==
"Playback") ||
1823 (commands[1] ==
"Frontend"));
1825 if (commands[1] ==
"Frontend")
1827 pbs->SetAsFrontend();
1829 frontend->m_name = commands[2];
1844 else if (commands[1] ==
"MediaServer")
1846 if (commands.size() < 3)
1848 LOG(VB_GENERAL, LOG_ERR,
LOC +
1849 "Received malformed ANN MediaServer query");
1850 errlist <<
"malformed_ann_query";
1860 pbs->setAsMediaServer();
1861 pbs->setBlockShutdown(
false);
1866 QString(
"CLIENT_CONNECTED HOSTNAME %1").arg(commands[2]));
1868 else if (commands[1] ==
"SlaveBackend")
1870 if (commands.size() < 4)
1872 LOG(VB_GENERAL, LOG_ERR,
LOC +
1873 QString(
"Received malformed ANN %1 query")
1875 errlist <<
"malformed_ann_query";
1888 LOG(VB_GENERAL, LOG_INFO,
LOC +
1889 QString(
"adding: %1 as a slave backend server")
1891 pbs->setAsSlaveBackend();
1892 pbs->setIP(commands[3]);
1897 QStringList::const_iterator sit = slist.
cbegin()+1;
1898 while (sit != slist.cend())
1901 if (!recinfo->GetChanID())
1911 bool wasAsleep =
true;
1915 if (elink->GetHostName() == commands[2])
1917 if (! (elink->IsWaking() || elink->IsAsleep()))
1919 elink->SetSocket(
pbs);
1927 QString message = QString(
"LOCAL_SLAVE_BACKEND_ONLINE %2")
1932 pbs->setBlockShutdown(
false);
1937 QString(
"SLAVE_CONNECTED HOSTNAME %1").arg(commands[2]));
1939 else if (commands[1] ==
"FileTransfer")
1941 if (slist.size() < 3)
1943 LOG(VB_GENERAL, LOG_ERR,
LOC +
1944 "Received malformed FileTransfer command");
1945 errlist <<
"malformed_filetransfer_command";
1950 LOG(VB_NETWORK, LOG_INFO,
LOC +
1951 "MainServer::HandleAnnounce FileTransfer");
1952 LOG(VB_NETWORK, LOG_INFO,
LOC +
1953 QString(
"adding: %1 as a remote file transfer") .arg(commands[2]));
1954 QStringList::const_iterator it = slist.cbegin();
1955 QString path = *(++it);
1956 QString wantgroup = *(++it);
1958 QStringList checkfiles;
1960 for (++it; it != slist.cend(); ++it)
1964 bool writemode =
false;
1965 bool usereadahead =
true;
1966 std::chrono::milliseconds timeout_ms = 2s;
1967 if (commands.size() > 3)
1968 writemode = (commands[3].toInt() != 0);
1970 if (commands.size() > 4)
1971 usereadahead = (commands[4].toInt() != 0);
1973 if (commands.size() > 5)
1974 timeout_ms = std::chrono::milliseconds(commands[5].toInt());
1978 if (wantgroup.isEmpty())
1979 wantgroup =
"Default";
1985 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to determine directory "
1986 "to write to in FileTransfer write command");
1987 errlist <<
"filetransfer_directory_not_found";
1994 LOG(VB_GENERAL, LOG_ERR,
LOC +
1995 QString(
"FileTransfer write filename is empty in path '%1'.")
1997 errlist <<
"filetransfer_filename_empty";
2002 if ((path.contains(
"/../")) ||
2003 (path.startsWith(
"../")))
2005 LOG(VB_GENERAL, LOG_ERR,
LOC +
2006 QString(
"FileTransfer write filename '%1' does not pass "
2007 "sanity checks.") .arg(path));
2008 errlist <<
"filetransfer_filename_dangerous";
2020 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Empty filename, cowardly aborting!");
2021 errlist <<
"filetransfer_filename_empty";
2030 LOG(VB_GENERAL, LOG_ERR,
LOC +
2031 QString(
"FileTransfer filename '%1' is actually a directory, "
2032 "cannot transfer.") .arg(
filename));
2033 errlist <<
"filetransfer_filename_is_a_directory";
2040 QString dirPath = finfo.absolutePath();
2044 if (!qdir.mkpath(dirPath))
2046 LOG(VB_GENERAL, LOG_ERR,
LOC +
2047 QString(
"FileTransfer filename '%1' is in a "
2048 "subdirectory which does not exist, and can "
2049 "not be created.") .arg(
filename));
2050 errlist <<
"filetransfer_unable_to_create_subdirectory";
2070 LOG(VB_GENERAL, LOG_ERR,
LOC +
2071 QString(
"Can't open %1").arg(
filename));
2072 errlist <<
"filetransfer_unable_to_open_file";
2079 LOG(VB_GENERAL, LOG_INFO,
LOC +
2080 QString(
"adding: %1(%2) as a file transfer")
2082 .arg(quintptr(socket),0,16));
2092 if (!checkfiles.empty())
2095 QDir dir = fi.absoluteDir();
2096 for (
const auto &
file : qAsConst(checkfiles))
2098 if (dir.exists(
file) &&
2099 ((
file).endsWith(
".srt") ||
2132 QStringList strList(
"ERROR");
2147 bool do_write =
false;
2162 LOG(VB_GENERAL, LOG_ERR,
LOC +
2163 "SendResponse: Unable to write to client socket, as it's no "
2179 QString playbackhost =
pbs->getHostname();
2181 QMap<QString,ProgramInfo*> recMap;
2186 QMap<QString,bool> isJobRunning =
2192 if ((
type ==
"Ascending") || (
type ==
"Play"))
2194 else if ((
type ==
"Descending") || (
type ==
"Delete"))
2199 destination, (
type ==
"Recording"),
2200 inUseMap, isJobRunning, recMap, sort);
2202 QMap<QString,ProgramInfo*>::iterator mit = recMap.begin();
2203 for (; mit != recMap.end(); mit = recMap.erase(mit))
2206 QStringList outputlist(QString::number(destination.
size()));
2207 QMap<QString, int> backendPortMap;
2211 for (
auto* proginfo : destination)
2222 proginfo->GetBasename()));
2223 if (!proginfo->GetFilesize())
2226 if (tmpURL.startsWith(
'/'))
2228 QFile checkFile(tmpURL);
2229 if (!tmpURL.isEmpty() && checkFile.exists())
2231 proginfo->SetFilesize(checkFile.size());
2232 if (proginfo->GetRecordingEndTime() <
2235 proginfo->SaveFilesize(proginfo->GetFilesize());
2244 if (proginfo->GetPathname().isEmpty())
2246 LOG(VB_GENERAL, LOG_ERR,
LOC +
2247 QString(
"HandleQueryRecordings() "
2248 "Couldn't find backend for:\n\t\t\t%1")
2251 proginfo->SetFilesize(0);
2252 proginfo->SetPathname(
"file not found");
2257 if (!proginfo->GetFilesize())
2261 LOG(VB_GENERAL, LOG_ERR,
LOC +
2262 "MainServer::HandleQueryRecordings()"
2263 "\n\t\t\tCould not fill program info "
2268 if (proginfo->GetRecordingEndTime() <
2271 proginfo->SaveFilesize(proginfo->GetFilesize());
2280 if (!backendPortMap.contains(
hostname))
2292 proginfo->ToStringList(outputlist);
2305 if (slist.size() < 3)
2307 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad QUERY_RECORDING query");
2312 QString command = slist[1].toUpper();
2315 if (command ==
"BASENAME")
2319 else if (command ==
"TIMESLOT")
2321 if (slist.size() < 4)
2323 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad QUERY_RECORDING query");
2328 pginfo =
new ProgramInfo(slist[2].toUInt(), recstartts);
2331 QStringList strlist;
2352 QString playbackhost = slist[1];
2354 QStringList::const_iterator it = slist.cbegin() + 2;
2369 const QFileInfo info(lpath);
2373 QStringList strlist;
2384 m_ms->DoDeleteThread(
this);
2391 std::this_thread::sleep_for(3s + std::chrono::microseconds(
MythRandom(0, 2000)));
2396 QString logInfo = QString(
"recording id %1 (chanid %2 at %3)")
2401 QString name = QString(
"deleteThread%1%2").arg(getpid()).arg(
MythRandom());
2407 QString msg = QString(
"ERROR opening database connection for Delete "
2408 "Thread for chanid %1 recorded at %2. Program "
2409 "will NOT be deleted.")
2412 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2422 QString msg = QString(
"ERROR retrieving program info when trying to "
2423 "delete program for chanid %1 recorded at %2. "
2424 "Recording will NOT be deleted.")
2427 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2437 if ((!checkFile.exists()) && pginfo.
GetFilesize() &&
2440 LOG(VB_GENERAL, LOG_ERR,
LOC +
2441 QString(
"ERROR when trying to delete file: %1. File "
2442 "doesn't exist. Database metadata will not be removed.")
2460 bool errmsg =
false;
2475 if ((fd < 0) && checkFile.exists())
2480 delete_file_immediately(ds->
m_filename, followLinks,
false);
2481 std::this_thread::sleep_for(2s);
2482 if (checkFile.exists())
2488 LOG(VB_GENERAL, LOG_ERR,
LOC +
2489 QString(
"Error deleting file: %1. Keeping metadata in database.")
2504 QStringList nameFilters;
2505 nameFilters.push_back(fInfo.fileName() +
"*.png");
2506 nameFilters.push_back(fInfo.fileName() +
"*.jpg");
2507 nameFilters.push_back(fInfo.fileName() +
".tmp");
2508 nameFilters.push_back(fInfo.fileName() +
".old");
2509 nameFilters.push_back(fInfo.fileName() +
".map");
2510 nameFilters.push_back(fInfo.fileName() +
".tmp.map");
2511 nameFilters.push_back(fInfo.baseName() +
".srt");
2513 QDir dir (fInfo.path());
2514 QFileInfoList miscFiles = dir.entryInfoList(nameFilters);
2516 for (
const auto &
file : qAsConst(miscFiles))
2518 QString sFileName =
file.absoluteFilePath();
2519 delete_file_immediately( sFileName, followLinks,
true);
2530 if (slowDeletes && fd >= 0)
2536 QString logInfo = QString(
"recording id %1 filename %2")
2539 LOG(VB_GENERAL, LOG_NOTICE,
"DeleteRecordedFiles - " + logInfo);
2543 query.
prepare(
"SELECT basename, hostname, storagegroup FROM recordedfile "
2544 "WHERE recordedid = :RECORDEDID;");
2547 if (!query.
exec() || !query.
size())
2550 LOG(VB_GENERAL, LOG_ERR,
LOC +
2551 QString(
"Error querying recordedfiles for %1.") .arg(logInfo));
2554 while (query.
next())
2556 QString basename = query.
value(0).toString();
2559 bool deleteInDB =
false;
2561 if (basename == QFileInfo(ds->
m_filename).fileName())
2590 update.
prepare(
"DELETE FROM recordedfile "
2591 "WHERE recordedid = :RECORDEDID "
2592 "AND basename = :BASENAME ;");
2594 update.
bindValue(
":BASENAME", basename);
2598 LOG(VB_GENERAL, LOG_ERR,
LOC +
2599 QString(
"Error querying recordedfile (%1) for %2.")
2600 .arg(query.
value(1).toString(), logInfo));
2608 QString logInfo = QString(
"recording id %1 (chanid %2 at %3)")
2612 LOG(VB_GENERAL, LOG_NOTICE,
"DoDeleteINDB - " + logInfo);
2615 query.
prepare(
"DELETE FROM recorded WHERE recordedid = :RECORDEDID AND "
2620 if (!query.
exec() || !query.
size())
2623 LOG(VB_GENERAL, LOG_ERR,
LOC +
2624 QString(
"Error deleting recorded entry for %1.") .arg(logInfo));
2627 std::this_thread::sleep_for(1s);
2630 QString msg = QString(
"RECORDING_LIST_CHANGE DELETE %1")
2635 std::this_thread::sleep_for(3s);
2637 query.
prepare(
"DELETE FROM recordedmarkup "
2638 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
2645 LOG(VB_GENERAL, LOG_ERR,
LOC +
2646 QString(
"Error deleting recordedmarkup for %1.") .arg(logInfo));
2649 query.
prepare(
"DELETE FROM recordedseek "
2650 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
2657 LOG(VB_GENERAL, LOG_ERR,
LOC +
2658 QString(
"Error deleting recordedseek for %1.")
2673 bool deleteBrokenSymlinks)
2677 QString linktext =
"";
2678 QByteArray fname =
filename.toLocal8Bit();
2680 LOG(VB_FILE, LOG_INFO,
LOC +
2681 QString(
"About to unlink/delete file: '%1'")
2682 .arg(fname.constData()));
2684 QString errmsg = QString(
"Delete Error '%1'").arg(fname.constData());
2685 if (finfo.isSymLink())
2688 QByteArray alink = linktext.toLocal8Bit();
2689 errmsg += QString(
" -> '%2'").arg(alink.constData());
2692 if (followLinks && finfo.isSymLink())
2694 if (!finfo.exists() && deleteBrokenSymlinks)
2695 unlink(fname.constData());
2700 unlink(fname.constData());
2703 else if (!finfo.isSymLink())
2709 int err = unlink(fname.constData());
2714 if (fd < 0 && errno != EISDIR)
2715 LOG(VB_GENERAL, LOG_ERR,
LOC + errmsg +
ENO);
2731 QByteArray fname =
filename.toLocal8Bit();
2732 QString msg = QString(
"Error deleting '%1'").arg(fname.constData());
2733 int fd = open(fname.constData(), O_WRONLY);
2737 if (errno == EISDIR)
2742 LOG(VB_GENERAL, LOG_ERR, msg +
" could not delete directory " +
ENO);
2748 LOG(VB_GENERAL, LOG_ERR, msg +
" could not open " +
ENO);
2752 else if (unlink(fname.constData()))
2754 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
" could not unlink " +
ENO);
2784 query.
prepare(
"SELECT COUNT(cardid) FROM capturecard;");
2786 cards = query.
value(0).toInt();
2790 constexpr std::chrono::milliseconds sleep_time = 500ms;
2791 const size_t min_tps = 8LL * 1024 * 1024;
2792 const auto calc_tps = (size_t) (cards * 1.2 * (22200000LL / 8.0));
2793 const size_t tps = std::max(min_tps, calc_tps);
2794 const auto increment = (size_t) (tps * (sleep_time.count() * 0.001F));
2796 LOG(VB_FILE, LOG_INFO,
LOC +
2797 QString(
"Truncating '%1' by %2 MB every %3 milliseconds")
2799 .arg(increment / (1024.0 * 1024.0), 0,
'f', 2)
2800 .arg(sleep_time.count()));
2802 GetMythDB()->GetDBManager()->PurgeIdleConnections(
false);
2808 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Truncating '%1' to %2 MB")
2809 .arg(
filename).arg(fsize / (1024.0 * 1024.0), 0,
'f', 2));
2812 int err = ftruncate(fd, fsize);
2815 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error truncating '%1'")
2819 return 0 ==
close(fd);
2824 if (pginfo && ((count % 100) == 0))
2829 std::this_thread::sleep_for(sleep_time);
2832 bool ok = (0 ==
close(fd));
2837 LOG(VB_FILE, LOG_INFO,
LOC +
2838 QString(
"Finished truncating '%1'").arg(
filename));
2848 pbssock =
pbs->getSocket();
2850 QStringList::const_iterator it = slist.cbegin() + 1;
2872 result = iter.key();
2877 QStringList outputlist( QString::number(result) );
2884 QStringList::const_iterator it = slist.cbegin() + 1;
2896 bool hasConflicts =
false;
2898 for (
auto *pInfo : schedList)
2916 pbssock =
pbs->getSocket();
2939 (*m_encoderList)[num]->StopRecording();
2947 QStringList outputlist(
"0" );
2972 recnum = iter.key();
2979 std::this_thread::sleep_for(100us);
2995 QStringList outputlist( QString::number(recnum) );
3002 bool forceMetadataDelete,
3010 qDebug() <<
"HandleDeleteRecording(chanid, starttime) Empty Recording ID";
3017 pbssock =
pbs->getSocket();
3019 QStringList outputlist( QString::number(0) );
3029 bool forceMetadataDelete)
3031 QStringList::const_iterator it = slist.cbegin() + 1;
3036 qDebug() <<
"HandleDeleteRecording(QStringList) Empty Recording ID";
3045 bool forceMetadataDelete,
bool lexpirer,
bool forgetHistory)
3047 int resultCode = -1;
3050 pbssock =
pbs->getSocket();
3052 bool justexpire = lexpirer ?
false :
3060 LOG(VB_GENERAL, LOG_ERR,
LOC +
3061 QString(
"ERROR when trying to delete file for %1. Unable "
3062 "to determine filename of recording.")
3068 QStringList outputlist(QString::number(resultCode));
3078 if (justexpire && !forceMetadataDelete &&
3087 QStringList outputlist( QString::number(0) );
3111 QStringList outputlist( QString::number(num) );
3121 bool fileExists = checkFile.exists();
3124 QFile checkFileUTF8(QString::fromUtf8(
filename.toLatin1().constData()));
3125 fileExists = checkFileUTF8.exists();
3134 if (fileExists || !recinfo.
GetFilesize() || forceMetadataDelete)
3140 qDebug() <<
"DoHandleDeleteRecording() Empty Recording ID";
3147 forceMetadataDelete);
3148 deleteThread->start();
3153 QString logInfo = QString(
"chanid %1")
3157 LOG(VB_GENERAL, LOG_ERR,
LOC +
3158 QString(
"ERROR when trying to delete file: %1. File doesn't "
3159 "exist. Database metadata will not be removed.")
3166 QStringList outputlist( QString::number(resultCode) );
3178 if (fileExists || !recinfo.
GetFilesize() || forceMetadataDelete)
3181 QString(
"REC_DELETED CHANID %1 STARTTIME %2")
3191 if (slist.size() == 3)
3200 QStringList::const_iterator it = slist.cbegin()+1;
3214 pbssock =
pbs->getSocket();
3228 QStringList outputlist( QString::number(ret) );
3265 result = QStringList(QString::number(1));
3268 result = QStringList(QString::number(0));
3286 LOG(VB_GENERAL, LOG_INFO,
LOC +
"HandleAddChildInput: Already locked");
3290 LOG(VB_GENERAL, LOG_INFO,
LOC +
3291 QString(
"HandleAddChildInput: Handling input %1").arg(inputid));
3301 LOG(VB_GENERAL, LOG_ERR,
LOC +
3302 QString(
"HandleAddChildInput: "
3303 "Failed to add child to input %1").arg(inputid));
3309 LOG(VB_GENERAL, LOG_INFO,
LOC +
3310 QString(
"HandleAddChildInput: Added child input %1").arg(childid));
3318 auto *tv =
new TVRec(childid);
3319 if (!tv || !tv->Init())
3321 LOG(VB_GENERAL, LOG_ERR,
LOC +
3322 QString(
"HandleAddChildInput: "
3323 "Failed to initialize input %1").arg(childid));
3332 (*m_encoderList)[childid] = enc;
3339 LOG(VB_GENERAL, LOG_ERR,
LOC +
3340 QString(
"HandleAddChildInput: "
3341 "Failed to add remote input %1").arg(childid));
3351 (*m_encoderList)[childid] = enc;
3360 auto *tv =
new TVRec(inputid);
3361 if (!tv || !tv->Init())
3363 LOG(VB_GENERAL, LOG_ERR,
LOC +
3364 QString(
"HandleAddChildInput: "
3365 "Failed to initialize input %1").arg(inputid));
3373 (*m_encoderList)[inputid] = enc;
3379 LOG(VB_GENERAL, LOG_INFO,
LOC +
3380 QString(
"HandleAddChildInput: "
3381 "Successfully handled input %1").arg(inputid));
3388 QStringList::const_iterator it = slist.cbegin() + 1;
3395 pbssock =
pbs->getSocket();
3398 QStringList outputlist( QString::number(0) );
3410 QStringList strlist;
3413 if (!sleepCmd.isEmpty())
3417 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
3418 "Received GO_TO_SLEEP command from master, running SleepCommand.");
3423 strlist <<
"ERROR: SleepCommand is empty";
3424 LOG(VB_GENERAL, LOG_ERR,
LOC +
3425 "ERROR: in HandleGoToSleep(), but no SleepCommand found!");
3441 QStringList strlist;
3473 QStringList strlist;
3492 QStringList shortlist;
3493 if (strlist.size() < 4)
3495 shortlist << QString(
"0");
3496 shortlist << QString(
"0");
3500 unsigned int index = (
uint)(strlist.size()) - 2;
3501 shortlist << strlist[index++];
3502 shortlist << strlist[index++];
3518 QStringList strlist;
3520 #if defined(_WIN32) || defined(Q_OS_ANDROID)
3521 strlist <<
"0" <<
"0" <<
"0";
3527 strlist <<
"getloadavg() failed";
3531 strlist << QString::number(loads[0])
3532 << QString::number(loads[1])
3533 << QString::number(loads[2]);
3548 QStringList strlist;
3549 std::chrono::seconds uptime = 0s;
3552 strlist << QString::number(uptime.count());
3556 strlist <<
"Could not determine uptime.";
3570 QStringList strlist;
3585 QStringList strlist;
3591 if (
getMemStats(totalMB, freeMB, totalVM, freeVM))
3593 strlist << QString::number(totalMB) << QString::number(freeMB)
3594 << QString::number(totalVM) << QString::number(freeVM);
3599 strlist <<
"Could not determine memory stats.";
3613 QStringList strlist;
3628 bool checkSlaves = slist[1].toInt() != 0;
3630 QStringList::const_iterator it = slist.cbegin() + 2;
3633 bool exists =
false;
3646 QStringList outputlist( QString::number(
static_cast<int>(exists)) );
3661 exists = QFileInfo::exists(pburl);
3666 QStringList strlist( QString::number(
static_cast<int>(exists)) );
3678 QString storageGroup =
"Default";
3683 switch (slist.size()) {
3685 if (!slist[3].isEmpty())
3689 if (slist[2].isEmpty())
3690 storageGroup = slist[2];
3698 LOG(VB_GENERAL, LOG_ERR,
LOC +
3699 QString(
"ERROR checking for file, filename '%1' "
3700 "fails sanity checks").arg(
filename));
3707 LOG(VB_GENERAL, LOG_ERR,
LOC +
3708 "ERROR, invalid input count for QUERY_FILE_HASH");
3746 QString storageGroup =
"Default";
3747 QStringList retlist;
3749 if (slist.size() > 2)
3750 storageGroup = slist[2];
3756 LOG(VB_GENERAL, LOG_ERR,
LOC +
3757 QString(
"ERROR checking for file, filename '%1' "
3758 "fails sanity checks").arg(
filename));
3764 if (storageGroup.isEmpty())
3765 storageGroup =
"Default";
3771 if (!fullname.isEmpty())
3774 retlist << fullname;
3776 struct stat fileinfo {};
3777 if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
3779 retlist << QString::number(fileinfo.st_dev);
3780 retlist << QString::number(fileinfo.st_ino);
3781 retlist << QString::number(fileinfo.st_mode);
3782 retlist << QString::number(fileinfo.st_nlink);
3783 retlist << QString::number(fileinfo.st_uid);
3784 retlist << QString::number(fileinfo.st_gid);
3785 retlist << QString::number(fileinfo.st_rdev);
3786 retlist << QString::number(fileinfo.st_size);
3791 retlist << QString::number(fileinfo.st_blksize);
3792 retlist << QString::number(fileinfo.st_blocks);
3794 retlist << QString::number(fileinfo.st_atime);
3795 retlist << QString::number(fileinfo.st_mtime);
3796 retlist << QString::number(fileinfo.st_ctime);
3808 query.
prepare(
"SELECT MAX(endtime) FROM program WHERE manualid = 0;");
3818 QDateTime GuideDataThrough;
3820 QStringList strlist;
3824 if (GuideDataThrough.isNull())
3825 strlist << QString(
"0000-00-00 00:00");
3827 strlist << GuideDataThrough.toString(
"yyyy-MM-dd hh:mm");
3833 const QString& tmptable,
int recordid)
3837 QStringList strList;
3841 if (tmptable.isEmpty())
3853 query.
prepare(
"SELECT NULL FROM record "
3854 "WHERE recordid = :RECID;");
3860 record->m_recordID = recordid;
3861 if (record->Load() &&
3867 query.
prepare(
"DELETE FROM program WHERE manualid = :RECID;");
3877 strList << QString::number(0);
3878 strList << QString::number(0);
3888 QStringList strList;
3893 strList << QString::number(0);
3903 QStringList::const_iterator it = slist.cbegin() + 1;
3906 QStringList strlist;
3911 strlist << QString::number(0);
3920 QStringList strList;
3925 strList << QString::number(0);
3934 QStringList strList;
3936 if ((sList.size() < 4) || (sList.size() > 5))
3938 LOG(VB_GENERAL, LOG_ERR,
LOC +
3939 QString(
"HandleSGGetFileList: Invalid Request. %1")
3940 .arg(sList.join(
"[]:[]")));
3941 strList <<
"EMPTY LIST";
3947 const QString& wantHost = sList.at(1);
3948 QHostAddress wantHostaddr(wantHost);
3949 const QString& groupname = sList.at(2);
3950 const QString& path = sList.at(3);
3951 bool fileNamesOnly =
false;
3953 if (sList.size() >= 5)
3954 fileNamesOnly = (sList.at(4).toInt() != 0);
3956 bool slaveUnreachable =
false;
3958 LOG(VB_FILE, LOG_INFO,
LOC +
3959 QString(
"HandleSGGetFileList: group = %1 host = %2 "
3960 " path = %3 wanthost = %4")
3961 .arg(groupname, host, path, wantHost));
3965 if ((host.toLower() == wantHost.toLower()) ||
3966 (!addr.isEmpty() && addr == wantHostaddr.toString()))
3969 LOG(VB_FILE, LOG_INFO,
LOC +
"HandleSGGetFileList: Getting local info");
3980 LOG(VB_FILE, LOG_INFO,
LOC +
3981 "HandleSGGetFileList: Getting remote info");
3985 slaveUnreachable =
false;
3989 LOG(VB_FILE, LOG_INFO,
LOC +
3990 QString(
"HandleSGGetFileList: Failed to grab slave socket "
3991 ": %1 :").arg(wantHost));
3992 slaveUnreachable =
true;
3997 if (slaveUnreachable)
3998 strList <<
"SLAVE UNREACHABLE: " << host;
4000 if (strList.isEmpty() || (strList.at(0) ==
"0"))
4001 strList <<
"EMPTY LIST";
4011 QString storageGroup = slist[2];
4013 bool allowFallback =
true;
4014 bool useRegex =
false;
4015 QStringList fileList;
4017 if (!QHostAddress(
hostname).isNull())
4019 LOG(VB_GENERAL, LOG_ERR, QString(
"Mainserver: QUERY_FINDFILE called "
4020 "with IP (%1) instead of hostname. "
4021 "This is invalid.").arg(
hostname));
4027 if (storageGroup.isEmpty())
4028 storageGroup =
"Default";
4033 LOG(VB_GENERAL, LOG_ERR,
LOC +
4034 QString(
"ERROR QueryFindFile, filename '%1' "
4035 "fails sanity checks").arg(
filename));
4036 fileList <<
"ERROR: Bad/Missing Filename";
4041 if (slist.size() >= 5)
4042 useRegex = (slist[4].toInt() > 0);
4044 if (slist.size() >= 6)
4045 allowFallback = (slist[5].toInt() > 0);
4047 LOG(VB_FILE, LOG_INFO,
LOC +
4048 QString(
"Looking for file '%1' on host '%2' in group '%3' (useregex: %4, allowfallback: %5")
4062 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
4064 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Looking in dir '%1' for '%2'")
4065 .arg(fi.path(), fi.fileName()));
4067 for (
int x = 0; x < files.size(); x++)
4069 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Found '%1 - %2'").arg(x).arg(files[x]));
4072 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
4073 for (
const QString&
file : qAsConst(filteredFiles))
4077 fi.path() +
'/' +
file,
4093 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Checking remote host '%1' for file").arg(
hostname));
4101 if (!slaveFiles.isEmpty() && slaveFiles[0] !=
"NOT FOUND" && !slaveFiles[0].startsWith(
"ERROR: "))
4102 fileList += slaveFiles;
4108 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Slave '%1' was unreachable").arg(
hostname));
4109 fileList << QString(
"ERROR: SLAVE UNREACHABLE: %1").arg(
hostname);
4117 if (
m_ismaster && fileList.isEmpty() && allowFallback)
4122 QString sql =
"SELECT DISTINCT hostname "
4123 "FROM storagegroup "
4124 "WHERE groupname = :GROUP "
4125 "AND hostname != :HOSTNAME";
4127 query.
bindValue(
":GROUP", storageGroup);
4133 fileList <<
"ERROR: failed to get host list";
4149 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
4151 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Looking in dir '%1' for '%2'")
4152 .arg(fi.path(), fi.fileName()));
4154 for (
int x = 0; x < files.size(); x++)
4156 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Found '%1 - %2'").arg(x).arg(files[x]));
4159 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
4161 for (
const QString&
file : qAsConst(filteredFiles))
4165 fi.path() +
'/' +
file,
4172 if (!fname.isEmpty())
4187 if (!slaveFiles.isEmpty() && slaveFiles[0] !=
"NOT FOUND" && !slaveFiles[0].startsWith(
"ERROR: "))
4188 fileList += slaveFiles;
4194 if (!fileList.isEmpty())
4199 if (fileList.isEmpty())
4201 fileList <<
"NOT FOUND";
4202 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"File was not found"));
4206 for (
int x = 0; x < fileList.size(); x++)
4208 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"File %1 was found at: '%2'").arg(x).arg(fileList[0]));
4221 QStringList strList;
4223 if (sList.size() < 4)
4225 LOG(VB_GENERAL, LOG_ERR,
LOC +
4226 QString(
"HandleSGFileQuery: Invalid Request. %1")
4227 .arg(sList.join(
"[]:[]")));
4228 strList <<
"EMPTY LIST";
4234 const QString& wantHost = sList.at(1);
4235 QHostAddress wantHostaddr(wantHost);
4236 const QString& groupname = sList.at(2);
4237 const QString&
filename = sList.at(3);
4239 bool allowFallback =
true;
4240 if (sList.size() >= 5)
4241 allowFallback = (sList.at(4).toInt() > 0);
4242 LOG(VB_FILE, LOG_ERR, QString(
"HandleSGFileQuery - allowFallback: %1").arg(allowFallback));
4244 bool slaveUnreachable =
false;
4246 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"HandleSGFileQuery: %1")
4251 if ((host.toLower() == wantHost.toLower()) ||
4252 (!addr.isEmpty() && addr == wantHostaddr.toString()))
4254 LOG(VB_FILE, LOG_INFO,
LOC +
"HandleSGFileQuery: Getting local info");
4263 LOG(VB_FILE, LOG_INFO,
LOC +
4264 "HandleSGFileQuery: Getting remote info");
4267 slaveUnreachable =
false;
4271 LOG(VB_FILE, LOG_INFO,
LOC +
4272 QString(
"HandleSGFileQuery: Failed to grab slave socket : %1 :")
4274 slaveUnreachable =
true;
4279 if (slaveUnreachable)
4280 strList <<
"SLAVE UNREACHABLE: " << wantHost;
4282 if (strList.count() == 0 || (strList.at(0) ==
"0"))
4283 strList <<
"EMPTY LIST";
4291 QString pbshost =
pbs->getHostname();
4293 QStringList strlist;
4302 if ((cardid != -1) && (cardid != elink->GetInputID()))
4305 if (elink->IsLocal())
4308 enchost = elink->GetHostName();
4310 if ((enchost == pbshost) &&
4311 (elink->IsConnected()) &&
4312 (!elink->IsBusy()) &&
4313 (!elink->IsTunerLocked()))
4327 QString msg = QString(
"Cardid %1 LOCKed for external use on %2.")
4328 .arg(retval).arg(pbshost);
4329 LOG(VB_GENERAL, LOG_INFO,
LOC + msg);
4332 query.
prepare(
"SELECT videodevice, audiodevice, "
4335 "WHERE cardid = :CARDID ;");
4341 strlist << QString::number(retval)
4342 << query.
value(0).toString()
4343 << query.
value(1).toString()
4344 << query.
value(2).toString();
4352 LOG(VB_GENERAL, LOG_ERR,
LOC +
4353 "MainServer::LockTuner(): Could not find "
4354 "card info in database");
4359 strlist <<
"-2" <<
"" <<
"" <<
"";
4365 strlist <<
"-1" <<
"" <<
"" <<
"";
4372 QStringList strlist;
4379 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleFreeTuner() " +
4380 QString(
"Unknown encoder: %1").arg(cardid));
4381 strlist <<
"FAILED";
4388 QString msg = QString(
"Cardid %1 FREED from external use on %2.")
4389 .arg(cardid).arg(
pbs->getHostname());
4390 LOG(VB_GENERAL, LOG_INFO,
LOC + msg);
4410 uint excluded_input)
4412 LOG(VB_CHANNEL, LOG_INFO,
4413 LOC + QString(
"Excluding input %1")
4414 .arg(excluded_input));
4417 std::vector<InputInfo> busyinputs;
4418 std::vector<InputInfo> freeinputs;
4419 QMap<uint, QSet<uint> > groupids;
4429 if (!elink->IsConnected() || elink->IsTunerLocked())
4431 LOG(VB_CHANNEL, LOG_INFO,
4432 LOC + QString(
"Input %1 is locked or not connected")
4437 std::vector<uint> infogroups;
4439 for (
uint group : infogroups)
4443 if (info.
m_inputId != excluded_input && elink->IsBusy(&busyinfo))
4445 LOG(VB_CHANNEL, LOG_DEBUG,
4446 LOC + QString(
"Input %1 is busy on %2/%3")
4450 busyinputs.push_back(info);
4454 LOG(VB_CHANNEL, LOG_DEBUG,
4455 LOC + QString(
"Input %1 is free")
4457 freeinputs.push_back(info);
4464 for (
auto & busyinfo : busyinputs)
4466 auto freeiter = freeinputs.begin();
4467 while (freeiter != freeinputs.end())
4471 if ((groupids[busyinfo.m_inputId] & groupids[freeinfo.
m_inputId])
4478 if (busyinfo.m_sourceId == freeinfo.
m_sourceId)
4480 LOG(VB_CHANNEL, LOG_DEBUG,
4481 LOC + QString(
"Input %1 is limited to %2/%3 by input %4")
4482 .arg(freeinfo.
m_inputId).arg(busyinfo.m_chanId)
4483 .arg(busyinfo.m_mplexId).arg(busyinfo.m_inputId));
4484 freeinfo.
m_chanId = busyinfo.m_chanId;
4485 freeinfo.
m_mplexId = busyinfo.m_mplexId;
4490 LOG(VB_CHANNEL, LOG_DEBUG,
4491 LOC + QString(
"Input %1 is unavailable by input %2")
4492 .arg(freeinfo.
m_inputId).arg(busyinfo.m_inputId));
4493 freeiter = freeinputs.erase(freeiter);
4499 QStringList strlist;
4500 for (
auto & input : freeinputs)
4502 LOG(VB_CHANNEL, LOG_INFO,
4503 LOC + QString(
"Input %1 is available on %2/%3")
4504 .arg(input.m_inputId).arg(input.m_chanId)
4505 .arg(input.m_mplexId));
4506 input.ToStringList(strlist);
4509 if (strlist.empty())
4534 if (commands.size() < 2 || slist.size() < 2)
4537 int recnum = commands[1].toInt();
4544 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleRecorderQuery() " +
4545 QString(
"Unknown encoder: %1").arg(recnum));
4546 QStringList retlist(
"bad" );
4552 QString command = slist[1];
4554 QStringList retlist;
4559 LOG(VB_GENERAL, LOG_ERR,
LOC +
" MainServer::HandleRecorderQuery() " +
4560 QString(
"Command %1 for unconnected encoder %2")
4561 .arg(command).arg(recnum));
4567 if (command ==
"IS_RECORDING")
4571 else if (command ==
"GET_FRAMERATE")
4575 else if (command ==
"GET_FRAMES_WRITTEN")
4579 else if (command ==
"GET_FILE_POSITION")
4583 else if (command ==
"GET_MAX_BITRATE")
4587 else if (command ==
"GET_CURRENT_RECORDING")
4602 else if (command ==
"GET_KEYFRAME_POS")
4604 long long desired = slist[2].toLongLong();
4607 else if (command ==
"FILL_POSITION_MAP")
4609 int64_t start = slist[2].toLongLong();
4610 int64_t end = slist[3].toLongLong();
4619 for (
auto it = map.cbegin(); it != map.cend(); ++it)
4621 retlist += QString::number(it.key());
4622 retlist += QString::number(*it);
4624 if (retlist.empty())
4628 else if (command ==
"FILL_DURATION_MAP")
4630 int64_t start = slist[2].toLongLong();
4631 int64_t end = slist[3].toLongLong();
4640 for (
auto it = map.cbegin(); it != map.cend(); ++it)
4642 retlist += QString::number(it.key());
4643 retlist += QString::number(*it);
4645 if (retlist.empty())
4649 else if (command ==
"GET_RECORDING")
4664 else if (command ==
"FRONTEND_READY")
4669 else if (command ==
"CANCEL_NEXT_RECORDING")
4671 QString cancel = slist[2];
4672 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
4673 QString(
"Received: CANCEL_NEXT_RECORDING %1").arg(cancel));
4677 else if (command ==
"SPAWN_LIVETV")
4679 QString chainid = slist[2];
4690 enc->
SpawnLiveTV(chain, slist[3].toInt() != 0, slist[4]);
4693 else if (command ==
"STOP_LIVETV")
4710 else if (command ==
"PAUSE")
4715 else if (command ==
"FINISH_RECORDING")
4720 else if (command ==
"SET_LIVE_RECORDING")
4722 int recording = slist[2].toInt();
4726 else if (command ==
"GET_INPUT")
4729 ret = (ret.isEmpty()) ?
"UNKNOWN" : ret;
4732 else if (command ==
"SET_INPUT")
4734 QString input = slist[2];
4735 QString ret = enc->
SetInput(input);
4736 ret = (ret.isEmpty()) ?
"UNKNOWN" : ret;
4739 else if (command ==
"TOGGLE_CHANNEL_FAVORITE")
4741 QString changroup = slist[2];
4745 else if (command ==
"CHANGE_CHANNEL")
4751 else if (command ==
"SET_CHANNEL")
4753 QString name = slist[2];
4757 else if (command ==
"SET_SIGNAL_MONITORING_RATE")
4759 auto rate = std::chrono::milliseconds(slist[2].toInt());
4760 int notifyFrontend = slist[3].toInt();
4762 retlist << QString::number(oldrate.count());
4764 else if (command ==
"GET_COLOUR")
4767 retlist << QString::number(ret);
4769 else if (command ==
"GET_CONTRAST")
4772 retlist << QString::number(ret);
4774 else if (command ==
"GET_BRIGHTNESS")
4777 retlist << QString::number(ret);
4779 else if (command ==
"GET_HUE")
4782 retlist << QString::number(ret);
4784 else if (command ==
"CHANGE_COLOUR")
4786 int type = slist[2].toInt();
4787 bool up = slist[3].toInt() != 0;
4790 retlist << QString::number(ret);
4792 else if (command ==
"CHANGE_CONTRAST")
4794 int type = slist[2].toInt();
4795 bool up = slist[3].toInt() != 0;
4798 retlist << QString::number(ret);
4800 else if (command ==
"CHANGE_BRIGHTNESS")
4802 int type= slist[2].toInt();
4803 bool up = slist[3].toInt() != 0;
4806 retlist << QString::number(ret);
4808 else if (command ==
"CHANGE_HUE")
4810 int type= slist[2].toInt();
4811 bool up = slist[3].toInt() != 0;
4814 retlist << QString::number(ret);
4816 else if (command ==
"CHECK_CHANNEL")
4818 QString name = slist[2];
4819 retlist << QString::number((
int)(enc->
CheckChannel(name)));
4821 else if (command ==
"SHOULD_SWITCH_CARD")
4823 QString chanid = slist[2];
4826 else if (command ==
"CHECK_CHANNEL_PREFIX")
4828 QString needed_spacer;
4829 QString
prefix = slist[2];
4830 uint complete_valid_channel_on_rec = 0;
4831 bool is_extra_char_useful =
false;
4834 prefix, complete_valid_channel_on_rec,
4835 is_extra_char_useful, needed_spacer);
4837 retlist << QString::number((
int)match);
4838 retlist << QString::number(complete_valid_channel_on_rec);
4839 retlist << QString::number((
int)is_extra_char_useful);
4840 retlist << ((needed_spacer.isEmpty()) ? QString(
"X") : needed_spacer);
4842 else if (command ==
"GET_NEXT_PROGRAM_INFO" && (slist.size() >= 6))
4844 QString channelname = slist[2];
4845 uint chanid = slist[3].toUInt();
4847 QString starttime = slist[5];
4850 QString subtitle =
"";
4852 QString category =
"";
4853 QString endtime =
"";
4854 QString callsign =
"";
4855 QString iconpath =
"";
4856 QString seriesid =
"";
4857 QString programid =
"";
4860 title, subtitle, desc, category, starttime,
4861 endtime, callsign, iconpath, channelname, chanid,
4862 seriesid, programid);
4873 retlist << QString::number(chanid);
4877 else if (command ==
"GET_CHANNEL_INFO")
4879 uint chanid = slist[2].toUInt();
4881 QString callsign =
"";
4882 QString channum =
"";
4883 QString channame =
"";
4887 callsign, channum, channame, xmltv);
4889 retlist << QString::number(chanid);
4890 retlist << QString::number(sourceid);
4898 LOG(VB_GENERAL, LOG_ERR,
LOC +
4899 QString(
"Unknown command: %1").arg(command));
4911 int recnum = commands[1].toInt();
4918 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleSetNextLiveTVDir() " +
4919 QString(
"Unknown encoder: %1").arg(recnum));
4920 QStringList retlist(
"bad" );
4929 QStringList retlist(
"OK" );
4937 uint chanid = slist[1].toUInt();
4938 uint sourceid = slist[2].toUInt();
4939 QString oldcnum =
cleanup(slist[3]);
4940 QString callsign =
cleanup(slist[4]);
4941 QString channum =
cleanup(slist[5]);
4942 QString channame =
cleanup(slist[6]);
4943 QString xmltv =
cleanup(slist[7]);
4945 QStringList retlist;
4946 if (!chanid || !sourceid)
4958 ok &= encoder->SetChannelInfo(chanid, sourceid, oldcnum,
4959 callsign, channum, channame, xmltv);
4964 retlist << ((ok) ?
"1" :
"0");
4973 int recnum = commands[1].toInt();
4974 QStringList retlist;
4981 LOG(VB_GENERAL, LOG_ERR,
LOC +
4982 QString(
"HandleRemoteEncoder(cmd %1) ").arg(slist[1]) +
4983 QString(
"Unknown encoder: %1").arg(recnum));
4992 QString command = slist[1];
4994 if (command ==
"GET_STATE")
4996 retlist << QString::number((
int)enc->
GetState());
4998 else if (command ==
"GET_SLEEPSTATUS")
5002 else if (command ==
"GET_FLAGS")
5004 retlist << QString::number(enc->
GetFlags());
5006 else if (command ==
"IS_BUSY")
5008 std::chrono::seconds time_buffer = 5s;
5009 if (slist.size() >= 3)
5010 time_buffer = std::chrono::seconds(slist[2].toInt());
5012 retlist << QString::number((
int)enc->
IsBusy(&busy_input, time_buffer));
5015 else if (command ==
"MATCHES_RECORDING" &&
5018 QStringList::const_iterator it = slist.cbegin() + 2;
5023 else if (command ==
"START_RECORDING" &&
5026 QStringList::const_iterator it = slist.cbegin() + 2;
5033 else if (command ==
"GET_RECORDING_STATUS")
5037 else if (command ==
"RECORD_PENDING" &&
5040 auto secsleft = std::chrono::seconds(slist[2].toInt());
5041 int haslater = slist[3].toInt();
5042 QStringList::const_iterator it = slist.cbegin() + 4;
5049 else if (command ==
"CANCEL_NEXT_RECORDING" &&
5050 (slist.size() >= 3))
5052 bool cancel = (
bool) slist[2].toInt();
5056 else if (command ==
"STOP_RECORDING")
5061 else if (command ==
"GET_MAX_BITRATE")
5065 else if (command ==
"GET_CURRENT_RECORDING")
5093 if (
pbs->isMediaServer())
5104 QStringList retlist;
5106 retlist.push_front(QString::number(retlist.size()));
5113 QStringList retlist;
5114 const QString& queryhostname = slist[1];
5119 if (slave !=
nullptr)
5135 QString fskey = fsInfo->getHostname() +
":" + fsInfo->getPath();
5145 size_t totalKBperMin = 0;
5150 if (!enc->IsConnected() || !enc->IsBusy())
5153 long long maxBitrate = enc->GetMaxBitrate();
5155 maxBitrate = 19500000LL;
5156 long long thisKBperMin = (((size_t)maxBitrate)*((size_t)15))>>11;
5157 totalKBperMin += thisKBperMin;
5158 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Cardid %1: max bitrate %2 KB/min")
5159 .arg(enc->GetInputID()).arg(thisKBperMin));
5163 LOG(VB_FILE, LOG_INFO,
LOC +
5164 QString(
"Maximal bitrate of busy encoders is %1 KB/min")
5165 .arg(totalKBperMin));
5167 return totalKBperMin;
5174 int64_t totalKB = -1;
5175 int64_t usedKB = -1;
5176 QMap <QString, bool>foundDirs;
5177 QString localStr =
"1";
5178 struct statfs statbuf {};
5180 groups.removeAll(
"LiveTV");
5181 QString specialGroups = groups.join(
"', '");
5182 QString sql = QString(
"SELECT MIN(id),dirname "
5183 "FROM storagegroup "
5184 "WHERE hostname = :HOSTNAME "
5185 "AND groupname NOT IN ( '%1' ) "
5186 "GROUP BY dirname;").arg(specialGroups);
5197 query.
prepare(
"SELECT MIN(id),dirname "
5198 "FROM storagegroup "
5199 "WHERE groupname = :GROUP "
5200 "GROUP BY dirname;");
5209 while (query.
next())
5211 dirID = query.
value(0).toString();
5215 currentDir = QString::fromUtf8(query.
value(1)
5216 .toByteArray().constData());
5217 if (currentDir.endsWith(
"/"))
5218 currentDir.remove(currentDir.length() - 1, 1);
5220 checkDir.setPath(currentDir);
5221 if (!foundDirs.contains(currentDir))
5223 if (checkDir.exists())
5225 QByteArray cdir = currentDir.toLatin1();
5227 memset(&statbuf, 0,
sizeof(statbuf));
5231 if (
statfs(currentDir.toLocal8Bit().constData(), &statbuf) == 0)
5234 char *fstypename = statbuf.f_fstypename;
5235 if ((!strcmp(fstypename,
"nfs")) ||
5236 (!strcmp(fstypename,
"afpfs")) ||
5237 (!strcmp(fstypename,
"smbfs")))
5239 #elif defined(__linux__)
5240 long fstype = statbuf.f_type;
5241 if ((fstype == 0x6969) ||
5242 (fstype == 0x517B) ||
5243 (fstype == (
long)0xFF534D42))
5250 strlist << currentDir;
5251 strlist << localStr;
5254 strlist << QString::number(bSize);
5255 strlist << QString::number(totalKB);
5256 strlist << QString::number(usedKB);
5258 foundDirs[currentDir] =
true;
5261 foundDirs[currentDir] =
false;
5268 QMap <QString, bool> backendsCounted;
5269 std::list<PlaybackSock *> localPlaybackList;
5275 if ((
pbs->IsDisconnected()) ||
5276 (!
pbs->isMediaServer()) ||
5278 (backendsCounted.contains(
pbs->getHostname())))
5281 backendsCounted[
pbs->getHostname()] =
true;
5283 localPlaybackList.push_back(
pbs);
5284 allHostList +=
"," +
pbs->getHostname();
5289 for (
auto &
pbs : localPlaybackList) {
5290 pbs->GetDiskSpace(strlist);
5298 QList<FileSystemInfo> fsInfos;
5299 QStringList::const_iterator it = strlist.cbegin();
5300 while (it != strlist.cend())
5306 fsInfo.
setLocal((*(it++)).toInt() > 0);
5313 fsInfos.push_back(fsInfo);
5319 maxWriteFiveSec = std::max((int64_t)2048, maxWriteFiveSec);
5321 for (
auto it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
5323 if (it1->getFSysID() == -1)
5327 it1->getHostname().section(
".", 0, 0) +
":" + it1->getPath());
5330 for (
auto it2 = it1 + 1; it2 != fsInfos.end(); )
5334 int bSize = std::max(32, std::max(it1->getBlockSize(), it2->getBlockSize()) / 1024);
5335 int64_t diffSize = it1->getTotalSpace() - it2->getTotalSpace();
5336 int64_t diffUsed = it1->getUsedSpace() - it2->getUsedSpace();
5338 diffSize = 0 - diffSize;
5340 diffUsed = 0 - diffUsed;
5342 if (it2->getFSysID() == -1 && (diffSize <= bSize) &&
5343 (diffUsed <= maxWriteFiveSec))
5345 if (!it1->getHostname().contains(it2->getHostname()))
5346 it1->setHostname(it1->getHostname() +
"," + it2->getHostname());
5347 it1->setPath(it1->getPath() +
"," +
5348 it2->getHostname().section(
".", 0, 0) +
":" + it2->getPath());
5349 it2 = fsInfos.erase(it2);
5361 for (
const auto & fsInfo : qAsConst(fsInfos))
5363 strlist << fsInfo.getHostname();
5364 strlist << fsInfo.getPath();
5365 strlist << QString::number(static_cast<int>(fsInfo.isLocal()));
5366 strlist << QString::number(fsInfo.getFSysID());
5367 strlist << QString::number(fsInfo.getGroupID());
5368 strlist << QString::number(fsInfo.getBlockSize());
5369 strlist << QString::number(fsInfo.getTotalSpace());
5370 strlist << QString::number(fsInfo.getUsedSpace());
5372 totalKB += fsInfo.getTotalSpace();
5373 usedKB += fsInfo.getUsedSpace();
5378 strlist << allHostList;
5379 strlist <<
"TotalDiskSpace";
5384 strlist << QString::number(totalKB);
5385 strlist << QString::number(usedKB);
5400 QStringList strlist;
5407 QStringList::const_iterator it = strlist.cbegin();
5408 while (it != strlist.cend())
5412 fsInfo.
setLocal((*(it++)).toInt() > 0);
5420 fsInfos.push_back(fsInfo);
5423 LOG(VB_SCHEDULE | VB_FILE, LOG_DEBUG,
LOC +
5424 "Determining unique filesystems");
5427 maxWriteFiveSec = std::max((
size_t)2048, maxWriteFiveSec);
5431 QList<FileSystemInfo>::iterator it1;
5434 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5435 "--- GetFilesystemInfos directory list start ---");
5436 for (it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
5439 QString(
"Dir: %1:%2")
5440 .arg(it1->getHostname(), it1->getPath());
5441 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC + msg) ;
5442 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5443 QString(
" Location: %1")
5444 .arg(it1->isLocal() ?
"Local" :
"Remote"));
5445 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5446 QString(
" fsID : %1")
5447 .arg(it1->getFSysID()));
5448 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5449 QString(
" dirID : %1")
5450 .arg(it1->getGroupID()));
5451 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5452 QString(
" BlkSize : %1")
5453 .arg(it1->getBlockSize()));
5454 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5455 QString(
" TotalKB : %1")
5456 .arg(it1->getTotalSpace()));
5457 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5458 QString(
" UsedKB : %1")
5459 .arg(it1->getUsedSpace()));
5460 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5461 QString(
" FreeKB : %1")
5462 .arg(it1->getFreeSpace()));
5464 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5465 "--- GetFilesystemInfos directory list end ---");
5474 const QString &src,
const QString &dst)
5477 QStringList retlist;
5479 if (src.isEmpty() || dst.isEmpty()
5480 || src.contains(
"..") || dst.contains(
".."))
5482 LOG(VB_GENERAL, LOG_ERR,
LOC +
5483 QString(
"HandleMoveFile: ERROR moving file '%1' -> '%2', "
5484 "a path fails sanity checks").arg(src, dst));
5485 retlist <<
"0" <<
"Invalid path";
5490 QString srcAbs = sgroup.
FindFile(src);
5491 if (srcAbs.isEmpty())
5493 LOG(VB_GENERAL, LOG_ERR,
LOC +
5494 QString(
"HandleMoveFile: Unable to find %1").arg(src));
5495 retlist <<
"0" <<
"Source file not found";
5503 QString dstAbs = sgroup.
FindFile(dst);
5504 if (!dstAbs.isEmpty() && QFileInfo(dstAbs).isFile())
5506 LOG(VB_GENERAL, LOG_ERR,
LOC +
5507 QString(
"HandleMoveFile: Destination exists at %1").arg(dstAbs));
5508 retlist <<
"0" <<
"Destination file exists";
5514 int sgPathSize = srcAbs.size() - src.size();
5515 dstAbs = srcAbs.mid(0, sgPathSize) + dst;
5529 LOG(VB_FILE, LOG_INFO, QString(
"MainServer::RenameThread: Renaming %1 -> %2")
5532 QStringList retlist;
5533 QFileInfo fi(
m_dst);
5535 if (QDir().mkpath(fi.path()) && QFile::rename(
m_src,
m_dst))
5541 retlist <<
"0" <<
"Rename failed";
5542 LOG(VB_FILE, LOG_ERR,
"MainServer::DoRenameThread: Rename failed");
5575 QStringList retlist;
5581 LOG(VB_GENERAL, LOG_ERR,
LOC +
5582 QString(
"ERROR deleting file, filename '%1' "
5583 "fails sanity checks").arg(
filename));
5594 if (fullfile.isEmpty()) {
5595 LOG(VB_GENERAL, LOG_ERR,
LOC +
5596 QString(
"Unable to find %1 in HandleDeleteFile()") .arg(
filename));
5605 QFile checkFile(fullfile);
5612 const QFileInfo info(fullfile);
5616 if ((fd < 0) && checkFile.exists())
5618 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error deleting file: %1.")
5638 auto *truncateThread =
new TruncateThread(
this, fullfile, fd, size);
5639 truncateThread->run();
5650 const QString &starttime,
5655 pbssock =
pbs->getSocket();
5658 frm_dir_map_t::const_iterator it;
5660 QStringList retlist;
5663 const ProgramInfo pginfo(chanid.toUInt(), recstartdt);
5672 for (it = markMap.cbegin(); it != markMap.cend(); ++it)
5675 QString intstr = QString(
"%1").arg(*it);
5677 retlist << QString::number(it.key());
5682 retlist.prepend(QString(
"%1").arg(rowcnt));
5691 const QString &starttime,
5706 const QString &starttime,
5722 const QString &starttime,
5733 pbssock =
pbs->getSocket();
5737 chanid.toUInt(), recstartts);
5739 QStringList retlist;
5740 retlist << QString::number(bookmark);
5759 pbssock =
pbs->getSocket();
5761 QString chanid = tokens[1];
5762 QString starttime = tokens[2];
5763 long long bookmark = tokens[3].toLongLong();
5766 QStringList retlist;
5776 retlist <<
"FAILED";
5789 pbssock =
pbs->getSocket();
5791 const QString&
hostname = tokens[1];
5792 const QString& setting = tokens[2];
5793 QStringList retlist;
5797 retlist << retvalue;
5805 bool synchronous = (command[0] ==
"DOWNLOAD_FILE_NOW");
5806 const QString& srcURL = command[1];
5807 const QString& storageGroup = command[2];
5812 QStringList retlist;
5816 pbssock =
pbs->getSocket();
5820 QFileInfo finfo(srcURL);
5824 if (outDir.isEmpty())
5826 LOG(VB_GENERAL, LOG_ERR,
LOC +
5827 QString(
"Unable to determine directory "
5828 "to write to in %1 write command").arg(command[0]));
5829 retlist <<
"downloadfile_directory_not_found";
5838 LOG(VB_GENERAL, LOG_ERR,
LOC +
5839 QString(
"ERROR: %1 write filename '%2' does not pass "
5840 "sanity checks.") .arg(command[0],
filename));
5841 retlist <<
"downloadfile_filename_dangerous";
5882 pbssock =
pbs->getSocket();
5884 const QString&
hostname = tokens[1];
5885 const QString& setting = tokens[2];
5886 const QString& svalue = tokens[3];
5887 QStringList retlist;
5902 QStringList retlist;
5922 QStringList strlist;
5928 QString sql =
"SELECT DISTINCT hostname "
5929 "FROM storagegroup "
5930 "WHERE groupname = 'Music'";
5942 LOG(VB_GENERAL, LOG_INFO,
LOC +
5943 QString(
"HandleScanMusic: running filescanner on master BE '%1'").arg(
hostname));
5955 LOG(VB_GENERAL, LOG_INFO,
LOC +
5956 QString(
"HandleScanMusic: asking slave '%1' to run file scanner").arg(
hostname));
5962 LOG(VB_GENERAL, LOG_INFO,
LOC +
5963 QString(
"HandleScanMusic: Failed to grab slave socket on '%1'").arg(
hostname));
5972 LOG(VB_GENERAL, LOG_INFO,
LOC +
5973 QString(
"HandleScanMusic: running filescanner on slave BE '%1'")
5991 QStringList strlist;
5995 const QString&
hostname = slist[1];
6003 LOG(VB_GENERAL, LOG_INFO,
LOC +
6004 QString(
"HandleMusicTagUpdateVolatile: asking slave '%1' to update the metadata").arg(
hostname));
6014 LOG(VB_GENERAL, LOG_INFO,
LOC +
6015 QString(
"HandleMusicTagUpdateVolatile: Failed to grab slave socket on '%1'").arg(
hostname));
6017 strlist <<
"ERROR: slave not found";
6026 QStringList paramList;
6027 paramList.append(QString(
"--songid='%1'").arg(slist[2]));
6028 paramList.append(QString(
"--rating='%1'").arg(slist[3]));
6029 paramList.append(QString(
"--playcount='%1'").arg(slist[4]));
6030 paramList.append(QString(
"--lastplayed='%1'").arg(slist[5]));
6032 QString command =
GetAppBinDir() +
"mythutil --updatemeta " + paramList.join(
" ");
6034 LOG(VB_GENERAL, LOG_INFO,
LOC +
6035 QString(
"HandleMusicTagUpdateVolatile: running %1'").arg(command));
6051 QStringList strlist;
6055 const QString&
hostname = slist[1];
6063 LOG(VB_GENERAL, LOG_INFO,
LOC +
6064 QString(
"HandleMusicCalcTrackLen: asking slave '%1' to update the track length").arg(
hostname));
6074 LOG(VB_GENERAL, LOG_INFO,
LOC +
6075 QString(
"HandleMusicCalcTrackLen: Failed to grab slave socket on '%1'").arg(
hostname));
6077 strlist <<
"ERROR: slave not found";
6086 QStringList paramList;
6087 paramList.append(QString(
"--songid='%1'").arg(slist[2]));
6089 QString command =
GetAppBinDir() +
"mythutil --calctracklen " + paramList.join(
" ");
6091 LOG(VB_GENERAL, LOG_INFO,
LOC +
6092 QString(
"HandleMusicCalcTrackLen: running %1'").arg(command));
6109 QStringList strlist;
6113 const QString&
hostname = slist[1];
6121 LOG(VB_GENERAL, LOG_INFO,
LOC +
6122 QString(
"HandleMusicTagUpdateMetadata: asking slave '%1' "
6123 "to update the metadata").arg(
hostname));
6133 LOG(VB_GENERAL, LOG_INFO,
LOC +
6134 QString(
"HandleMusicTagUpdateMetadata: Failed to grab "
6135 "slave socket on '%1'").arg(
hostname));
6137 strlist <<
"ERROR: slave not found";
6146 int songID = slist[2].toInt();
6152 LOG(VB_GENERAL, LOG_ERR,
LOC +
6153 QString(
"HandleMusicTagUpdateMetadata: "
6154 "Cannot find metadata for trackid: %1")
6157 strlist <<
"ERROR: track not found";
6170 LOG(VB_GENERAL, LOG_ERR,
LOC +
6171 QString(
"HandleMusicTagUpdateMetadata: "
6172 "Failed to write to tag for trackid: %1")
6175 strlist <<
"ERROR: write to tag failed";
6195 QStringList strlist;
6199 const QString&
hostname = slist[1];
6207 LOG(VB_GENERAL, LOG_INFO,
LOC +
6208 QString(
"HandleMusicFindAlbumArt: asking slave '%1' "
6209 "to update the albumart").arg(
hostname));
6219 LOG(VB_GENERAL, LOG_INFO,
LOC +
6220 QString(
"HandleMusicFindAlbumArt: Failed to grab "
6221 "slave socket on '%1'").arg(
hostname));
6223 strlist <<
"ERROR: slave not found";
6232 int songID = slist[2].toInt();
6233 bool updateDatabase = (slist[3].toInt() == 1);
6239 LOG(VB_GENERAL, LOG_ERR,
LOC +
6240 QString(
"HandleMusicFindAlbumArt: "
6241 "Cannot find metadata for trackid: %1").arg(songID));
6243 strlist <<
"ERROR: track not found";