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>
63 #include "libmythbase/mythversion.h"
94 static constexpr std::chrono::milliseconds
PRT_TIMEOUT { 10ms };
98 #define LOC QString("MainServer: ")
99 #define LOC_WARN QString("MainServer, Warning: ")
100 #define LOC_ERR QString("MainServer, Error: ")
105 bool followLinks,
bool checkexists)
109 bool success1 =
true;
110 bool success2 =
true;
112 LOG(VB_FILE, LOG_INFO,
LOC +
113 QString(
"About to delete file: %1").arg(
filename));
117 if (finfo.isSymLink())
121 QFile target(linktext);
122 success1 = target.remove();
125 LOG(VB_GENERAL, LOG_ERR,
LOC +
126 QString(
"Error deleting '%1' -> '%2'")
131 if (!checkexists || checkFile.exists())
133 success2 = checkFile.remove();
136 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error deleting '%1': %2")
140 return success1 && success2;
205 QMutexLocker locker(&
m_lock);
215 m_wait.wait(locker.mutex(), left.count());
221 QMutexLocker locker(&
m_lock);
246 QMap<int, EncoderLink *> *_tvList,
248 m_encoderList(_tvList),
250 m_ismaster(master), m_threadPool(
"ProcessRequestPool"),
251 m_sched(
sched), m_expirer(_expirer)
273 bool v4IsSet = !config_v4.isNull();
278 bool v6IsSet = !config_v6.isNull();
280 if (v6IsSet && !listenAddrs.contains(config_v6))
281 LOG(VB_GENERAL, LOG_WARNING,
LOC +
282 "Unable to find IPv6 address to bind");
284 if (v4IsSet && !listenAddrs.contains(config_v4))
285 LOG(VB_GENERAL, LOG_WARNING,
LOC +
286 "Unable to find IPv4 address to bind");
288 if ((v4IsSet && !listenAddrs.contains(config_v4))
289 && (v6IsSet && !listenAddrs.contains(config_v6))
292 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to find either IPv4 or IPv6 "
293 "address we can bind to, exiting");
326 QList<FileSystemInfo> m_fsInfos;
443 auto *ms =
new MythSocket(socketDescriptor,
this);
444 if (ms->IsConnected())
456 QCoreApplication::processEvents();
464 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"No data on sock %1")
478 QStringList listline;
481 if (!
pbs->ReadStringList(listline) || listline.empty())
484 LOG(VB_GENERAL, LOG_INFO,
"No data in ProcessRequestWork()");
489 else if (!bIsControl)
496 LOG(VB_GENERAL, LOG_INFO,
LOC +
"No data in ProcessRequestWork()");
500 QString line = listline[0];
502 line = line.simplified();
503 QStringList tokens = line.split(
' ', Qt::SkipEmptyParts);
504 QString command = tokens[0];
506 if (command ==
"MYTH_PROTO_VERSION")
508 if (tokens.size() < 2)
514 if (command ==
"ANN")
519 if (command ==
"DONE")
530 LOG(VB_GENERAL, LOG_ERR,
LOC +
"ProcessRequest unknown socket");
536 if (command ==
"QUERY_FILETRANSFER")
538 if (tokens.size() != 2)
543 else if (command ==
"QUERY_RECORDINGS")
545 if (tokens.size() != 2)
550 else if (command ==
"QUERY_RECORDING")
554 else if (command ==
"GO_TO_SLEEP")
558 else if (command ==
"QUERY_FREE_SPACE")
562 else if (command ==
"QUERY_FREE_SPACE_LIST")
566 else if (command ==
"QUERY_FREE_SPACE_SUMMARY")
570 else if (command ==
"QUERY_LOAD")
574 else if (command ==
"QUERY_UPTIME")
578 else if (command ==
"QUERY_HOSTNAME")
582 else if (command ==
"QUERY_MEMSTATS")
586 else if (command ==
"QUERY_TIME_ZONE")
590 else if (command ==
"QUERY_CHECKFILE")
594 else if (command ==
"QUERY_FILE_EXISTS")
596 if (listline.size() < 2)
601 else if (command ==
"QUERY_FINDFILE")
603 if (listline.size() < 4)
608 else if (command ==
"QUERY_FILE_HASH")
610 if (listline.size() < 3)
615 else if (command ==
"QUERY_GUIDEDATATHROUGH")
619 else if (command ==
"DELETE_FILE")
621 if (listline.size() < 3)
626 else if (command ==
"MOVE_FILE")
628 if (listline.size() < 4)
633 else if (command ==
"STOP_RECORDING")
637 else if (command ==
"CHECK_RECORDING")
641 else if (command ==
"DELETE_RECORDING")
643 if (3 <= tokens.size() && tokens.size() <= 5)
645 bool force = (tokens.size() >= 4) && (tokens[3] ==
"FORCE");
646 bool forget = (tokens.size() >= 5) && (tokens[4] ==
"FORGET");
654 else if (command ==
"FORCE_DELETE_RECORDING")
658 else if (command ==
"UNDELETE_RECORDING")
662 else if (command ==
"ADD_CHILD_INPUT")
667 LOG(VB_GENERAL, LOG_ERR,
LOC +
668 "ADD_CHILD_INPUT command received in master context");
669 reslist << QString(
"ERROR: Called in master context");
671 else if (tokens.size() != 2)
673 reslist <<
"ERROR: Bad ADD_CHILD_INPUT request";
681 reslist << QString(
"ERROR: Failed to add child input");
685 else if (command ==
"RESCHEDULE_RECORDINGS")
687 listline.pop_front();
690 else if (command ==
"FORGET_RECORDING")
694 else if (command ==
"QUERY_GETALLPENDING")
696 if (tokens.size() == 1)
698 else if (tokens.size() == 2)
703 else if (command ==
"QUERY_GETALLSCHEDULED")
707 else if (command ==
"QUERY_GETCONFLICTING")
711 else if (command ==
"QUERY_GETEXPIRING")
715 else if (command ==
"QUERY_SG_GETFILELIST")
719 else if (command ==
"QUERY_SG_FILEQUERY")
723 else if (command ==
"GET_FREE_INPUT_INFO")
725 if (tokens.size() != 2)
730 else if (command ==
"QUERY_RECORDER")
732 if (tokens.size() != 2)
737 else if ((command ==
"QUERY_RECORDING_DEVICE") ||
738 (command ==
"QUERY_RECORDING_DEVICES"))
742 else if (command ==
"SET_NEXT_LIVETV_DIR")
744 if (tokens.size() != 3)
749 else if (command ==
"SET_CHANNEL_INFO")
753 else if (command ==
"QUERY_REMOTEENCODER")
755 if (tokens.size() != 2)
760 else if (command ==
"GET_RECORDER_FROM_NUM")
764 else if (command ==
"GET_RECORDER_NUM")
768 else if (command ==
"QUERY_GENPIXMAP2")
772 else if (command ==
"QUERY_PIXMAP_LASTMODIFIED")
776 else if (command ==
"QUERY_PIXMAP_GET_IF_MODIFIED")
780 else if (command ==
"QUERY_ISRECORDING")
784 else if (command ==
"MESSAGE")
786 if ((listline.size() >= 2) && (listline[1].startsWith(
"SET_VERBOSE")))
788 else if ((listline.size() >= 2) &&
789 (listline[1].startsWith(
"SET_LOG_LEVEL")))
794 else if (command ==
"FILL_PROGRAM_INFO")
798 else if (command ==
"LOCK_TUNER")
800 if (tokens.size() == 1)
802 else if (tokens.size() == 2)
807 else if (command ==
"FREE_TUNER")
809 if (tokens.size() != 2)
814 else if (command ==
"QUERY_ACTIVE_BACKENDS")
818 else if (command ==
"QUERY_IS_ACTIVE_BACKEND")
820 if (tokens.size() != 1)
825 else if (command ==
"QUERY_COMMBREAK")
827 if (tokens.size() != 3)
832 else if (command ==
"QUERY_CUTLIST")
834 if (tokens.size() != 3)
839 else if (command ==
"QUERY_BOOKMARK")
841 if (tokens.size() != 3)
846 else if (command ==
"SET_BOOKMARK")
848 if (tokens.size() != 4)
853 else if (command ==
"QUERY_SETTING")
855 if (tokens.size() != 3)
860 else if (command ==
"SET_SETTING")
862 if (tokens.size() != 4)
867 else if (command ==
"SCAN_VIDEOS")
871 else if (command ==
"SCAN_MUSIC")
875 else if (command ==
"MUSIC_TAG_UPDATE_VOLATILE")
877 if (listline.size() != 6)
882 else if (command ==
"MUSIC_CALC_TRACK_LENGTH")
884 if (listline.size() != 3)
889 else if (command ==
"MUSIC_TAG_UPDATE_METADATA")
891 if (listline.size() != 3)
896 else if (command ==
"MUSIC_FIND_ALBUMART")
898 if (listline.size() != 4)
903 else if (command ==
"MUSIC_TAG_GETIMAGE")
905 if (listline.size() < 4)
910 else if (command ==
"MUSIC_TAG_ADDIMAGE")
912 if (listline.size() < 5)
917 else if (command ==
"MUSIC_TAG_REMOVEIMAGE")
919 if (listline.size() < 4)
924 else if (command ==
"MUSIC_TAG_CHANGEIMAGE")
926 if (listline.size() < 5)
931 else if (command ==
"MUSIC_LYRICS_FIND")
933 if (listline.size() < 3)
938 else if (command ==
"MUSIC_LYRICS_GETGRABBERS")
942 else if (command ==
"MUSIC_LYRICS_SAVE")
944 if (listline.size() < 3)
949 else if (command ==
"IMAGE_SCAN")
952 QStringList reply = (listline.size() == 2)
954 : QStringList(
"ERROR") <<
"Bad: " << listline;
958 else if (command ==
"IMAGE_COPY")
961 QStringList reply = (listline.size() >= 2)
963 : QStringList(
"ERROR") <<
"Bad: " << listline;
967 else if (command ==
"IMAGE_MOVE")
970 QStringList reply = (listline.size() == 4)
972 HandleDbMove(listline[1], listline[2], listline[3])
973 : QStringList(
"ERROR") <<
"Bad: " << listline;
977 else if (command ==
"IMAGE_DELETE")
980 QStringList reply = (listline.size() == 2)
982 : QStringList(
"ERROR") <<
"Bad: " << listline;
986 else if (command ==
"IMAGE_HIDE")
989 QStringList reply = (listline.size() == 3)
991 HandleHide(listline[1].toInt() != 0, listline[2])
992 : QStringList(
"ERROR") <<
"Bad: " << listline;
996 else if (command ==
"IMAGE_TRANSFORM")
999 QStringList reply = (listline.size() == 3)
1001 HandleTransform(listline[1].toInt(), listline[2])
1002 : QStringList(
"ERROR") <<
"Bad: " << listline;
1006 else if (command ==
"IMAGE_RENAME")
1009 QStringList reply = (listline.size() == 3)
1011 : QStringList(
"ERROR") <<
"Bad: " << listline;
1015 else if (command ==
"IMAGE_CREATE_DIRS")
1018 QStringList reply = (listline.size() >= 4)
1020 HandleDirs(listline[1], listline[2].toInt() != 0, listline.mid(3))
1021 : QStringList(
"ERROR") <<
"Bad: " << listline;
1025 else if (command ==
"IMAGE_COVER")
1028 QStringList reply = (listline.size() == 3)
1030 HandleCover(listline[1].toInt(), listline[2].toInt())
1031 : QStringList(
"ERROR") <<
"Bad: " << listline;
1035 else if (command ==
"IMAGE_IGNORE")
1038 QStringList reply = (listline.size() == 2)
1040 : QStringList(
"ERROR") <<
"Bad: " << listline;
1044 else if (command ==
"ALLOW_SHUTDOWN")
1046 if (tokens.size() != 1)
1051 else if (command ==
"BLOCK_SHUTDOWN")
1053 if (tokens.size() != 1)
1058 else if (command ==
"SHUTDOWN_NOW")
1060 if (tokens.size() != 1)
1065 if (listline.size() >= 2)
1066 halt_cmd = listline[1];
1068 if (!halt_cmd.isEmpty())
1070 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
1071 "Going down now as of Mainserver request!");
1080 else if (command ==
"BACKEND_MESSAGE")
1082 const QString& message = listline[1];
1083 QStringList extra( listline[2] );
1084 for (
int i = 3; i < listline.size(); i++)
1085 extra << listline[i];
1089 else if ((command ==
"DOWNLOAD_FILE") ||
1090 (command ==
"DOWNLOAD_FILE_NOW"))
1092 if (listline.size() != 4)
1097 else if (command ==
"REFRESH_BACKEND")
1099 LOG(VB_GENERAL, LOG_INFO ,
LOC +
"Reloading backend settings");
1102 else if (command ==
"OK")
1104 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Got 'OK' out of sequence.");
1106 else if (command ==
"UNKNOWN_COMMAND")
1108 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Got 'UNKNOWN_COMMAND' out of sequence.");
1112 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unknown command: " + command);
1116 QStringList strlist;
1117 strlist <<
"UNKNOWN_COMMAND";
1130 QStringList broadcast;
1131 QSet<QString> receivers;
1149 auto *me =
dynamic_cast<MythEvent *
>(e);
1153 QString message = me->
Message();
1155 if ((message ==
"PREVIEW_SUCCESS" || message ==
"PREVIEW_QUEUED") &&
1156 me->ExtraDataCount() >= 5)
1159 uint recordingID = me->ExtraData(0).toUInt();
1160 const QString&
filename = me->ExtraData(1);
1161 const QString& msg = me->ExtraData(2);
1162 const QString& datetime = me->ExtraData(3);
1164 if (message ==
"PREVIEW_QUEUED")
1166 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1167 QString(
"Preview Queued: '%1' '%2'")
1173 ok = ok &&
file.open(QIODevice::ReadOnly);
1177 QByteArray data =
file.readAll();
1178 QStringList extra(
"OK");
1179 extra.push_back(QString::number(recordingID));
1180 extra.push_back(msg);
1181 extra.push_back(datetime);
1182 extra.push_back(QString::number(data.size()));
1183 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1184 quint16 checksum = qChecksum(data.constData(), data.size());
1186 quint16 checksum = qChecksum(data);
1188 extra.push_back(QString::number(checksum));
1189 extra.push_back(QString(data.toBase64()));
1191 for (
uint i = 4 ; i < (
uint) me->ExtraDataCount(); i++)
1193 const QString& token = me->ExtraData(i);
1194 extra.push_back(token);
1198 receivers.insert(*it);
1203 if (receivers.empty())
1205 LOG(VB_GENERAL, LOG_ERR,
LOC +
1206 "PREVIEW_SUCCESS but no receivers.");
1210 broadcast.push_back(
"BACKEND_MESSAGE");
1211 broadcast.push_back(
"GENERATED_PIXMAP");
1216 message =
"PREVIEW_FAILED";
1222 if (message ==
"PREVIEW_FAILED" && me->ExtraDataCount() >= 5)
1224 const QString& pginfokey = me->ExtraData(0);
1225 const QString& msg = me->ExtraData(2);
1227 QStringList extra(
"ERROR");
1228 extra.push_back(pginfokey);
1229 extra.push_back(msg);
1230 for (
uint i = 4 ; i < (
uint) me->ExtraDataCount(); i++)
1232 const QString& token = me->ExtraData(i);
1233 extra.push_back(token);
1237 receivers.insert(*it);
1242 if (receivers.empty())
1244 LOG(VB_GENERAL, LOG_ERR,
LOC +
1245 "PREVIEW_FAILED but no receivers.");
1249 broadcast.push_back(
"BACKEND_MESSAGE");
1250 broadcast.push_back(
"GENERATED_PIXMAP");
1254 if (me->Message().startsWith(
"AUTO_EXPIRE"))
1256 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1257 if (tokens.size() != 3)
1259 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad AUTO_EXPIRE message");
1283 QString msg = QString(
"Cannot find program info for '%1', "
1284 "while attempting to Auto-Expire.")
1285 .arg(me->Message());
1286 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
1292 if (me->Message().startsWith(
"QUERY_NEXT_LIVETV_DIR") &&
m_sched)
1294 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1295 if (tokens.size() != 2)
1297 LOG(VB_GENERAL, LOG_ERR,
LOC +
1298 QString(
"Bad %1 message").arg(tokens[0]));
1306 if (me->Message().startsWith(
"STOP_RECORDING"))
1308 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1309 if (tokens.size() < 3 || tokens.size() > 3)
1311 LOG(VB_GENERAL, LOG_ERR,
LOC +
1312 QString(
"Bad STOP_RECORDING message: %1")
1313 .arg(me->Message()));
1326 LOG(VB_GENERAL, LOG_ERR,
LOC +
1327 QString(
"Cannot find program info for '%1' while "
1328 "attempting to stop recording.").arg(me->Message()));
1334 if ((me->Message().startsWith(
"DELETE_RECORDING")) ||
1335 (me->Message().startsWith(
"FORCE_DELETE_RECORDING")))
1337 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1338 if (tokens.size() < 3 || tokens.size() > 5)
1340 LOG(VB_GENERAL, LOG_ERR,
LOC +
1341 QString(
"Bad %1 message").arg(tokens[0]));
1345 bool force = (tokens.size() >= 4) && (tokens[3] ==
"FORCE");
1346 bool forget = (tokens.size() >= 5) && (tokens[4] ==
"FORGET");
1353 if (tokens[0] ==
"FORCE_DELETE_RECORDING")
1360 LOG(VB_GENERAL, LOG_ERR,
LOC +
1361 QString(
"Cannot find program info for '%1' while "
1362 "attempting to delete.").arg(me->Message()));
1368 if (me->Message().startsWith(
"UNDELETE_RECORDING"))
1370 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1371 if (tokens.size() < 3 || tokens.size() > 3)
1373 LOG(VB_GENERAL, LOG_ERR,
LOC +
1374 QString(
"Bad UNDELETE_RECORDING message: %1")
1375 .arg(me->Message()));
1388 LOG(VB_GENERAL, LOG_ERR,
LOC +
1389 QString(
"Cannot find program info for '%1' while "
1390 "attempting to undelete.").arg(me->Message()));
1396 if (me->Message().startsWith(
"ADD_CHILD_INPUT"))
1398 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1401 LOG(VB_GENERAL, LOG_ERR,
LOC +
1402 "ADD_CHILD_INPUT event received in slave context");
1404 else if (tokens.size() != 2)
1406 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad ADD_CHILD_INPUT message");
1415 if (me->Message().startsWith(
"RESCHEDULE_RECORDINGS") &&
m_sched)
1417 const QStringList& request = me->ExtraDataList();
1422 if (me->Message().startsWith(
"SCHEDULER_ADD_RECORDING") &&
m_sched)
1427 LOG(VB_GENERAL, LOG_ERR,
LOC +
1428 "Bad SCHEDULER_ADD_RECORDING message");
1436 if (me->Message().startsWith(
"UPDATE_RECORDING_STATUS") &&
m_sched)
1438 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1439 if (tokens.size() != 6)
1441 LOG(VB_GENERAL, LOG_ERR,
LOC +
1442 "Bad UPDATE_RECORDING_STATUS message");
1446 uint cardid = tokens[1].toUInt();
1447 uint chanid = tokens[2].toUInt();
1452 recstatus, recendts);
1458 if (me->Message().startsWith(
"LIVETV_EXITED"))
1460 const QString& chainid = me->ExtraData();
1468 if (me->Message() ==
"CLEAR_SETTINGS_CACHE")
1471 if (me->Message().startsWith(
"RESET_IDLETIME") &&
m_sched)
1474 if (me->Message() ==
"LOCAL_RECONNECT_TO_MASTER")
1477 if (me->Message() ==
"LOCAL_SLAVE_BACKEND_ENCODERS_OFFLINE")
1480 if (me->Message().startsWith(
"LOCAL_"))
1483 if (me->Message() ==
"CREATE_THUMBNAILS")
1486 if (me->Message() ==
"IMAGE_GET_METADATA")
1489 std::unique_ptr<MythEvent> mod_me {
nullptr};
1490 if (me->Message().startsWith(
"MASTER_UPDATE_REC_INFO"))
1492 QStringList tokens = me->Message().simplified().split(
" ");
1493 uint recordedid = 0;
1494 if (tokens.size() >= 2)
1495 recordedid = tokens[1].toUInt();
1496 if (recordedid == 0)
1510 mod_me = std::make_unique<MythEvent>(
"RECORDING_LIST_CHANGE UPDATE", list);
1518 if (me->Message().startsWith(
"DOWNLOAD_FILE"))
1520 QStringList extraDataList = me->ExtraDataList();
1521 QString localFile = extraDataList[1];
1522 QFile
file(localFile);
1523 QStringList tokens = me->Message().simplified().split(
" ");
1531 if ((tokens.size() >= 2) && (tokens[1] ==
"FINISHED"))
1534 mod_me = std::make_unique<MythEvent>(me->Message(), extraDataList);
1537 if (broadcast.empty())
1539 broadcast.push_back(
"BACKEND_MESSAGE");
1540 if (mod_me !=
nullptr)
1542 broadcast.push_back(mod_me->Message());
1543 broadcast += mod_me->ExtraDataList();
1547 broadcast.push_back(me->Message());
1548 broadcast += me->ExtraDataList();
1553 if (!broadcast.empty())
1556 std::vector<PlaybackSock *> localPBSList;
1561 localPBSList.push_back(
pbs);
1565 bool sendGlobal =
false;
1566 if (
m_ismaster && broadcast[1].startsWith(
"GLOBAL_"))
1568 broadcast[1].replace(
"GLOBAL_",
"LOCAL_");
1569 MythEvent me(broadcast[1], broadcast[2]);
1575 QSet<PlaybackSock*> sentSet;
1577 bool isSystemEvent = broadcast[1].startsWith(
"SYSTEM_EVENT ");
1580 std::vector<PlaybackSock*>::const_iterator iter;
1581 for (iter = localPBSList.begin(); iter != localPBSList.end(); ++iter)
1585 if (sentSet.contains(
pbs) ||
pbs->IsDisconnected())
1588 if (!receivers.empty() && !receivers.contains(
pbs->getHostname()))
1591 sentSet.insert(
pbs);
1593 bool reallysendit =
false;
1595 if (broadcast[1] ==
"CLEAR_SETTINGS_CACHE")
1598 (
pbs->isSlaveBackend() ||
pbs->wantsEvents()))
1599 reallysendit =
true;
1601 else if (sendGlobal)
1603 if (
pbs->isSlaveBackend())
1604 reallysendit =
true;
1606 else if (
pbs->wantsEvents())
1608 reallysendit =
true;
1615 if (!
pbs->wantsSystemEvents())
1619 if (!
pbs->wantsOnlySystemEvents())
1621 if (sentSetSystemEvent.contains(
pbs->getHostname()))
1624 sentSetSystemEvent <<
pbs->getHostname();
1627 else if (
pbs->wantsOnlySystemEvents())
1639 for (iter = localPBSList.begin(); iter != localPBSList.end(); ++iter)
1657 QStringList retlist;
1658 const QString&
version = slist[1];
1659 if (
version != MYTH_PROTO_VERSION)
1661 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1662 "MainServer::HandleVersion - Client speaks protocol version " +
1663 version +
" but we speak " + MYTH_PROTO_VERSION +
'!');
1664 retlist <<
"REJECT" << MYTH_PROTO_VERSION;
1670 if (slist.size() < 3)
1672 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1673 "MainServer::HandleVersion - Client did not pass protocol "
1674 "token. Refusing connection!");
1675 retlist <<
"REJECT" << MYTH_PROTO_VERSION;
1681 const QString& token = slist[2];
1682 if (token != QString::fromUtf8(MYTH_PROTO_TOKEN))
1684 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1685 QString(
"MainServer::HandleVersion - Client sent incorrect "
1686 "protocol token \"%1\" for protocol version. Refusing "
1687 "connection!").arg(token));
1688 retlist <<
"REJECT" << MYTH_PROTO_VERSION;
1694 retlist <<
"ACCEPT" << MYTH_PROTO_VERSION;
1723 QStringList retlist(
"OK" );
1724 QStringList errlist(
"ERROR" );
1726 if (commands.size() < 3 || commands.size() > 6)
1729 if (commands.size() == 2)
1730 info = QString(
" %1").arg(commands[1]);
1732 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Received malformed ANN%1 query")
1735 errlist <<
"malformed_ann_query";
1743 if (
pbs->getSocket() == socket)
1745 LOG(VB_GENERAL, LOG_WARNING,
LOC +
1746 QString(
"Client %1 is trying to announce a socket "
1756 if (commands[1] ==
"Playback" || commands[1] ==
"Monitor" ||
1757 commands[1] ==
"Frontend")
1759 if (commands.size() < 4)
1761 LOG(VB_GENERAL, LOG_ERR,
LOC +
1762 QString(
"Received malformed ANN %1 query")
1765 errlist <<
"malformed_ann_query";
1782 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"MainServer::ANN %1")
1784 LOG(VB_GENERAL, LOG_INFO,
LOC +
1785 QString(
"adding: %1(%2) as a client (events: %3)")
1787 .arg(quintptr(socket),0,16)
1789 pbs->setBlockShutdown((commands[1] ==
"Playback") ||
1790 (commands[1] ==
"Frontend"));
1792 if (commands[1] ==
"Frontend")
1794 pbs->SetAsFrontend();
1796 frontend->m_name = commands[2];
1811 else if (commands[1] ==
"MediaServer")
1813 if (commands.size() < 3)
1815 LOG(VB_GENERAL, LOG_ERR,
LOC +
1816 "Received malformed ANN MediaServer query");
1817 errlist <<
"malformed_ann_query";
1826 pbs->setAsMediaServer();
1827 pbs->setBlockShutdown(
false);
1832 QString(
"CLIENT_CONNECTED HOSTNAME %1").arg(commands[2]));
1834 else if (commands[1] ==
"SlaveBackend")
1836 if (commands.size() < 4)
1838 LOG(VB_GENERAL, LOG_ERR,
LOC +
1839 QString(
"Received malformed ANN %1 query")
1841 errlist <<
"malformed_ann_query";
1853 LOG(VB_GENERAL, LOG_INFO,
LOC +
1854 QString(
"adding: %1 as a slave backend server")
1856 pbs->setAsSlaveBackend();
1857 pbs->setIP(commands[3]);
1862 QStringList::const_iterator sit = slist.
cbegin()+1;
1863 while (sit != slist.cend())
1866 if (!recinfo->GetChanID())
1876 bool wasAsleep =
true;
1880 if (elink->GetHostName() == commands[2])
1882 if (! (elink->IsWaking() || elink->IsAsleep()))
1884 elink->SetSocket(
pbs);
1892 QString message = QString(
"LOCAL_SLAVE_BACKEND_ONLINE %2")
1897 pbs->setBlockShutdown(
false);
1902 QString(
"SLAVE_CONNECTED HOSTNAME %1").arg(commands[2]));
1904 else if (commands[1] ==
"FileTransfer")
1906 if (slist.size() < 3)
1908 LOG(VB_GENERAL, LOG_ERR,
LOC +
1909 "Received malformed FileTransfer command");
1910 errlist <<
"malformed_filetransfer_command";
1915 LOG(VB_NETWORK, LOG_INFO,
LOC +
1916 "MainServer::HandleAnnounce FileTransfer");
1917 LOG(VB_NETWORK, LOG_INFO,
LOC +
1918 QString(
"adding: %1 as a remote file transfer") .arg(commands[2]));
1919 QStringList::const_iterator it = slist.cbegin();
1920 QString path = *(++it);
1921 QString wantgroup = *(++it);
1923 QStringList checkfiles;
1925 for (++it; it != slist.cend(); ++it)
1929 bool writemode =
false;
1930 bool usereadahead =
true;
1931 std::chrono::milliseconds timeout_ms = 2s;
1932 if (commands.size() > 3)
1933 writemode = (commands[3].toInt() != 0);
1935 if (commands.size() > 4)
1936 usereadahead = (commands[4].toInt() != 0);
1938 if (commands.size() > 5)
1939 timeout_ms = std::chrono::milliseconds(commands[5].toInt());
1943 if (wantgroup.isEmpty())
1944 wantgroup =
"Default";
1950 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to determine directory "
1951 "to write to in FileTransfer write command");
1952 errlist <<
"filetransfer_directory_not_found";
1959 LOG(VB_GENERAL, LOG_ERR,
LOC +
1960 QString(
"FileTransfer write filename is empty in path '%1'.")
1962 errlist <<
"filetransfer_filename_empty";
1967 if ((path.contains(
"/../")) ||
1968 (path.startsWith(
"../")))
1970 LOG(VB_GENERAL, LOG_ERR,
LOC +
1971 QString(
"FileTransfer write filename '%1' does not pass "
1972 "sanity checks.") .arg(path));
1973 errlist <<
"filetransfer_filename_dangerous";
1987 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Empty filename, cowardly aborting!");
1988 errlist <<
"filetransfer_filename_empty";
1997 LOG(VB_GENERAL, LOG_ERR,
LOC +
1998 QString(
"FileTransfer filename '%1' is actually a directory, "
1999 "cannot transfer.") .arg(
filename));
2000 errlist <<
"filetransfer_filename_is_a_directory";
2007 QString dirPath = finfo.absolutePath();
2011 if (!qdir.mkpath(dirPath))
2013 LOG(VB_GENERAL, LOG_ERR,
LOC +
2014 QString(
"FileTransfer filename '%1' is in a "
2015 "subdirectory which does not exist, and can "
2016 "not be created.") .arg(
filename));
2017 errlist <<
"filetransfer_unable_to_create_subdirectory";
2037 LOG(VB_GENERAL, LOG_ERR,
LOC +
2038 QString(
"Can't open %1").arg(
filename));
2039 errlist <<
"filetransfer_unable_to_open_file";
2046 LOG(VB_GENERAL, LOG_INFO,
LOC +
2047 QString(
"adding: %1(%2) as a file transfer")
2049 .arg(quintptr(socket),0,16));
2059 if (!checkfiles.empty())
2062 QDir dir = fi.absoluteDir();
2063 for (
const auto &
file : std::as_const(checkfiles))
2065 if (dir.exists(
file) &&
2066 ((
file).endsWith(
".srt") ||
2099 QStringList strList(
"ERROR");
2114 bool do_write =
false;
2129 LOG(VB_GENERAL, LOG_ERR,
LOC +
2130 "SendResponse: Unable to write to client socket, as it's no "
2146 QString playbackhost =
pbs->getHostname();
2148 QMap<QString,ProgramInfo*> recMap;
2153 QMap<QString,bool> isJobRunning =
2159 if ((
type ==
"Ascending") || (
type ==
"Play"))
2161 else if ((
type ==
"Descending") || (
type ==
"Delete"))
2166 destination, (
type ==
"Recording"),
2167 inUseMap, isJobRunning, recMap, sort);
2169 QMap<QString,ProgramInfo*>::iterator mit = recMap.begin();
2170 for (; mit != recMap.end(); mit = recMap.erase(mit))
2173 QStringList outputlist(QString::number(destination.
size()));
2174 QMap<QString, int> backendPortMap;
2178 for (
auto* proginfo : destination)
2189 proginfo->GetBasename()));
2190 if (!proginfo->GetFilesize())
2193 if (tmpURL.startsWith(
'/'))
2195 QFile checkFile(tmpURL);
2196 if (!tmpURL.isEmpty() && checkFile.exists())
2198 proginfo->SetFilesize(checkFile.size());
2199 if (proginfo->GetRecordingEndTime() <
2202 proginfo->SaveFilesize(proginfo->GetFilesize());
2211 if (proginfo->GetPathname().isEmpty())
2213 LOG(VB_GENERAL, LOG_ERR,
LOC +
2214 QString(
"HandleQueryRecordings() "
2215 "Couldn't find backend for:\n\t\t\t%1")
2218 proginfo->SetFilesize(0);
2219 proginfo->SetPathname(
"file not found");
2224 if (!proginfo->GetFilesize())
2228 LOG(VB_GENERAL, LOG_ERR,
LOC +
2229 "MainServer::HandleQueryRecordings()"
2230 "\n\t\t\tCould not fill program info "
2235 if (proginfo->GetRecordingEndTime() <
2238 proginfo->SaveFilesize(proginfo->GetFilesize());
2247 if (!backendPortMap.contains(
hostname))
2259 proginfo->ToStringList(outputlist);
2272 if (slist.size() < 3)
2274 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad QUERY_RECORDING query");
2279 QString command = slist[1].toUpper();
2282 if (command ==
"BASENAME")
2286 else if (command ==
"TIMESLOT")
2288 if (slist.size() < 4)
2290 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad QUERY_RECORDING query");
2295 pginfo =
new ProgramInfo(slist[2].toUInt(), recstartts);
2298 QStringList strlist;
2319 const QString& playbackhost = slist[1];
2321 QStringList::const_iterator it = slist.cbegin() + 2;
2336 const QFileInfo
info(lpath);
2340 QStringList strlist;
2351 m_ms->DoDeleteThread(
this);
2358 std::this_thread::sleep_for(3s + std::chrono::microseconds(
MythRandom(0, 2000)));
2363 QString logInfo = QString(
"recording id %1 (chanid %2 at %3)")
2368 QString name = QString(
"deleteThread%1%2").arg(getpid()).arg(
MythRandom());
2374 QString msg = QString(
"ERROR opening database connection for Delete "
2375 "Thread for chanid %1 recorded at %2. Program "
2376 "will NOT be deleted.")
2379 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2389 QString msg = QString(
"ERROR retrieving program info when trying to "
2390 "delete program for chanid %1 recorded at %2. "
2391 "Recording will NOT be deleted.")
2394 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2404 if ((!checkFile.exists()) && pginfo.
GetFilesize() &&
2407 LOG(VB_GENERAL, LOG_ERR,
LOC +
2408 QString(
"ERROR when trying to delete file: %1. File "
2409 "doesn't exist. Database metadata will not be removed.")
2427 bool errmsg =
false;
2442 if ((fd < 0) && checkFile.exists())
2448 std::this_thread::sleep_for(2s);
2449 if (checkFile.exists())
2455 LOG(VB_GENERAL, LOG_ERR,
LOC +
2456 QString(
"Error deleting file: %1. Keeping metadata in database.")
2471 QStringList nameFilters;
2472 nameFilters.push_back(fInfo.fileName() +
"*.png");
2473 nameFilters.push_back(fInfo.fileName() +
"*.jpg");
2474 nameFilters.push_back(fInfo.fileName() +
".tmp");
2475 nameFilters.push_back(fInfo.fileName() +
".old");
2476 nameFilters.push_back(fInfo.fileName() +
".map");
2477 nameFilters.push_back(fInfo.fileName() +
".tmp.map");
2478 nameFilters.push_back(fInfo.baseName() +
".srt");
2480 QDir dir (fInfo.path());
2481 QFileInfoList miscFiles = dir.entryInfoList(nameFilters);
2483 for (
const auto &
file : std::as_const(miscFiles))
2485 QString sFileName =
file.absoluteFilePath();
2497 if (slowDeletes && fd >= 0)
2503 QString logInfo = QString(
"recording id %1 filename %2")
2506 LOG(VB_GENERAL, LOG_NOTICE,
"DeleteRecordedFiles - " + logInfo);
2510 query.
prepare(
"SELECT basename, hostname, storagegroup FROM recordedfile "
2511 "WHERE recordedid = :RECORDEDID;");
2514 if (!query.
exec() || !query.
size())
2517 LOG(VB_GENERAL, LOG_ERR,
LOC +
2518 QString(
"Error querying recordedfiles for %1.") .arg(logInfo));
2521 while (query.
next())
2523 QString basename = query.
value(0).toString();
2526 bool deleteInDB =
false;
2528 if (basename == QFileInfo(ds->
m_filename).fileName())
2557 update.
prepare(
"DELETE FROM recordedfile "
2558 "WHERE recordedid = :RECORDEDID "
2559 "AND basename = :BASENAME ;");
2561 update.
bindValue(
":BASENAME", basename);
2565 LOG(VB_GENERAL, LOG_ERR,
LOC +
2566 QString(
"Error querying recordedfile (%1) for %2.")
2567 .arg(query.
value(1).toString(), logInfo));
2575 QString logInfo = QString(
"recording id %1 (chanid %2 at %3)")
2579 LOG(VB_GENERAL, LOG_NOTICE,
"DoDeleteINDB - " + logInfo);
2582 query.
prepare(
"DELETE FROM recorded WHERE recordedid = :RECORDEDID AND "
2587 if (!query.
exec() || !query.
size())
2590 LOG(VB_GENERAL, LOG_ERR,
LOC +
2591 QString(
"Error deleting recorded entry for %1.") .arg(logInfo));
2594 std::this_thread::sleep_for(1s);
2597 QString msg = QString(
"RECORDING_LIST_CHANGE DELETE %1")
2602 std::this_thread::sleep_for(3s);
2604 query.
prepare(
"DELETE FROM recordedmarkup "
2605 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
2612 LOG(VB_GENERAL, LOG_ERR,
LOC +
2613 QString(
"Error deleting recordedmarkup for %1.") .arg(logInfo));
2616 query.
prepare(
"DELETE FROM recordedseek "
2617 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
2624 LOG(VB_GENERAL, LOG_ERR,
LOC +
2625 QString(
"Error deleting recordedseek for %1.")
2640 bool deleteBrokenSymlinks)
2644 QString linktext =
"";
2645 QByteArray fname =
filename.toLocal8Bit();
2648 LOG(VB_FILE, LOG_INFO,
LOC +
2649 QString(
"About to unlink/delete file: '%1'")
2650 .arg(fname.constData()));
2652 QString errmsg = QString(
"Delete Error '%1'").arg(fname.constData());
2653 if (finfo.isSymLink())
2656 QByteArray alink = linktext.toLocal8Bit();
2657 errmsg += QString(
" -> '%2'").arg(alink.constData());
2660 if (followLinks && finfo.isSymLink())
2662 if (!finfo.exists() && deleteBrokenSymlinks)
2663 unlink(fname.constData());
2669 unlink(fname.constData());
2672 else if (!finfo.isSymLink())
2679 int err = unlink(fname.constData());
2684 if (fd < 0 && open_errno != EISDIR)
2685 LOG(VB_GENERAL, LOG_ERR,
LOC + errmsg +
ENO);
2701 QByteArray fname =
filename.toLocal8Bit();
2702 QString msg = QString(
"Error deleting '%1'").arg(fname.constData());
2703 int fd = open(fname.constData(), O_WRONLY);
2707 if (errno == EISDIR)
2712 LOG(VB_GENERAL, LOG_ERR, msg +
" could not delete directory " +
ENO);
2718 LOG(VB_GENERAL, LOG_ERR, msg +
" could not open " +
ENO);
2722 else if (unlink(fname.constData()))
2724 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
" could not unlink " +
ENO);
2754 query.
prepare(
"SELECT COUNT(cardid) FROM capturecard;");
2756 cards = query.
value(0).toInt();
2760 constexpr std::chrono::milliseconds sleep_time = 500ms;
2761 const size_t min_tps = 8LL * 1024 * 1024;
2762 const auto calc_tps = (size_t) (cards * 1.2 * (22200000LL / 8.0));
2763 const size_t tps = std::max(min_tps, calc_tps);
2764 const auto increment = (size_t) (tps * (sleep_time.count() * 0.001F));
2766 LOG(VB_FILE, LOG_INFO,
LOC +
2767 QString(
"Truncating '%1' by %2 MB every %3 milliseconds")
2769 .arg(increment / (1024.0 * 1024.0), 0,
'f', 2)
2770 .arg(sleep_time.count()));
2772 GetMythDB()->GetDBManager()->PurgeIdleConnections(
false);
2778 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Truncating '%1' to %2 MB")
2779 .arg(
filename).arg(fsize / (1024.0 * 1024.0), 0,
'f', 2));
2782 int err = ftruncate(fd, fsize);
2785 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error truncating '%1'")
2789 return 0 ==
close(fd);
2794 if (pginfo && ((count % 100) == 0))
2799 std::this_thread::sleep_for(sleep_time);
2802 bool ok = (0 ==
close(fd));
2807 LOG(VB_FILE, LOG_INFO,
LOC +
2808 QString(
"Finished truncating '%1'").arg(
filename));
2818 pbssock =
pbs->getSocket();
2820 QStringList::const_iterator it = slist.cbegin() + 1;
2842 result = iter.key();
2847 QStringList outputlist( QString::number(result) );
2854 QStringList::const_iterator it = slist.cbegin() + 1;
2866 bool hasConflicts =
false;
2868 for (
auto *pInfo : schedList)
2886 pbssock =
pbs->getSocket();
2909 (*m_encoderList)[num]->StopRecording();
2917 QStringList outputlist(
"0" );
2942 recnum = iter.key();
2949 std::this_thread::sleep_for(100us);
2965 QStringList outputlist( QString::number(recnum) );
2972 bool forceMetadataDelete,
2980 qDebug() <<
"HandleDeleteRecording(chanid, starttime) Empty Recording ID";
2987 pbssock =
pbs->getSocket();
2989 QStringList outputlist( QString::number(0) );
2999 bool forceMetadataDelete)
3001 QStringList::const_iterator it = slist.cbegin() + 1;
3006 qDebug() <<
"HandleDeleteRecording(QStringList) Empty Recording ID";
3015 bool forceMetadataDelete,
bool lexpirer,
bool forgetHistory)
3017 int resultCode = -1;
3020 pbssock =
pbs->getSocket();
3022 bool justexpire = lexpirer ?
false :
3030 LOG(VB_GENERAL, LOG_ERR,
LOC +
3031 QString(
"ERROR when trying to delete file for %1. Unable "
3032 "to determine filename of recording.")
3038 QStringList outputlist(QString::number(resultCode));
3048 if (justexpire && !forceMetadataDelete &&
3057 QStringList outputlist( QString::number(0) );
3081 QStringList outputlist( QString::number(num) );
3091 bool fileExists = checkFile.exists();
3094 QFile checkFileUTF8(QString::fromUtf8(
filename.toLatin1().constData()));
3095 fileExists = checkFileUTF8.exists();
3104 if (fileExists || !recinfo.
GetFilesize() || forceMetadataDelete)
3110 qDebug() <<
"DoHandleDeleteRecording() Empty Recording ID";
3117 forceMetadataDelete);
3118 deleteThread->start();
3123 QString logInfo = QString(
"chanid %1")
3127 LOG(VB_GENERAL, LOG_ERR,
LOC +
3128 QString(
"ERROR when trying to delete file: %1. File doesn't "
3129 "exist. Database metadata will not be removed.")
3136 QStringList outputlist( QString::number(resultCode) );
3148 if (fileExists || !recinfo.
GetFilesize() || forceMetadataDelete)
3151 QString(
"REC_DELETED CHANID %1 STARTTIME %2")
3161 if (slist.size() == 3)
3170 QStringList::const_iterator it = slist.cbegin()+1;
3184 pbssock =
pbs->getSocket();
3198 QStringList outputlist( QString::number(ret) );
3235 result = QStringList(QString::number(1));
3239 result = QStringList(QString::number(0));
3258 LOG(VB_GENERAL, LOG_INFO,
LOC +
"HandleAddChildInput: Already locked");
3262 LOG(VB_GENERAL, LOG_INFO,
LOC +
3263 QString(
"HandleAddChildInput: Handling input %1").arg(inputid));
3273 LOG(VB_GENERAL, LOG_ERR,
LOC +
3274 QString(
"HandleAddChildInput: "
3275 "Failed to add child to input %1").arg(inputid));
3281 LOG(VB_GENERAL, LOG_INFO,
LOC +
3282 QString(
"HandleAddChildInput: Added child input %1").arg(childid));
3290 auto *tv =
new TVRec(childid);
3291 if (!tv || !tv->Init())
3293 LOG(VB_GENERAL, LOG_ERR,
LOC +
3294 QString(
"HandleAddChildInput: "
3295 "Failed to initialize input %1").arg(childid));
3304 (*m_encoderList)[childid] = enc;
3311 LOG(VB_GENERAL, LOG_ERR,
LOC +
3312 QString(
"HandleAddChildInput: "
3313 "Failed to add remote input %1").arg(childid));
3323 (*m_encoderList)[childid] = enc;
3332 auto *tv =
new TVRec(inputid);
3333 if (!tv || !tv->Init())
3335 LOG(VB_GENERAL, LOG_ERR,
LOC +
3336 QString(
"HandleAddChildInput: "
3337 "Failed to initialize input %1").arg(inputid));
3345 (*m_encoderList)[inputid] = enc;
3351 LOG(VB_GENERAL, LOG_INFO,
LOC +
3352 QString(
"HandleAddChildInput: "
3353 "Successfully handled input %1").arg(inputid));
3360 QStringList::const_iterator it = slist.cbegin() + 1;
3367 pbssock =
pbs->getSocket();
3370 QStringList outputlist( QString::number(0) );
3382 QStringList strlist;
3385 if (!sleepCmd.isEmpty())
3389 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
3390 "Received GO_TO_SLEEP command from master, running SleepCommand.");
3395 strlist <<
"ERROR: SleepCommand is empty";
3396 LOG(VB_GENERAL, LOG_ERR,
LOC +
3397 "ERROR: in HandleGoToSleep(), but no SleepCommand found!");
3413 QStringList strlist;
3447 QStringList strlist;
3466 QStringList shortlist;
3467 if (strlist.size() < 4)
3469 shortlist << QString(
"0");
3470 shortlist << QString(
"0");
3474 unsigned int index = (
uint)(strlist.size()) - 2;
3475 shortlist << strlist[index++];
3476 shortlist << strlist[index++];
3492 QStringList strlist;
3494 #if defined(_WIN32) || defined(Q_OS_ANDROID)
3495 strlist <<
"0" <<
"0" <<
"0";
3501 strlist <<
"getloadavg() failed";
3505 strlist << QString::number(loads[0])
3506 << QString::number(loads[1])
3507 << QString::number(loads[2]);
3522 QStringList strlist;
3523 std::chrono::seconds uptime = 0s;
3526 strlist << QString::number(uptime.count());
3530 strlist <<
"Could not determine uptime.";
3544 QStringList strlist;
3559 QStringList strlist;
3565 if (
getMemStats(totalMB, freeMB, totalVM, freeVM))
3567 strlist << QString::number(totalMB) << QString::number(freeMB)
3568 << QString::number(totalVM) << QString::number(freeVM);
3573 strlist <<
"Could not determine memory stats.";
3587 QStringList strlist;
3602 bool checkSlaves = slist[1].toInt() != 0;
3604 QStringList::const_iterator it = slist.cbegin() + 2;
3620 QStringList outputlist( QString::number(
static_cast<int>(
exists)) );
3640 QStringList strlist( QString::number(
static_cast<int>(
exists)) );
3652 QString storageGroup =
"Default";
3657 switch (slist.size()) {
3659 if (!slist[3].isEmpty())
3663 if (slist[2].isEmpty())
3664 storageGroup = slist[2];
3672 LOG(VB_GENERAL, LOG_ERR,
LOC +
3673 QString(
"ERROR checking for file, filename '%1' "
3674 "fails sanity checks").arg(
filename));
3681 LOG(VB_GENERAL, LOG_ERR,
LOC +
3682 "ERROR, invalid input count for QUERY_FILE_HASH");
3719 const QString&
filename = slist[1];
3720 QString storageGroup =
"Default";
3721 QStringList retlist;
3723 if (slist.size() > 2)
3724 storageGroup = slist[2];
3730 LOG(VB_GENERAL, LOG_ERR,
LOC +
3731 QString(
"ERROR checking for file, filename '%1' "
3732 "fails sanity checks").arg(
filename));
3738 if (storageGroup.isEmpty())
3739 storageGroup =
"Default";
3745 if (!fullname.isEmpty())
3748 retlist << fullname;
3750 struct stat fileinfo {};
3751 if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
3753 retlist << QString::number(fileinfo.st_dev);
3754 retlist << QString::number(fileinfo.st_ino);
3755 retlist << QString::number(fileinfo.st_mode);
3756 retlist << QString::number(fileinfo.st_nlink);
3757 retlist << QString::number(fileinfo.st_uid);
3758 retlist << QString::number(fileinfo.st_gid);
3759 retlist << QString::number(fileinfo.st_rdev);
3760 retlist << QString::number(fileinfo.st_size);
3765 retlist << QString::number(fileinfo.st_blksize);
3766 retlist << QString::number(fileinfo.st_blocks);
3768 retlist << QString::number(fileinfo.st_atime);
3769 retlist << QString::number(fileinfo.st_mtime);
3770 retlist << QString::number(fileinfo.st_ctime);
3784 query.
prepare(
"SELECT MAX(endtime) FROM program WHERE manualid = 0;");
3794 QDateTime GuideDataThrough;
3796 QStringList strlist;
3800 if (GuideDataThrough.isNull())
3801 strlist << QString(
"0000-00-00 00:00");
3803 strlist << GuideDataThrough.toString(
"yyyy-MM-dd hh:mm");
3809 const QString& tmptable,
int recordid)
3813 QStringList strList;
3817 if (tmptable.isEmpty())
3829 query.
prepare(
"SELECT NULL FROM record "
3830 "WHERE recordid = :RECID;");
3836 record->m_recordID = recordid;
3837 if (record->Load() &&
3843 query.
prepare(
"DELETE FROM program WHERE manualid = :RECID;");
3853 strList << QString::number(0);
3854 strList << QString::number(0);
3864 QStringList strList;
3869 strList << QString::number(0);
3879 QStringList::const_iterator it = slist.cbegin() + 1;
3882 QStringList strlist;
3887 strlist << QString::number(0);
3896 QStringList strList;
3901 strList << QString::number(0);
3910 QStringList strList;
3912 if ((sList.size() < 4) || (sList.size() > 5))
3914 LOG(VB_GENERAL, LOG_ERR,
LOC +
3915 QString(
"HandleSGGetFileList: Invalid Request. %1")
3916 .arg(sList.join(
"[]:[]")));
3917 strList <<
"EMPTY LIST";
3923 const QString& wantHost = sList.at(1);
3924 QHostAddress wantHostaddr(wantHost);
3925 const QString& groupname = sList.at(2);
3926 const QString& path = sList.at(3);
3927 bool fileNamesOnly =
false;
3929 if (sList.size() >= 5)
3930 fileNamesOnly = (sList.at(4).toInt() != 0);
3932 bool slaveUnreachable =
false;
3934 LOG(VB_FILE, LOG_INFO,
LOC +
3935 QString(
"HandleSGGetFileList: group = %1 host = %2 "
3936 " path = %3 wanthost = %4")
3937 .arg(groupname, host, path, wantHost));
3941 if ((host.toLower() == wantHost.toLower()) ||
3942 (!addr.isEmpty() && addr == wantHostaddr.toString()))
3945 LOG(VB_FILE, LOG_INFO,
LOC +
"HandleSGGetFileList: Getting local info");
3956 LOG(VB_FILE, LOG_INFO,
LOC +
3957 "HandleSGGetFileList: Getting remote info");
3961 slaveUnreachable =
false;
3965 LOG(VB_FILE, LOG_INFO,
LOC +
3966 QString(
"HandleSGGetFileList: Failed to grab slave socket "
3967 ": %1 :").arg(wantHost));
3968 slaveUnreachable =
true;
3973 if (slaveUnreachable)
3974 strList <<
"SLAVE UNREACHABLE: " << host;
3976 if (strList.isEmpty() || (strList.at(0) ==
"0"))
3977 strList <<
"EMPTY LIST";
3987 QString storageGroup = slist[2];
3989 bool allowFallback =
true;
3990 bool useRegex =
false;
3991 QStringList fileList;
3993 if (!QHostAddress(
hostname).isNull())
3995 LOG(VB_GENERAL, LOG_ERR, QString(
"Mainserver: QUERY_FINDFILE called "
3996 "with IP (%1) instead of hostname. "
3997 "This is invalid.").arg(
hostname));
4003 if (storageGroup.isEmpty())
4004 storageGroup =
"Default";
4009 LOG(VB_GENERAL, LOG_ERR,
LOC +
4010 QString(
"ERROR QueryFindFile, filename '%1' "
4011 "fails sanity checks").arg(
filename));
4012 fileList <<
"ERROR: Bad/Missing Filename";
4017 if (slist.size() >= 5)
4018 useRegex = (slist[4].toInt() > 0);
4020 if (slist.size() >= 6)
4021 allowFallback = (slist[5].toInt() > 0);
4023 LOG(VB_FILE, LOG_INFO,
LOC +
4024 QString(
"Looking for file '%1' on host '%2' in group '%3' (useregex: %4, allowfallback: %5")
4038 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
4040 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Looking in dir '%1' for '%2'")
4041 .arg(fi.path(), fi.fileName()));
4043 for (
int x = 0; x < files.size(); x++)
4045 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Found '%1 - %2'").arg(x).arg(files[x]));
4048 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
4049 for (
const QString&
file : std::as_const(filteredFiles))
4053 fi.path() +
'/' +
file,
4069 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Checking remote host '%1' for file").arg(
hostname));
4077 if (!slaveFiles.isEmpty() && slaveFiles[0] !=
"NOT FOUND" && !slaveFiles[0].startsWith(
"ERROR: "))
4078 fileList += slaveFiles;
4084 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Slave '%1' was unreachable").arg(
hostname));
4085 fileList << QString(
"ERROR: SLAVE UNREACHABLE: %1").arg(
hostname);
4093 if (
m_ismaster && fileList.isEmpty() && allowFallback)
4098 QString sql =
"SELECT DISTINCT hostname "
4099 "FROM storagegroup "
4100 "WHERE groupname = :GROUP "
4101 "AND hostname != :HOSTNAME";
4103 query.
bindValue(
":GROUP", storageGroup);
4109 fileList <<
"ERROR: failed to get host list";
4125 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
4127 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Looking in dir '%1' for '%2'")
4128 .arg(fi.path(), fi.fileName()));
4130 for (
int x = 0; x < files.size(); x++)
4132 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Found '%1 - %2'").arg(x).arg(files[x]));
4135 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
4137 for (
const QString&
file : std::as_const(filteredFiles))
4141 fi.path() +
'/' +
file,
4148 if (!fname.isEmpty())
4163 if (!slaveFiles.isEmpty() && slaveFiles[0] !=
"NOT FOUND" && !slaveFiles[0].startsWith(
"ERROR: "))
4164 fileList += slaveFiles;
4170 if (!fileList.isEmpty())
4175 if (fileList.isEmpty())
4177 fileList <<
"NOT FOUND";
4178 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"File was not found"));
4182 for (
int x = 0; x < fileList.size(); x++)
4184 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"File %1 was found at: '%2'").arg(x).arg(fileList[0]));
4197 QStringList strList;
4199 if (sList.size() < 4)
4201 LOG(VB_GENERAL, LOG_ERR,
LOC +
4202 QString(
"HandleSGFileQuery: Invalid Request. %1")
4203 .arg(sList.join(
"[]:[]")));
4204 strList <<
"EMPTY LIST";
4210 const QString& wantHost = sList.at(1);
4211 QHostAddress wantHostaddr(wantHost);
4212 const QString& groupname = sList.at(2);
4213 const QString&
filename = sList.at(3);
4215 bool allowFallback =
true;
4216 if (sList.size() >= 5)
4217 allowFallback = (sList.at(4).toInt() > 0);
4218 LOG(VB_FILE, LOG_ERR, QString(
"HandleSGFileQuery - allowFallback: %1").arg(allowFallback));
4220 bool slaveUnreachable =
false;
4222 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"HandleSGFileQuery: %1")
4227 if ((host.toLower() == wantHost.toLower()) ||
4228 (!addr.isEmpty() && addr == wantHostaddr.toString()))
4230 LOG(VB_FILE, LOG_INFO,
LOC +
"HandleSGFileQuery: Getting local info");
4239 LOG(VB_FILE, LOG_INFO,
LOC +
4240 "HandleSGFileQuery: Getting remote info");
4243 slaveUnreachable =
false;
4247 LOG(VB_FILE, LOG_INFO,
LOC +
4248 QString(
"HandleSGFileQuery: Failed to grab slave socket : %1 :")
4250 slaveUnreachable =
true;
4255 if (slaveUnreachable)
4256 strList <<
"SLAVE UNREACHABLE: " << wantHost;
4258 if (strList.count() == 0 || (strList.at(0) ==
"0"))
4259 strList <<
"EMPTY LIST";
4267 QString pbshost =
pbs->getHostname();
4269 QStringList strlist;
4278 if ((cardid != -1) && (cardid != elink->GetInputID()))
4281 if (elink->IsLocal())
4284 enchost = elink->GetHostName();
4286 if ((enchost == pbshost) &&
4287 (elink->IsConnected()) &&
4288 (!elink->IsBusy()) &&
4289 (!elink->IsTunerLocked()))
4303 QString msg = QString(
"Cardid %1 LOCKed for external use on %2.")
4304 .arg(retval).arg(pbshost);
4305 LOG(VB_GENERAL, LOG_INFO,
LOC + msg);
4308 query.
prepare(
"SELECT videodevice, audiodevice, "
4311 "WHERE cardid = :CARDID ;");
4317 strlist << QString::number(retval)
4318 << query.
value(0).toString()
4319 << query.
value(1).toString()
4320 << query.
value(2).toString();
4328 LOG(VB_GENERAL, LOG_ERR,
LOC +
4329 "MainServer::LockTuner(): Could not find "
4330 "card info in database");
4335 strlist <<
"-2" <<
"" <<
"" <<
"";
4341 strlist <<
"-1" <<
"" <<
"" <<
"";
4348 QStringList strlist;
4355 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleFreeTuner() " +
4356 QString(
"Unknown encoder: %1").arg(cardid));
4357 strlist <<
"FAILED";
4364 QString msg = QString(
"Cardid %1 FREED from external use on %2.")
4365 .arg(cardid).arg(
pbs->getHostname());
4366 LOG(VB_GENERAL, LOG_INFO,
LOC + msg);
4386 uint excluded_input)
4388 LOG(VB_CHANNEL, LOG_INFO,
4389 LOC + QString(
"Excluding input %1")
4390 .arg(excluded_input));
4393 std::vector<InputInfo> busyinputs;
4394 std::vector<InputInfo> freeinputs;
4395 QMap<uint, QSet<uint> > groupids;
4403 info.m_inputId = elink->GetInputID();
4405 if (!elink->IsConnected() || elink->IsTunerLocked())
4407 LOG(VB_CHANNEL, LOG_INFO,
4408 LOC + QString(
"Input %1 is locked or not connected")
4409 .arg(
info.m_inputId));
4413 std::vector<uint> infogroups;
4415 for (
uint group : infogroups)
4416 groupids[
info.m_inputId].insert(group);
4419 if (
info.m_inputId != excluded_input && elink->IsBusy(&busyinfo))
4421 LOG(VB_CHANNEL, LOG_DEBUG,
4422 LOC + QString(
"Input %1 is busy on %2/%3")
4426 busyinputs.push_back(
info);
4428 else if (
info.m_liveTvOrder)
4430 LOG(VB_CHANNEL, LOG_DEBUG,
4431 LOC + QString(
"Input %1 is free")
4432 .arg(
info.m_inputId));
4433 freeinputs.push_back(
info);
4440 for (
auto & busyinfo : busyinputs)
4442 auto freeiter = freeinputs.begin();
4443 while (freeiter != freeinputs.end())
4447 if ((groupids[busyinfo.m_inputId] & groupids[freeinfo.
m_inputId])
4454 if (busyinfo.m_sourceId == freeinfo.
m_sourceId)
4456 LOG(VB_CHANNEL, LOG_DEBUG,
4457 LOC + QString(
"Input %1 is limited to %2/%3 by input %4")
4458 .arg(freeinfo.
m_inputId).arg(busyinfo.m_chanId)
4459 .arg(busyinfo.m_mplexId).arg(busyinfo.m_inputId));
4460 freeinfo.
m_chanId = busyinfo.m_chanId;
4461 freeinfo.
m_mplexId = busyinfo.m_mplexId;
4466 LOG(VB_CHANNEL, LOG_DEBUG,
4467 LOC + QString(
"Input %1 is unavailable by input %2")
4468 .arg(freeinfo.
m_inputId).arg(busyinfo.m_inputId));
4469 freeiter = freeinputs.erase(freeiter);
4475 QStringList strlist;
4476 for (
auto & input : freeinputs)
4478 LOG(VB_CHANNEL, LOG_INFO,
4479 LOC + QString(
"Input %1 is available on %2/%3")
4480 .arg(input.m_inputId).arg(input.m_chanId)
4481 .arg(input.m_mplexId));
4482 input.ToStringList(strlist);
4485 if (strlist.empty())
4510 if (commands.size() < 2 || slist.size() < 2)
4513 int recnum = commands[1].toInt();
4520 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleRecorderQuery() " +
4521 QString(
"Unknown encoder: %1").arg(recnum));
4522 QStringList retlist(
"bad" );
4528 const QString& command = slist[1];
4530 QStringList retlist;
4535 LOG(VB_GENERAL, LOG_ERR,
LOC +
" MainServer::HandleRecorderQuery() " +
4536 QString(
"Command %1 for unconnected encoder %2")
4537 .arg(command).arg(recnum));
4543 if (command ==
"IS_RECORDING")
4547 else if (command ==
"GET_FRAMERATE")
4551 else if (command ==
"GET_FRAMES_WRITTEN")
4555 else if (command ==
"GET_FILE_POSITION")
4559 else if (command ==
"GET_MAX_BITRATE")
4563 else if (command ==
"GET_CURRENT_RECORDING")
4568 info->ToStringList(retlist);
4578 else if (command ==
"GET_KEYFRAME_POS")
4580 long long desired = slist[2].toLongLong();
4583 else if (command ==
"FILL_POSITION_MAP")
4585 int64_t start = slist[2].toLongLong();
4586 int64_t end = slist[3].toLongLong();
4595 for (
auto it = map.cbegin(); it != map.cend(); ++it)
4597 retlist += QString::number(it.key());
4598 retlist += QString::number(*it);
4600 if (retlist.empty())
4604 else if (command ==
"FILL_DURATION_MAP")
4606 int64_t start = slist[2].toLongLong();
4607 int64_t end = slist[3].toLongLong();
4616 for (
auto it = map.cbegin(); it != map.cend(); ++it)
4618 retlist += QString::number(it.key());
4619 retlist += QString::number(*it);
4621 if (retlist.empty())
4625 else if (command ==
"GET_RECORDING")
4640 else if (command ==
"FRONTEND_READY")
4645 else if (command ==
"CANCEL_NEXT_RECORDING")
4647 const QString& cancel = slist[2];
4648 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
4649 QString(
"Received: CANCEL_NEXT_RECORDING %1").arg(cancel));
4653 else if (command ==
"SPAWN_LIVETV")
4655 const QString& chainid = slist[2];
4666 enc->
SpawnLiveTV(chain, slist[3].toInt() != 0, slist[4]);
4669 else if (command ==
"STOP_LIVETV")
4686 else if (command ==
"PAUSE")
4691 else if (command ==
"FINISH_RECORDING")
4696 else if (command ==
"SET_LIVE_RECORDING")
4698 int recording = slist[2].toInt();
4702 else if (command ==
"GET_INPUT")
4705 ret = (ret.isEmpty()) ?
"UNKNOWN" : ret;
4708 else if (command ==
"SET_INPUT")
4710 const QString& input = slist[2];
4711 QString ret = enc->
SetInput(input);
4712 ret = (ret.isEmpty()) ?
"UNKNOWN" : ret;
4715 else if (command ==
"TOGGLE_CHANNEL_FAVORITE")
4717 const QString& changroup = slist[2];
4721 else if (command ==
"CHANGE_CHANNEL")
4727 else if (command ==
"SET_CHANNEL")
4729 const QString& name = slist[2];
4733 else if (command ==
"SET_SIGNAL_MONITORING_RATE")
4735 auto rate = std::chrono::milliseconds(slist[2].toInt());
4736 int notifyFrontend = slist[3].toInt();
4738 retlist << QString::number(oldrate.count());
4740 else if (command ==
"GET_COLOUR")
4743 retlist << QString::number(ret);
4745 else if (command ==
"GET_CONTRAST")
4748 retlist << QString::number(ret);
4750 else if (command ==
"GET_BRIGHTNESS")
4753 retlist << QString::number(ret);
4755 else if (command ==
"GET_HUE")
4758 retlist << QString::number(ret);
4760 else if (command ==
"CHANGE_COLOUR")
4762 int type = slist[2].toInt();
4763 bool up = slist[3].toInt() != 0;
4766 retlist << QString::number(ret);
4768 else if (command ==
"CHANGE_CONTRAST")
4770 int type = slist[2].toInt();
4771 bool up = slist[3].toInt() != 0;
4774 retlist << QString::number(ret);
4776 else if (command ==
"CHANGE_BRIGHTNESS")
4778 int type= slist[2].toInt();
4779 bool up = slist[3].toInt() != 0;
4782 retlist << QString::number(ret);
4784 else if (command ==
"CHANGE_HUE")
4786 int type= slist[2].toInt();
4787 bool up = slist[3].toInt() != 0;
4790 retlist << QString::number(ret);
4792 else if (command ==
"CHECK_CHANNEL")
4794 const QString& name = slist[2];
4795 retlist << QString::number((
int)(enc->
CheckChannel(name)));
4797 else if (command ==
"SHOULD_SWITCH_CARD")
4799 const QString& chanid = slist[2];
4802 else if (command ==
"CHECK_CHANNEL_PREFIX")
4804 QString needed_spacer;
4805 const QString&
prefix = slist[2];
4806 uint complete_valid_channel_on_rec = 0;
4807 bool is_extra_char_useful =
false;
4810 prefix, complete_valid_channel_on_rec,
4811 is_extra_char_useful, needed_spacer);
4813 retlist << QString::number((
int)match);
4814 retlist << QString::number(complete_valid_channel_on_rec);
4815 retlist << QString::number((
int)is_extra_char_useful);
4816 retlist << ((needed_spacer.isEmpty()) ? QString(
"X") : needed_spacer);
4818 else if (command ==
"GET_NEXT_PROGRAM_INFO" && (slist.size() >= 6))
4820 QString channelname = slist[2];
4821 uint chanid = slist[3].toUInt();
4823 QString starttime = slist[5];
4826 QString subtitle =
"";
4828 QString category =
"";
4829 QString endtime =
"";
4830 QString callsign =
"";
4831 QString iconpath =
"";
4832 QString seriesid =
"";
4833 QString programid =
"";
4836 title, subtitle, desc, category, starttime,
4837 endtime, callsign, iconpath, channelname, chanid,
4838 seriesid, programid);
4849 retlist << QString::number(chanid);
4853 else if (command ==
"GET_CHANNEL_INFO")
4855 uint chanid = slist[2].toUInt();
4857 QString callsign =
"";
4858 QString channum =
"";
4859 QString channame =
"";
4863 callsign, channum, channame, xmltv);
4865 retlist << QString::number(chanid);
4866 retlist << QString::number(sourceid);
4874 LOG(VB_GENERAL, LOG_ERR,
LOC +
4875 QString(
"Unknown command: %1").arg(command));
4887 int recnum = commands[1].toInt();
4894 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleSetNextLiveTVDir() " +
4895 QString(
"Unknown encoder: %1").arg(recnum));
4896 QStringList retlist(
"bad" );
4905 QStringList retlist(
"OK" );
4913 uint chanid = slist[1].toUInt();
4914 uint sourceid = slist[2].toUInt();
4915 QString oldcnum =
cleanup(slist[3]);
4916 QString callsign =
cleanup(slist[4]);
4917 QString channum =
cleanup(slist[5]);
4918 QString channame =
cleanup(slist[6]);
4919 QString xmltv =
cleanup(slist[7]);
4921 QStringList retlist;
4922 if (!chanid || !sourceid)
4934 ok &= encoder->SetChannelInfo(chanid, sourceid, oldcnum,
4935 callsign, channum, channame, xmltv);
4940 retlist << ((ok) ?
"1" :
"0");
4949 int recnum = commands[1].toInt();
4950 QStringList retlist;
4957 LOG(VB_GENERAL, LOG_ERR,
LOC +
4958 QString(
"HandleRemoteEncoder(cmd %1) ").arg(slist[1]) +
4959 QString(
"Unknown encoder: %1").arg(recnum));
4968 const QString& command = slist[1];
4970 if (command ==
"GET_STATE")
4972 retlist << QString::number((
int)enc->
GetState());
4974 else if (command ==
"GET_SLEEPSTATUS")
4978 else if (command ==
"GET_FLAGS")
4980 retlist << QString::number(enc->
GetFlags());
4982 else if (command ==
"IS_BUSY")
4984 std::chrono::seconds time_buffer = 5s;
4985 if (slist.size() >= 3)
4986 time_buffer = std::chrono::seconds(slist[2].toInt());
4988 retlist << QString::number((
int)enc->
IsBusy(&busy_input, time_buffer));
4991 else if (command ==
"MATCHES_RECORDING" &&
4994 QStringList::const_iterator it = slist.cbegin() + 2;
4999 else if (command ==
"START_RECORDING" &&
5002 QStringList::const_iterator it = slist.cbegin() + 2;
5009 else if (command ==
"GET_RECORDING_STATUS")
5013 else if (command ==
"RECORD_PENDING" &&
5016 auto secsleft = std::chrono::seconds(slist[2].toInt());
5017 int haslater = slist[3].toInt();
5018 QStringList::const_iterator it = slist.cbegin() + 4;
5025 else if (command ==
"CANCEL_NEXT_RECORDING" &&
5026 (slist.size() >= 3))
5028 bool cancel = (
bool) slist[2].toInt();
5032 else if (command ==
"STOP_RECORDING")
5037 else if (command ==
"GET_MAX_BITRATE")
5041 else if (command ==
"GET_CURRENT_RECORDING")
5046 info->ToStringList(retlist);
5069 if (
pbs->isMediaServer())
5080 QStringList retlist;
5082 retlist.push_front(QString::number(retlist.size()));
5089 QStringList retlist;
5090 const QString& queryhostname = slist[1];
5095 if (slave !=
nullptr)
5115 QString fskey = fsInfo->getHostname() +
":" + fsInfo->getPath();
5125 size_t totalKBperMin = 0;
5130 if (!enc->IsConnected() || !enc->IsBusy())
5133 long long maxBitrate = enc->GetMaxBitrate();
5135 maxBitrate = 19500000LL;
5136 long long thisKBperMin = (((size_t)maxBitrate)*((size_t)15))>>11;
5137 totalKBperMin += thisKBperMin;
5138 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Cardid %1: max bitrate %2 KB/min")
5139 .arg(enc->GetInputID()).arg(thisKBperMin));
5143 LOG(VB_FILE, LOG_INFO,
LOC +
5144 QString(
"Maximal bitrate of busy encoders is %1 KB/min")
5145 .arg(totalKBperMin));
5147 return totalKBperMin;
5154 int64_t totalKB = -1;
5155 int64_t usedKB = -1;
5156 QMap <QString, bool>foundDirs;
5157 QString localStr =
"1";
5158 struct statfs statbuf {};
5160 groups.removeAll(
"LiveTV");
5161 QString specialGroups = groups.join(
"', '");
5162 QString sql = QString(
"SELECT MIN(id),dirname "
5163 "FROM storagegroup "
5164 "WHERE hostname = :HOSTNAME "
5165 "AND groupname NOT IN ( '%1' ) "
5166 "GROUP BY dirname;").arg(specialGroups);
5177 query.
prepare(
"SELECT MIN(id),dirname "
5178 "FROM storagegroup "
5179 "WHERE groupname = :GROUP "
5180 "GROUP BY dirname;");
5189 while (query.
next())
5191 dirID = query.
value(0).toString();
5195 currentDir = QString::fromUtf8(query.
value(1)
5196 .toByteArray().constData());
5197 if (currentDir.endsWith(
"/"))
5198 currentDir.remove(currentDir.length() - 1, 1);
5200 checkDir.setPath(currentDir);
5201 if (!foundDirs.contains(currentDir))
5203 if (checkDir.exists())
5205 QByteArray cdir = currentDir.toLatin1();
5207 memset(&statbuf, 0,
sizeof(statbuf));
5211 if (
statfs(currentDir.toLocal8Bit().constData(), &statbuf) == 0)
5214 char *fstypename = statbuf.f_fstypename;
5215 if ((!strcmp(fstypename,
"nfs")) ||
5216 (!strcmp(fstypename,
"afpfs")) ||
5217 (!strcmp(fstypename,
"smbfs")))
5219 #elif defined(__linux__)
5220 long fstype = statbuf.f_type;
5221 if ((fstype == 0x6969) ||
5222 (fstype == 0x517B) ||
5223 (fstype == (
long)0xFF534D42))
5230 strlist << currentDir;
5231 strlist << localStr;
5234 strlist << QString::number(bSize);
5235 strlist << QString::number(totalKB);
5236 strlist << QString::number(usedKB);
5238 foundDirs[currentDir] =
true;
5242 foundDirs[currentDir] =
false;
5250 QMap <QString, bool> backendsCounted;
5251 std::list<PlaybackSock *> localPlaybackList;
5257 if ((
pbs->IsDisconnected()) ||
5258 (!
pbs->isMediaServer()) ||
5260 (backendsCounted.contains(
pbs->getHostname())))
5263 backendsCounted[
pbs->getHostname()] =
true;
5265 localPlaybackList.push_back(
pbs);
5266 allHostList +=
"," +
pbs->getHostname();
5271 for (
auto &
pbs : localPlaybackList) {
5272 pbs->GetDiskSpace(strlist);
5280 QList<FileSystemInfo> fsInfos;
5281 QStringList::const_iterator it = strlist.cbegin();
5282 while (it != strlist.cend())
5288 fsInfo.
setLocal((*(it++)).toInt() > 0);
5295 fsInfos.push_back(fsInfo);
5301 maxWriteFiveSec = std::max((int64_t)2048, maxWriteFiveSec);
5303 for (
auto it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
5305 if (it1->getFSysID() == -1)
5309 it1->getHostname().section(
".", 0, 0) +
":" + it1->getPath());
5312 for (
auto it2 = it1 + 1; it2 != fsInfos.end(); )
5316 int bSize = std::max(32, std::max(it1->getBlockSize(), it2->getBlockSize()) / 1024);
5317 int64_t diffSize = it1->getTotalSpace() - it2->getTotalSpace();
5318 int64_t diffUsed = it1->getUsedSpace() - it2->getUsedSpace();
5320 diffSize = 0 - diffSize;
5322 diffUsed = 0 - diffUsed;
5324 if (it2->getFSysID() == -1 && (diffSize <= bSize) &&
5325 (diffUsed <= maxWriteFiveSec))
5327 if (!it1->getHostname().contains(it2->getHostname()))
5328 it1->setHostname(it1->getHostname() +
"," + it2->getHostname());
5329 it1->setPath(it1->getPath() +
"," +
5330 it2->getHostname().section(
".", 0, 0) +
":" + it2->getPath());
5331 it2 = fsInfos.erase(it2);
5343 for (
const auto & fsInfo : std::as_const(fsInfos))
5345 strlist << fsInfo.getHostname();
5346 strlist << fsInfo.getPath();
5347 strlist << QString::number(static_cast<int>(fsInfo.isLocal()));
5348 strlist << QString::number(fsInfo.getFSysID());
5349 strlist << QString::number(fsInfo.getGroupID());
5350 strlist << QString::number(fsInfo.getBlockSize());
5351 strlist << QString::number(fsInfo.getTotalSpace());
5352 strlist << QString::number(fsInfo.getUsedSpace());
5354 totalKB += fsInfo.getTotalSpace();
5355 usedKB += fsInfo.getUsedSpace();
5360 strlist << allHostList;
5361 strlist <<
"TotalDiskSpace";
5366 strlist << QString::number(totalKB);
5367 strlist << QString::number(usedKB);
5382 QStringList strlist;
5389 QStringList::const_iterator it = strlist.cbegin();
5390 while (it != strlist.cend())
5394 fsInfo.
setLocal((*(it++)).toInt() > 0);
5402 fsInfos.push_back(fsInfo);
5405 LOG(VB_SCHEDULE | VB_FILE, LOG_DEBUG,
LOC +
5406 "Determining unique filesystems");
5409 maxWriteFiveSec = std::max((
size_t)2048, maxWriteFiveSec);
5413 QList<FileSystemInfo>::iterator it1;
5416 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5417 "--- GetFilesystemInfos directory list start ---");
5418 for (it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
5421 QString(
"Dir: %1:%2")
5422 .arg(it1->getHostname(), it1->getPath());
5423 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC + msg) ;
5424 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5425 QString(
" Location: %1")
5426 .arg(it1->isLocal() ?
"Local" :
"Remote"));
5427 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5428 QString(
" fsID : %1")
5429 .arg(it1->getFSysID()));
5430 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5431 QString(
" dirID : %1")
5432 .arg(it1->getGroupID()));
5433 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5434 QString(
" BlkSize : %1")
5435 .arg(it1->getBlockSize()));
5436 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5437 QString(
" TotalKB : %1")
5438 .arg(it1->getTotalSpace()));
5439 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5440 QString(
" UsedKB : %1")
5441 .arg(it1->getUsedSpace()));
5442 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5443 QString(
" FreeKB : %1")
5444 .arg(it1->getFreeSpace()));
5446 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5447 "--- GetFilesystemInfos directory list end ---");
5456 const QString &src,
const QString &dst)
5459 QStringList retlist;
5461 if (src.isEmpty() || dst.isEmpty()
5462 || src.contains(
"..") || dst.contains(
".."))
5464 LOG(VB_GENERAL, LOG_ERR,
LOC +
5465 QString(
"HandleMoveFile: ERROR moving file '%1' -> '%2', "
5466 "a path fails sanity checks").arg(src, dst));
5467 retlist <<
"0" <<
"Invalid path";
5472 QString srcAbs = sgroup.
FindFile(src);
5473 if (srcAbs.isEmpty())
5475 LOG(VB_GENERAL, LOG_ERR,
LOC +
5476 QString(
"HandleMoveFile: Unable to find %1").arg(src));
5477 retlist <<
"0" <<
"Source file not found";
5485 QString dstAbs = sgroup.
FindFile(dst);
5486 if (!dstAbs.isEmpty() && QFileInfo(dstAbs).isFile())
5488 LOG(VB_GENERAL, LOG_ERR,
LOC +
5489 QString(
"HandleMoveFile: Destination exists at %1").arg(dstAbs));
5490 retlist <<
"0" <<
"Destination file exists";
5496 int sgPathSize = srcAbs.size() - src.size();
5497 dstAbs = srcAbs.mid(0, sgPathSize) + dst;
5511 LOG(VB_FILE, LOG_INFO, QString(
"MainServer::RenameThread: Renaming %1 -> %2")
5514 QStringList retlist;
5515 QFileInfo fi(
m_dst);
5517 if (QDir().mkpath(fi.path()) && QFile::rename(
m_src,
m_dst))
5523 retlist <<
"0" <<
"Rename failed";
5524 LOG(VB_FILE, LOG_ERR,
"MainServer::DoRenameThread: Rename failed");
5557 QStringList retlist;
5563 LOG(VB_GENERAL, LOG_ERR,
LOC +
5564 QString(
"ERROR deleting file, filename '%1' "
5565 "fails sanity checks").arg(
filename));
5576 if (fullfile.isEmpty()) {
5577 LOG(VB_GENERAL, LOG_ERR,
LOC +
5578 QString(
"Unable to find %1 in HandleDeleteFile()") .arg(
filename));
5587 QFile checkFile(fullfile);
5594 const QFileInfo
info(fullfile);
5598 if ((fd < 0) && checkFile.exists())
5600 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error deleting file: %1.")
5620 auto *truncateThread =
new TruncateThread(
this, fullfile, fd, size);
5621 truncateThread->run();
5632 const QString &starttime,
5637 pbssock =
pbs->getSocket();
5640 frm_dir_map_t::const_iterator it;
5642 QStringList retlist;
5645 const ProgramInfo pginfo(chanid.toUInt(), recstartdt);
5654 for (it = markMap.cbegin(); it != markMap.cend(); ++it)
5657 QString intstr = QString(
"%1").arg(*it);
5659 retlist << QString::number(it.key());
5664 retlist.prepend(QString(
"%1").arg(rowcnt));
5673 const QString &starttime,
5688 const QString &starttime,
5704 const QString &starttime,
5715 pbssock =
pbs->getSocket();
5719 chanid.toUInt(), recstartts);
5721 QStringList retlist;
5722 retlist << QString::number(bookmark);
5741 pbssock =
pbs->getSocket();
5743 const QString& chanid = tokens[1];
5744 const QString& starttime = tokens[2];
5745 long long bookmark = tokens[3].toLongLong();
5748 QStringList retlist;
5759 retlist <<
"FAILED";
5773 pbssock =
pbs->getSocket();
5775 const QString&
hostname = tokens[1];
5776 const QString& setting = tokens[2];
5777 QStringList retlist;
5781 retlist << retvalue;
5789 bool synchronous = (command[0] ==
"DOWNLOAD_FILE_NOW");
5790 const QString& srcURL = command[1];
5791 const QString& storageGroup = command[2];
5796 QStringList retlist;
5800 pbssock =
pbs->getSocket();
5804 QFileInfo finfo(srcURL);
5808 if (outDir.isEmpty())
5810 LOG(VB_GENERAL, LOG_ERR,
LOC +
5811 QString(
"Unable to determine directory "
5812 "to write to in %1 write command").arg(command[0]));
5813 retlist <<
"downloadfile_directory_not_found";
5822 LOG(VB_GENERAL, LOG_ERR,
LOC +
5823 QString(
"ERROR: %1 write filename '%2' does not pass "
5824 "sanity checks.") .arg(command[0],
filename));
5825 retlist <<
"downloadfile_filename_dangerous";
5868 pbssock =
pbs->getSocket();
5870 const QString&
hostname = tokens[1];
5871 const QString& setting = tokens[2];
5872 const QString& svalue = tokens[3];
5873 QStringList retlist;
5888 QStringList retlist;
5910 QStringList strlist;
5916 QString sql =
"SELECT DISTINCT hostname "
5917 "FROM storagegroup "
5918 "WHERE groupname = 'Music'";
5930 LOG(VB_GENERAL, LOG_INFO,
LOC +
5931 QString(
"HandleScanMusic: running filescanner on master BE '%1'").arg(
hostname));
5943 LOG(VB_GENERAL, LOG_INFO,
LOC +
5944 QString(
"HandleScanMusic: asking slave '%1' to run file scanner").arg(
hostname));
5950 LOG(VB_GENERAL, LOG_INFO,
LOC +
5951 QString(
"HandleScanMusic: Failed to grab slave socket on '%1'").arg(
hostname));
5960 LOG(VB_GENERAL, LOG_INFO,
LOC +
5961 QString(
"HandleScanMusic: running filescanner on slave BE '%1'")
5979 QStringList strlist;
5983 const QString&
hostname = slist[1];
5991 LOG(VB_GENERAL, LOG_INFO,
LOC +
5992 QString(
"HandleMusicTagUpdateVolatile: asking slave '%1' to update the metadata").arg(
hostname));
6002 LOG(VB_GENERAL, LOG_INFO,
LOC +
6003 QString(
"HandleMusicTagUpdateVolatile: Failed to grab slave socket on '%1'").arg(
hostname));
6005 strlist <<
"ERROR: slave not found";
6014 QStringList paramList;
6015 paramList.append(QString(
"--songid='%1'").arg(slist[2]));
6016 paramList.append(QString(
"--rating='%1'").arg(slist[3]));
6017 paramList.append(QString(
"--playcount='%1'").arg(slist[4]));
6018 paramList.append(QString(
"--lastplayed='%1'").arg(slist[5]));
6020 QString command =
GetAppBinDir() +
"mythutil --updatemeta " + paramList.join(
" ");
6022 LOG(VB_GENERAL, LOG_INFO,
LOC +
6023 QString(
"HandleMusicTagUpdateVolatile: running %1'").arg(command));
6039 QStringList strlist;
6043 const QString&
hostname = slist[1];
6051 LOG(VB_GENERAL, LOG_INFO,
LOC +
6052 QString(
"HandleMusicCalcTrackLen: asking slave '%1' to update the track length").arg(
hostname));
6062 LOG(VB_GENERAL, LOG_INFO,
LOC +
6063 QString(
"HandleMusicCalcTrackLen: Failed to grab slave socket on '%1'").arg(
hostname));
6065 strlist <<
"ERROR: slave not found";
6074 QStringList paramList;
6075 paramList.append(QString(
"--songid='%1'").arg(slist[2]));
6077 QString command =
GetAppBinDir() +
"mythutil --calctracklen " + paramList.join(
" ");
6079 LOG(VB_GENERAL, LOG_INFO,
LOC +
6080 QString(
"HandleMusicCalcTrackLen: running %1'").arg(command));
6097 QStringList strlist;
6101 const QString&
hostname = slist[1];
6109 LOG(VB_GENERAL, LOG_INFO,
LOC +
6110 QString(
"HandleMusicTagUpdateMetadata: asking slave '%1' "
6111 "to update the metadata").arg(
hostname));
6121 LOG(VB_GENERAL, LOG_INFO,
LOC +
6122 QString(
"HandleMusicTagUpdateMetadata: Failed to grab "
6123 "slave socket on '%1'").arg(
hostname));
6125 strlist <<
"ERROR: slave not found";
6134 int songID = slist[2].toInt();
6140 LOG(VB_GENERAL, LOG_ERR,
LOC +
6141 QString(
"HandleMusicTagUpdateMetadata: "
6142 "Cannot find metadata for trackid: %1")
6145 strlist <<
"ERROR: track not found";
6158 LOG(VB_GENERAL, LOG_ERR,
LOC +
6159 QString(
"HandleMusicTagUpdateMetadata: "
6160 "Failed to write to tag for trackid: %1")
6163 strlist <<
"ERROR: write to tag failed";
6183 QStringList strlist;
6187 const QString&
hostname = slist[1];
6195 LOG(VB_GENERAL, LOG_INFO,
LOC +
6196 QString(
"HandleMusicFindAlbumArt: asking slave '%1' "
6197 "to update the albumart").arg(
hostname));
6207 LOG(VB_GENERAL, LOG_INFO,
LOC +
6208 QString(
"HandleMusicFindAlbumArt: Failed to grab "
6209 "slave socket on '%1'").arg(
hostname));
6211 strlist <<
"ERROR: slave not found";
6220 int songID = slist[2].toInt();
6221 bool updateDatabase = (slist[3].toInt() == 1);
6227 LOG(VB_GENERAL, LOG_ERR,
LOC +
6228 QString(
"HandleMusicFindAlbumArt: "
6229 "Cannot find metadata for trackid: %1").arg(songID));
6231 strlist <<
"ERROR: track not found";
6241 QDir dir = fi.absoluteDir();
6244 "*.png;*.jpg;*.jpeg;*.gif;*.bmp");
6245 dir.setNameFilters(nameFilter.split(
";"));
6247 QStringList files = dir.entryList();
6252 fi.setFile(mdata->
Filename(
false));
6253 QString startDir = fi.path();
6255 for (
const QString&
file : std::as_const(files))
6259 image->m_filename = startDir +
'/' + fi.fileName();
6261 image->m_embedded =
false;
6263 image->m_description =
"";
6264 images->addImage(image);
6276 for (
int x = 0; x < artList.count(); x++)
6280 images->addImage(image);
6288 LOG(VB_GENERAL, LOG_ERR,
LOC +
6289 QString(
"HandleMusicFindAlbumArt: "
6290 "Failed to find a tagger for trackid: %1").arg(songID));
6295 images->dumpToDatabase();
6298 strlist.append(QString(
"%1").arg(images->getImageCount()));
6300 for (
uint x = 0; x < images->getImageCount(); x++)
6303 strlist.append(QString(
"%1").arg(image->
m_id));
6304 strlist.append(QString(
"%1").arg((
int)image->
m_imageType));
6305 strlist.append(QString(
"%1").arg(
static_cast<int>(image->
m_embedded)));
6313 QStringList paramList;
6314 paramList.append(QString(
"--songid='%1'").arg(mdata->
ID()));
6315 paramList.append(QString(
"--imagetype='%1'").arg(image->
m_imageType));
6317 QString command =
GetAppBinDir() +
"mythutil --extractimage " + paramList.join(
" ");
6335 QStringList strlist;
6339 const QString&
hostname = slist[1];
6340 const QString& songid = slist[2];
6341 const QString& imagetype = slist[3];
6349 LOG(VB_GENERAL, LOG_INFO,
LOC +
6350 QString(
"HandleMusicTagGetImage: asking slave '%1' to "
6351 "extract the image").arg(
hostname));
6361 LOG(VB_GENERAL, LOG_INFO,
LOC +
6362 QString(
"HandleMusicTagGetImage: Failed to grab slave "
6367 QStringList paramList;
6368 paramList.append(QString(
"--songid='%1'").arg(songid));
6369 paramList.append(QString(
"--imagetype='%1'").arg(imagetype));
6371 QString command =
GetAppBinDir() +
"mythutil --extractimage " + paramList.join(
" ");
6389 QStringList strlist;
6393 const QString&
hostname = slist[1];
6401 LOG(VB_GENERAL, LOG_INFO,
LOC +
6402 QString(
"HandleMusicTagChangeImage: asking slave '%1' "
6403 "to update the metadata").arg(
hostname));
6413 LOG(VB_GENERAL, LOG_INFO,
LOC +
6414 QString(
"HandleMusicTagChangeImage: Failed to grab "
6415 "slave socket on '%1'").arg(
hostname));
6417 strlist <<
"ERROR: slave not found";
6425 int songID = slist[2].toInt();
6426 auto oldType = (
ImageType)slist[3].toInt();
6427 auto newType = (
ImageType)slist[4].toInt();
6434 LOG(VB_GENERAL, LOG_ERR,
LOC +
6435 QString(
"HandleMusicTagChangeImage: "
6436 "Cannot find metadata for trackid: %1")
6439 strlist <<
"ERROR: track not found";
6480 LOG(VB_GENERAL, LOG_ERR,
"HandleMusicTagChangeImage: failed to change image type");
6482 strlist <<
"ERROR: failed to change image type";
6500 image->
m_filename = fi.path() + QString(
"/%1-%2.jpg")
6514 QStringList paramList;
6515 paramList.append(QString(
"--songid='%1'").arg(mdata->
ID()));
6516 paramList.append(QString(
"--imagetype='%1'").arg(image->
m_imageType));
6518 QString command =
GetAppBinDir() +
"mythutil --extractimage " + paramList.join(
" ");
6531 image->
m_filename = fi.absolutePath() + QString(
"/%1.jpg")
6556 QStringList strlist;
6560 const QString&
hostname = slist[1];
6568 LOG(VB_GENERAL, LOG_INFO,
LOC +
6569 QString(
"HandleMusicTagAddImage: asking slave '%1' "
6570 "to add the image").arg(
hostname));
6580 LOG(VB_GENERAL, LOG_INFO,
LOC +
6581 QString(
"HandleMusicTagAddImage: Failed to grab "
6582 "slave socket on '%1'").arg(
hostname));
6584 strlist <<
"ERROR: slave not found";
6593 int songID = slist[2].toInt();
6594 const QString&
filename = slist[3];
6595 auto imageType = (
ImageType) slist[4].toInt();
6601 LOG(VB_GENERAL, LOG_ERR,
LOC +
6602 QString(
"HandleMusicTagAddImage: Cannot find metadata for trackid: %1")
6605 strlist <<
"ERROR: track not found";
6617 LOG(VB_GENERAL, LOG_ERR,
LOC +
6618 "HandleMusicTagAddImage: failed to find a tagger for track");
6620 strlist <<
"ERROR: tagger not found";
6631 LOG(VB_GENERAL, LOG_ERR,
LOC +
6632 "HandleMusicTagAddImage: asked to write album art to the tag "
6633 "but the tagger doesn't support it!");
6635 strlist <<
"ERROR: embedded images not supported by tag";
6646 bool isDirectoryImage =
false;
6649 if (imageFilename.isEmpty())
6653 imageFilename = fi.absolutePath() +
'/' +
filename;
6654 isDirectoryImage =
true;
6659 LOG(VB_GENERAL, LOG_ERR,
LOC +
6660 QString(
"HandleMusicTagAddImage: cannot find image file %1").arg(
filename));
6662 strlist <<
"ERROR: failed to find image file";
6678 LOG(VB_GENERAL, LOG_ERR,
LOC +
"HandleMusicTagAddImage: failed to write album art to tag");
6680 strlist <<
"ERROR: failed to write album art to tag";
6685 if (!isDirectoryImage)
6686 QFile::remove(imageFilename);
6694 if (!isDirectoryImage)
6695 QFile::remove(imageFilename);
6711 QStringList strlist;
6715 const QString&
hostname = slist[1];
6723 LOG(VB_GENERAL, LOG_INFO,
LOC +
6724 QString(
"HandleMusicTagRemoveImage: asking slave '%1' "
6725 "to remove the image").arg(
hostname));
6735 LOG(VB_GENERAL, LOG_INFO,
LOC +
6736 QString(
"HandleMusicTagRemoveImage: Failed to grab "
6737 "slave socket on '%1'").arg(
hostname));
6739 strlist <<
"ERROR: slave not found";
6747 int songID = slist[2].toInt();
6748 int imageID = slist[3].toInt();
6755 LOG(VB_GENERAL, LOG_ERR,
LOC +
6756 QString(
"HandleMusicTagRemoveImage: Cannot find metadata for trackid: %1")
6759 strlist <<
"ERROR: track not found";
6771 LOG(VB_GENERAL, LOG_ERR,
LOC +
6772 "HandleMusicTagRemoveImage: failed to find a tagger for track");
6774 strlist <<
"ERROR: tagger not found";
6785 LOG(VB_GENERAL, LOG_ERR,
LOC +
"HandleMusicTagRemoveImage: asked to remove album art "
6786 "from the tag but the tagger doesn't support it!");
6788 strlist <<
"ERROR: embedded images not supported by tag";
6801 LOG(VB_GENERAL, LOG_ERR,
LOC +
6802 QString(
"HandleMusicTagRemoveImage: Cannot find image for imageid: %1")
6805 strlist <<
"ERROR: image not found";
6817 LOG(VB_GENERAL, LOG_ERR,
LOC +
"HandleMusicTagRemoveImage: failed to remove album art from tag");
6819 strlist <<
"ERROR: failed to remove album art from tag";
6838 QStringList strlist;
6842 const QString&
hostname = slist[1];
6843 const QString& songid = slist[2];
6844 const QString& grabberName = slist[3];
6845 QString artist =
"";
6849 if (slist.size() == 7)
6862 LOG(VB_GENERAL, LOG_INFO,
LOC +
6863 QString(
"HandleMusicFindLyrics: asking slave '%1' to "
6874 LOG(VB_GENERAL, LOG_INFO,
LOC +
6875 QString(
"HandleMusicFindLyrics: Failed to grab slave "
6880 QStringList paramList;
6881 paramList.append(QString(
"--songid='%1'").arg(songid));
6882 paramList.append(QString(
"--grabber='%1'").arg(grabberName));
6884 if (!artist.isEmpty())
6885 paramList.append(QString(
"--artist=\"%1\"").arg(artist));
6887 if (!album.isEmpty())
6888 paramList.append(QString(
"--album=\"%1\"").arg(album));
6890 if (!title.isEmpty())
6891 paramList.append(QString(
"--title=\"%1\"").arg(title));
6893 QString command =
GetAppBinDir() +
"mythutil --findlyrics " + paramList.join(
" ");
6927 QStringList strlist;
6931 QString scriptDir =
GetShareDir() +
"metadata/Music/lyrics";
6936 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find lyric scripts directory: %1").arg(scriptDir));
6937 strlist << QString(
"ERROR: Cannot find lyric scripts directory: %1").arg(scriptDir);
6945 d.setFilter(QDir::Files | QDir::NoDotAndDotDot);
6946 d.setNameFilters(QStringList(
"*.py"));
6947 QFileInfoList list =
d.entryInfoList();
6950 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find any lyric scripts in: %1").arg(scriptDir));
6951 strlist << QString(
"ERROR: Cannot find any lyric scripts in: %1").arg(scriptDir);
6959 QStringList scripts;
6960 for (
const auto & fi : std::as_const(list))
6962 LOG(VB_FILE, LOG_NOTICE, QString(
"Found lyric script at: %1").arg(fi.filePath()));
6963 scripts.append(fi.filePath());
6966 QStringList grabbers;
6969 for (
int x = 0; x < scripts.count(); x++)
6971 QStringList
args { scripts.at(x),
"-v" };
6973 p.start(PYTHON_EXE,
args);
6974 p.waitForFinished(-1);
6975 QString result =
p.readAllStandardOutput();
6977 QDomDocument domDoc;
6978 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
6981 int errorColumn = 0;
6983 if (!domDoc.setContent(result,
false, &errorMsg, &errorLine, &errorColumn))
6985 LOG(VB_GENERAL, LOG_ERR,
6986 QString(
"FindLyrics: Could not parse version from %1").arg(scripts.at(x)) +
6987 QString(
"\n\t\t\tError at line: %1 column: %2 msg: %3").arg(errorLine).arg(errorColumn).arg(errorMsg));
6991 auto parseResult = domDoc.setContent(result);
6994 LOG(VB_GENERAL, LOG_ERR,
6995 QString(
"FindLyrics: Could not parse version from %1")
6996 .arg(scripts.at(x)) +
6997 QString(
"\n\t\t\tError at line: %1 column: %2 msg: %3")
6998 .arg(parseResult.errorLine).arg(parseResult.errorColumn)
6999 .arg(parseResult.errorMessage));
7004 QDomNodeList itemList = domDoc.elementsByTagName(
"grabber");
7005 QDomNode itemNode = itemList.item(0);
7007 grabbers.append(itemNode.namedItem(QString(
"name")).toElement().text());
7014 for (
int x = 0; x < grabbers.count(); x++)
7015 strlist.append(grabbers.at(x));
7026 QStringList strlist;
7030 const QString&
hostname = slist[1];
7031 int songID = slist[2].toInt();
7039 LOG(VB_GENERAL, LOG_INFO,
LOC +
7040 QString(
"HandleMusicSaveLyrics: asking slave '%1' to "
7051 LOG(VB_GENERAL, LOG_INFO,
LOC +
7052 QString(
"HandleMusicSaveLyrics: Failed to grab slave "
7060 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find metadata for trackid: %1").arg(songID));
7061 strlist << QString(
"ERROR: Cannot find metadata for trackid: %1").arg(songID);
7069 QString lyricsFile =
GetConfDir() + QString(
"/MythMusic/Lyrics/%1.txt").arg(songID);
7073 QFile::remove(lyricsFile);
7076 QFile
file(QLatin1String(qPrintable(lyricsFile)));
7078 if (
file.open(QIODevice::WriteOnly))
7080 QTextStream stream(&
file);
7081 for (
int x = 3; x < slist.count(); x++)
7082 stream << slist.at(x);
7094 QStringList &commands,
7099 int recnum = commands[1].toInt();
7100 const QString& command = slist[1];
7102 QStringList retlist;
7108 if (command ==
"DONE")
7116 LOG(VB_GENERAL, LOG_ERR,
LOC +
7117 QString(
"Unknown file transfer socket: %1").arg(recnum));
7118 retlist << QString(
"ERROR: Unknown file transfer socket: %1")
7130 if (command ==
"REQUEST_BLOCK")
7132 int size = slist[2].toInt();
7136 else if (command ==
"WRITE_BLOCK")
7138 int size = slist[2].toInt();
7140 retlist << QString::number(ft->
WriteBlock(size));
7142 else if (command ==
"SEEK")
7144 long long pos = slist[2].toLongLong();
7145 int whence = slist[3].toInt();
7146 long long curpos = slist[4].toLongLong();
7148 long long ret = ft->
Seek(curpos, pos, whence);
7149 retlist << QString::number(ret);
7151 else if (command ==
"IS_OPEN")
7153 bool isopen = ft->
isOpen();
7155 retlist << QString::number(static_cast<int>(isopen));
7157 else if (command ==
"REOPEN")
7159 retlist << QString::number(static_cast<int>(ft->
ReOpen(slist[2])));
7161 else if (command ==
"DONE")
7166 else if (command ==
"SET_TIMEOUT")
7168 bool fast = slist[2].toInt() != 0;
7172 else if (command ==
"REQUEST_SIZE")
7180 LOG(VB_GENERAL, LOG_ERR,
LOC +
7181 QString(
"Unknown command: %1").arg(command));
7182 retlist <<
"ERROR" <<
"invalid_call";
7196 QStringList::const_iterator it = slist.cbegin() + 1;
7208 retval = iter.key();
7214 QStringList strlist( QString::number(retval) );
7231 strlist <<
"nohost";
7243 int recordernum = slist[1].toInt();
7245 QStringList strlist;
7268 strlist <<
"nohost";
7277 if (slist.size() < 2)
7282 const QString& message = slist[1];
7283 QStringList extra_data;
7284 for (
uint i = 2; i < (
uint) slist.size(); i++)
7285 extra_data.push_back(slist[i]);
7287 if (extra_data.empty())
7298 QStringList retlist(
"OK" );
7306 QStringList retlist;
7308 const QString& newverbose = slist[1];
7309 int len = newverbose.length();
7315 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
7316 QString(
"Verbose mask changed, new mask is: %1").arg(
verboseString));
7322 LOG(VB_GENERAL, LOG_ERR,
LOC +
7323 QString(
"Invalid SET_VERBOSE string: '%1'").arg(newverbose));
7324 retlist <<
"Failed";
7333 QStringList retlist;
7334 const QString& newstring = slist[1];
7335 LogLevel_t newlevel = LOG_UNKNOWN;
7337 int len = newstring.length();
7341 if (newlevel != LOG_UNKNOWN)
7345 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
7346 QString(
"Log level changed, new level is: %1")
7353 if (newlevel == LOG_UNKNOWN)
7355 LOG(VB_GENERAL, LOG_ERR,
LOC +
7356 QString(
"Invalid SET_VERBOSE string: '%1'").arg(newstring));
7357 retlist <<
"Failed";
7367 int RecordingsInProgress = 0;
7368 int LiveTVRecordingsInProgress = 0;
7369 QStringList retlist;
7374 if (elink->IsBusyRecording()) {
7375 RecordingsInProgress++;
7378 if (
info &&
info->GetRecordingGroup() ==
"LiveTV")
7379 LiveTVRecordingsInProgress++;
7386 retlist << QString::number(RecordingsInProgress);
7387 retlist << QString::number(LiveTVRecordingsInProgress);
7396 if (slist.size() < 3)
7398 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Too few params in pixmap request");
7399 QStringList outputlist(
"ERROR");
7400 outputlist +=
"TOO_FEW_PARAMS";
7405 bool time_fmt_sec =
true;
7406 std::chrono::seconds time = std::chrono::seconds::max();
7407 long long frame = -1;
7411 bool has_extra_data =
false;
7413 QString token = slist[1];
7414 if (token.isEmpty())
7416 LOG(VB_GENERAL, LOG_ERR,
LOC +
7417 "Failed to parse pixmap request. Token absent");
7418 QStringList outputlist(
"ERROR");
7419 outputlist +=
"TOKEN_ABSENT";
7424 QStringList::const_iterator it = slist.cbegin() + 2;
7425 QStringList::const_iterator end = slist.cend();
7430 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to parse pixmap request. "
7431 "ProgramInfo missing pathname");
7432 QStringList outputlist(
"BAD");
7433 outputlist +=
"NO_PATHNAME";
7437 if (token.toLower() ==
"do_not_care")
7439 token = QString(
"%1:%2")
7442 if (it != slist.cend())
7443 (time_fmt_sec = ((*it).toLower() ==
"s")), ++it;
7444 if (it != slist.cend())
7447 time = std::chrono::seconds((*it).toLongLong()), ++it;
7449 frame = (*it).toLongLong(), ++it;
7451 if (it != slist.cend())
7452 (outputfile = *it), ++it;
7453 outputfile = (outputfile ==
"<EMPTY>") ? QString() : outputfile;
7454 if (it != slist.cend())
7456 width = (*it).toInt(&ok); ++it;
7457 width = (ok) ? width : -1;
7459 if (it != slist.cend())
7461 height = (*it).toInt(&ok); ++it;
7462 height = (ok) ? height : -1;
7463 has_extra_data =
true;
7465 QSize outputsize = QSize(width, height);
7469 auto pos_text = (time != std::chrono::seconds::max())
7470 ? QString::number(time.count()) +
"s"
7471 : QString::number(frame) +
"f";
7472 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
7473 QString(
"HandleGenPreviewPixmap got extra data\n\t\t\t"
7476 .arg(width).arg(height).arg(outputfile));
7491 QStringList outputlist;
7494 if (time != std::chrono::seconds::max())
7497 token, &pginfo, time, -1, outputfile, outputsize);
7502 token, &pginfo, std::chrono::seconds::max(), frame, outputfile, outputsize);
7512 if (outputlist.empty() || outputlist[0] !=
"OK")
7518 LOG(VB_GENERAL, LOG_ERR,
LOC +
7519 QString(
"HandleGenPreviewPixmap() "
7520 "Couldn't find backend for:\n\t\t\t%1")
7526 LOG(VB_GENERAL, LOG_ERR,
LOC +
"HandleGenPreviewPixmap: Unable to "
7527 "find file locally, unable to make preview image.");
7528 QStringList outputlist(
"ERROR" );
7529 outputlist +=
"FILE_INACCESSIBLE";
7537 if (time != std::chrono::seconds::max()) {
7539 pginfo, outputsize, outputfile, time, -1, token);
7542 pginfo, outputsize, outputfile, -1s, frame, token);
7550 QStringList outputlist(
"OK");
7551 if (!outputfile.isEmpty())
7552 outputlist += outputfile;
7560 QStringList::const_iterator it = slist.cbegin() + 1;
7565 QStringList strlist;
7578 strlist = (slavetime.isValid()) ?
7579 QStringList(QString::number(slavetime.toSecsSinceEpoch())) :
7586 LOG(VB_GENERAL, LOG_ERR,
LOC +
7587 QString(
"HandlePixmapLastModified() "
7588 "Couldn't find backend for:\n\t\t\t%1")
7594 LOG(VB_GENERAL, LOG_ERR,
LOC +
7595 "MainServer: HandlePixmapLastModified: Unable to "
7596 "find file locally, unable to get last modified date.");
7597 QStringList outputlist(
"BAD" );
7608 QDateTime lastmodified = finfo.lastModified();
7609 if (lastmodified.isValid())
7610 strlist = QStringList(QString::number(lastmodified.toSecsSinceEpoch()));
7612 strlist = QStringList(QString::number(UINT_MAX));
7616 strlist = QStringList(
"BAD" );
7625 QStringList strlist;
7630 strlist = QStringList(
"ERROR");
7631 strlist +=
"1: Parameter list too short";
7636 QDateTime cachemodified;
7637 if (!slist[1].isEmpty() && (slist[1].toInt() != -1))
7642 int max_file_size = slist[2].toInt();
7644 QStringList::const_iterator it = slist.begin() + 3;
7649 strlist = QStringList(
"ERROR");
7650 strlist +=
"2: Invalid ProgramInfo";
7661 size_t fsize = finfo.size();
7662 QDateTime lastmodified = finfo.lastModified();
7663 bool out_of_date = !cachemodified.isValid() ||
7664 (lastmodified > cachemodified);
7666 if (out_of_date && (fsize > 0) && ((ssize_t)fsize < max_file_size))
7670 bool open_ok =
file.open(QIODevice::ReadOnly);
7672 data =
file.readAll();
7674 if (!data.isEmpty())
7676 LOG(VB_FILE, LOG_INFO,
LOC +
7677 QString(
"Read preview file '%1'")
7679 if (lastmodified.isValid())
7680 strlist += QString::number(lastmodified.toSecsSinceEpoch());
7682 strlist += QString::number(UINT_MAX);
7683 strlist += QString::number(data.size());
7684 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
7685 quint16 checksum = qChecksum(data.constData(), data.size());
7687 quint16 checksum = qChecksum(data);
7689 strlist += QString::number(checksum);
7690 strlist += QString(data.toBase64());
7694 LOG(VB_GENERAL, LOG_ERR,
LOC +
7695 QString(
"Failed to read preview file '%1'")
7698 strlist = QStringList(
"ERROR");
7700 QString(
"3: Failed to read preview file '%1'%2")
7702 (open_ok) ?
"" :
" open failed");
7705 else if (out_of_date && (max_file_size > 0))
7707 if (fsize >= (
size_t) max_file_size)
7709 strlist = QStringList(
"WARNING");
7710 strlist += QString(
"1: Preview file too big %1 > %2")
7711 .arg(fsize).arg(max_file_size);
7715 strlist = QStringList(
"ERROR");
7716 strlist +=
"4: Preview file is invalid";
7721 if (lastmodified.isValid())
7722 strlist += QString::number(lastmodified.toSecsSinceEpoch());
7724 strlist += QString::number(UINT_MAX);
7738 strlist = QStringList(
"ERROR");
7740 "5: Could not locate mythbackend that made this recording";
7749 if (!strlist.empty())
7756 strlist = QStringList(
"WARNING");
7757 strlist +=
"2: Could not locate requested file";
7763 QStringList retlist(
"OK" );
7769 pbs->setBlockShutdown(blockShutdown);
7772 QStringList retlist(
"OK" );
7817 QList<uint> disconnectedSlaves;
7829 MythEvent me(
"LOCAL_RECONNECT_TO_MASTER");
7835 disconnectedSlaves.clear();
7836 bool needsReschedule =
false;
7840 LOG(VB_GENERAL, LOG_ERR,
LOC +
7841 QString(
"Slave backend: %1 no longer connected")
7842 .arg(
pbs->getHostname()));
7844 bool isFallingAsleep =
true;
7848 if (elink->GetSocket() ==
pbs)
7850 if (!elink->IsFallingAsleep())
7851 isFallingAsleep =
false;
7853 elink->SetSocket(
nullptr);
7855 disconnectedSlaves.push_back(elink->GetInputID());
7859 if (
m_sched && !isFallingAsleep)
7860 needsReschedule =
true;
7862 QString message = QString(
"LOCAL_SLAVE_BACKEND_OFFLINE %1")
7863 .arg(
pbs->getHostname());
7871 QString(
"SLAVE_DISCONNECTED HOSTNAME %1")
7872 .arg(
pbs->getHostname()));
7881 if (chain !=
nullptr)
7892 std::this_thread::sleep_for(500us);
7894 if (enc->IsBusy() &&
7895 enc->GetChainID() == chain->
GetID())
7906 LOG(VB_GENERAL, LOG_INFO, QString(
"%1 sock(%2) '%3' disconnected")
7907 .arg(
pbs->getBlockShutdown() ?
"Playback" :
"Monitor")
7908 .arg(quintptr(socket),0,16)
7909 .arg(
pbs->getHostname()) );
7910 pbs->SetDisconnected();
7915 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Playback sock still exists?");
7923 if (!disconnectedSlaves.isEmpty())
7932 MythEvent me(
"LOCAL_CONNECTION_CLOSED");
7946 LOG(VB_GENERAL, LOG_INFO, QString(
"BEFileTransfer sock(%1) disconnected")
7947 .arg(quintptr(socket),0,16) );
7959 LOG(VB_GENERAL, LOG_INFO, QString(
"Control sock(%1) disconnected")
7960 .arg(quintptr(socket),0,16) );
7970 LOG(VB_GENERAL, LOG_WARNING,
LOC +
7971 QString(
"Unknown socket closing MythSocket(0x%1)")
7972 .arg((intptr_t)socket,0,16));
7985 if (
pbs->isSlaveBackend() &&
8008 if (
pbs->isMediaServer() &&
8024 { return sock == pbs->getSocket(); });
8032 if (
id == ft->getSocket()->GetSocketDescriptor())
8041 if (sock == ft->getSocket())
8051 if (chain->GetID() ==
id)
8061 if (chain->IsHostSocket(sock))
8071 if (chain->ProgramIsAt(pginfo) >= 0)
8091 std::vector<LiveTVChain*> newChains;
8096 newChains.push_back(entry);
8106 if (closeApplication)
8112 QString lpath = QString(path);
8114 if (lpath.section(
'/', -2, -2) ==
"channels")
8117 QString
file = lpath.section(
'/', -1);
8121 query.
prepare(
"SELECT icon FROM channel "
8122 "WHERE deleted IS NULL AND icon LIKE :FILENAME ;");
8127 lpath = query.
value(0).toString();
8136 lpath = lpath.section(
'/', -1);
8138 QString fpath = lpath;
8139 if (fpath.endsWith(
".png"))
8140 fpath = fpath.left(fpath.length() - 4);
8146 if (pburl.startsWith(
"/"))
8148 lpath = pburl.section(
'/', 0, -2) +
"/" + lpath;
8149 LOG(VB_FILE, LOG_INFO,
LOC +
8150 QString(
"Local file path: %1").arg(lpath));
8154 LOG(VB_GENERAL, LOG_ERR,
LOC +
8155 QString(
"ERROR: LocalFilePath unable to find local "
8156 "path for '%1', found '%2' instead.")
8157 .arg(lpath, pburl));
8161 else if (!lpath.isEmpty())
8164 QString opath = lpath;
8167 if (!wantgroup.isEmpty())
8169 sgroup.
Init(wantgroup);
8170 lpath = QString(path);
8174 lpath = QFileInfo(lpath).fileName();
8177 QString tmpFile = sgroup.
FindFile(lpath);
8178 if (!tmpFile.isEmpty())
8181 LOG(VB_FILE, LOG_INFO,
LOC +
8182 QString(
"LocalFilePath(%1 '%2'), found file through "
8183 "exhaustive search at '%3'")
8184 .arg(path, opath, lpath));
8188 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"ERROR: LocalFilePath "
8189 "unable to find local path for '%1'.") .arg(path));
8205 auto *masterServerSock =
new MythSocket(-1,
this);
8210 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
8211 QString(
"Connecting to master server: %1:%2")
8212 .arg(server).arg(port));
8214 if (!masterServerSock->ConnectToHost(server, port))
8216 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
8217 "Connection to master server timed out.");
8219 masterServerSock->DecrRef();
8223 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Connected successfully");
8225 QString str = QString(
"ANN SlaveBackend %1 %2")
8229 QStringList strlist( str );
8234 elink->CancelNextRecording(
true);
8252 masterServerSock->SetReadyReadCallbackEnabled(
false);
8253 if (!masterServerSock->SendReceiveStringList(strlist, 1) ||
8254 (strlist[0] ==
"ERROR"))
8256 masterServerSock->DecrRef();
8257 masterServerSock =
nullptr;
8258 if (strlist.empty())
8260 LOG(VB_GENERAL, LOG_ERR,
LOC +
8261 "Failed to open master server socket, timeout");
8265 LOG(VB_GENERAL, LOG_ERR,
LOC +
8266 "Failed to open master server socket" +
8267 ((strlist.size() >= 2) ?
8268 QString(
", error was %1").arg(strlist[1]) :
8269 QString(
", remote error")));
8274 masterServerSock->SetReadyReadCallbackEnabled(
true);
8289 bool foundClient =
false;
8299 if ((*it)->isSlaveBackend())
8304 if (onlyBlockingClients && !(*it)->getBlockShutdown())
8312 return (foundClient);
8321 QStringList bcast(
"SHUTDOWN_NOW" );
8328 if (
pbs->isSlaveBackend())
8329 pbs->getSocket()->WriteStringList(bcast);
8339 bool needsReschedule =
event.ExtraData(0).toUInt() != 0U;
8340 for (
int i = 1; i <
event.ExtraDataCount(); i++)
8343 if (needsReschedule)
8349 const QList<uint> &offlineEncoderIDs,
bool needsReschedule)
8351 QStringList extraData;
8352 extraData.push_back(
8353 QString::number(
static_cast<uint>(needsReschedule)));
8355 QList<uint>::const_iterator it;
8356 for (it = offlineEncoderIDs.begin(); it != offlineEncoderIDs.end(); ++it)
8357 extraData.push_back(QString::number(*it));
8359 MythEvent me(
"LOCAL_SLAVE_BACKEND_ENCODERS_OFFLINE", extraData);
8365 #if CONFIG_SYSTEMD_NOTIFY
8366 QStringList status2;
8369 status2 << QString(
"Master backend.");
8371 status2 << QString(
"Slave backend.");
8376 int playback = 0, frontend = 0, monitor = 0, slave = 0, media = 0;
8382 if (
pbs->IsDisconnected())
8384 if (
pbs->isSlaveBackend())
8386 else if (
pbs->isMediaServer())
8388 else if (
pbs->IsFrontend())
8390 else if (
pbs->getBlockShutdown())
8395 status2 << QString(
"Connections: Pl %1, Fr %2, Mo %3, Sl %4, MS %5, FT %6, Co %7")
8396 .arg(playback).arg(frontend).arg(monitor).arg(slave).arg(media)
8407 if (not elink->IsLocal())
8409 switch (elink->GetState())
8428 for (
auto & recording : recordings)
8436 while (!recordings.empty())
8440 recordings.pop_back();
8444 QString(
"Recordings: active %1, scheduled %2")
8445 .arg(active).arg(scheduled);
8449 QString status(
"STATUS=" + status2.join(
' '));
8450 (void)sd_notify(0, qPrintable(status));