6 #include <QCoreApplication>
11 #include <QRegularExpression>
12 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
13 #include <QStringConverter>
15 #include <QStringList>
16 #include <QTextStream>
53 #define LOC QString("NetworkControl: ")
54 #define LOC_ERR QString("NetworkControl Error: ")
56 #define FE_SHORT_TO 2000
57 #define FE_LONG_TO 10000
60 (QEvent::Type) QEvent::registerEventType();
62 (QEvent::Type) QEvent::registerEventType();
72 QString
const& test,
int minchars = 1)
74 if (test.length() < minchars)
75 return command.toLower() == test.toLower();
76 return test.toLower() == command.left(test.length()).toLower();
80 m_commandThread(new
MThread(
"NetworkControl", this))
83 m_jumpMap[
"channelpriorities"] =
"Channel Recording Priorities";
86 m_jumpMap[
"managerecordings"] =
"Manage Recordings / Fix Conflicts";
95 m_jumpMap[
"programfinder"] =
"Program Finder";
96 m_jumpMap[
"programguide"] =
"Program Guide";
98 m_jumpMap[
"musicplaylists"] =
"Select music playlists";
99 m_jumpMap[
"playbackrecordings"] =
"TV Recording Playback";
100 m_jumpMap[
"videobrowser"] =
"Video Browser";
101 m_jumpMap[
"videogallery"] =
"Video Gallery";
102 m_jumpMap[
"videolistings"] =
"Video Listings";
103 m_jumpMap[
"videomanager"] =
"Video Manager";
104 m_jumpMap[
"zoneminderconsole"] =
"ZoneMinder Console";
105 m_jumpMap[
"zoneminderliveview"] =
"ZoneMinder Live View";
106 m_jumpMap[
"zoneminderevents"] =
"ZoneMinder Events";
108 m_jumpMap[
"channelrecpriority"] =
"Channel Recording Priorities";
109 m_jumpMap[
"viewscheduled"] =
"Manage Recordings / Fix Conflicts";
110 m_jumpMap[
"previousbox"] =
"Previously Recorded";
111 m_jumpMap[
"progfinder"] =
"Program Finder";
112 m_jumpMap[
"guidegrid"] =
"Program Guide";
113 m_jumpMap[
"managerecrules"] =
"Manage Recording Rules";
114 m_jumpMap[
"statusbox"] =
"Status Screen";
115 m_jumpMap[
"playbackbox"] =
"TV Recording Playback";
116 m_jumpMap[
"pbb"] =
"TV Recording Playback";
118 m_jumpMap[
"reloadtheme"] =
"Reload Theme";
119 m_jumpMap[
"showborders"] =
"Toggle Show Widget Borders";
120 m_jumpMap[
"shownames"] =
"Toggle Show Widget Names";
129 m_keyMap[
"return"] = Qt::Key_Return;
130 m_keyMap[
"pageup"] = Qt::Key_PageUp;
131 m_keyMap[
"pagedown"] = Qt::Key_PageDown;
132 m_keyMap[
"escape"] = Qt::Key_Escape;
134 m_keyMap[
"backtab"] = Qt::Key_Backtab;
136 m_keyMap[
"backspace"] = Qt::Key_Backspace;
137 m_keyMap[
"insert"] = Qt::Key_Insert;
138 m_keyMap[
"delete"] = Qt::Key_Delete;
145 m_keyMap[
"underscore"] = Qt::Key_Underscore;
147 m_keyMap[
"period"] = Qt::Key_Period;
149 m_keyMap[
"numbersign"] = Qt::Key_NumberSign;
150 m_keyMap[
"poundsign"] = Qt::Key_NumberSign;
151 m_keyMap[
"hash"] = Qt::Key_NumberSign;
153 m_keyMap[
"bracketleft"] = Qt::Key_BracketLeft;
154 m_keyMap[
"["] = Qt::Key_BracketLeft;
155 m_keyMap[
"bracketright"] = Qt::Key_BracketRight;
156 m_keyMap[
"]"] = Qt::Key_BracketRight;
157 m_keyMap[
"backslash"] = Qt::Key_Backslash;
159 m_keyMap[
"dollar"] = Qt::Key_Dollar;
161 m_keyMap[
"percent"] = Qt::Key_Percent;
163 m_keyMap[
"ampersand"] = Qt::Key_Ampersand;
165 m_keyMap[
"parenleft"] = Qt::Key_ParenLeft;
167 m_keyMap[
"parenright"] = Qt::Key_ParenRight;
169 m_keyMap[
"asterisk"] = Qt::Key_Asterisk;
171 m_keyMap[
"question"] = Qt::Key_Question;
177 m_keyMap[
"semicolon"] = Qt::Key_Semicolon;
183 m_keyMap[
"greater"] = Qt::Key_Greater;
259 "mythfrontend shutting down, connection closing...");
322 else if ((nc->
getArg(0).toLower() ==
"exit") || (nc->
getArg(0).toLower() ==
"quit"))
324 QCoreApplication::postEvent(
this,
327 else if (! nc->
getArg(0).isEmpty())
329 result = QString(
"INVALID command '%1', try 'help' for more info")
342 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Client Socket disconnected");
349 if (ncc->getSocket()->state() == QTcpSocket::UnconnectedState)
367 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"deleteClient(%1), unable to "
368 "locate specified NetworkControlClient").arg((
long long)ncc));
375 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"New connection established."));
386 connect(client, &QAbstractSocket::disconnected,
389 welcomeStr =
"MythFrontend Network Control\r\n";
390 welcomeStr +=
"Type 'help' for usage information\r\n"
391 "---------------------------------";
403 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
421 auto *socket = (QTcpSocket *)sender();
425 while (socket->canReadLine())
427 QString lineIn = socket->readLine();
429 static const QRegularExpression
badChars
430 {
"[^-a-zA-Z0-9\\s\\.:_#/$%&()*+,;<=>?\\[\\]\\|]" };
434 lineIn = lineIn.simplified();
435 if (lineIn.isEmpty())
438 LOG(VB_NETWORK, LOG_INFO,
LOC +
439 QString(
"emit commandReceived(%1)").arg(lineIn));
446 LOG(VB_NETWORK, LOG_INFO,
LOC +
447 QString(
"NetworkControl::receiveCommand(%1)").arg(command));
448 auto *ncc = qobject_cast<NetworkControlClient *>(sender());
460 QString result =
"OK";
463 return QString(
"ERROR: See 'help %1' for usage information")
474 std::this_thread::sleep_for(10ms);
481 QString result =
"OK";
482 QKeyEvent *
event =
nullptr;
485 return QString(
"ERROR: See 'help %1' for usage information")
488 QObject *keyDest =
nullptr;
493 return QString(
"ERROR: Application has no main window!\n");
496 while (curToken < nc->getArgCount())
498 int tokenLen = nc->
getArg(curToken).length();
500 if (nc->
getArg(curToken) ==
"sleep")
502 std::this_thread::sleep_for(1s);
514 event =
new QKeyEvent(QEvent::KeyPress, keyCode, Qt::NoModifier,
516 QCoreApplication::postEvent(keyDest, event);
518 event =
new QKeyEvent(QEvent::KeyRelease, keyCode, Qt::NoModifier,
520 QCoreApplication::postEvent(keyDest, event);
522 else if (((tokenLen == 1) &&
523 (nc->
getArg(curToken).at(0).isLetterOrNumber())) ||
525 (nc->
getArg(curToken).contains(
"+"))))
527 QKeySequence a(nc->
getArg(curToken));
528 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
530 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
534 QStringList tokenParts = nc->
getArg(curToken).split(
'+');
537 while (partNum < (tokenParts.size() - 1))
539 if (tokenParts[partNum].toUpper() ==
"CTRL")
540 modifiers |= Qt::ControlModifier;
541 if (tokenParts[partNum].toUpper() ==
"SHIFT")
542 modifiers |= Qt::ShiftModifier;
543 if (tokenParts[partNum].toUpper() ==
"ALT")
544 modifiers |= Qt::AltModifier;
545 if (tokenParts[partNum].toUpper() ==
"META")
546 modifiers |= Qt::MetaModifier;
552 int keyCode = a[0].key();
553 Qt::KeyboardModifiers modifiers = a[0].keyboardModifiers();
557 if (nc->
getArg(curToken) == nc->
getArg(curToken).toUpper())
558 modifiers |= Qt::ShiftModifier;
563 event =
new QKeyEvent(QEvent::KeyPress, keyCode, modifiers,
565 QCoreApplication::postEvent(keyDest, event);
567 event =
new QKeyEvent(QEvent::KeyRelease, keyCode, modifiers,
569 QCoreApplication::postEvent(keyDest, event);
573 return QString(
"ERROR: Invalid syntax at '%1', see 'help %2' for "
586 QString result =
"OK";
590 return QString(
"ERROR: See 'help %1' for usage information")
596 if (
GetMythUI()->GetCurrentLocation().toLower() !=
"mainmenu")
603 (
GetMythUI()->GetCurrentLocation().toLower() !=
"mainmenu"))
604 std::this_thread::sleep_for(10ms);
607 if (
GetMythUI()->GetCurrentLocation().toLower() ==
"mainmenu")
615 return QString(
"Unable to change to main menu to start playback!");
619 (nc->
getArg(2).contains(QRegularExpression(
"^\\d+$"))) &&
620 (nc->
getArg(3).contains(QRegularExpression(
621 R
"(^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ?$)"))))
623 if (
GetMythUI()->GetCurrentLocation().toLower() ==
"playback")
625 QString msg = QString(
"NETWORK_CONTROL STOP");
632 (
GetMythUI()->GetCurrentLocation().toLower() ==
"playback"))
633 std::this_thread::sleep_for(10ms);
636 if (
GetMythUI()->GetCurrentLocation().toLower() !=
"playbackbox")
642 while (!timer.hasExpired(10000) &&
643 (
GetMythUI()->GetCurrentLocation().toLower() !=
"playbackbox"))
644 std::this_thread::sleep_for(10ms);
648 std::this_thread::sleep_for(10ms);
651 if (
GetMythUI()->GetCurrentLocation().toLower() ==
"playbackbox")
657 QString msg = QString(
"NETWORK_CONTROL %1 PROGRAM %2 %3 %4")
660 QString::number(clientID));
671 std::this_thread::sleep_for(10ms);
676 result =
"ERROR: Timed out waiting for reply from player";
681 result = QString(
"ERROR: Unable to change to PlaybackBox from "
682 "%1, cannot play requested file.")
689 if (
GetMythUI()->GetCurrentLocation().toLower() !=
"playmusic")
691 return QString(
"ERROR: You are in %1 mode and this command is "
692 "only for MythMusic")
702 message = QString(
"MUSIC_COMMAND %1 PLAY").arg(
hostname);
704 message = QString(
"MUSIC_COMMAND %1 PAUSE").arg(
hostname);
706 message = QString(
"MUSIC_COMMAND %1 STOP").arg(
hostname);
718 qApp->processEvents();
719 std::this_thread::sleep_for(10ms);
738 qApp->processEvents();
739 std::this_thread::sleep_for(10ms);
758 qApp->processEvents();
759 std::this_thread::sleep_for(10ms);
768 return QString(
"ERROR: Invalid 'play music' command");
774 message = QString(
"MUSIC_COMMAND %1 SET_VOLUME %2")
779 message = QString(
"MUSIC_COMMAND %1 PLAY_TRACK %2")
784 message = QString(
"MUSIC_COMMAND %1 PLAY_URL %2")
789 message = QString(
"MUSIC_COMMAND %1 PLAY_FILE '%2'")
794 return QString(
"ERROR: Invalid 'play music' command");
798 return QString(
"ERROR: Invalid 'play music' command");
802 else if (
GetMythUI()->GetCurrentLocation().toLower() !=
"playback")
804 return QString(
"ERROR: You are in %1 mode and this command is only "
810 if (nc->
getArg(2).contains(QRegularExpression(
"^\\d+$")))
811 message = QString(
"NETWORK_CONTROL CHANID %1").arg(nc->
getArg(2));
813 return QString(
"ERROR: See 'help %1' for usage information")
819 return "ERROR: See 'help play' for usage information";
822 message =
"NETWORK_CONTROL CHANNEL UP";
824 message =
"NETWORK_CONTROL CHANNEL DOWN";
825 else if (nc->
getArg(2).contains(QRegularExpression(
"^[-\\.\\d_#]+$")))
826 message = QString(
"NETWORK_CONTROL CHANNEL %1").arg(nc->
getArg(2));
828 return QString(
"ERROR: See 'help %1' for usage information")
834 return QString(
"ERROR: See 'help %1' for usage information")
838 message =
"NETWORK_CONTROL SEEK BEGINNING";
840 message =
"NETWORK_CONTROL SEEK FORWARD";
843 message =
"NETWORK_CONTROL SEEK BACKWARD";
844 else if (nc->
getArg(2).contains(QRegularExpression(R
"(^\d\d:\d\d:\d\d$)")))
846 int hours = nc->
getArg(2).mid(0, 2).toInt();
847 int minutes = nc->
getArg(2).mid(3, 2).toInt();
848 int seconds = nc->
getArg(2).mid(6, 2).toInt();
849 message = QString(
"NETWORK_CONTROL SEEK POSITION %1")
850 .arg((hours * 3600) + (minutes * 60) + seconds);
853 return QString(
"ERROR: See 'help %1' for usage information")
859 return QString(
"ERROR: See 'help %1' for usage information")
862 QString token2 = nc->
getArg(2).toLower();
863 if ((token2.contains(QRegularExpression(R
"(^\-*\d+x$)"))) ||
864 (token2.contains(QRegularExpression(R"(^\-*\d+\/\d+x$)"))) ||
865 (token2.contains(QRegularExpression(R"(^\-*\d*\.\d+x$)"))))
866 message = QString("NETWORK_CONTROL SPEED %1").arg(token2);
868 message = QString(
"NETWORK_CONTROL SPEED normal");
870 message = QString(
"NETWORK_CONTROL SPEED 0x");
872 return QString(
"ERROR: See 'help %1' for usage information")
881 message = QString(
"NETWORK_CONTROL STOP");
885 (!nc->
getArg(2).toLower().contains(QRegularExpression(
"^\\d+%?$"))))
887 return QString(
"ERROR: See 'help %1' for usage information")
891 message = QString(
"NETWORK_CONTROL VOLUME %1")
892 .arg(nc->
getArg(2).toLower());
897 message = QString(
"NETWORK_CONTROL SUBTITLES 0");
898 else if (!nc->
getArg(2).toLower().contains(QRegularExpression(
"^\\d+$")))
900 return QString(
"ERROR: See 'help %1' for usage information")
905 message = QString(
"NETWORK_CONTROL SUBTITLES %1")
910 return QString(
"ERROR: See 'help %1' for usage information")
913 if (!message.isEmpty())
924 QString result =
"OK";
927 return QString(
"ERROR: See 'help %1' for usage information")
932 bool fullPath =
false;
933 bool mainStackOnly =
true;
936 fullPath = (nc->
getArg(2).toLower() ==
"true" || nc->
getArg(2) ==
"1");
938 mainStackOnly = (nc->
getArg(3).toLower() ==
"true" || nc->
getArg(3) ==
"1");
944 if (location ==
"Playback")
948 QString message = QString(
"NETWORK_CONTROL QUERY POSITION");
955 std::this_thread::sleep_for(10ms);
960 result =
"ERROR: Timed out waiting for reply from player";
977 return QString(
"VERSION: %1/%2 %3 %4 QT/%5 DBSchema/%6")
983 QString::number(dbSchema));
991 std::chrono::seconds uptime = 0s;
994 str = QString::number(uptime.count());
996 str = QString(
"Could not determine uptime.");
1004 str = QString(
"getloadavg() failed");
1006 str = QString(
"%1 %2 %3").arg(loads[0]).arg(loads[1]).arg(loads[2]);
1017 if (
getMemStats(totalMB, freeMB, totalVM, freeVM))
1019 str = QString(
"%1 %2 %3 %4")
1020 .arg(totalMB).arg(freeMB).arg(totalVM).arg(freeVM);
1024 str = QString(
"Could not determine memory stats.");
1034 if (location !=
"Playback")
1038 QString message = QString(
"NETWORK_CONTROL QUERY VOLUME");
1042 QElapsedTimer timer;
1045 std::this_thread::sleep_for(10ms);
1050 str =
"ERROR: Timed out waiting for reply from player";
1056 (nc->
getArg(2).contains(QRegularExpression(
"^\\d+$"))) &&
1057 (nc->
getArg(3).contains(QRegularExpression(
1058 R
"(^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ?$)"))))
1068 nc->
getArg(3).toLower().toUInt());
1069 return QString(
"ERROR: See 'help %1' for usage information "
1070 "(parameters mismatch)").arg(nc->
getArg(0));
1073 return QString(
"ERROR: See 'help %1' for usage information")
1082 return QString(
"ERROR: See 'help %1' for usage information")
1085 if (nc->
getArg(1) ==
"verbose")
1088 return QString(
"ERROR: Missing filter name.");
1092 return QString(
"ERROR: Separate filters with commas with no "
1093 "space: playback,audio\r\n See 'help %1' for usage "
1094 "information").arg(nc->
getArg(0));
1098 QString result =
"OK";
1102 if (pva_result != 0 )
1106 result +=
" Previous filter: " + oldVerboseString +
"\r\n";
1109 LOG(VB_GENERAL, LOG_NOTICE,
1110 QString(
"Verbose mask changed, new level is: %1")
1116 return QString(
"ERROR: See 'help %1' for usage information")
1123 return "MythUIText";
1125 return "MythUITextEdit";
1127 return "MythUIGroup";
1129 return "MythUIButton";
1131 return "MythUICheckBox";
1133 return "MythUIShape";
1135 return "MythUIButtonList";
1137 return "MythUIImage";
1139 return "MythUISpinBox";
1142 return "MythUIWebBrowser";
1145 return "MythUIClock";
1147 return "MythUIStateType";
1149 return "MythUIProgressBar";
1151 return "MythUIButtonTree";
1153 return "MythUIScrollBar";
1155 return "MythUIVideo";
1157 return "MythUIGuideGrid";
1159 return "MythUIEditBar";
1167 return QString(
"ERROR: See 'help %1' for usage information")
1170 if (nc->
getArg(1) ==
"getthemeinfo")
1174 return QString(
"%1 - %2").arg(
themeName, themeDir);
1176 if (nc->
getArg(1) ==
"reload")
1182 if (nc->
getArg(1) ==
"showborders")
1188 if (nc->
getArg(1) ==
"shownames")
1194 if (nc->
getArg(1) ==
"getwidgetnames")
1199 path = nc->
getArg(2).split(
'/');
1211 return QString(
"ERROR: no top screen found!");
1215 while (!path.isEmpty())
1217 QString childName = path.takeFirst();
1218 currType = currType->
GetChild(childName);
1220 return QString(
"ERROR: Failed to find child '%1'").arg(childName);
1226 for (
int i = 0; i < children->count(); i++)
1229 QString widgetName =
type->objectName();
1231 result += QString(
"%1 - %2\n\r").arg(widgetName, -20).arg(widgetType);
1236 if (nc->
getArg(1) ==
"getarea")
1239 return QString(
"ERROR: Missing widget name.");
1241 QString widgetName = nc->
getArg(2);
1242 QStringList path = widgetName.split(
'/');
1254 return QString(
"ERROR: no top screen found!");
1258 while (path.count() > 1)
1260 QString childName = path.takeFirst();
1261 currType = currType->
GetChild(childName);
1263 return QString(
"ERROR: Failed to find child '%1'").arg(childName);
1268 return QString(
"ERROR: widget '%1' not found!").arg(widgetName);
1270 int x =
type->GetFullArea().x();
1271 int y =
type->GetFullArea().y();
1272 int w =
type->GetFullArea().width();
1273 int h =
type->GetFullArea().height();
1274 return QString(
"The area of '%1' is x:%2, y:%3, w:%4, h:%5")
1275 .arg(widgetName).arg(x).arg(y).arg(w).arg(h);
1277 if (nc->
getArg(1) ==
"setarea")
1280 return QString(
"ERROR: Missing widget name.");
1283 return QString(
"ERROR: Missing X, Y, Width or Height.");
1285 QString widgetName = nc->
getArg(2);
1286 QStringList path = widgetName.split(
'/');
1287 QString x = nc->
getArg(3);
1288 QString y = nc->
getArg(4);
1289 QString w = nc->
getArg(5);
1290 QString h = nc->
getArg(6);
1303 return QString(
"ERROR: no top screen found!");
1305 while (path.count() > 1)
1307 QString childName = path.takeFirst();
1308 currType = currType->
GetChild(childName);
1310 return QString(
"ERROR: Failed to find child '%1'").arg(childName);
1315 return QString(
"ERROR: widget '%1' not found!").arg(widgetName);
1319 return QString(
"Changed area of '%1' to x:%2, y:%3, w:%4, h:%5")
1320 .arg(widgetName, x, y, w, h);
1323 return QString(
"ERROR: See 'help %1' for usage information")
1349 QMap<QString, QString>::Iterator it;
1351 "Usage: jump JUMPPOINT\r\n"
1353 "Where JUMPPOINT is one of the following:\r\n";
1357 helpText += it.key().leftJustified(20,
' ',
true) +
" - " +
1364 "key LETTER - Send the letter key specified\r\n"
1365 "key NUMBER - Send the number key specified\r\n"
1366 "key CODE - Send one of the following key codes\r\n"
1369 QMap<QString, int>::Iterator it;
1378 helpText += it.key();
1385 "play volume NUMBER% - Change volume to given percentage value\r\n"
1386 "play channel up - Change channel Up\r\n"
1387 "play channel down - Change channel Down\r\n"
1388 "play channel NUMBER - Change to a specific channel number\r\n"
1389 "play chanid NUMBER - Change to a specific channel id (chanid)\r\n"
1390 "play file FILENAME - Play FILENAME (FILENAME may be a file or a myth:// URL)\r\n"
1391 "play program CHANID yyyy-MM-ddThh:mm:ss\r\n"
1392 " - Play program with chanid & starttime\r\n"
1393 "play program CHANID yyyy-MM-ddThh:mm:ss resume\r\n"
1394 " - Resume program with chanid & starttime\r\n"
1395 "play save preview\r\n"
1396 " - Save preview image from current position\r\n"
1397 "play save preview FILENAME\r\n"
1398 " - Save preview image to FILENAME\r\n"
1399 "play save preview FILENAME WxH\r\n"
1400 " - Save preview image of size WxH\r\n"
1401 "play seek beginning - Seek to the beginning of the recording\r\n"
1402 "play seek forward - Skip forward in the video\r\n"
1403 "play seek backward - Skip backwards in the video\r\n"
1404 "play seek HH:MM:SS - Seek to a specific position\r\n"
1405 "play speed pause - Pause playback\r\n"
1406 "play speed normal - Playback at normal speed\r\n"
1407 "play speed 1x - Playback at normal speed\r\n"
1408 "play speed SPEEDx - Playback where SPEED must be a decimal\r\n"
1409 "play speed 1/8x - Playback at 1/8x speed\r\n"
1410 "play speed 1/4x - Playback at 1/4x speed\r\n"
1411 "play speed 1/3x - Playback at 1/3x speed\r\n"
1412 "play speed 1/2x - Playback at 1/2x speed\r\n"
1413 "play stop - Stop playback\r\n"
1414 "play subtitles [#] - Switch on indicated subtitle tracks\r\n"
1415 "play music play - Resume playback (MythMusic)\r\n"
1416 "play music pause - Pause playback (MythMusic)\r\n"
1417 "play music stop - Stop Playback (MythMusic)\r\n"
1418 "play music setvolume N - Set volume to number (MythMusic)\r\n"
1419 "play music getvolume - Get current volume (MythMusic)\r\n"
1420 "play music getmeta - Get metadata for current track (MythMusic)\r\n"
1421 "play music getstatus - Get music player status playing/paused/stopped (MythMusic)\r\n"
1422 "play music file NAME - Play specified file (MythMusic)\r\n"
1423 "play music track N - Switch to specified track (MythMusic)\r\n"
1424 "play music url URL - Play specified URL (MythMusic)\r\n";
1429 "query location - Query current screen or location\r\n"
1430 "query volume - Query the current playback volume\r\n"
1431 "query recordings - List currently available recordings\r\n"
1432 "query recording CHANID STARTTIME\r\n"
1433 " - List info about the specified program\r\n"
1434 "query liveTV - List current TV schedule\r\n"
1435 "query liveTV CHANID - Query current program for specified channel\r\n"
1436 "query load - List 1/5/15 load averages\r\n"
1437 "query memstats - List free and total, physical and swap memory\r\n"
1438 "query time - Query current time on frontend\r\n"
1439 "query uptime - Query machine uptime\r\n"
1440 "query verbose - Get current VERBOSE mask\r\n"
1441 "query version - Query Frontend version details\r\n"
1442 "query channels - Query available channels\r\n"
1443 "query channels START LIMIT - Query available channels from START and limit results to LIMIT lines\r\n";
1448 "set verbose debug-mask - "
1449 "Change the VERBOSE mask to 'debug-mask'\r\n"
1450 " (i.e. 'set verbose playback,audio')\r\n"
1451 " use 'set verbose default' to revert\r\n"
1452 " back to the default level of\r\n";
1454 else if (
is_abbrev(
"screenshot", command))
1457 "screenshot - Takes a screenshot and saves it as screenshot.png\r\n"
1458 "screenshot WxH - Saves the screenshot as a WxH size image\r\n";
1460 else if (command ==
"exit")
1463 "exit - Terminates session\r\n\r\n";
1465 else if ((
is_abbrev(
"message", command)))
1468 "message - Displays a simple text message popup\r\n";
1470 else if ((
is_abbrev(
"notification", command)))
1473 "notification - Displays a simple text message notification\r\n";
1478 "theme getthemeinfo - Display the name and location of the current theme\r\n"
1479 "theme reload - Reload the theme\r\n"
1480 "theme showborders - Toggle showing widget borders\r\n"
1481 "theme shownames ON/OFF - Toggle showing widget names\r\n"
1482 "theme getwidgetnames PATH - Display the name and type of all the child widgets from PATH\r\n"
1483 "theme getarea WIDGETNAME - Get the area of widget WIDGET on the active screen\r\n"
1484 "theme setarea WIDGETNAME X Y W H - Change the area of widget WIDGET to X Y W H on the active screen\r\n";
1487 if (!helpText.isEmpty())
1490 if (!command.isEmpty())
1491 helpText += QString(
"Unknown command '%1'\r\n\r\n").arg(command);
1494 "Valid Commands:\r\n"
1495 "---------------\r\n"
1496 "jump - Jump to a specified location in Myth\r\n"
1497 "key - Send a keypress to the program\r\n"
1498 "play - Playback related commands\r\n"
1499 "query - Queries\r\n"
1501 "screenshot - Capture screenshot\r\n"
1502 "message - Display a simple text message\r\n"
1503 "notification - Display a simple text notification\r\n"
1504 "theme - Theme related commands\r\n"
1505 "exit - Exit Network Control\r\n"
1507 "Type 'help COMMANDNAME' for help on any specific command.\r\n";
1515 return QString(
"ERROR: See 'help %1' for usage information")
1518 QString message = nc->
getCommand().remove(0, 7).trimmed();
1521 qApp->postEvent(window, me);
1522 return QString(
"OK");
1528 return QString(
"ERROR: See 'help %1' for usage information")
1531 QString message = nc->
getCommand().remove(0, 12).trimmed();
1534 return QString(
"OK");
1539 QCoreApplication::postEvent(
1544 const QString &reply)
1553 QRegularExpression crlfRegEx(
"\r\n$");
1554 QRegularExpression crlfcrlfRegEx(
"\r\n.*\r\n");
1559 if (client && clientStream && client->state() == QTcpSocket::ConnectedState)
1561 *clientStream << reply;
1563 if ((!reply.contains(crlfRegEx)) ||
1564 ( reply.contains(crlfcrlfRegEx)))
1565 *clientStream <<
"\r\n" <<
m_prompt;
1567 clientStream->flush();
1576 auto *me =
dynamic_cast<MythEvent *
>(e);
1580 const QString& message = me->
Message();
1582 if (message.startsWith(
"MUSIC_CONTROL"))
1584 QStringList tokens = message.simplified().split(
" ");
1585 if ((tokens.size() >= 4) &&
1586 (tokens[1] ==
"ANSWER") &&
1590 for (
int i = 4; i < tokens.size(); i++)
1591 m_answer += QString(
" ") + tokens[i];
1596 else if (message.startsWith(
"NETWORK_CONTROL"))
1598 QStringList tokens = message.simplified().split(
" ");
1599 if ((tokens.size() >= 3) &&
1600 (tokens[1] ==
"ANSWER"))
1603 for (
int i = 3; i < tokens.size(); i++)
1604 m_answer += QString(
" ") + tokens[i];
1607 else if ((tokens.size() >= 4) &&
1608 (tokens[1] ==
"RESPONSE"))
1612 for (
int i = 4; i < tokens.size(); i++)
1613 m_answer += QString(
" ") + tokens[i];
1651 if (ncce ==
nullptr)
1663 bool appendCRLF =
true;
1664 QString queryStr(
"SELECT chanid, starttime, endtime, title, subtitle "
1666 "WHERE starttime < :START AND endtime > :END ");
1668 if (!chanID.isEmpty())
1670 queryStr +=
" AND chanid = :CHANID";
1674 queryStr +=
" ORDER BY starttime, endtime, chanid";
1679 if (!chanID.isEmpty())
1686 while (query.
next())
1688 QString title = query.
value(3).toString();
1689 QString subtitle = query.
value(4).toString();
1691 if (!subtitle.isEmpty())
1692 title += QString(
" -\"%1\"").arg(subtitle);
1693 QByteArray atitle = title.toLocal8Bit();
1696 QString(
"%1 %2 %3 %4")
1697 .arg(QString::number(query.
value(0).toInt()).rightJustified(5,
' '),
1708 result =
"ERROR: Unable to retrieve current schedule list.";
1718 bool appendCRLF =
true;
1720 queryStr =
"SELECT chanid, starttime, title, subtitle "
1721 "FROM recorded WHERE deletepending = 0 ";
1723 if ((!chanid.isEmpty()) && (!starttime.isEmpty()))
1725 queryStr +=
"AND chanid = " + chanid +
" "
1726 "AND starttime = '" + starttime +
"' ";
1730 queryStr +=
"ORDER BY starttime, title;";
1738 while (query.
next())
1740 title = query.
value(2).toString();
1741 subtitle = query.
value(3).toString();
1743 if (!subtitle.isEmpty())
1745 episode = QString(
"%1 -\"%2\"").arg(title, subtitle);
1754 .arg(query.
value(0).toString(),
1763 result =
"ERROR: Unable to retrieve recordings list.";
1773 uint sqlStart = start;
1779 queryStr =
"select chanid, callsign, name from channel "
1780 "where deleted IS NULL and visible > 0 "
1781 "ORDER BY callsign";
1785 QString limitStr = QString(
" LIMIT %1,%2").arg(sqlStart).arg(limit);
1786 queryStr += limitStr;
1792 result =
"ERROR: Unable to retrieve channel list.";
1800 result += QString(R
"(0:0 0 "Invalid" "Invalid")");
1804 while (query.
next())
1809 result += QString(
"%1:%2 %3 \"%4\" \"%5\"\r\n")
1810 .arg(cnt).arg(maxcnt)
1811 .arg(query.
value(0).toString(),
1812 query.
value(1).toString(),
1813 query.
value(2).toString());
1826 QStringList size = nc->
getArg(1).split(
'x');
1827 if (size.size() == 2)
1829 width = size[0].toInt();
1830 height = size[1].toInt();
1836 if (width && height)
1838 args << QString::number(width);
1839 args << QString::number(height);
1843 qApp->postEvent(window, me);
1850 for(
int i=0 ; i<arg ; i++) {
1851 QString argstr = c.simplified().split(
" ")[0];
1852 c = c.mid(argstr.length()).trimmed();