13 #include "libmythbase/mythconfig.h"
16 #include <sys/ioctl.h>
18 #if CONFIG_SYSTEMD_NOTIFY
19 #include <systemd/sd-daemon.h>
25 #else // if !__linux__
26 # include <sys/param.h>
28 # include <sys/mount.h>
34 #include <QCoreApplication>
38 #include <QWaitCondition>
39 #include <QWriteLocker>
41 #include <QRegularExpression>
45 #include <QNetworkInterface>
46 #include <QNetworkProxy>
47 #include <QHostAddress>
64 #include "libmythbase/mythversion.h"
95 static constexpr std::chrono::milliseconds
PRT_TIMEOUT { 10ms };
99 #define LOC QString("MainServer: ")
100 #define LOC_WARN QString("MainServer, Warning: ")
101 #define LOC_ERR QString("MainServer, Error: ")
105 bool delete_file_immediately(
const QString &
filename,
106 bool followLinks,
bool checkexists)
110 bool success1 =
true;
111 bool success2 =
true;
113 LOG(VB_FILE, LOG_INFO,
LOC +
114 QString(
"About to delete file: %1").arg(
filename));
118 if (finfo.isSymLink())
122 QFile target(linktext);
123 success1 = target.remove();
126 LOG(VB_GENERAL, LOG_ERR,
LOC +
127 QString(
"Error deleting '%1' -> '%2'")
132 if (!checkexists || checkFile.exists())
134 success2 = checkFile.remove();
137 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error deleting '%1': %2")
141 return success1 && success2;
206 QMutexLocker locker(&
m_lock);
216 m_wait.wait(locker.mutex(), left.count());
222 QMutexLocker locker(&
m_lock);
247 QMap<int, EncoderLink *> *_tvList,
249 m_encoderList(_tvList),
250 m_ismaster(master), m_threadPool(
"ProcessRequestPool"),
251 m_sched(
sched), m_expirer(_expirer)
274 bool v4IsSet = !config_v4.isNull();
279 bool v6IsSet = !config_v6.isNull();
281 if (v6IsSet && !listenAddrs.contains(config_v6))
282 LOG(VB_GENERAL, LOG_WARNING,
LOC +
283 "Unable to find IPv6 address to bind");
285 if (v4IsSet && !listenAddrs.contains(config_v4))
286 LOG(VB_GENERAL, LOG_WARNING,
LOC +
287 "Unable to find IPv4 address to bind");
289 if ((v4IsSet && !listenAddrs.contains(config_v4))
290 && (v6IsSet && !listenAddrs.contains(config_v6))
293 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to find either IPv4 or IPv6 "
294 "address we can bind to, exiting");
327 QList<FileSystemInfo> m_fsInfos;
444 auto *ms =
new MythSocket(socketDescriptor,
this);
445 if (ms->IsConnected())
457 QCoreApplication::processEvents();
465 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"No data on sock %1")
479 QStringList listline;
482 if (!
pbs->ReadStringList(listline) || listline.empty())
485 LOG(VB_GENERAL, LOG_INFO,
"No data in ProcessRequestWork()");
490 else if (!bIsControl)
497 LOG(VB_GENERAL, LOG_INFO,
LOC +
"No data in ProcessRequestWork()");
501 QString line = listline[0];
503 line = line.simplified();
504 QStringList tokens = line.split(
' ', Qt::SkipEmptyParts);
505 QString command = tokens[0];
507 if (command ==
"MYTH_PROTO_VERSION")
509 if (tokens.size() < 2)
515 if (command ==
"ANN")
520 if (command ==
"DONE")
531 LOG(VB_GENERAL, LOG_ERR,
LOC +
"ProcessRequest unknown socket");
537 if (command ==
"QUERY_FILETRANSFER")
539 if (tokens.size() != 2)
544 else if (command ==
"QUERY_RECORDINGS")
546 if (tokens.size() != 2)
551 else if (command ==
"QUERY_RECORDING")
555 else if (command ==
"GO_TO_SLEEP")
559 else if (command ==
"QUERY_FREE_SPACE")
563 else if (command ==
"QUERY_FREE_SPACE_LIST")
567 else if (command ==
"QUERY_FREE_SPACE_SUMMARY")
571 else if (command ==
"QUERY_LOAD")
575 else if (command ==
"QUERY_UPTIME")
579 else if (command ==
"QUERY_HOSTNAME")
583 else if (command ==
"QUERY_MEMSTATS")
587 else if (command ==
"QUERY_TIME_ZONE")
591 else if (command ==
"QUERY_CHECKFILE")
595 else if (command ==
"QUERY_FILE_EXISTS")
597 if (listline.size() < 2)
602 else if (command ==
"QUERY_FINDFILE")
604 if (listline.size() < 4)
609 else if (command ==
"QUERY_FILE_HASH")
611 if (listline.size() < 3)
616 else if (command ==
"QUERY_GUIDEDATATHROUGH")
620 else if (command ==
"DELETE_FILE")
622 if (listline.size() < 3)
627 else if (command ==
"MOVE_FILE")
629 if (listline.size() < 4)
634 else if (command ==
"STOP_RECORDING")
638 else if (command ==
"CHECK_RECORDING")
642 else if (command ==
"DELETE_RECORDING")
644 if (3 <= tokens.size() && tokens.size() <= 5)
646 bool force = (tokens.size() >= 4) && (tokens[3] ==
"FORCE");
647 bool forget = (tokens.size() >= 5) && (tokens[4] ==
"FORGET");
653 else if (command ==
"FORCE_DELETE_RECORDING")
657 else if (command ==
"UNDELETE_RECORDING")
661 else if (command ==
"ADD_CHILD_INPUT")
666 LOG(VB_GENERAL, LOG_ERR,
LOC +
667 "ADD_CHILD_INPUT command received in master context");
668 reslist << QString(
"ERROR: Called in master context");
670 else if (tokens.size() != 2)
671 reslist <<
"ERROR: Bad ADD_CHILD_INPUT request";
675 reslist << QString(
"ERROR: Failed to add child input");
678 else if (command ==
"RESCHEDULE_RECORDINGS")
680 listline.pop_front();
683 else if (command ==
"FORGET_RECORDING")
687 else if (command ==
"QUERY_GETALLPENDING")
689 if (tokens.size() == 1)
691 else if (tokens.size() == 2)
696 else if (command ==
"QUERY_GETALLSCHEDULED")
700 else if (command ==
"QUERY_GETCONFLICTING")
704 else if (command ==
"QUERY_GETEXPIRING")
708 else if (command ==
"QUERY_SG_GETFILELIST")
712 else if (command ==
"QUERY_SG_FILEQUERY")
716 else if (command ==
"GET_FREE_INPUT_INFO")
718 if (tokens.size() != 2)
723 else if (command ==
"QUERY_RECORDER")
725 if (tokens.size() != 2)
730 else if ((command ==
"QUERY_RECORDING_DEVICE") ||
731 (command ==
"QUERY_RECORDING_DEVICES"))
735 else if (command ==
"SET_NEXT_LIVETV_DIR")
737 if (tokens.size() != 3)
742 else if (command ==
"SET_CHANNEL_INFO")
746 else if (command ==
"QUERY_REMOTEENCODER")
748 if (tokens.size() != 2)
753 else if (command ==
"GET_RECORDER_FROM_NUM")
757 else if (command ==
"GET_RECORDER_NUM")
761 else if (command ==
"QUERY_GENPIXMAP2")
765 else if (command ==
"QUERY_PIXMAP_LASTMODIFIED")
769 else if (command ==
"QUERY_PIXMAP_GET_IF_MODIFIED")
773 else if (command ==
"QUERY_ISRECORDING")
777 else if (command ==
"MESSAGE")
779 if ((listline.size() >= 2) && (listline[1].startsWith(
"SET_VERBOSE")))
781 else if ((listline.size() >= 2) &&
782 (listline[1].startsWith(
"SET_LOG_LEVEL")))
787 else if (command ==
"FILL_PROGRAM_INFO")
791 else if (command ==
"LOCK_TUNER")
793 if (tokens.size() == 1)
795 else if (tokens.size() == 2)
800 else if (command ==
"FREE_TUNER")
802 if (tokens.size() != 2)
807 else if (command ==
"QUERY_ACTIVE_BACKENDS")
811 else if (command ==
"QUERY_IS_ACTIVE_BACKEND")
813 if (tokens.size() != 1)
818 else if (command ==
"QUERY_COMMBREAK")
820 if (tokens.size() != 3)
825 else if (command ==
"QUERY_CUTLIST")
827 if (tokens.size() != 3)
832 else if (command ==
"QUERY_BOOKMARK")
834 if (tokens.size() != 3)
839 else if (command ==
"SET_BOOKMARK")
841 if (tokens.size() != 4)
846 else if (command ==
"QUERY_SETTING")
848 if (tokens.size() != 3)
853 else if (command ==
"SET_SETTING")
855 if (tokens.size() != 4)
860 else if (command ==
"SCAN_VIDEOS")
864 else if (command ==
"SCAN_MUSIC")
868 else if (command ==
"MUSIC_TAG_UPDATE_VOLATILE")
870 if (listline.size() != 6)
875 else if (command ==
"MUSIC_CALC_TRACK_LENGTH")
877 if (listline.size() != 3)
882 else if (command ==
"MUSIC_TAG_UPDATE_METADATA")
884 if (listline.size() != 3)
889 else if (command ==
"MUSIC_FIND_ALBUMART")
891 if (listline.size() != 4)
896 else if (command ==
"MUSIC_TAG_GETIMAGE")
898 if (listline.size() < 4)
903 else if (command ==
"MUSIC_TAG_ADDIMAGE")
905 if (listline.size() < 5)
910 else if (command ==
"MUSIC_TAG_REMOVEIMAGE")
912 if (listline.size() < 4)
917 else if (command ==
"MUSIC_TAG_CHANGEIMAGE")
919 if (listline.size() < 5)
924 else if (command ==
"MUSIC_LYRICS_FIND")
926 if (listline.size() < 3)
931 else if (command ==
"MUSIC_LYRICS_GETGRABBERS")
935 else if (command ==
"MUSIC_LYRICS_SAVE")
937 if (listline.size() < 3)
942 else if (command ==
"IMAGE_SCAN")
945 QStringList reply = (listline.size() == 2)
947 : QStringList(
"ERROR") <<
"Bad: " << listline;
951 else if (command ==
"IMAGE_COPY")
954 QStringList reply = (listline.size() >= 2)
956 : QStringList(
"ERROR") <<
"Bad: " << listline;
960 else if (command ==
"IMAGE_MOVE")
963 QStringList reply = (listline.size() == 4)
965 HandleDbMove(listline[1], listline[2], listline[3])
966 : QStringList(
"ERROR") <<
"Bad: " << listline;
970 else if (command ==
"IMAGE_DELETE")
973 QStringList reply = (listline.size() == 2)
975 : QStringList(
"ERROR") <<
"Bad: " << listline;
979 else if (command ==
"IMAGE_HIDE")
982 QStringList reply = (listline.size() == 3)
984 HandleHide(listline[1].toInt() != 0, listline[2])
985 : QStringList(
"ERROR") <<
"Bad: " << listline;
989 else if (command ==
"IMAGE_TRANSFORM")
992 QStringList reply = (listline.size() == 3)
994 HandleTransform(listline[1].toInt(), listline[2])
995 : QStringList(
"ERROR") <<
"Bad: " << listline;
999 else if (command ==
"IMAGE_RENAME")
1002 QStringList reply = (listline.size() == 3)
1004 : QStringList(
"ERROR") <<
"Bad: " << listline;
1008 else if (command ==
"IMAGE_CREATE_DIRS")
1011 QStringList reply = (listline.size() >= 4)
1013 HandleDirs(listline[1], listline[2].toInt() != 0, listline.mid(3))
1014 : QStringList(
"ERROR") <<
"Bad: " << listline;
1018 else if (command ==
"IMAGE_COVER")
1021 QStringList reply = (listline.size() == 3)
1023 HandleCover(listline[1].toInt(), listline[2].toInt())
1024 : QStringList(
"ERROR") <<
"Bad: " << listline;
1028 else if (command ==
"IMAGE_IGNORE")
1031 QStringList reply = (listline.size() == 2)
1033 : QStringList(
"ERROR") <<
"Bad: " << listline;
1037 else if (command ==
"ALLOW_SHUTDOWN")
1039 if (tokens.size() != 1)
1044 else if (command ==
"BLOCK_SHUTDOWN")
1046 if (tokens.size() != 1)
1051 else if (command ==
"SHUTDOWN_NOW")
1053 if (tokens.size() != 1)
1058 if (listline.size() >= 2)
1059 halt_cmd = listline[1];
1061 if (!halt_cmd.isEmpty())
1063 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
1064 "Going down now as of Mainserver request!");
1071 else if (command ==
"BACKEND_MESSAGE")
1073 QString message = listline[1];
1074 QStringList extra( listline[2] );
1075 for (
int i = 3; i < listline.size(); i++)
1076 extra << listline[i];
1080 else if ((command ==
"DOWNLOAD_FILE") ||
1081 (command ==
"DOWNLOAD_FILE_NOW"))
1083 if (listline.size() != 4)
1088 else if (command ==
"REFRESH_BACKEND")
1090 LOG(VB_GENERAL, LOG_INFO ,
LOC +
"Reloading backend settings");
1093 else if (command ==
"OK")
1095 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Got 'OK' out of sequence.");
1097 else if (command ==
"UNKNOWN_COMMAND")
1099 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Got 'UNKNOWN_COMMAND' out of sequence.");
1103 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unknown command: " + command);
1107 QStringList strlist;
1108 strlist <<
"UNKNOWN_COMMAND";
1121 QStringList broadcast;
1122 QSet<QString> receivers;
1140 auto *me =
dynamic_cast<MythEvent *
>(e);
1144 QString message = me->
Message();
1146 if ((message ==
"PREVIEW_SUCCESS" || message ==
"PREVIEW_QUEUED") &&
1147 me->ExtraDataCount() >= 5)
1150 uint recordingID = me->ExtraData(0).toUInt();
1151 const QString&
filename = me->ExtraData(1);
1152 const QString& msg = me->ExtraData(2);
1153 const QString& datetime = me->ExtraData(3);
1155 if (message ==
"PREVIEW_QUEUED")
1157 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1158 QString(
"Preview Queued: '%1' '%2'")
1164 ok = ok &&
file.open(QIODevice::ReadOnly);
1168 QByteArray data =
file.readAll();
1169 QStringList extra(
"OK");
1170 extra.push_back(QString::number(recordingID));
1171 extra.push_back(msg);
1172 extra.push_back(datetime);
1173 extra.push_back(QString::number(data.size()));
1174 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1175 quint16 checksum = qChecksum(data.constData(), data.size());
1177 quint16 checksum = qChecksum(data);
1179 extra.push_back(QString::number(checksum));
1180 extra.push_back(QString(data.toBase64()));
1182 for (
uint i = 4 ; i < (
uint) me->ExtraDataCount(); i++)
1184 const QString& token = me->ExtraData(i);
1185 extra.push_back(token);
1189 receivers.insert(*it);
1194 if (receivers.empty())
1196 LOG(VB_GENERAL, LOG_ERR,
LOC +
1197 "PREVIEW_SUCCESS but no receivers.");
1201 broadcast.push_back(
"BACKEND_MESSAGE");
1202 broadcast.push_back(
"GENERATED_PIXMAP");
1207 message =
"PREVIEW_FAILED";
1213 if (message ==
"PREVIEW_FAILED" && me->ExtraDataCount() >= 5)
1215 const QString& pginfokey = me->ExtraData(0);
1216 const QString& msg = me->ExtraData(2);
1218 QStringList extra(
"ERROR");
1219 extra.push_back(pginfokey);
1220 extra.push_back(msg);
1221 for (
uint i = 4 ; i < (
uint) me->ExtraDataCount(); i++)
1223 const QString& token = me->ExtraData(i);
1224 extra.push_back(token);
1228 receivers.insert(*it);
1233 if (receivers.empty())
1235 LOG(VB_GENERAL, LOG_ERR,
LOC +
1236 "PREVIEW_FAILED but no receivers.");
1240 broadcast.push_back(
"BACKEND_MESSAGE");
1241 broadcast.push_back(
"GENERATED_PIXMAP");
1245 if (me->Message().startsWith(
"AUTO_EXPIRE"))
1247 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1248 if (tokens.size() != 3)
1250 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad AUTO_EXPIRE message");
1274 QString msg = QString(
"Cannot find program info for '%1', "
1275 "while attempting to Auto-Expire.")
1276 .arg(me->Message());
1277 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
1283 if (me->Message().startsWith(
"QUERY_NEXT_LIVETV_DIR") &&
m_sched)
1285 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1286 if (tokens.size() != 2)
1288 LOG(VB_GENERAL, LOG_ERR,
LOC +
1289 QString(
"Bad %1 message").arg(tokens[0]));
1297 if (me->Message().startsWith(
"STOP_RECORDING"))
1299 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1300 if (tokens.size() < 3 || tokens.size() > 3)
1302 LOG(VB_GENERAL, LOG_ERR,
LOC +
1303 QString(
"Bad STOP_RECORDING message: %1")
1304 .arg(me->Message()));
1317 LOG(VB_GENERAL, LOG_ERR,
LOC +
1318 QString(
"Cannot find program info for '%1' while "
1319 "attempting to stop recording.").arg(me->Message()));
1325 if ((me->Message().startsWith(
"DELETE_RECORDING")) ||
1326 (me->Message().startsWith(
"FORCE_DELETE_RECORDING")))
1328 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1329 if (tokens.size() < 3 || tokens.size() > 5)
1331 LOG(VB_GENERAL, LOG_ERR,
LOC +
1332 QString(
"Bad %1 message").arg(tokens[0]));
1336 bool force = (tokens.size() >= 4) && (tokens[3] ==
"FORCE");
1337 bool forget = (tokens.size() >= 5) && (tokens[4] ==
"FORGET");
1344 if (tokens[0] ==
"FORCE_DELETE_RECORDING")
1351 LOG(VB_GENERAL, LOG_ERR,
LOC +
1352 QString(
"Cannot find program info for '%1' while "
1353 "attempting to delete.").arg(me->Message()));
1359 if (me->Message().startsWith(
"UNDELETE_RECORDING"))
1361 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1362 if (tokens.size() < 3 || tokens.size() > 3)
1364 LOG(VB_GENERAL, LOG_ERR,
LOC +
1365 QString(
"Bad UNDELETE_RECORDING message: %1")
1366 .arg(me->Message()));
1379 LOG(VB_GENERAL, LOG_ERR,
LOC +
1380 QString(
"Cannot find program info for '%1' while "
1381 "attempting to undelete.").arg(me->Message()));
1387 if (me->Message().startsWith(
"ADD_CHILD_INPUT"))
1389 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1392 LOG(VB_GENERAL, LOG_ERR,
LOC +
1393 "ADD_CHILD_INPUT event received in slave context");
1395 else if (tokens.size() != 2)
1397 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad ADD_CHILD_INPUT message");
1406 if (me->Message().startsWith(
"RESCHEDULE_RECORDINGS") &&
m_sched)
1408 const QStringList& request = me->ExtraDataList();
1413 if (me->Message().startsWith(
"SCHEDULER_ADD_RECORDING") &&
m_sched)
1418 LOG(VB_GENERAL, LOG_ERR,
LOC +
1419 "Bad SCHEDULER_ADD_RECORDING message");
1427 if (me->Message().startsWith(
"UPDATE_RECORDING_STATUS") &&
m_sched)
1429 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
1430 if (tokens.size() != 6)
1432 LOG(VB_GENERAL, LOG_ERR,
LOC +
1433 "Bad UPDATE_RECORDING_STATUS message");
1437 uint cardid = tokens[1].toUInt();
1438 uint chanid = tokens[2].toUInt();
1443 recstatus, recendts);
1449 if (me->Message().startsWith(
"LIVETV_EXITED"))
1451 const QString& chainid = me->ExtraData();
1459 if (me->Message() ==
"CLEAR_SETTINGS_CACHE")
1462 if (me->Message().startsWith(
"RESET_IDLETIME") &&
m_sched)
1465 if (me->Message() ==
"LOCAL_RECONNECT_TO_MASTER")
1468 if (me->Message() ==
"LOCAL_SLAVE_BACKEND_ENCODERS_OFFLINE")
1471 if (me->Message().startsWith(
"LOCAL_"))
1474 if (me->Message() ==
"CREATE_THUMBNAILS")
1477 if (me->Message() ==
"IMAGE_GET_METADATA")
1480 std::unique_ptr<MythEvent> mod_me {
nullptr};
1481 if (me->Message().startsWith(
"MASTER_UPDATE_REC_INFO"))
1483 QStringList tokens = me->Message().simplified().split(
" ");
1484 uint recordedid = 0;
1485 if (tokens.size() >= 2)
1486 recordedid = tokens[1].toUInt();
1487 if (recordedid == 0)
1501 mod_me = std::make_unique<MythEvent>(
"RECORDING_LIST_CHANGE UPDATE", list);
1509 if (me->Message().startsWith(
"DOWNLOAD_FILE"))
1511 QStringList extraDataList = me->ExtraDataList();
1512 QString localFile = extraDataList[1];
1513 QFile
file(localFile);
1514 QStringList tokens = me->Message().simplified().split(
" ");
1522 if ((tokens.size() >= 2) && (tokens[1] ==
"FINISHED"))
1525 mod_me = std::make_unique<MythEvent>(me->Message(), extraDataList);
1528 if (broadcast.empty())
1530 broadcast.push_back(
"BACKEND_MESSAGE");
1531 if (mod_me !=
nullptr)
1533 broadcast.push_back(mod_me->Message());
1534 broadcast += mod_me->ExtraDataList();
1538 broadcast.push_back(me->Message());
1539 broadcast += me->ExtraDataList();
1544 if (!broadcast.empty())
1547 std::vector<PlaybackSock *> localPBSList;
1552 localPBSList.push_back(
pbs);
1556 bool sendGlobal =
false;
1557 if (
m_ismaster && broadcast[1].startsWith(
"GLOBAL_"))
1559 broadcast[1].replace(
"GLOBAL_",
"LOCAL_");
1560 MythEvent me(broadcast[1], broadcast[2]);
1566 QSet<PlaybackSock*> sentSet;
1568 bool isSystemEvent = broadcast[1].startsWith(
"SYSTEM_EVENT ");
1571 std::vector<PlaybackSock*>::const_iterator iter;
1572 for (iter = localPBSList.begin(); iter != localPBSList.end(); ++iter)
1576 if (sentSet.contains(
pbs) ||
pbs->IsDisconnected())
1579 if (!receivers.empty() && !receivers.contains(
pbs->getHostname()))
1582 sentSet.insert(
pbs);
1584 bool reallysendit =
false;
1586 if (broadcast[1] ==
"CLEAR_SETTINGS_CACHE")
1589 (
pbs->isSlaveBackend() ||
pbs->wantsEvents()))
1590 reallysendit =
true;
1592 else if (sendGlobal)
1594 if (
pbs->isSlaveBackend())
1595 reallysendit =
true;
1597 else if (
pbs->wantsEvents())
1599 reallysendit =
true;
1606 if (!
pbs->wantsSystemEvents())
1610 if (!
pbs->wantsOnlySystemEvents())
1612 if (sentSetSystemEvent.contains(
pbs->getHostname()))
1615 sentSetSystemEvent <<
pbs->getHostname();
1618 else if (
pbs->wantsOnlySystemEvents())
1628 for (iter = localPBSList.begin(); iter != localPBSList.end(); ++iter)
1646 QStringList retlist;
1647 const QString&
version = slist[1];
1648 if (
version != MYTH_PROTO_VERSION)
1650 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1651 "MainServer::HandleVersion - Client speaks protocol version " +
1652 version +
" but we speak " + MYTH_PROTO_VERSION +
'!');
1653 retlist <<
"REJECT" << MYTH_PROTO_VERSION;
1659 if (slist.size() < 3)
1661 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1662 "MainServer::HandleVersion - Client did not pass protocol "
1663 "token. Refusing connection!");
1664 retlist <<
"REJECT" << MYTH_PROTO_VERSION;
1670 const QString& token = slist[2];
1671 if (token != QString::fromUtf8(MYTH_PROTO_TOKEN))
1673 LOG(VB_GENERAL, LOG_CRIT,
LOC +
1674 QString(
"MainServer::HandleVersion - Client sent incorrect "
1675 "protocol token \"%1\" for protocol version. Refusing "
1676 "connection!").arg(token));
1677 retlist <<
"REJECT" << MYTH_PROTO_VERSION;
1683 retlist <<
"ACCEPT" << MYTH_PROTO_VERSION;
1712 QStringList retlist(
"OK" );
1713 QStringList errlist(
"ERROR" );
1715 if (commands.size() < 3 || commands.size() > 6)
1718 if (commands.size() == 2)
1719 info = QString(
" %1").arg(commands[1]);
1721 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Received malformed ANN%1 query")
1724 errlist <<
"malformed_ann_query";
1732 if (
pbs->getSocket() == socket)
1734 LOG(VB_GENERAL, LOG_WARNING,
LOC +
1735 QString(
"Client %1 is trying to announce a socket "
1745 if (commands[1] ==
"Playback" || commands[1] ==
"Monitor" ||
1746 commands[1] ==
"Frontend")
1748 if (commands.size() < 4)
1750 LOG(VB_GENERAL, LOG_ERR,
LOC +
1751 QString(
"Received malformed ANN %1 query")
1754 errlist <<
"malformed_ann_query";
1771 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"MainServer::ANN %1")
1773 LOG(VB_GENERAL, LOG_INFO,
LOC +
1774 QString(
"adding: %1(%2) as a client (events: %3)")
1776 .arg(quintptr(socket),0,16)
1778 pbs->setBlockShutdown((commands[1] ==
"Playback") ||
1779 (commands[1] ==
"Frontend"));
1781 if (commands[1] ==
"Frontend")
1783 pbs->SetAsFrontend();
1785 frontend->m_name = commands[2];
1800 else if (commands[1] ==
"MediaServer")
1802 if (commands.size() < 3)
1804 LOG(VB_GENERAL, LOG_ERR,
LOC +
1805 "Received malformed ANN MediaServer query");
1806 errlist <<
"malformed_ann_query";
1816 pbs->setAsMediaServer();
1817 pbs->setBlockShutdown(
false);
1822 QString(
"CLIENT_CONNECTED HOSTNAME %1").arg(commands[2]));
1824 else if (commands[1] ==
"SlaveBackend")
1826 if (commands.size() < 4)
1828 LOG(VB_GENERAL, LOG_ERR,
LOC +
1829 QString(
"Received malformed ANN %1 query")
1831 errlist <<
"malformed_ann_query";
1844 LOG(VB_GENERAL, LOG_INFO,
LOC +
1845 QString(
"adding: %1 as a slave backend server")
1847 pbs->setAsSlaveBackend();
1848 pbs->setIP(commands[3]);
1853 QStringList::const_iterator sit = slist.
cbegin()+1;
1854 while (sit != slist.cend())
1857 if (!recinfo->GetChanID())
1867 bool wasAsleep =
true;
1871 if (elink->GetHostName() == commands[2])
1873 if (! (elink->IsWaking() || elink->IsAsleep()))
1875 elink->SetSocket(
pbs);
1883 QString message = QString(
"LOCAL_SLAVE_BACKEND_ONLINE %2")
1888 pbs->setBlockShutdown(
false);
1893 QString(
"SLAVE_CONNECTED HOSTNAME %1").arg(commands[2]));
1895 else if (commands[1] ==
"FileTransfer")
1897 if (slist.size() < 3)
1899 LOG(VB_GENERAL, LOG_ERR,
LOC +
1900 "Received malformed FileTransfer command");
1901 errlist <<
"malformed_filetransfer_command";
1906 LOG(VB_NETWORK, LOG_INFO,
LOC +
1907 "MainServer::HandleAnnounce FileTransfer");
1908 LOG(VB_NETWORK, LOG_INFO,
LOC +
1909 QString(
"adding: %1 as a remote file transfer") .arg(commands[2]));
1910 QStringList::const_iterator it = slist.cbegin();
1911 QString path = *(++it);
1912 QString wantgroup = *(++it);
1914 QStringList checkfiles;
1916 for (++it; it != slist.cend(); ++it)
1920 bool writemode =
false;
1921 bool usereadahead =
true;
1922 std::chrono::milliseconds timeout_ms = 2s;
1923 if (commands.size() > 3)
1924 writemode = (commands[3].toInt() != 0);
1926 if (commands.size() > 4)
1927 usereadahead = (commands[4].toInt() != 0);
1929 if (commands.size() > 5)
1930 timeout_ms = std::chrono::milliseconds(commands[5].toInt());
1934 if (wantgroup.isEmpty())
1935 wantgroup =
"Default";
1941 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to determine directory "
1942 "to write to in FileTransfer write command");
1943 errlist <<
"filetransfer_directory_not_found";
1950 LOG(VB_GENERAL, LOG_ERR,
LOC +
1951 QString(
"FileTransfer write filename is empty in path '%1'.")
1953 errlist <<
"filetransfer_filename_empty";
1958 if ((path.contains(
"/../")) ||
1959 (path.startsWith(
"../")))
1961 LOG(VB_GENERAL, LOG_ERR,
LOC +
1962 QString(
"FileTransfer write filename '%1' does not pass "
1963 "sanity checks.") .arg(path));
1964 errlist <<
"filetransfer_filename_dangerous";
1976 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Empty filename, cowardly aborting!");
1977 errlist <<
"filetransfer_filename_empty";
1986 LOG(VB_GENERAL, LOG_ERR,
LOC +
1987 QString(
"FileTransfer filename '%1' is actually a directory, "
1988 "cannot transfer.") .arg(
filename));
1989 errlist <<
"filetransfer_filename_is_a_directory";
1996 QString dirPath = finfo.absolutePath();
2000 if (!qdir.mkpath(dirPath))
2002 LOG(VB_GENERAL, LOG_ERR,
LOC +
2003 QString(
"FileTransfer filename '%1' is in a "
2004 "subdirectory which does not exist, and can "
2005 "not be created.") .arg(
filename));
2006 errlist <<
"filetransfer_unable_to_create_subdirectory";
2026 LOG(VB_GENERAL, LOG_ERR,
LOC +
2027 QString(
"Can't open %1").arg(
filename));
2028 errlist <<
"filetransfer_unable_to_open_file";
2035 LOG(VB_GENERAL, LOG_INFO,
LOC +
2036 QString(
"adding: %1(%2) as a file transfer")
2038 .arg(quintptr(socket),0,16));
2048 if (!checkfiles.empty())
2051 QDir dir = fi.absoluteDir();
2052 for (
const auto &
file : std::as_const(checkfiles))
2054 if (dir.exists(
file) &&
2055 ((
file).endsWith(
".srt") ||
2088 QStringList strList(
"ERROR");
2103 bool do_write =
false;
2118 LOG(VB_GENERAL, LOG_ERR,
LOC +
2119 "SendResponse: Unable to write to client socket, as it's no "
2135 QString playbackhost =
pbs->getHostname();
2137 QMap<QString,ProgramInfo*> recMap;
2142 QMap<QString,bool> isJobRunning =
2148 if ((
type ==
"Ascending") || (
type ==
"Play"))
2150 else if ((
type ==
"Descending") || (
type ==
"Delete"))
2155 destination, (
type ==
"Recording"),
2156 inUseMap, isJobRunning, recMap, sort);
2158 QMap<QString,ProgramInfo*>::iterator mit = recMap.begin();
2159 for (; mit != recMap.end(); mit = recMap.erase(mit))
2162 QStringList outputlist(QString::number(destination.
size()));
2163 QMap<QString, int> backendPortMap;
2167 for (
auto* proginfo : destination)
2178 proginfo->GetBasename()));
2179 if (!proginfo->GetFilesize())
2182 if (tmpURL.startsWith(
'/'))
2184 QFile checkFile(tmpURL);
2185 if (!tmpURL.isEmpty() && checkFile.exists())
2187 proginfo->SetFilesize(checkFile.size());
2188 if (proginfo->GetRecordingEndTime() <
2191 proginfo->SaveFilesize(proginfo->GetFilesize());
2200 if (proginfo->GetPathname().isEmpty())
2202 LOG(VB_GENERAL, LOG_ERR,
LOC +
2203 QString(
"HandleQueryRecordings() "
2204 "Couldn't find backend for:\n\t\t\t%1")
2207 proginfo->SetFilesize(0);
2208 proginfo->SetPathname(
"file not found");
2213 if (!proginfo->GetFilesize())
2217 LOG(VB_GENERAL, LOG_ERR,
LOC +
2218 "MainServer::HandleQueryRecordings()"
2219 "\n\t\t\tCould not fill program info "
2224 if (proginfo->GetRecordingEndTime() <
2227 proginfo->SaveFilesize(proginfo->GetFilesize());
2236 if (!backendPortMap.contains(
hostname))
2248 proginfo->ToStringList(outputlist);
2261 if (slist.size() < 3)
2263 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad QUERY_RECORDING query");
2268 QString command = slist[1].toUpper();
2271 if (command ==
"BASENAME")
2275 else if (command ==
"TIMESLOT")
2277 if (slist.size() < 4)
2279 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bad QUERY_RECORDING query");
2284 pginfo =
new ProgramInfo(slist[2].toUInt(), recstartts);
2287 QStringList strlist;
2308 QString playbackhost = slist[1];
2310 QStringList::const_iterator it = slist.cbegin() + 2;
2325 const QFileInfo info(lpath);
2329 QStringList strlist;
2340 m_ms->DoDeleteThread(
this);
2347 std::this_thread::sleep_for(3s + std::chrono::microseconds(
MythRandom(0, 2000)));
2352 QString logInfo = QString(
"recording id %1 (chanid %2 at %3)")
2357 QString name = QString(
"deleteThread%1%2").arg(getpid()).arg(
MythRandom());
2363 QString msg = QString(
"ERROR opening database connection for Delete "
2364 "Thread for chanid %1 recorded at %2. Program "
2365 "will NOT be deleted.")
2368 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2378 QString msg = QString(
"ERROR retrieving program info when trying to "
2379 "delete program for chanid %1 recorded at %2. "
2380 "Recording will NOT be deleted.")
2383 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2393 if ((!checkFile.exists()) && pginfo.
GetFilesize() &&
2396 LOG(VB_GENERAL, LOG_ERR,
LOC +
2397 QString(
"ERROR when trying to delete file: %1. File "
2398 "doesn't exist. Database metadata will not be removed.")
2416 bool errmsg =
false;
2431 if ((fd < 0) && checkFile.exists())
2436 delete_file_immediately(ds->
m_filename, followLinks,
false);
2437 std::this_thread::sleep_for(2s);
2438 if (checkFile.exists())
2444 LOG(VB_GENERAL, LOG_ERR,
LOC +
2445 QString(
"Error deleting file: %1. Keeping metadata in database.")
2460 QStringList nameFilters;
2461 nameFilters.push_back(fInfo.fileName() +
"*.png");
2462 nameFilters.push_back(fInfo.fileName() +
"*.jpg");
2463 nameFilters.push_back(fInfo.fileName() +
".tmp");
2464 nameFilters.push_back(fInfo.fileName() +
".old");
2465 nameFilters.push_back(fInfo.fileName() +
".map");
2466 nameFilters.push_back(fInfo.fileName() +
".tmp.map");
2467 nameFilters.push_back(fInfo.baseName() +
".srt");
2469 QDir dir (fInfo.path());
2470 QFileInfoList miscFiles = dir.entryInfoList(nameFilters);
2472 for (
const auto &
file : std::as_const(miscFiles))
2474 QString sFileName =
file.absoluteFilePath();
2475 delete_file_immediately( sFileName, followLinks,
true);
2486 if (slowDeletes && fd >= 0)
2492 QString logInfo = QString(
"recording id %1 filename %2")
2495 LOG(VB_GENERAL, LOG_NOTICE,
"DeleteRecordedFiles - " + logInfo);
2499 query.
prepare(
"SELECT basename, hostname, storagegroup FROM recordedfile "
2500 "WHERE recordedid = :RECORDEDID;");
2503 if (!query.
exec() || !query.
size())
2506 LOG(VB_GENERAL, LOG_ERR,
LOC +
2507 QString(
"Error querying recordedfiles for %1.") .arg(logInfo));
2510 while (query.
next())
2512 QString basename = query.
value(0).toString();
2515 bool deleteInDB =
false;
2517 if (basename == QFileInfo(ds->
m_filename).fileName())
2546 update.
prepare(
"DELETE FROM recordedfile "
2547 "WHERE recordedid = :RECORDEDID "
2548 "AND basename = :BASENAME ;");
2550 update.
bindValue(
":BASENAME", basename);
2554 LOG(VB_GENERAL, LOG_ERR,
LOC +
2555 QString(
"Error querying recordedfile (%1) for %2.")
2556 .arg(query.
value(1).toString(), logInfo));
2564 QString logInfo = QString(
"recording id %1 (chanid %2 at %3)")
2568 LOG(VB_GENERAL, LOG_NOTICE,
"DoDeleteINDB - " + logInfo);
2571 query.
prepare(
"DELETE FROM recorded WHERE recordedid = :RECORDEDID AND "
2576 if (!query.
exec() || !query.
size())
2579 LOG(VB_GENERAL, LOG_ERR,
LOC +
2580 QString(
"Error deleting recorded entry for %1.") .arg(logInfo));
2583 std::this_thread::sleep_for(1s);
2586 QString msg = QString(
"RECORDING_LIST_CHANGE DELETE %1")
2591 std::this_thread::sleep_for(3s);
2593 query.
prepare(
"DELETE FROM recordedmarkup "
2594 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
2601 LOG(VB_GENERAL, LOG_ERR,
LOC +
2602 QString(
"Error deleting recordedmarkup for %1.") .arg(logInfo));
2605 query.
prepare(
"DELETE FROM recordedseek "
2606 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
2613 LOG(VB_GENERAL, LOG_ERR,
LOC +
2614 QString(
"Error deleting recordedseek for %1.")
2629 bool deleteBrokenSymlinks)
2633 QString linktext =
"";
2634 QByteArray fname =
filename.toLocal8Bit();
2636 LOG(VB_FILE, LOG_INFO,
LOC +
2637 QString(
"About to unlink/delete file: '%1'")
2638 .arg(fname.constData()));
2640 QString errmsg = QString(
"Delete Error '%1'").arg(fname.constData());
2641 if (finfo.isSymLink())
2644 QByteArray alink = linktext.toLocal8Bit();
2645 errmsg += QString(
" -> '%2'").arg(alink.constData());
2648 if (followLinks && finfo.isSymLink())
2650 if (!finfo.exists() && deleteBrokenSymlinks)
2651 unlink(fname.constData());
2656 unlink(fname.constData());
2659 else if (!finfo.isSymLink())
2665 int err = unlink(fname.constData());
2670 if (fd < 0 && errno != EISDIR)
2671 LOG(VB_GENERAL, LOG_ERR,
LOC + errmsg +
ENO);
2687 QByteArray fname =
filename.toLocal8Bit();
2688 QString msg = QString(
"Error deleting '%1'").arg(fname.constData());
2689 int fd = open(fname.constData(), O_WRONLY);
2693 if (errno == EISDIR)
2698 LOG(VB_GENERAL, LOG_ERR, msg +
" could not delete directory " +
ENO);
2704 LOG(VB_GENERAL, LOG_ERR, msg +
" could not open " +
ENO);
2708 else if (unlink(fname.constData()))
2710 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
" could not unlink " +
ENO);
2740 query.
prepare(
"SELECT COUNT(cardid) FROM capturecard;");
2742 cards = query.
value(0).toInt();
2746 constexpr std::chrono::milliseconds sleep_time = 500ms;
2747 const size_t min_tps = 8LL * 1024 * 1024;
2748 const auto calc_tps = (size_t) (cards * 1.2 * (22200000LL / 8.0));
2749 const size_t tps = std::max(min_tps, calc_tps);
2750 const auto increment = (size_t) (tps * (sleep_time.count() * 0.001F));
2752 LOG(VB_FILE, LOG_INFO,
LOC +
2753 QString(
"Truncating '%1' by %2 MB every %3 milliseconds")
2755 .arg(increment / (1024.0 * 1024.0), 0,
'f', 2)
2756 .arg(sleep_time.count()));
2758 GetMythDB()->GetDBManager()->PurgeIdleConnections(
false);
2764 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Truncating '%1' to %2 MB")
2765 .arg(
filename).arg(fsize / (1024.0 * 1024.0), 0,
'f', 2));
2768 int err = ftruncate(fd, fsize);
2771 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error truncating '%1'")
2775 return 0 ==
close(fd);
2780 if (pginfo && ((count % 100) == 0))
2785 std::this_thread::sleep_for(sleep_time);
2788 bool ok = (0 ==
close(fd));
2793 LOG(VB_FILE, LOG_INFO,
LOC +
2794 QString(
"Finished truncating '%1'").arg(
filename));
2804 pbssock =
pbs->getSocket();
2806 QStringList::const_iterator it = slist.cbegin() + 1;
2828 result = iter.key();
2833 QStringList outputlist( QString::number(result) );
2840 QStringList::const_iterator it = slist.cbegin() + 1;
2852 bool hasConflicts =
false;
2854 for (
auto *pInfo : schedList)
2872 pbssock =
pbs->getSocket();
2895 (*m_encoderList)[num]->StopRecording();
2903 QStringList outputlist(
"0" );
2928 recnum = iter.key();
2935 std::this_thread::sleep_for(100us);
2951 QStringList outputlist( QString::number(recnum) );
2958 bool forceMetadataDelete,
2966 qDebug() <<
"HandleDeleteRecording(chanid, starttime) Empty Recording ID";
2973 pbssock =
pbs->getSocket();
2975 QStringList outputlist( QString::number(0) );
2985 bool forceMetadataDelete)
2987 QStringList::const_iterator it = slist.cbegin() + 1;
2992 qDebug() <<
"HandleDeleteRecording(QStringList) Empty Recording ID";
3001 bool forceMetadataDelete,
bool lexpirer,
bool forgetHistory)
3003 int resultCode = -1;
3006 pbssock =
pbs->getSocket();
3008 bool justexpire = lexpirer ?
false :
3016 LOG(VB_GENERAL, LOG_ERR,
LOC +
3017 QString(
"ERROR when trying to delete file for %1. Unable "
3018 "to determine filename of recording.")
3024 QStringList outputlist(QString::number(resultCode));
3034 if (justexpire && !forceMetadataDelete &&
3043 QStringList outputlist( QString::number(0) );
3067 QStringList outputlist( QString::number(num) );
3077 bool fileExists = checkFile.exists();
3080 QFile checkFileUTF8(QString::fromUtf8(
filename.toLatin1().constData()));
3081 fileExists = checkFileUTF8.exists();
3090 if (fileExists || !recinfo.
GetFilesize() || forceMetadataDelete)
3096 qDebug() <<
"DoHandleDeleteRecording() Empty Recording ID";
3103 forceMetadataDelete);
3104 deleteThread->start();
3109 QString logInfo = QString(
"chanid %1")
3113 LOG(VB_GENERAL, LOG_ERR,
LOC +
3114 QString(
"ERROR when trying to delete file: %1. File doesn't "
3115 "exist. Database metadata will not be removed.")
3122 QStringList outputlist( QString::number(resultCode) );
3134 if (fileExists || !recinfo.
GetFilesize() || forceMetadataDelete)
3137 QString(
"REC_DELETED CHANID %1 STARTTIME %2")
3147 if (slist.size() == 3)
3156 QStringList::const_iterator it = slist.cbegin()+1;
3170 pbssock =
pbs->getSocket();
3184 QStringList outputlist( QString::number(ret) );
3221 result = QStringList(QString::number(1));
3224 result = QStringList(QString::number(0));
3242 LOG(VB_GENERAL, LOG_INFO,
LOC +
"HandleAddChildInput: Already locked");
3246 LOG(VB_GENERAL, LOG_INFO,
LOC +
3247 QString(
"HandleAddChildInput: Handling input %1").arg(inputid));
3257 LOG(VB_GENERAL, LOG_ERR,
LOC +
3258 QString(
"HandleAddChildInput: "
3259 "Failed to add child to input %1").arg(inputid));
3265 LOG(VB_GENERAL, LOG_INFO,
LOC +
3266 QString(
"HandleAddChildInput: Added child input %1").arg(childid));
3274 auto *tv =
new TVRec(childid);
3275 if (!tv || !tv->Init())
3277 LOG(VB_GENERAL, LOG_ERR,
LOC +
3278 QString(
"HandleAddChildInput: "
3279 "Failed to initialize input %1").arg(childid));
3288 (*m_encoderList)[childid] = enc;
3295 LOG(VB_GENERAL, LOG_ERR,
LOC +
3296 QString(
"HandleAddChildInput: "
3297 "Failed to add remote input %1").arg(childid));
3307 (*m_encoderList)[childid] = enc;
3316 auto *tv =
new TVRec(inputid);
3317 if (!tv || !tv->Init())
3319 LOG(VB_GENERAL, LOG_ERR,
LOC +
3320 QString(
"HandleAddChildInput: "
3321 "Failed to initialize input %1").arg(inputid));
3329 (*m_encoderList)[inputid] = enc;
3335 LOG(VB_GENERAL, LOG_INFO,
LOC +
3336 QString(
"HandleAddChildInput: "
3337 "Successfully handled input %1").arg(inputid));
3344 QStringList::const_iterator it = slist.cbegin() + 1;
3351 pbssock =
pbs->getSocket();
3354 QStringList outputlist( QString::number(0) );
3366 QStringList strlist;
3369 if (!sleepCmd.isEmpty())
3373 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
3374 "Received GO_TO_SLEEP command from master, running SleepCommand.");
3379 strlist <<
"ERROR: SleepCommand is empty";
3380 LOG(VB_GENERAL, LOG_ERR,
LOC +
3381 "ERROR: in HandleGoToSleep(), but no SleepCommand found!");
3397 QStringList strlist;
3429 QStringList strlist;
3448 QStringList shortlist;
3449 if (strlist.size() < 4)
3451 shortlist << QString(
"0");
3452 shortlist << QString(
"0");
3456 unsigned int index = (
uint)(strlist.size()) - 2;
3457 shortlist << strlist[index++];
3458 shortlist << strlist[index++];
3474 QStringList strlist;
3476 #if defined(_WIN32) || defined(Q_OS_ANDROID)
3477 strlist <<
"0" <<
"0" <<
"0";
3483 strlist <<
"getloadavg() failed";
3487 strlist << QString::number(loads[0])
3488 << QString::number(loads[1])
3489 << QString::number(loads[2]);
3504 QStringList strlist;
3505 std::chrono::seconds uptime = 0s;
3508 strlist << QString::number(uptime.count());
3512 strlist <<
"Could not determine uptime.";
3526 QStringList strlist;
3541 QStringList strlist;
3547 if (
getMemStats(totalMB, freeMB, totalVM, freeVM))
3549 strlist << QString::number(totalMB) << QString::number(freeMB)
3550 << QString::number(totalVM) << QString::number(freeVM);
3555 strlist <<
"Could not determine memory stats.";
3569 QStringList strlist;
3584 bool checkSlaves = slist[1].toInt() != 0;
3586 QStringList::const_iterator it = slist.cbegin() + 2;
3589 bool exists =
false;
3602 QStringList outputlist( QString::number(
static_cast<int>(exists)) );
3617 exists = QFileInfo::exists(pburl);
3622 QStringList strlist( QString::number(
static_cast<int>(exists)) );
3634 QString storageGroup =
"Default";
3639 switch (slist.size()) {
3641 if (!slist[3].isEmpty())
3645 if (slist[2].isEmpty())
3646 storageGroup = slist[2];
3654 LOG(VB_GENERAL, LOG_ERR,
LOC +
3655 QString(
"ERROR checking for file, filename '%1' "
3656 "fails sanity checks").arg(
filename));
3663 LOG(VB_GENERAL, LOG_ERR,
LOC +
3664 "ERROR, invalid input count for QUERY_FILE_HASH");
3702 QString storageGroup =
"Default";
3703 QStringList retlist;
3705 if (slist.size() > 2)
3706 storageGroup = slist[2];
3712 LOG(VB_GENERAL, LOG_ERR,
LOC +
3713 QString(
"ERROR checking for file, filename '%1' "
3714 "fails sanity checks").arg(
filename));
3720 if (storageGroup.isEmpty())
3721 storageGroup =
"Default";
3727 if (!fullname.isEmpty())
3730 retlist << fullname;
3732 struct stat fileinfo {};
3733 if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
3735 retlist << QString::number(fileinfo.st_dev);
3736 retlist << QString::number(fileinfo.st_ino);
3737 retlist << QString::number(fileinfo.st_mode);
3738 retlist << QString::number(fileinfo.st_nlink);
3739 retlist << QString::number(fileinfo.st_uid);
3740 retlist << QString::number(fileinfo.st_gid);
3741 retlist << QString::number(fileinfo.st_rdev);
3742 retlist << QString::number(fileinfo.st_size);
3747 retlist << QString::number(fileinfo.st_blksize);
3748 retlist << QString::number(fileinfo.st_blocks);
3750 retlist << QString::number(fileinfo.st_atime);
3751 retlist << QString::number(fileinfo.st_mtime);
3752 retlist << QString::number(fileinfo.st_ctime);
3764 query.
prepare(
"SELECT MAX(endtime) FROM program WHERE manualid = 0;");
3774 QDateTime GuideDataThrough;
3776 QStringList strlist;
3780 if (GuideDataThrough.isNull())
3781 strlist << QString(
"0000-00-00 00:00");
3783 strlist << GuideDataThrough.toString(
"yyyy-MM-dd hh:mm");
3789 const QString& tmptable,
int recordid)
3793 QStringList strList;
3797 if (tmptable.isEmpty())
3809 query.
prepare(
"SELECT NULL FROM record "
3810 "WHERE recordid = :RECID;");
3816 record->m_recordID = recordid;
3817 if (record->Load() &&
3823 query.
prepare(
"DELETE FROM program WHERE manualid = :RECID;");
3833 strList << QString::number(0);
3834 strList << QString::number(0);
3844 QStringList strList;
3849 strList << QString::number(0);
3859 QStringList::const_iterator it = slist.cbegin() + 1;
3862 QStringList strlist;
3867 strlist << QString::number(0);
3876 QStringList strList;
3881 strList << QString::number(0);
3890 QStringList strList;
3892 if ((sList.size() < 4) || (sList.size() > 5))
3894 LOG(VB_GENERAL, LOG_ERR,
LOC +
3895 QString(
"HandleSGGetFileList: Invalid Request. %1")
3896 .arg(sList.join(
"[]:[]")));
3897 strList <<
"EMPTY LIST";
3903 const QString& wantHost = sList.at(1);
3904 QHostAddress wantHostaddr(wantHost);
3905 const QString& groupname = sList.at(2);
3906 const QString& path = sList.at(3);
3907 bool fileNamesOnly =
false;
3909 if (sList.size() >= 5)
3910 fileNamesOnly = (sList.at(4).toInt() != 0);
3912 bool slaveUnreachable =
false;
3914 LOG(VB_FILE, LOG_INFO,
LOC +
3915 QString(
"HandleSGGetFileList: group = %1 host = %2 "
3916 " path = %3 wanthost = %4")
3917 .arg(groupname, host, path, wantHost));
3921 if ((host.toLower() == wantHost.toLower()) ||
3922 (!addr.isEmpty() && addr == wantHostaddr.toString()))
3925 LOG(VB_FILE, LOG_INFO,
LOC +
"HandleSGGetFileList: Getting local info");
3936 LOG(VB_FILE, LOG_INFO,
LOC +
3937 "HandleSGGetFileList: Getting remote info");
3941 slaveUnreachable =
false;
3945 LOG(VB_FILE, LOG_INFO,
LOC +
3946 QString(
"HandleSGGetFileList: Failed to grab slave socket "
3947 ": %1 :").arg(wantHost));
3948 slaveUnreachable =
true;
3953 if (slaveUnreachable)
3954 strList <<
"SLAVE UNREACHABLE: " << host;
3956 if (strList.isEmpty() || (strList.at(0) ==
"0"))
3957 strList <<
"EMPTY LIST";
3967 QString storageGroup = slist[2];
3969 bool allowFallback =
true;
3970 bool useRegex =
false;
3971 QStringList fileList;
3973 if (!QHostAddress(
hostname).isNull())
3975 LOG(VB_GENERAL, LOG_ERR, QString(
"Mainserver: QUERY_FINDFILE called "
3976 "with IP (%1) instead of hostname. "
3977 "This is invalid.").arg(
hostname));
3983 if (storageGroup.isEmpty())
3984 storageGroup =
"Default";
3989 LOG(VB_GENERAL, LOG_ERR,
LOC +
3990 QString(
"ERROR QueryFindFile, filename '%1' "
3991 "fails sanity checks").arg(
filename));
3992 fileList <<
"ERROR: Bad/Missing Filename";
3997 if (slist.size() >= 5)
3998 useRegex = (slist[4].toInt() > 0);
4000 if (slist.size() >= 6)
4001 allowFallback = (slist[5].toInt() > 0);
4003 LOG(VB_FILE, LOG_INFO,
LOC +
4004 QString(
"Looking for file '%1' on host '%2' in group '%3' (useregex: %4, allowfallback: %5")
4018 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
4020 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Looking in dir '%1' for '%2'")
4021 .arg(fi.path(), fi.fileName()));
4023 for (
int x = 0; x < files.size(); x++)
4025 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Found '%1 - %2'").arg(x).arg(files[x]));
4028 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
4029 for (
const QString&
file : std::as_const(filteredFiles))
4033 fi.path() +
'/' +
file,
4049 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Checking remote host '%1' for file").arg(
hostname));
4057 if (!slaveFiles.isEmpty() && slaveFiles[0] !=
"NOT FOUND" && !slaveFiles[0].startsWith(
"ERROR: "))
4058 fileList += slaveFiles;
4064 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Slave '%1' was unreachable").arg(
hostname));
4065 fileList << QString(
"ERROR: SLAVE UNREACHABLE: %1").arg(
hostname);
4073 if (
m_ismaster && fileList.isEmpty() && allowFallback)
4078 QString sql =
"SELECT DISTINCT hostname "
4079 "FROM storagegroup "
4080 "WHERE groupname = :GROUP "
4081 "AND hostname != :HOSTNAME";
4083 query.
bindValue(
":GROUP", storageGroup);
4089 fileList <<
"ERROR: failed to get host list";
4105 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
4107 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Looking in dir '%1' for '%2'")
4108 .arg(fi.path(), fi.fileName()));
4110 for (
int x = 0; x < files.size(); x++)
4112 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Found '%1 - %2'").arg(x).arg(files[x]));
4115 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
4117 for (
const QString&
file : std::as_const(filteredFiles))
4121 fi.path() +
'/' +
file,
4128 if (!fname.isEmpty())
4143 if (!slaveFiles.isEmpty() && slaveFiles[0] !=
"NOT FOUND" && !slaveFiles[0].startsWith(
"ERROR: "))
4144 fileList += slaveFiles;
4150 if (!fileList.isEmpty())
4155 if (fileList.isEmpty())
4157 fileList <<
"NOT FOUND";
4158 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"File was not found"));
4162 for (
int x = 0; x < fileList.size(); x++)
4164 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"File %1 was found at: '%2'").arg(x).arg(fileList[0]));
4177 QStringList strList;
4179 if (sList.size() < 4)
4181 LOG(VB_GENERAL, LOG_ERR,
LOC +
4182 QString(
"HandleSGFileQuery: Invalid Request. %1")
4183 .arg(sList.join(
"[]:[]")));
4184 strList <<
"EMPTY LIST";
4190 const QString& wantHost = sList.at(1);
4191 QHostAddress wantHostaddr(wantHost);
4192 const QString& groupname = sList.at(2);
4193 const QString&
filename = sList.at(3);
4195 bool allowFallback =
true;
4196 if (sList.size() >= 5)
4197 allowFallback = (sList.at(4).toInt() > 0);
4198 LOG(VB_FILE, LOG_ERR, QString(
"HandleSGFileQuery - allowFallback: %1").arg(allowFallback));
4200 bool slaveUnreachable =
false;
4202 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"HandleSGFileQuery: %1")
4207 if ((host.toLower() == wantHost.toLower()) ||
4208 (!addr.isEmpty() && addr == wantHostaddr.toString()))
4210 LOG(VB_FILE, LOG_INFO,
LOC +
"HandleSGFileQuery: Getting local info");
4219 LOG(VB_FILE, LOG_INFO,
LOC +
4220 "HandleSGFileQuery: Getting remote info");
4223 slaveUnreachable =
false;
4227 LOG(VB_FILE, LOG_INFO,
LOC +
4228 QString(
"HandleSGFileQuery: Failed to grab slave socket : %1 :")
4230 slaveUnreachable =
true;
4235 if (slaveUnreachable)
4236 strList <<
"SLAVE UNREACHABLE: " << wantHost;
4238 if (strList.count() == 0 || (strList.at(0) ==
"0"))
4239 strList <<
"EMPTY LIST";
4247 QString pbshost =
pbs->getHostname();
4249 QStringList strlist;
4258 if ((cardid != -1) && (cardid != elink->GetInputID()))
4261 if (elink->IsLocal())
4264 enchost = elink->GetHostName();
4266 if ((enchost == pbshost) &&
4267 (elink->IsConnected()) &&
4268 (!elink->IsBusy()) &&
4269 (!elink->IsTunerLocked()))
4283 QString msg = QString(
"Cardid %1 LOCKed for external use on %2.")
4284 .arg(retval).arg(pbshost);
4285 LOG(VB_GENERAL, LOG_INFO,
LOC + msg);
4288 query.
prepare(
"SELECT videodevice, audiodevice, "
4291 "WHERE cardid = :CARDID ;");
4297 strlist << QString::number(retval)
4298 << query.
value(0).toString()
4299 << query.
value(1).toString()
4300 << query.
value(2).toString();
4308 LOG(VB_GENERAL, LOG_ERR,
LOC +
4309 "MainServer::LockTuner(): Could not find "
4310 "card info in database");
4315 strlist <<
"-2" <<
"" <<
"" <<
"";
4321 strlist <<
"-1" <<
"" <<
"" <<
"";
4328 QStringList strlist;
4335 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleFreeTuner() " +
4336 QString(
"Unknown encoder: %1").arg(cardid));
4337 strlist <<
"FAILED";
4344 QString msg = QString(
"Cardid %1 FREED from external use on %2.")
4345 .arg(cardid).arg(
pbs->getHostname());
4346 LOG(VB_GENERAL, LOG_INFO,
LOC + msg);
4366 uint excluded_input)
4368 LOG(VB_CHANNEL, LOG_INFO,
4369 LOC + QString(
"Excluding input %1")
4370 .arg(excluded_input));
4373 std::vector<InputInfo> busyinputs;
4374 std::vector<InputInfo> freeinputs;
4375 QMap<uint, QSet<uint> > groupids;
4385 if (!elink->IsConnected() || elink->IsTunerLocked())
4387 LOG(VB_CHANNEL, LOG_INFO,
4388 LOC + QString(
"Input %1 is locked or not connected")
4393 std::vector<uint> infogroups;
4395 for (
uint group : infogroups)
4399 if (info.
m_inputId != excluded_input && elink->IsBusy(&busyinfo))
4401 LOG(VB_CHANNEL, LOG_DEBUG,
4402 LOC + QString(
"Input %1 is busy on %2/%3")
4406 busyinputs.push_back(info);
4410 LOG(VB_CHANNEL, LOG_DEBUG,
4411 LOC + QString(
"Input %1 is free")
4413 freeinputs.push_back(info);
4420 for (
auto & busyinfo : busyinputs)
4422 auto freeiter = freeinputs.begin();
4423 while (freeiter != freeinputs.end())
4427 if ((groupids[busyinfo.m_inputId] & groupids[freeinfo.
m_inputId])
4434 if (busyinfo.m_sourceId == freeinfo.
m_sourceId)
4436 LOG(VB_CHANNEL, LOG_DEBUG,
4437 LOC + QString(
"Input %1 is limited to %2/%3 by input %4")
4438 .arg(freeinfo.
m_inputId).arg(busyinfo.m_chanId)
4439 .arg(busyinfo.m_mplexId).arg(busyinfo.m_inputId));
4440 freeinfo.
m_chanId = busyinfo.m_chanId;
4441 freeinfo.
m_mplexId = busyinfo.m_mplexId;
4446 LOG(VB_CHANNEL, LOG_DEBUG,
4447 LOC + QString(
"Input %1 is unavailable by input %2")
4448 .arg(freeinfo.
m_inputId).arg(busyinfo.m_inputId));
4449 freeiter = freeinputs.erase(freeiter);
4455 QStringList strlist;
4456 for (
auto & input : freeinputs)
4458 LOG(VB_CHANNEL, LOG_INFO,
4459 LOC + QString(
"Input %1 is available on %2/%3")
4460 .arg(input.m_inputId).arg(input.m_chanId)
4461 .arg(input.m_mplexId));
4462 input.ToStringList(strlist);
4465 if (strlist.empty())
4490 if (commands.size() < 2 || slist.size() < 2)
4493 int recnum = commands[1].toInt();
4500 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleRecorderQuery() " +
4501 QString(
"Unknown encoder: %1").arg(recnum));
4502 QStringList retlist(
"bad" );
4508 QString command = slist[1];
4510 QStringList retlist;
4515 LOG(VB_GENERAL, LOG_ERR,
LOC +
" MainServer::HandleRecorderQuery() " +
4516 QString(
"Command %1 for unconnected encoder %2")
4517 .arg(command).arg(recnum));
4523 if (command ==
"IS_RECORDING")
4527 else if (command ==
"GET_FRAMERATE")
4531 else if (command ==
"GET_FRAMES_WRITTEN")
4535 else if (command ==
"GET_FILE_POSITION")
4539 else if (command ==
"GET_MAX_BITRATE")
4543 else if (command ==
"GET_CURRENT_RECORDING")
4558 else if (command ==
"GET_KEYFRAME_POS")
4560 long long desired = slist[2].toLongLong();
4563 else if (command ==
"FILL_POSITION_MAP")
4565 int64_t start = slist[2].toLongLong();
4566 int64_t end = slist[3].toLongLong();
4575 for (
auto it = map.cbegin(); it != map.cend(); ++it)
4577 retlist += QString::number(it.key());
4578 retlist += QString::number(*it);
4580 if (retlist.empty())
4584 else if (command ==
"FILL_DURATION_MAP")
4586 int64_t start = slist[2].toLongLong();
4587 int64_t end = slist[3].toLongLong();
4596 for (
auto it = map.cbegin(); it != map.cend(); ++it)
4598 retlist += QString::number(it.key());
4599 retlist += QString::number(*it);
4601 if (retlist.empty())
4605 else if (command ==
"GET_RECORDING")
4620 else if (command ==
"FRONTEND_READY")
4625 else if (command ==
"CANCEL_NEXT_RECORDING")
4627 QString cancel = slist[2];
4628 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
4629 QString(
"Received: CANCEL_NEXT_RECORDING %1").arg(cancel));
4633 else if (command ==
"SPAWN_LIVETV")
4635 QString chainid = slist[2];
4646 enc->
SpawnLiveTV(chain, slist[3].toInt() != 0, slist[4]);
4649 else if (command ==
"STOP_LIVETV")
4666 else if (command ==
"PAUSE")
4671 else if (command ==
"FINISH_RECORDING")
4676 else if (command ==
"SET_LIVE_RECORDING")
4678 int recording = slist[2].toInt();
4682 else if (command ==
"GET_INPUT")
4685 ret = (ret.isEmpty()) ?
"UNKNOWN" : ret;
4688 else if (command ==
"SET_INPUT")
4690 QString input = slist[2];
4691 QString ret = enc->
SetInput(input);
4692 ret = (ret.isEmpty()) ?
"UNKNOWN" : ret;
4695 else if (command ==
"TOGGLE_CHANNEL_FAVORITE")
4697 QString changroup = slist[2];
4701 else if (command ==
"CHANGE_CHANNEL")
4707 else if (command ==
"SET_CHANNEL")
4709 QString name = slist[2];
4713 else if (command ==
"SET_SIGNAL_MONITORING_RATE")
4715 auto rate = std::chrono::milliseconds(slist[2].toInt());
4716 int notifyFrontend = slist[3].toInt();
4718 retlist << QString::number(oldrate.count());
4720 else if (command ==
"GET_COLOUR")
4723 retlist << QString::number(ret);
4725 else if (command ==
"GET_CONTRAST")
4728 retlist << QString::number(ret);
4730 else if (command ==
"GET_BRIGHTNESS")
4733 retlist << QString::number(ret);
4735 else if (command ==
"GET_HUE")
4738 retlist << QString::number(ret);
4740 else if (command ==
"CHANGE_COLOUR")
4742 int type = slist[2].toInt();
4743 bool up = slist[3].toInt() != 0;
4746 retlist << QString::number(ret);
4748 else if (command ==
"CHANGE_CONTRAST")
4750 int type = slist[2].toInt();
4751 bool up = slist[3].toInt() != 0;
4754 retlist << QString::number(ret);
4756 else if (command ==
"CHANGE_BRIGHTNESS")
4758 int type= slist[2].toInt();
4759 bool up = slist[3].toInt() != 0;
4762 retlist << QString::number(ret);
4764 else if (command ==
"CHANGE_HUE")
4766 int type= slist[2].toInt();
4767 bool up = slist[3].toInt() != 0;
4770 retlist << QString::number(ret);
4772 else if (command ==
"CHECK_CHANNEL")
4774 QString name = slist[2];
4775 retlist << QString::number((
int)(enc->
CheckChannel(name)));
4777 else if (command ==
"SHOULD_SWITCH_CARD")
4779 QString chanid = slist[2];
4782 else if (command ==
"CHECK_CHANNEL_PREFIX")
4784 QString needed_spacer;
4785 QString
prefix = slist[2];
4786 uint complete_valid_channel_on_rec = 0;
4787 bool is_extra_char_useful =
false;
4790 prefix, complete_valid_channel_on_rec,
4791 is_extra_char_useful, needed_spacer);
4793 retlist << QString::number((
int)match);
4794 retlist << QString::number(complete_valid_channel_on_rec);
4795 retlist << QString::number((
int)is_extra_char_useful);
4796 retlist << ((needed_spacer.isEmpty()) ? QString(
"X") : needed_spacer);
4798 else if (command ==
"GET_NEXT_PROGRAM_INFO" && (slist.size() >= 6))
4800 QString channelname = slist[2];
4801 uint chanid = slist[3].toUInt();
4803 QString starttime = slist[5];
4806 QString subtitle =
"";
4808 QString category =
"";
4809 QString endtime =
"";
4810 QString callsign =
"";
4811 QString iconpath =
"";
4812 QString seriesid =
"";
4813 QString programid =
"";
4816 title, subtitle, desc, category, starttime,
4817 endtime, callsign, iconpath, channelname, chanid,
4818 seriesid, programid);
4829 retlist << QString::number(chanid);
4833 else if (command ==
"GET_CHANNEL_INFO")
4835 uint chanid = slist[2].toUInt();
4837 QString callsign =
"";
4838 QString channum =
"";
4839 QString channame =
"";
4843 callsign, channum, channame, xmltv);
4845 retlist << QString::number(chanid);
4846 retlist << QString::number(sourceid);
4854 LOG(VB_GENERAL, LOG_ERR,
LOC +
4855 QString(
"Unknown command: %1").arg(command));
4867 int recnum = commands[1].toInt();
4874 LOG(VB_GENERAL, LOG_ERR,
LOC +
"MainServer::HandleSetNextLiveTVDir() " +
4875 QString(
"Unknown encoder: %1").arg(recnum));
4876 QStringList retlist(
"bad" );
4885 QStringList retlist(
"OK" );
4893 uint chanid = slist[1].toUInt();
4894 uint sourceid = slist[2].toUInt();
4895 QString oldcnum =
cleanup(slist[3]);
4896 QString callsign =
cleanup(slist[4]);
4897 QString channum =
cleanup(slist[5]);
4898 QString channame =
cleanup(slist[6]);
4899 QString xmltv =
cleanup(slist[7]);
4901 QStringList retlist;
4902 if (!chanid || !sourceid)
4914 ok &= encoder->SetChannelInfo(chanid, sourceid, oldcnum,
4915 callsign, channum, channame, xmltv);
4920 retlist << ((ok) ?
"1" :
"0");
4929 int recnum = commands[1].toInt();
4930 QStringList retlist;
4937 LOG(VB_GENERAL, LOG_ERR,
LOC +
4938 QString(
"HandleRemoteEncoder(cmd %1) ").arg(slist[1]) +
4939 QString(
"Unknown encoder: %1").arg(recnum));
4948 QString command = slist[1];
4950 if (command ==
"GET_STATE")
4952 retlist << QString::number((
int)enc->
GetState());
4954 else if (command ==
"GET_SLEEPSTATUS")
4958 else if (command ==
"GET_FLAGS")
4960 retlist << QString::number(enc->
GetFlags());
4962 else if (command ==
"IS_BUSY")
4964 std::chrono::seconds time_buffer = 5s;
4965 if (slist.size() >= 3)
4966 time_buffer = std::chrono::seconds(slist[2].toInt());
4968 retlist << QString::number((
int)enc->
IsBusy(&busy_input, time_buffer));
4971 else if (command ==
"MATCHES_RECORDING" &&
4974 QStringList::const_iterator it = slist.cbegin() + 2;
4979 else if (command ==
"START_RECORDING" &&
4982 QStringList::const_iterator it = slist.cbegin() + 2;
4989 else if (command ==
"GET_RECORDING_STATUS")
4993 else if (command ==
"RECORD_PENDING" &&
4996 auto secsleft = std::chrono::seconds(slist[2].toInt());
4997 int haslater = slist[3].toInt();
4998 QStringList::const_iterator it = slist.cbegin() + 4;
5005 else if (command ==
"CANCEL_NEXT_RECORDING" &&
5006 (slist.size() >= 3))
5008 bool cancel = (
bool) slist[2].toInt();
5012 else if (command ==
"STOP_RECORDING")
5017 else if (command ==
"GET_MAX_BITRATE")
5021 else if (command ==
"GET_CURRENT_RECORDING")
5049 if (
pbs->isMediaServer())
5060 QStringList retlist;
5062 retlist.push_front(QString::number(retlist.size()));
5069 QStringList retlist;
5070 const QString& queryhostname = slist[1];
5075 if (slave !=
nullptr)
5091 QString fskey = fsInfo->getHostname() +
":" + fsInfo->getPath();
5101 size_t totalKBperMin = 0;
5106 if (!enc->IsConnected() || !enc->IsBusy())
5109 long long maxBitrate = enc->GetMaxBitrate();
5111 maxBitrate = 19500000LL;
5112 long long thisKBperMin = (((size_t)maxBitrate)*((size_t)15))>>11;
5113 totalKBperMin += thisKBperMin;
5114 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Cardid %1: max bitrate %2 KB/min")
5115 .arg(enc->GetInputID()).arg(thisKBperMin));
5119 LOG(VB_FILE, LOG_INFO,
LOC +
5120 QString(
"Maximal bitrate of busy encoders is %1 KB/min")
5121 .arg(totalKBperMin));
5123 return totalKBperMin;
5130 int64_t totalKB = -1;
5131 int64_t usedKB = -1;
5132 QMap <QString, bool>foundDirs;
5133 QString localStr =
"1";
5134 struct statfs statbuf {};
5136 groups.removeAll(
"LiveTV");
5137 QString specialGroups = groups.join(
"', '");
5138 QString sql = QString(
"SELECT MIN(id),dirname "
5139 "FROM storagegroup "
5140 "WHERE hostname = :HOSTNAME "
5141 "AND groupname NOT IN ( '%1' ) "
5142 "GROUP BY dirname;").arg(specialGroups);
5153 query.
prepare(
"SELECT MIN(id),dirname "
5154 "FROM storagegroup "
5155 "WHERE groupname = :GROUP "
5156 "GROUP BY dirname;");
5165 while (query.
next())
5167 dirID = query.
value(0).toString();
5171 currentDir = QString::fromUtf8(query.
value(1)
5172 .toByteArray().constData());
5173 if (currentDir.endsWith(
"/"))
5174 currentDir.remove(currentDir.length() - 1, 1);
5176 checkDir.setPath(currentDir);
5177 if (!foundDirs.contains(currentDir))
5179 if (checkDir.exists())
5181 QByteArray cdir = currentDir.toLatin1();
5183 memset(&statbuf, 0,
sizeof(statbuf));
5187 if (
statfs(currentDir.toLocal8Bit().constData(), &statbuf) == 0)
5190 char *fstypename = statbuf.f_fstypename;
5191 if ((!strcmp(fstypename,
"nfs")) ||
5192 (!strcmp(fstypename,
"afpfs")) ||
5193 (!strcmp(fstypename,
"smbfs")))
5195 #elif defined(__linux__)
5196 long fstype = statbuf.f_type;
5197 if ((fstype == 0x6969) ||
5198 (fstype == 0x517B) ||
5199 (fstype == (
long)0xFF534D42))
5206 strlist << currentDir;
5207 strlist << localStr;
5210 strlist << QString::number(bSize);
5211 strlist << QString::number(totalKB);
5212 strlist << QString::number(usedKB);
5214 foundDirs[currentDir] =
true;
5217 foundDirs[currentDir] =
false;
5224 QMap <QString, bool> backendsCounted;
5225 std::list<PlaybackSock *> localPlaybackList;
5231 if ((
pbs->IsDisconnected()) ||
5232 (!
pbs->isMediaServer()) ||
5234 (backendsCounted.contains(
pbs->getHostname())))
5237 backendsCounted[
pbs->getHostname()] =
true;
5239 localPlaybackList.push_back(
pbs);
5240 allHostList +=
"," +
pbs->getHostname();
5245 for (
auto &
pbs : localPlaybackList) {
5246 pbs->GetDiskSpace(strlist);
5254 QList<FileSystemInfo> fsInfos;
5255 QStringList::const_iterator it = strlist.cbegin();
5256 while (it != strlist.cend())
5262 fsInfo.
setLocal((*(it++)).toInt() > 0);
5269 fsInfos.push_back(fsInfo);
5275 maxWriteFiveSec = std::max((int64_t)2048, maxWriteFiveSec);
5277 for (
auto it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
5279 if (it1->getFSysID() == -1)
5283 it1->getHostname().section(
".", 0, 0) +
":" + it1->getPath());
5286 for (
auto it2 = it1 + 1; it2 != fsInfos.end(); )
5290 int bSize = std::max(32, std::max(it1->getBlockSize(), it2->getBlockSize()) / 1024);
5291 int64_t diffSize = it1->getTotalSpace() - it2->getTotalSpace();
5292 int64_t diffUsed = it1->getUsedSpace() - it2->getUsedSpace();
5294 diffSize = 0 - diffSize;
5296 diffUsed = 0 - diffUsed;
5298 if (it2->getFSysID() == -1 && (diffSize <= bSize) &&
5299 (diffUsed <= maxWriteFiveSec))
5301 if (!it1->getHostname().contains(it2->getHostname()))
5302 it1->setHostname(it1->getHostname() +
"," + it2->getHostname());
5303 it1->setPath(it1->getPath() +
"," +
5304 it2->getHostname().section(
".", 0, 0) +
":" + it2->getPath());
5305 it2 = fsInfos.erase(it2);
5317 for (
const auto & fsInfo : std::as_const(fsInfos))
5319 strlist << fsInfo.getHostname();
5320 strlist << fsInfo.getPath();
5321 strlist << QString::number(static_cast<int>(fsInfo.isLocal()));
5322 strlist << QString::number(fsInfo.getFSysID());
5323 strlist << QString::number(fsInfo.getGroupID());
5324 strlist << QString::number(fsInfo.getBlockSize());
5325 strlist << QString::number(fsInfo.getTotalSpace());
5326 strlist << QString::number(fsInfo.getUsedSpace());
5328 totalKB += fsInfo.getTotalSpace();
5329 usedKB += fsInfo.getUsedSpace();
5334 strlist << allHostList;
5335 strlist <<
"TotalDiskSpace";
5340 strlist << QString::number(totalKB);
5341 strlist << QString::number(usedKB);
5356 QStringList strlist;
5363 QStringList::const_iterator it = strlist.cbegin();
5364 while (it != strlist.cend())
5368 fsInfo.
setLocal((*(it++)).toInt() > 0);
5376 fsInfos.push_back(fsInfo);
5379 LOG(VB_SCHEDULE | VB_FILE, LOG_DEBUG,
LOC +
5380 "Determining unique filesystems");
5383 maxWriteFiveSec = std::max((
size_t)2048, maxWriteFiveSec);
5387 QList<FileSystemInfo>::iterator it1;
5390 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5391 "--- GetFilesystemInfos directory list start ---");
5392 for (it1 = fsInfos.begin(); it1 != fsInfos.end(); ++it1)
5395 QString(
"Dir: %1:%2")
5396 .arg(it1->getHostname(), it1->getPath());
5397 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC + msg) ;
5398 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5399 QString(
" Location: %1")
5400 .arg(it1->isLocal() ?
"Local" :
"Remote"));
5401 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5402 QString(
" fsID : %1")
5403 .arg(it1->getFSysID()));
5404 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5405 QString(
" dirID : %1")
5406 .arg(it1->getGroupID()));
5407 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5408 QString(
" BlkSize : %1")
5409 .arg(it1->getBlockSize()));
5410 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5411 QString(
" TotalKB : %1")
5412 .arg(it1->getTotalSpace()));
5413 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5414 QString(
" UsedKB : %1")
5415 .arg(it1->getUsedSpace()));
5416 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5417 QString(
" FreeKB : %1")
5418 .arg(it1->getFreeSpace()));
5420 LOG(VB_FILE | VB_SCHEDULE, LOG_INFO,
LOC +
5421 "--- GetFilesystemInfos directory list end ---");
5430 const QString &src,
const QString &dst)
5433 QStringList retlist;
5435 if (src.isEmpty() || dst.isEmpty()
5436 || src.contains(
"..") || dst.contains(
".."))
5438 LOG(VB_GENERAL, LOG_ERR,
LOC +
5439 QString(
"HandleMoveFile: ERROR moving file '%1' -> '%2', "
5440 "a path fails sanity checks").arg(src, dst));
5441 retlist <<
"0" <<
"Invalid path";
5446 QString srcAbs = sgroup.
FindFile(src);
5447 if (srcAbs.isEmpty())
5449 LOG(VB_GENERAL, LOG_ERR,
LOC +
5450 QString(
"HandleMoveFile: Unable to find %1").arg(src));
5451 retlist <<
"0" <<
"Source file not found";
5459 QString dstAbs = sgroup.
FindFile(dst);
5460 if (!dstAbs.isEmpty() && QFileInfo(dstAbs).isFile())
5462 LOG(VB_GENERAL, LOG_ERR,
LOC +
5463 QString(
"HandleMoveFile: Destination exists at %1").arg(dstAbs));
5464 retlist <<
"0" <<
"Destination file exists";
5470 int sgPathSize = srcAbs.size() - src.size();
5471 dstAbs = srcAbs.mid(0, sgPathSize) + dst;
5485 LOG(VB_FILE, LOG_INFO, QString(
"MainServer::RenameThread: Renaming %1 -> %2")
5488 QStringList retlist;
5489 QFileInfo fi(
m_dst);
5491 if (QDir().mkpath(fi.path()) && QFile::rename(
m_src,
m_dst))
5497 retlist <<
"0" <<
"Rename failed";
5498 LOG(VB_FILE, LOG_ERR,
"MainServer::DoRenameThread: Rename failed");
5531 QStringList retlist;
5537 LOG(VB_GENERAL, LOG_ERR,
LOC +
5538 QString(
"ERROR deleting file, filename '%1' "
5539 "fails sanity checks").arg(
filename));
5550 if (fullfile.isEmpty()) {
5551 LOG(VB_GENERAL, LOG_ERR,
LOC +
5552 QString(
"Unable to find %1 in HandleDeleteFile()") .arg(
filename));
5561 QFile checkFile(fullfile);
5568 const QFileInfo info(fullfile);
5572 if ((fd < 0) && checkFile.exists())
5574 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error deleting file: %1.")
5594 auto *truncateThread =
new TruncateThread(
this, fullfile, fd, size);
5595 truncateThread->run();
5606 const QString &starttime,
5611 pbssock =
pbs->getSocket();
5614 frm_dir_map_t::const_iterator it;
5616 QStringList retlist;
5619 const ProgramInfo pginfo(chanid.toUInt(), recstartdt);
5628 for (it = markMap.cbegin(); it != markMap.cend(); ++it)
5631 QString intstr = QString(
"%1").arg(*it);
5633 retlist << QString::number(it.key());
5638 retlist.prepend(QString(
"%1").arg(rowcnt));
5647 const QString &starttime,
5662 const QString &starttime,
5678 const QString &starttime,
5689 pbssock =
pbs->getSocket();
5693 chanid.toUInt(), recstartts);
5695 QStringList retlist;
5696 retlist << QString::number(bookmark);
5715 pbssock =
pbs->getSocket();
5717 QString chanid = tokens[1];
5718 QString starttime = tokens[2];
5719 long long bookmark = tokens[3].toLongLong();
5722 QStringList retlist;
5732 retlist <<
"FAILED";
5745 pbssock =
pbs->getSocket();
5747 const QString&
hostname = tokens[1];
5748 const QString& setting = tokens[2];
5749 QStringList retlist;
5753 retlist << retvalue;
5761 bool synchronous = (command[0] ==
"DOWNLOAD_FILE_NOW");
5762 const QString& srcURL = command[1];
5763 const QString& storageGroup = command[2];
5768 QStringList retlist;
5772 pbssock =
pbs->getSocket();
5776 QFileInfo finfo(srcURL);
5780 if (outDir.isEmpty())
5782 LOG(VB_GENERAL, LOG_ERR,
LOC +
5783 QString(
"Unable to determine directory "
5784 "to write to in %1 write command").arg(command[0]));
5785 retlist <<
"downloadfile_directory_not_found";
5794 LOG(VB_GENERAL, LOG_ERR,
LOC +
5795 QString(
"ERROR: %1 write filename '%2' does not pass "
5796 "sanity checks.") .arg(command[0],
filename));
5797 retlist <<
"downloadfile_filename_dangerous";
5838 pbssock =
pbs->getSocket();
5840 const QString&
hostname = tokens[1];
5841 const QString& setting = tokens[2];
5842 const QString& svalue = tokens[3];
5843 QStringList retlist;
5858 QStringList retlist;
5878 QStringList strlist;
5884 QString sql =
"SELECT DISTINCT hostname "
5885 "FROM storagegroup "
5886 "WHERE groupname = 'Music'";
5898 LOG(VB_GENERAL, LOG_INFO,
LOC +
5899 QString(
"HandleScanMusic: running filescanner on master BE '%1'").arg(
hostname));
5911 LOG(VB_GENERAL, LOG_INFO,
LOC +
5912 QString(
"HandleScanMusic: asking slave '%1' to run file scanner").arg(
hostname));
5918 LOG(VB_GENERAL, LOG_INFO,
LOC +
5919 QString(
"HandleScanMusic: Failed to grab slave socket on '%1'").arg(
hostname));
5928 LOG(VB_GENERAL, LOG_INFO,
LOC +
5929 QString(
"HandleScanMusic: running filescanner on slave BE '%1'")
5947 QStringList strlist;
5951 const QString&
hostname = slist[1];
5959 LOG(VB_GENERAL, LOG_INFO,
LOC +
5960 QString(
"HandleMusicTagUpdateVolatile: asking slave '%1' to update the metadata").arg(
hostname));
5970 LOG(VB_GENERAL, LOG_INFO,
LOC +
5971 QString(
"HandleMusicTagUpdateVolatile: Failed to grab slave socket on '%1'").arg(
hostname));
5973 strlist <<
"ERROR: slave not found";
5982 QStringList paramList;
5983 paramList.append(QString(
"--songid='%1'").arg(slist[2]));
5984 paramList.append(QString(
"--rating='%1'").arg(slist[3]));
5985 paramList.append(QString(
"--playcount='%1'").arg(slist[4]));
5986 paramList.append(QString(
"--lastplayed='%1'").arg(slist[5]));
5988 QString command =
GetAppBinDir() +
"mythutil --updatemeta " + paramList.join(
" ");
5990 LOG(VB_GENERAL, LOG_INFO,
LOC +
5991 QString(
"HandleMusicTagUpdateVolatile: running %1'").arg(command));
6007 QStringList strlist;
6011 const QString&
hostname = slist[1];
6019 LOG(VB_GENERAL, LOG_INFO,
LOC +
6020 QString(
"HandleMusicCalcTrackLen: asking slave '%1' to update the track length").arg(
hostname));
6030 LOG(VB_GENERAL, LOG_INFO,
LOC +
6031 QString(
"HandleMusicCalcTrackLen: Failed to grab slave socket on '%1'").arg(
hostname));
6033 strlist <<
"ERROR: slave not found";
6042 QStringList paramList;
6043 paramList.append(QString(
"--songid='%1'").arg(slist[2]));
6045 QString command =
GetAppBinDir() +
"mythutil --calctracklen " + paramList.join(
" ");
6047 LOG(VB_GENERAL, LOG_INFO,
LOC +
6048 QString(
"HandleMusicCalcTrackLen: running %1'").arg(command));
6065 QStringList strlist;
6069 const QString&
hostname = slist[1];
6077 LOG(VB_GENERAL, LOG_INFO,
LOC +
6078 QString(
"HandleMusicTagUpdateMetadata: asking slave '%1' "
6079 "to update the metadata").arg(
hostname));
6089 LOG(VB_GENERAL, LOG_INFO,
LOC +
6090 QString(
"HandleMusicTagUpdateMetadata: Failed to grab "
6091 "slave socket on '%1'").arg(
hostname));
6093 strlist <<
"ERROR: slave not found";
6102 int songID = slist[2].toInt();
6108 LOG(VB_GENERAL, LOG_ERR,
LOC +
6109 QString(
"HandleMusicTagUpdateMetadata: "
6110 "Cannot find metadata for trackid: %1")
6113 strlist <<
"ERROR: track not found";
6126 LOG(VB_GENERAL, LOG_ERR,
LOC +
6127 QString(
"HandleMusicTagUpdateMetadata: "
6128 "Failed to write to tag for trackid: %1")
6131 strlist <<
"ERROR: write to tag failed";
6151 QStringList strlist;
6155 const QString&
hostname = slist[1];
6163 LOG(VB_GENERAL, LOG_INFO,
LOC +
6164 QString(
"HandleMusicFindAlbumArt: asking slave '%1' "
6165 "to update the albumart").arg(
hostname));
6175 LOG(VB_GENERAL, LOG_INFO,
LOC +
6176 QString(
"HandleMusicFindAlbumArt: Failed to grab "
6177 "slave socket on '%1'").arg(
hostname));
6179 strlist <<
"ERROR: slave not found";
6188 int songID = slist[2].toInt();
6189 bool updateDatabase = (slist[3].toInt() == 1);
6195 LOG(VB_GENERAL, LOG_ERR,
LOC +
6196 QString(
"HandleMusicFindAlbumArt: "
6197 "Cannot find metadata for trackid: %1").arg(songID));
6199 strlist <<
"ERROR: track not found";
6209 QDir dir = fi.absoluteDir();
6212 "*.png;*.jpg;*.jpeg;*.gif;*.bmp");
6213 dir.setNameFilters(nameFilter.split(
";"));
6215 QStringList files = dir.entryList();
6220 fi.setFile(mdata->
Filename(
false));
6221 QString startDir = fi.path();
6223 for (
const QString&
file : std::as_const(files))
6227 image->m_filename = startDir +
'/' + fi.fileName();
6229 image->m_embedded =
false;
6231 image->m_description =
"";
6232 images->addImage(image);
6244 for (
int x = 0; x < artList.count(); x++)
6248 images->addImage(image);
6256 LOG(VB_GENERAL, LOG_ERR,
LOC +
6257 QString(
"HandleMusicFindAlbumArt: "
6258 "Failed to find a tagger for trackid: %1").arg(songID));
6263 images->dumpToDatabase();
6266 strlist.append(QString(
"%1").arg(images->getImageCount()));
6268 for (
uint x = 0; x < images->getImageCount(); x++)
6271 strlist.append(QString(
"%1").arg(image->
m_id));
6272 strlist.append(QString(
"%1").arg((
int)image->
m_imageType));
6273 strlist.append(QString(
"%1").arg(
static_cast<int>(image->
m_embedded)));
6281 QStringList paramList;
6282 paramList.append(QString(
"--songid='%1'").arg(mdata->
ID()));
6283 paramList.append(QString(
"--imagetype='%1'").arg(image->
m_imageType));
6285 QString command =
GetAppBinDir() +
"mythutil --extractimage " + paramList.join(
" ");
6303 QStringList strlist;
6307 const QString&
hostname = slist[1];
6308 const QString& songid = slist[2];
6309 const QString& imagetype = slist[3];
6317 LOG(VB_GENERAL, LOG_INFO,
LOC +
6318 QString(
"HandleMusicTagGetImage: asking slave '%1' to "
6319 "extract the image").arg(
hostname));
6329 LOG(VB_GENERAL, LOG_INFO,
LOC +
6330 QString(
"HandleMusicTagGetImage: Failed to grab slave "
6335 QStringList paramList;
6336 paramList.append(QString(
"--songid='%1'").arg(songid));
6337 paramList.append(QString(
"--imagetype='%1'").arg(imagetype));
6339 QString command =
GetAppBinDir() +
"mythutil --extractimage " + paramList.join(
" ");
6357 QStringList strlist;
6361 const QString&
hostname = slist[1];
6369 LOG(VB_GENERAL, LOG_INFO,
LOC +
6370 QString(
"HandleMusicTagChangeImage: asking slave '%1' "
6371 "to update the metadata").arg(
hostname));
6381 LOG(VB_GENERAL, LOG_INFO,
LOC +
6382 QString(
"HandleMusicTagChangeImage: Failed to grab "
6383 "slave socket on '%1'").arg(
hostname));
6385 strlist <<
"ERROR: slave not found";
6393 int songID = slist[2].toInt();
6394 auto oldType = (
ImageType)slist[3].toInt();
6395 auto newType = (
ImageType)slist[4].toInt();
6402 LOG(VB_GENERAL, LOG_ERR,
LOC +
6403 QString(
"HandleMusicTagChangeImage: "
6404 "Cannot find metadata for trackid: %1")
6407 strlist <<
"ERROR: track not found";
6448 LOG(VB_GENERAL, LOG_ERR,
"HandleMusicTagChangeImage: failed to change image type");
6450 strlist <<
"ERROR: failed to change image type";
6468 image->
m_filename = fi.path() + QString(
"/%1-%2.jpg")
6482 QStringList paramList;
6483 paramList.append(QString(
"--songid='%1'").arg(mdata->
ID()));
6484 paramList.append(QString(
"--imagetype='%1'").arg(image->
m_imageType));
6486 QString command =
GetAppBinDir() +
"mythutil --extractimage " + paramList.join(
" ");
6499 image->
m_filename = fi.absolutePath() + QString(
"/%1.jpg")
6524 QStringList strlist;
6528 const QString&
hostname = slist[1];
6536 LOG(VB_GENERAL, LOG_INFO,
LOC +
6537 QString(
"HandleMusicTagAddImage: asking slave '%1' "
6538 "to add the image").arg(
hostname));
6548 LOG(VB_GENERAL, LOG_INFO,
LOC +
6549 QString(
"HandleMusicTagAddImage: Failed to grab "
6550 "slave socket on '%1'").arg(
hostname));
6552 strlist <<
"ERROR: slave not found";
6561 int songID = slist[2].toInt();
6562 const QString&
filename = slist[3];
6563 auto imageType = (
ImageType) slist[4].toInt();
6569 LOG(VB_GENERAL, LOG_ERR,
LOC +
6570 QString(
"HandleMusicTagAddImage: Cannot find metadata for trackid: %1")
6573 strlist <<
"ERROR: track not found";
6585 LOG(VB_GENERAL, LOG_ERR,
LOC +
6586 "HandleMusicTagAddImage: failed to find a tagger for track");
6588 strlist <<
"ERROR: tagger not found";
6599 LOG(VB_GENERAL, LOG_ERR,
LOC +
6600 "HandleMusicTagAddImage: asked to write album art to the tag "
6601 "but the tagger doesn't support it!");
6603 strlist <<
"ERROR: embedded images not supported by tag";
6614 bool isDirectoryImage =
false;
6617 if (imageFilename.isEmpty())
6621 imageFilename = fi.absolutePath() +
'/' +
filename;
6622 isDirectoryImage =
true;
6625 if (!QFile::exists(imageFilename))
6627 LOG(VB_GENERAL, LOG_ERR,
LOC +
6628 QString(
"HandleMusicTagAddImage: cannot find image file %1").arg(
filename));
6630 strlist <<
"ERROR: failed to find image file";
6646 LOG(VB_GENERAL, LOG_ERR,
LOC +
"HandleMusicTagAddImage: failed to write album art to tag");
6648 strlist <<
"ERROR: failed to write album art to tag";
6653 if (!isDirectoryImage)
6654 QFile::remove(imageFilename);
6662 if (!isDirectoryImage)
6663 QFile::remove(imageFilename);
6679 QStringList strlist;
6683 const QString&
hostname = slist[1];
6691 LOG(VB_GENERAL, LOG_INFO,
LOC +
6692 QString(
"HandleMusicTagRemoveImage: asking slave '%1' "
6693 "to remove the image").arg(
hostname));
6703 LOG(VB_GENERAL, LOG_INFO,
LOC +
6704 QString(
"HandleMusicTagRemoveImage: Failed to grab "
6705 "slave socket on '%1'").arg(
hostname));
6707 strlist <<
"ERROR: slave not found";
6715 int songID = slist[2].toInt();
6716 int imageID = slist[3].toInt();
6723 LOG(VB_GENERAL, LOG_ERR,
LOC +
6724 QString(
"HandleMusicTagRemoveImage: Cannot find metadata for trackid: %1")
6727 strlist <<
"ERROR: track not found";
6739 LOG(VB_GENERAL, LOG_ERR,
LOC +
6740 "HandleMusicTagRemoveImage: failed to find a tagger for track");
6742 strlist <<
"ERROR: tagger not found";
6753 LOG(VB_GENERAL, LOG_ERR,
LOC +
"HandleMusicTagRemoveImage: asked to remove album art "
6754 "from the tag but the tagger doesn't support it!");
6756 strlist <<
"ERROR: embedded images not supported by tag";
6769 LOG(VB_GENERAL, LOG_ERR,
LOC +
6770 QString(
"HandleMusicTagRemoveImage: Cannot find image for imageid: %1")
6773 strlist <<
"ERROR: image not found";
6785 LOG(VB_GENERAL, LOG_ERR,
LOC +
"HandleMusicTagRemoveImage: failed to remove album art from tag");
6787 strlist <<
"ERROR: failed to remove album art from tag";
6806 QStringList strlist;
6810 const QString&
hostname = slist[1];
6811 const QString& songid = slist[2];
6812 const QString& grabberName = slist[3];
6813 QString artist =
"";
6817 if (slist.size() == 7)
6830 LOG(VB_GENERAL, LOG_INFO,
LOC +
6831 QString(
"HandleMusicFindLyrics: asking slave '%1' to "
6842 LOG(VB_GENERAL, LOG_INFO,
LOC +
6843 QString(
"HandleMusicFindLyrics: Failed to grab slave "
6848 QStringList paramList;
6849 paramList.append(QString(
"--songid='%1'").arg(songid));
6850 paramList.append(QString(
"--grabber='%1'").arg(grabberName));
6852 if (!artist.isEmpty())
6853 paramList.append(QString(
"--artist=\"%1\"").arg(artist));
6855 if (!album.isEmpty())
6856 paramList.append(QString(
"--album=\"%1\"").arg(album));
6858 if (!title.isEmpty())
6859 paramList.append(QString(
"--title=\"%1\"").arg(title));
6861 QString command =
GetAppBinDir() +
"mythutil --findlyrics " + paramList.join(
" ");
6895 QStringList strlist;
6899 QString scriptDir =
GetShareDir() +
"metadata/Music/lyrics";
6904 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find lyric scripts directory: %1").arg(scriptDir));
6905 strlist << QString(
"ERROR: Cannot find lyric scripts directory: %1").arg(scriptDir);
6913 d.setFilter(QDir::Files | QDir::NoDotAndDotDot);
6914 d.setNameFilters(QStringList(
"*.py"));
6915 QFileInfoList list =
d.entryInfoList();
6918 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find any lyric scripts in: %1").arg(scriptDir));
6919 strlist << QString(
"ERROR: Cannot find any lyric scripts in: %1").arg(scriptDir);
6927 QStringList scripts;
6928 for (
const auto & fi : std::as_const(list))
6930 LOG(VB_FILE, LOG_NOTICE, QString(
"Found lyric script at: %1").arg(fi.filePath()));
6931 scripts.append(fi.filePath());
6934 QStringList grabbers;
6937 for (
int x = 0; x < scripts.count(); x++)
6939 QStringList
args { scripts.at(x),
"-v" };
6941 p.start(PYTHON_EXE,
args);
6942 p.waitForFinished(-1);
6943 QString result =
p.readAllStandardOutput();
6945 QDomDocument domDoc;
6948 int errorColumn = 0;
6950 if (!domDoc.setContent(result,
false, &errorMsg, &errorLine, &errorColumn))
6952 LOG(VB_GENERAL, LOG_ERR,
6953 QString(
"FindLyrics: Could not parse version from %1").arg(scripts.at(x)) +
6954 QString(
"\n\t\t\tError at line: %1 column: %2 msg: %3").arg(errorLine).arg(errorColumn).arg(errorMsg));
6958 QDomNodeList itemList = domDoc.elementsByTagName(
"grabber");
6959 QDomNode itemNode = itemList.item(0);
6961 grabbers.append(itemNode.namedItem(QString(
"name")).toElement().text());
6968 for (
int x = 0; x < grabbers.count(); x++)
6969 strlist.append(grabbers.at(x));
6980 QStringList strlist;
6984 const QString&
hostname = slist[1];
6985 int songID = slist[2].toInt();
6993 LOG(VB_GENERAL, LOG_INFO,
LOC +
6994 QString(
"HandleMusicSaveLyrics: asking slave '%1' to "
7005 LOG(VB_GENERAL, LOG_INFO,
LOC +
7006 QString(
"HandleMusicSaveLyrics: Failed to grab slave "
7014 LOG(VB_GENERAL, LOG_ERR, QString(
"Cannot find metadata for trackid: %1").arg(songID));
7015 strlist << QString(
"ERROR: Cannot find metadata for trackid: %1").arg(songID);
7023 QString lyricsFile =
GetConfDir() + QString(
"/MythMusic/Lyrics/%1.txt").arg(songID);
7026 if (QFile::exists(lyricsFile))
7027 QFile::remove(lyricsFile);
7030 QFile
file(QLatin1String(qPrintable(lyricsFile)));
7032 if (
file.open(QIODevice::WriteOnly))
7034 QTextStream stream(&
file);
7035 for (
int x = 3; x < slist.count(); x++)
7036 stream << slist.at(x);
7048 QStringList &commands,
7053 int recnum = commands[1].toInt();
7054 QString command = slist[1];
7056 QStringList retlist;
7062 if (command ==
"DONE")
7070 LOG(VB_GENERAL, LOG_ERR,
LOC +
7071 QString(
"Unknown file transfer socket: %1").arg(recnum));
7072 retlist << QString(
"ERROR: Unknown file transfer socket: %1")
7084 if (command ==
"REQUEST_BLOCK")
7086 int size = slist[2].toInt();
7090 else if (command ==
"WRITE_BLOCK")
7092 int size = slist[2].toInt();
7094 retlist << QString::number(ft->
WriteBlock(size));
7096 else if (command ==
"SEEK")
7098 long long pos = slist[2].toLongLong();
7099 int whence = slist[3].toInt();
7100 long long curpos = slist[4].toLongLong();
7102 long long ret = ft->
Seek(curpos, pos, whence);
7103 retlist << QString::number(ret);
7105 else if (command ==
"IS_OPEN")
7107 bool isopen = ft->
isOpen();
7109 retlist << QString::number(static_cast<int>(isopen));
7111 else if (command ==
"REOPEN")
7113 retlist << QString::number(static_cast<int>(ft->
ReOpen(slist[2])));
7115 else if (command ==
"DONE")
7120 else if (command ==
"SET_TIMEOUT")
7122 bool fast = slist[2].toInt() != 0;
7126 else if (command ==
"REQUEST_SIZE")
7134 LOG(VB_GENERAL, LOG_ERR,
LOC +
7135 QString(
"Unknown command: %1").arg(command));
7136 retlist <<
"ERROR" <<
"invalid_call";
7150 QStringList::const_iterator it = slist.cbegin() + 1;
7162 retval = iter.key();
7168 QStringList strlist( QString::number(retval) );
7185 strlist <<
"nohost";
7197 int recordernum = slist[1].toInt();
7199 QStringList strlist;
7222 strlist <<
"nohost";
7231 if (slist.size() < 2)
7236 QString message = slist[1];
7237 QStringList extra_data;
7238 for (
uint i = 2; i < (
uint) slist.size(); i++)
7239 extra_data.push_back(slist[i]);
7241 if (extra_data.empty())
7252 QStringList retlist(
"OK" );
7260 QStringList retlist;
7262 const QString& newverbose = slist[1];
7263 int len = newverbose.length();
7269 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
7270 QString(
"Verbose mask changed, new mask is: %1").arg(
verboseString));
7276 LOG(VB_GENERAL, LOG_ERR,
LOC +
7277 QString(
"Invalid SET_VERBOSE string: '%1'").arg(newverbose));
7278 retlist <<
"Failed";
7287 QStringList retlist;
7288 const QString& newstring = slist[1];
7289 LogLevel_t newlevel = LOG_UNKNOWN;
7291 int len = newstring.length();
7295 if (newlevel != LOG_UNKNOWN)
7299 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
7300 QString(
"Log level changed, new level is: %1")
7307 if (newlevel == LOG_UNKNOWN)
7309 LOG(VB_GENERAL, LOG_ERR,
LOC +
7310 QString(
"Invalid SET_VERBOSE string: '%1'").arg(newstring));
7311 retlist <<
"Failed";
7321 int RecordingsInProgress = 0;
7322 int LiveTVRecordingsInProgress = 0;
7323 QStringList retlist;
7328 if (elink->IsBusyRecording()) {
7329 RecordingsInProgress++;
7333 LiveTVRecordingsInProgress++;
7340 retlist << QString::number(RecordingsInProgress);
7341 retlist << QString::number(LiveTVRecordingsInProgress);
7350 if (slist.size() < 3)
7352 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Too few params in pixmap request");
7353 QStringList outputlist(
"ERROR");
7354 outputlist +=
"TOO_FEW_PARAMS";
7359 bool time_fmt_sec =
true;
7360 std::chrono::seconds time = std::chrono::seconds::max();
7361 long long frame = -1;
7365 bool has_extra_data =
false;
7367 QString token = slist[1];
7368 if (token.isEmpty())
7370 LOG(VB_GENERAL, LOG_ERR,
LOC +
7371 "Failed to parse pixmap request. Token absent");
7372 QStringList outputlist(
"ERROR");
7373 outputlist +=
"TOKEN_ABSENT";
7378 QStringList::const_iterator it = slist.cbegin() + 2;
7379 QStringList::const_iterator end = slist.cend();
7384 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to parse pixmap request. "
7385 "ProgramInfo missing pathname");
7386 QStringList outputlist(
"BAD");
7387 outputlist +=
"NO_PATHNAME";
7391 if (token.toLower() ==
"do_not_care")
7393 token = QString(
"%1:%2")
7396 if (it != slist.cend())
7397 (time_fmt_sec = ((*it).toLower() ==
"s")), ++it;
7398 if (it != slist.cend())
7401 time = std::chrono::seconds((*it).toLongLong()), ++it;
7403 frame = (*it).toLongLong(), ++it;
7405 if (it != slist.cend())
7406 (outputfile = *it), ++it;
7407 outputfile = (outputfile ==
"<EMPTY>") ? QString() : outputfile;
7408 if (it != slist.cend())
7410 width = (*it).toInt(&ok); ++it;
7411 width = (ok) ? width : -1;
7413 if (it != slist.cend())
7415 height = (*it).toInt(&ok); ++it;
7416 height = (ok) ? height : -1;
7417 has_extra_data =
true;
7419 QSize outputsize = QSize(width, height);
7423 auto pos_text = (time != std::chrono::seconds::max())
7424 ? QString::number(time.count()) +
"s"
7425 : QString::number(frame) +
"f";
7426 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
7427 QString(
"HandleGenPreviewPixmap got extra data\n\t\t\t"
7430 .arg(width).arg(height).arg(outputfile));
7445 QStringList outputlist;
7448 if (time != std::chrono::seconds::max())
7451 token, &pginfo, time, -1, outputfile, outputsize);
7456 token, &pginfo, std::chrono::seconds::max(), frame, outputfile, outputsize);
7466 if (outputlist.empty() || outputlist[0] !=
"OK")
7472 LOG(VB_GENERAL, LOG_ERR,
LOC +
7473 QString(
"HandleGenPreviewPixmap() "
7474 "Couldn't find backend for:\n\t\t\t%1")
7480 LOG(VB_GENERAL, LOG_ERR,
LOC +
"HandleGenPreviewPixmap: Unable to "
7481 "find file locally, unable to make preview image.");
7482 QStringList outputlist(
"ERROR" );
7483 outputlist +=
"FILE_INACCESSIBLE";
7491 if (time != std::chrono::seconds::max()) {
7493 pginfo, outputsize, outputfile, time, -1, token);
7496 pginfo, outputsize, outputfile, -1s, frame, token);
7504 QStringList outputlist(
"OK");
7505 if (!outputfile.isEmpty())
7506 outputlist += outputfile;
7514 QStringList::const_iterator it = slist.cbegin() + 1;
7519 QStringList strlist;
7532 strlist = (slavetime.isValid()) ?
7533 QStringList(QString::number(slavetime.toSecsSinceEpoch())) :
7540 LOG(VB_GENERAL, LOG_ERR,
LOC +
7541 QString(
"HandlePixmapLastModified() "
7542 "Couldn't find backend for:\n\t\t\t%1")
7548 LOG(VB_GENERAL, LOG_ERR,
LOC +
7549 "MainServer: HandlePixmapLastModified: Unable to "
7550 "find file locally, unable to get last modified date.");
7551 QStringList outputlist(
"BAD" );
7562 QDateTime lastmodified = finfo.lastModified();
7563 if (lastmodified.isValid())
7564 strlist = QStringList(QString::number(lastmodified.toSecsSinceEpoch()));
7566 strlist = QStringList(QString::number(UINT_MAX));
7569 strlist = QStringList(
"BAD" );
7577 QStringList strlist;
7582 strlist = QStringList(
"ERROR");
7583 strlist +=
"1: Parameter list too short";
7588 QDateTime cachemodified;
7589 if (!slist[1].isEmpty() && (slist[1].toInt() != -1))
7594 int max_file_size = slist[2].toInt();
7596 QStringList::const_iterator it = slist.begin() + 3;
7601 strlist = QStringList(
"ERROR");
7602 strlist +=
"2: Invalid ProgramInfo";
7613 size_t fsize = finfo.size();
7614 QDateTime lastmodified = finfo.lastModified();
7615 bool out_of_date = !cachemodified.isValid() ||
7616 (lastmodified > cachemodified);
7618 if (out_of_date && (fsize > 0) && ((ssize_t)fsize < max_file_size))
7622 bool open_ok =
file.open(QIODevice::ReadOnly);
7624 data =
file.readAll();
7626 if (!data.isEmpty())
7628 LOG(VB_FILE, LOG_INFO,
LOC +
7629 QString(
"Read preview file '%1'")
7631 if (lastmodified.isValid())
7632 strlist += QString::number(lastmodified.toSecsSinceEpoch());
7634 strlist += QString::number(UINT_MAX);
7635 strlist += QString::number(data.size());
7636 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
7637 quint16 checksum = qChecksum(data.constData(), data.size());
7639 quint16 checksum = qChecksum(data);
7641 strlist += QString::number(checksum);
7642 strlist += QString(data.toBase64());
7646 LOG(VB_GENERAL, LOG_ERR,
LOC +
7647 QString(
"Failed to read preview file '%1'")
7650 strlist = QStringList(
"ERROR");
7652 QString(
"3: Failed to read preview file '%1'%2")
7654 (open_ok) ?
"" :
" open failed");
7657 else if (out_of_date && (max_file_size > 0))
7659 if (fsize >= (
size_t) max_file_size)
7661 strlist = QStringList(
"WARNING");
7662 strlist += QString(
"1: Preview file too big %1 > %2")
7663 .arg(fsize).arg(max_file_size);
7667 strlist = QStringList(
"ERROR");
7668 strlist +=
"4: Preview file is invalid";
7673 if (lastmodified.isValid())
7674 strlist += QString::number(lastmodified.toSecsSinceEpoch());
7676 strlist += QString::number(UINT_MAX);
7690 strlist = QStringList(
"ERROR");
7692 "5: Could not locate mythbackend that made this recording";
7701 if (!strlist.empty())
7708 strlist = QStringList(
"WARNING");
7709 strlist +=
"2: Could not locate requested file";
7715 QStringList retlist(
"OK" );
7721 pbs->setBlockShutdown(blockShutdown);
7724 QStringList retlist(
"OK" );
7769 QList<uint> disconnectedSlaves;
7781 MythEvent me(
"LOCAL_RECONNECT_TO_MASTER");
7787 disconnectedSlaves.clear();
7788 bool needsReschedule =
false;
7792 LOG(VB_GENERAL, LOG_ERR,
LOC +
7793 QString(
"Slave backend: %1 no longer connected")
7794 .arg(
pbs->getHostname()));
7796 bool isFallingAsleep =
true;
7800 if (elink->GetSocket() ==
pbs)
7802 if (!elink->IsFallingAsleep())
7803 isFallingAsleep =
false;
7805 elink->SetSocket(
nullptr);
7807 disconnectedSlaves.push_back(elink->GetInputID());
7811 if (
m_sched && !isFallingAsleep)
7812 needsReschedule =
true;
7814 QString message = QString(
"LOCAL_SLAVE_BACKEND_OFFLINE %1")
7815 .arg(
pbs->getHostname());
7823 QString(
"SLAVE_DISCONNECTED HOSTNAME %1")
7824 .arg(
pbs->getHostname()));
7833 if (chain !=
nullptr)
7844 std::this_thread::sleep_for(500us);
7846 if (enc->IsBusy() &&
7847 enc->GetChainID() == chain->
GetID())
7858 LOG(VB_GENERAL, LOG_INFO, QString(
"%1 sock(%2) '%3' disconnected")
7859 .arg(
pbs->getBlockShutdown() ?
"Playback" :
"Monitor")
7860 .arg(quintptr(socket),0,16)
7861 .arg(
pbs->getHostname()) );
7862 pbs->SetDisconnected();
7867 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Playback sock still exists?");
7875 if (!disconnectedSlaves.isEmpty())
7884 MythEvent me(
"LOCAL_CONNECTION_CLOSED");
7898 LOG(VB_GENERAL, LOG_INFO, QString(
"BEFileTransfer sock(%1) disconnected")
7899 .arg(quintptr(socket),0,16) );
7911 LOG(VB_GENERAL, LOG_INFO, QString(
"Control sock(%1) disconnected")
7912 .arg(quintptr(socket),0,16) );
7922 LOG(VB_GENERAL, LOG_WARNING,
LOC +
7923 QString(
"Unknown socket closing MythSocket(0x%1)")
7924 .arg((intptr_t)socket,0,16));
7937 if (
pbs->isSlaveBackend() &&
7960 if (
pbs->isMediaServer() &&
7976 { return sock == pbs->getSocket(); });
7984 if (
id == ft->getSocket()->GetSocketDescriptor())
7993 if (sock == ft->getSocket())
8003 if (chain->GetID() ==
id)
8013 if (chain->IsHostSocket(sock))
8023 if (chain->ProgramIsAt(pginfo) >= 0)
8043 std::vector<LiveTVChain*> newChains;
8048 newChains.push_back(entry);
8058 if (closeApplication)
8064 QString lpath = QString(path);
8066 if (lpath.section(
'/', -2, -2) ==
"channels")
8069 QString
file = lpath.section(
'/', -1);
8073 query.
prepare(
"SELECT icon FROM channel "
8074 "WHERE deleted IS NULL AND icon LIKE :FILENAME ;");
8079 lpath = query.
value(0).toString();
8088 lpath = lpath.section(
'/', -1);
8090 QString fpath = lpath;
8091 if (fpath.endsWith(
".png"))
8092 fpath = fpath.left(fpath.length() - 4);
8098 if (pburl.startsWith(
"/"))
8100 lpath = pburl.section(
'/', 0, -2) +
"/" + lpath;
8101 LOG(VB_FILE, LOG_INFO,
LOC +
8102 QString(
"Local file path: %1").arg(lpath));
8106 LOG(VB_GENERAL, LOG_ERR,
LOC +
8107 QString(
"ERROR: LocalFilePath unable to find local "
8108 "path for '%1', found '%2' instead.")
8109 .arg(lpath, pburl));
8113 else if (!lpath.isEmpty())
8116 QString opath = lpath;
8119 if (!wantgroup.isEmpty())
8121 sgroup.
Init(wantgroup);
8122 lpath = QString(path);
8126 lpath = QFileInfo(lpath).fileName();
8129 QString tmpFile = sgroup.
FindFile(lpath);
8130 if (!tmpFile.isEmpty())
8133 LOG(VB_FILE, LOG_INFO,
LOC +
8134 QString(
"LocalFilePath(%1 '%2'), found file through "
8135 "exhaustive search at '%3'")
8136 .arg(path, opath, lpath));
8140 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"ERROR: LocalFilePath "
8141 "unable to find local path for '%1'.") .arg(path));
8157 auto *masterServerSock =
new MythSocket(-1,
this);
8162 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
8163 QString(
"Connecting to master server: %1:%2")
8164 .arg(server).arg(port));
8166 if (!masterServerSock->ConnectToHost(server, port))
8168 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
8169 "Connection to master server timed out.");
8171 masterServerSock->DecrRef();
8175 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Connected successfully");
8177 QString str = QString(
"ANN SlaveBackend %1 %2")
8181 QStringList strlist( str );
8186 elink->CancelNextRecording(
true);
8204 masterServerSock->SetReadyReadCallbackEnabled(
false);
8205 if (!masterServerSock->SendReceiveStringList(strlist, 1) ||
8206 (strlist[0] ==
"ERROR"))
8208 masterServerSock->DecrRef();
8209 masterServerSock =
nullptr;
8210 if (strlist.empty())
8212 LOG(VB_GENERAL, LOG_ERR,
LOC +
8213 "Failed to open master server socket, timeout");
8217 LOG(VB_GENERAL, LOG_ERR,
LOC +
8218 "Failed to open master server socket" +
8219 ((strlist.size() >= 2) ?
8220 QString(
", error was %1").arg(strlist[1]) :
8221 QString(
", remote error")));
8226 masterServerSock->SetReadyReadCallbackEnabled(
true);
8241 bool foundClient =
false;
8251 if ((*it)->isSlaveBackend())
8256 if (onlyBlockingClients && !(*it)->getBlockShutdown())
8264 return (foundClient);
8273 QStringList bcast(
"SHUTDOWN_NOW" );
8280 if (
pbs->isSlaveBackend())
8281 pbs->getSocket()->WriteStringList(bcast);
8291 bool needsReschedule =
event.ExtraData(0).toUInt() != 0U;
8292 for (
int i = 1; i <
event.ExtraDataCount(); i++)
8295 if (needsReschedule)
8301 const QList<uint> &offlineEncoderIDs,
bool needsReschedule)
8303 QStringList extraData;
8304 extraData.push_back(
8305 QString::number(
static_cast<uint>(needsReschedule)));
8307 QList<uint>::const_iterator it;
8308 for (it = offlineEncoderIDs.begin(); it != offlineEncoderIDs.end(); ++it)
8309 extraData.push_back(QString::number(*it));
8311 MythEvent me(
"LOCAL_SLAVE_BACKEND_ENCODERS_OFFLINE", extraData);
8317 #if CONFIG_SYSTEMD_NOTIFY
8318 QStringList status2;
8321 status2 << QString(
"Master backend.");
8323 status2 << QString(
"Slave backend.");
8328 int playback = 0, frontend = 0, monitor = 0, slave = 0, media = 0;
8334 if (
pbs->IsDisconnected())
8336 if (
pbs->isSlaveBackend())
8338 else if (
pbs->isMediaServer())
8340 else if (
pbs->IsFrontend())
8342 else if (
pbs->getBlockShutdown())
8347 status2 << QString(
"Connections: Pl %1, Fr %2, Mo %3, Sl %4, MS %5, FT %6, Co %7")
8348 .arg(playback).arg(frontend).arg(monitor).arg(slave).arg(media)
8359 if (not elink->IsLocal())
8361 switch (elink->GetState())
8380 for (
auto & recording : recordings)
8388 while (!recordings.empty())
8392 recordings.pop_back();
8396 QString(
"Recordings: active %1, scheduled %2")
8397 .arg(active).arg(scheduled);
8401 QString status(
"STATUS=" + status2.join(
' '));
8402 (void)sd_notify(0, qPrintable(status));