00001 #include <unistd.h>
00002
00003 #include <QCoreApplication>
00004 #include <QRegExp>
00005 #include <QStringList>
00006 #include <QTextStream>
00007 #include <QDir>
00008 #include <QKeyEvent>
00009 #include <QEvent>
00010 #include <QMap>
00011
00012 #include "mythcorecontext.h"
00013 #include "mythversion.h"
00014 #include "networkcontrol.h"
00015 #include "programinfo.h"
00016 #include "remoteutil.h"
00017 #include "previewgenerator.h"
00018 #include "compat.h"
00019 #include "mythsystemevent.h"
00020 #include "mythdirs.h"
00021 #include "mythlogging.h"
00022
00023
00024 #include "mythmainwindow.h"
00025 #include "mythuihelper.h"
00026
00027 #define LOC QString("NetworkControl: ")
00028 #define LOC_ERR QString("NetworkControl Error: ")
00029
00030 #define FE_SHORT_TO 2000
00031 #define FE_LONG_TO 10000
00032
00033 static QEvent::Type kNetworkControlDataReadyEvent =
00034 (QEvent::Type) QEvent::registerEventType();
00035 QEvent::Type NetworkControlCloseEvent::kEventType =
00036 (QEvent::Type) QEvent::registerEventType();
00037
00045 static bool is_abbrev(QString const& command,
00046 QString const& test, int minchars = 1)
00047 {
00048 if (test.length() < minchars)
00049 return command.toLower() == test.toLower();
00050 else
00051 return test.toLower() == command.left(test.length()).toLower();
00052 }
00053
00054 NetworkControl::NetworkControl() :
00055 ServerPool(), prompt("# "),
00056 gotAnswer(false), answer(""),
00057 clientLock(QMutex::Recursive),
00058 commandThread(new MThread("NetworkControl", this)),
00059 stopCommandThread(false)
00060 {
00061
00062 jumpMap["channelpriorities"] = "Channel Recording Priorities";
00063 jumpMap["livetv"] = "Live TV";
00064 jumpMap["livetvinguide"] = "Live TV In Guide";
00065 jumpMap["mainmenu"] = "Main Menu";
00066 jumpMap["managerecordings"] = "Manage Recordings / Fix Conflicts";
00067 jumpMap["mythgallery"] = "MythGallery";
00068 jumpMap["mythvideo"] = "Video Default";
00069 jumpMap["mythweather"] = "MythWeather";
00070 jumpMap["mythgame"] = "MythGame";
00071 jumpMap["mythnews"] = "MythNews";
00072 jumpMap["playdvd"] = "Play Disc";
00073 jumpMap["playmusic"] = "Play music";
00074 jumpMap["programfinder"] = "Program Finder";
00075 jumpMap["programguide"] = "Program Guide";
00076 jumpMap["recordingpriorities"] = "Program Recording Priorities";
00077 jumpMap["ripcd"] = "Rip CD";
00078 jumpMap["musicplaylists"] = "Select music playlists";
00079 jumpMap["deleterecordings"] = "TV Recording Deletion";
00080 jumpMap["playbackrecordings"] = "TV Recording Playback";
00081 jumpMap["videobrowser"] = "Video Browser";
00082 jumpMap["videogallery"] = "Video Gallery";
00083 jumpMap["videolistings"] = "Video Listings";
00084 jumpMap["videomanager"] = "Video Manager";
00085 jumpMap["zoneminderconsole"] = "ZoneMinder Console";
00086 jumpMap["zoneminderliveview"] = "ZoneMinder Live View";
00087 jumpMap["zoneminderevents"] = "ZoneMinder Events";
00088
00089 jumpMap["channelrecpriority"] = "Channel Recording Priorities";
00090 jumpMap["viewscheduled"] = "Manage Recordings / Fix Conflicts";
00091 jumpMap["previousbox"] = "Previously Recorded";
00092 jumpMap["progfinder"] = "Program Finder";
00093 jumpMap["guidegrid"] = "Program Guide";
00094 jumpMap["programrecpriority"] = "Program Recording Priorities";
00095 jumpMap["statusbox"] = "Status Screen";
00096 jumpMap["deletebox"] = "TV Recording Deletion";
00097 jumpMap["playbackbox"] = "TV Recording Playback";
00098 jumpMap["pbb"] = "TV Recording Playback";
00099
00100 keyMap["up"] = Qt::Key_Up;
00101 keyMap["down"] = Qt::Key_Down;
00102 keyMap["left"] = Qt::Key_Left;
00103 keyMap["right"] = Qt::Key_Right;
00104 keyMap["home"] = Qt::Key_Home;
00105 keyMap["end"] = Qt::Key_End;
00106 keyMap["enter"] = Qt::Key_Enter;
00107 keyMap["return"] = Qt::Key_Return;
00108 keyMap["pageup"] = Qt::Key_PageUp;
00109 keyMap["pagedown"] = Qt::Key_PageDown;
00110 keyMap["escape"] = Qt::Key_Escape;
00111 keyMap["tab"] = Qt::Key_Tab;
00112 keyMap["backtab"] = Qt::Key_Backtab;
00113 keyMap["space"] = Qt::Key_Space;
00114 keyMap["backspace"] = Qt::Key_Backspace;
00115 keyMap["insert"] = Qt::Key_Insert;
00116 keyMap["delete"] = Qt::Key_Delete;
00117 keyMap["plus"] = Qt::Key_Plus;
00118 keyMap["+"] = Qt::Key_Plus;
00119 keyMap["comma"] = Qt::Key_Comma;
00120 keyMap[","] = Qt::Key_Comma;
00121 keyMap["minus"] = Qt::Key_Minus;
00122 keyMap["-"] = Qt::Key_Minus;
00123 keyMap["underscore"] = Qt::Key_Underscore;
00124 keyMap["_"] = Qt::Key_Underscore;
00125 keyMap["period"] = Qt::Key_Period;
00126 keyMap["."] = Qt::Key_Period;
00127 keyMap["numbersign"] = Qt::Key_NumberSign;
00128 keyMap["poundsign"] = Qt::Key_NumberSign;
00129 keyMap["hash"] = Qt::Key_NumberSign;
00130 keyMap["#"] = Qt::Key_NumberSign;
00131 keyMap["bracketleft"] = Qt::Key_BracketLeft;
00132 keyMap["["] = Qt::Key_BracketLeft;
00133 keyMap["bracketright"] = Qt::Key_BracketRight;
00134 keyMap["]"] = Qt::Key_BracketRight;
00135 keyMap["backslash"] = Qt::Key_Backslash;
00136 keyMap["\\"] = Qt::Key_Backslash;
00137 keyMap["dollar"] = Qt::Key_Dollar;
00138 keyMap["$"] = Qt::Key_Dollar;
00139 keyMap["percent"] = Qt::Key_Percent;
00140 keyMap["%"] = Qt::Key_Percent;
00141 keyMap["ampersand"] = Qt::Key_Ampersand;
00142 keyMap["&"] = Qt::Key_Ampersand;
00143 keyMap["parenleft"] = Qt::Key_ParenLeft;
00144 keyMap["("] = Qt::Key_ParenLeft;
00145 keyMap["parenright"] = Qt::Key_ParenRight;
00146 keyMap[")"] = Qt::Key_ParenRight;
00147 keyMap["asterisk"] = Qt::Key_Asterisk;
00148 keyMap["*"] = Qt::Key_Asterisk;
00149 keyMap["question"] = Qt::Key_Question;
00150 keyMap["?"] = Qt::Key_Question;
00151 keyMap["slash"] = Qt::Key_Slash;
00152 keyMap["/"] = Qt::Key_Slash;
00153 keyMap["colon"] = Qt::Key_Colon;
00154 keyMap[":"] = Qt::Key_Colon;
00155 keyMap["semicolon"] = Qt::Key_Semicolon;
00156 keyMap[";"] = Qt::Key_Semicolon;
00157 keyMap["less"] = Qt::Key_Less;
00158 keyMap["<"] = Qt::Key_Less;
00159 keyMap["equal"] = Qt::Key_Equal;
00160 keyMap["="] = Qt::Key_Equal;
00161 keyMap["greater"] = Qt::Key_Greater;
00162 keyMap[">"] = Qt::Key_Greater;
00163 keyMap["bar"] = Qt::Key_Bar;
00164 keyMap["pipe"] = Qt::Key_Bar;
00165 keyMap["|"] = Qt::Key_Bar;
00166 keyMap["f1"] = Qt::Key_F1;
00167 keyMap["f2"] = Qt::Key_F2;
00168 keyMap["f3"] = Qt::Key_F3;
00169 keyMap["f4"] = Qt::Key_F4;
00170 keyMap["f5"] = Qt::Key_F5;
00171 keyMap["f6"] = Qt::Key_F6;
00172 keyMap["f7"] = Qt::Key_F7;
00173 keyMap["f8"] = Qt::Key_F8;
00174 keyMap["f9"] = Qt::Key_F9;
00175 keyMap["f10"] = Qt::Key_F10;
00176 keyMap["f11"] = Qt::Key_F11;
00177 keyMap["f12"] = Qt::Key_F12;
00178 keyMap["f13"] = Qt::Key_F13;
00179 keyMap["f14"] = Qt::Key_F14;
00180 keyMap["f15"] = Qt::Key_F15;
00181 keyMap["f16"] = Qt::Key_F16;
00182 keyMap["f17"] = Qt::Key_F17;
00183 keyMap["f18"] = Qt::Key_F18;
00184 keyMap["f19"] = Qt::Key_F19;
00185 keyMap["f20"] = Qt::Key_F20;
00186 keyMap["f21"] = Qt::Key_F21;
00187 keyMap["f22"] = Qt::Key_F22;
00188 keyMap["f23"] = Qt::Key_F23;
00189 keyMap["f24"] = Qt::Key_F24;
00190
00191 keyTextMap[Qt::Key_Plus] = "+";
00192 keyTextMap[Qt::Key_Comma] = ",";
00193 keyTextMap[Qt::Key_Minus] = "-";
00194 keyTextMap[Qt::Key_Underscore] = "_";
00195 keyTextMap[Qt::Key_Period] = ".";
00196 keyTextMap[Qt::Key_NumberSign] = "#";
00197 keyTextMap[Qt::Key_BracketLeft] = "[";
00198 keyTextMap[Qt::Key_BracketRight] = "]";
00199 keyTextMap[Qt::Key_Backslash] = "\\";
00200 keyTextMap[Qt::Key_Dollar] = "$";
00201 keyTextMap[Qt::Key_Percent] = "%";
00202 keyTextMap[Qt::Key_Ampersand] = "&";
00203 keyTextMap[Qt::Key_ParenLeft] = "(";
00204 keyTextMap[Qt::Key_ParenRight] = ")";
00205 keyTextMap[Qt::Key_Asterisk] = "*";
00206 keyTextMap[Qt::Key_Question] = "?";
00207 keyTextMap[Qt::Key_Slash] = "/";
00208 keyTextMap[Qt::Key_Colon] = ":";
00209 keyTextMap[Qt::Key_Semicolon] = ";";
00210 keyTextMap[Qt::Key_Less] = "<";
00211 keyTextMap[Qt::Key_Equal] = "=";
00212 keyTextMap[Qt::Key_Greater] = ">";
00213 keyTextMap[Qt::Key_Bar] = "|";
00214
00215 commandThread->start();
00216
00217 gCoreContext->addListener(this);
00218
00219 connect(this, SIGNAL(newConnection(QTcpSocket*)),
00220 this, SLOT(newConnection(QTcpSocket*)));
00221 }
00222
00223 NetworkControl::~NetworkControl(void)
00224 {
00225 gCoreContext->removeListener(this);
00226
00227 clientLock.lock();
00228 while (!clients.isEmpty())
00229 {
00230 NetworkControlClient *ncc = clients.takeFirst();
00231 delete ncc;
00232 }
00233 clientLock.unlock();
00234
00235 nrLock.lock();
00236 networkControlReplies.push_back(new NetworkCommand(NULL,
00237 "mythfrontend shutting down, connection closing..."));
00238 nrLock.unlock();
00239
00240 notifyDataAvailable();
00241
00242 ncLock.lock();
00243 stopCommandThread = true;
00244 ncCond.wakeOne();
00245 ncLock.unlock();
00246 commandThread->wait();
00247 delete commandThread;
00248 commandThread = NULL;
00249 }
00250
00251 void NetworkControl::run(void)
00252 {
00253 QMutexLocker locker(&ncLock);
00254 while (!stopCommandThread)
00255 {
00256 while (networkControlCommands.empty() && !stopCommandThread)
00257 ncCond.wait(&ncLock);
00258 if (!stopCommandThread)
00259 {
00260 NetworkCommand *nc = networkControlCommands.front();
00261 networkControlCommands.pop_front();
00262 locker.unlock();
00263 processNetworkControlCommand(nc);
00264 locker.relock();
00265 }
00266 }
00267 }
00268
00269 void NetworkControl::processNetworkControlCommand(NetworkCommand *nc)
00270 {
00271 QMutexLocker locker(&clientLock);
00272 QString result;
00273
00274 int clientID = clients.indexOf(nc->getClient());
00275
00276 if (is_abbrev("jump", nc->getArg(0)))
00277 result = processJump(nc);
00278 else if (is_abbrev("key", nc->getArg(0)))
00279 result = processKey(nc);
00280 else if (is_abbrev("play", nc->getArg(0)))
00281 result = processPlay(nc, clientID);
00282 else if (is_abbrev("query", nc->getArg(0)))
00283 result = processQuery(nc);
00284 else if (is_abbrev("set", nc->getArg(0)))
00285 result = processSet(nc);
00286 else if (is_abbrev("screenshot", nc->getArg(0)))
00287 result = saveScreenshot(nc);
00288 else if (is_abbrev("help", nc->getArg(0)))
00289 result = processHelp(nc);
00290 else if (is_abbrev("message", nc->getArg(0)))
00291 result = processMessage(nc);
00292 else if ((nc->getArg(0).toLower() == "exit") || (nc->getArg(0).toLower() == "quit"))
00293 QCoreApplication::postEvent(this,
00294 new NetworkControlCloseEvent(nc->getClient()));
00295 else if (! nc->getArg(0).isEmpty())
00296 result = QString("INVALID command '%1', try 'help' for more info")
00297 .arg(nc->getArg(0));
00298
00299 nrLock.lock();
00300 networkControlReplies.push_back(new NetworkCommand(nc->getClient(),result));
00301 nrLock.unlock();
00302
00303 notifyDataAvailable();
00304 }
00305
00306 void NetworkControl::deleteClient(void)
00307 {
00308 LOG(VB_GENERAL, LOG_INFO, LOC + "Client Socket disconnected");
00309 QMutexLocker locker(&clientLock);
00310
00311 gCoreContext->SendSystemEvent("NET_CTRL_DISCONNECTED");
00312
00313 QList<NetworkControlClient *>::const_iterator it;
00314 for (it = clients.begin(); it != clients.end(); ++it)
00315 {
00316 NetworkControlClient *ncc = *it;
00317 if (ncc->getSocket()->state() == QTcpSocket::UnconnectedState)
00318 {
00319 deleteClient(ncc);
00320 return;
00321 }
00322 }
00323 }
00324
00325 void NetworkControl::deleteClient(NetworkControlClient *ncc)
00326 {
00327 int index = clients.indexOf(ncc);
00328 if (index >= 0)
00329 {
00330 clients.removeAt(index);
00331
00332 delete ncc;
00333 }
00334 else
00335 LOG(VB_GENERAL, LOG_ERR, LOC + QString("deleteClient(%1), unable to "
00336 "locate specified NetworkControlClient").arg((long long)ncc));
00337 }
00338
00339 void NetworkControl::newConnection(QTcpSocket *client)
00340 {
00341 QString welcomeStr;
00342
00343 LOG(VB_GENERAL, LOG_INFO, LOC + QString("New connection established."));
00344
00345 gCoreContext->SendSystemEvent("NET_CTRL_CONNECTED");
00346
00347 NetworkControlClient *ncc = new NetworkControlClient(client);
00348
00349 QMutexLocker locker(&clientLock);
00350 clients.push_back(ncc);
00351
00352 connect(ncc, SIGNAL(commandReceived(QString&)), this,
00353 SLOT(receiveCommand(QString&)));
00354 connect(client, SIGNAL(disconnected()), this, SLOT(deleteClient()));
00355
00356 welcomeStr = "MythFrontend Network Control\r\n";
00357 welcomeStr += "Type 'help' for usage information\r\n"
00358 "---------------------------------";
00359 nrLock.lock();
00360 networkControlReplies.push_back(new NetworkCommand(ncc,welcomeStr));
00361 nrLock.unlock();
00362
00363 notifyDataAvailable();
00364 }
00365
00366 NetworkControlClient::NetworkControlClient(QTcpSocket *s)
00367 {
00368 m_socket = s;
00369 m_textStream = new QTextStream(s);
00370 m_textStream->setCodec("UTF-8");
00371 connect(m_socket, SIGNAL(readyRead()), this, SLOT(readClient()));
00372 }
00373
00374 NetworkControlClient::~NetworkControlClient()
00375 {
00376 m_socket->close();
00377 m_socket->deleteLater();
00378
00379 delete m_textStream;
00380 }
00381
00382 void NetworkControlClient::readClient(void)
00383 {
00384 QTcpSocket *socket = (QTcpSocket *)sender();
00385 if (!socket)
00386 return;
00387
00388 QString lineIn;
00389 while (socket->canReadLine())
00390 {
00391 lineIn = socket->readLine();
00392 #if 0
00393 lineIn.replace(QRegExp("[^-a-zA-Z0-9\\s\\.:_#/$%&()*+,;<=>?\\[\\]\\|]"),
00394 "");
00395 #endif
00396
00397
00398 lineIn.replace(QRegExp("[\r\n]"), "");
00399 lineIn.replace(QRegExp("^\\s"), "");
00400
00401 if (lineIn.isEmpty())
00402 continue;
00403
00404 LOG(VB_NETWORK, LOG_INFO, LOC +
00405 QString("emit commandReceived(%1)").arg(lineIn));
00406 emit commandReceived(lineIn);
00407 }
00408 }
00409
00410 void NetworkControl::receiveCommand(QString &command)
00411 {
00412 LOG(VB_NETWORK, LOG_INFO, LOC +
00413 QString("NetworkControl::receiveCommand(%1)").arg(command));
00414 NetworkControlClient *ncc = static_cast<NetworkControlClient *>(sender());
00415 if (!ncc)
00416 return;
00417
00418 ncLock.lock();
00419 networkControlCommands.push_back(new NetworkCommand(ncc,command));
00420 ncCond.wakeOne();
00421 ncLock.unlock();
00422 }
00423
00424 QString NetworkControl::processJump(NetworkCommand *nc)
00425 {
00426 QString result = "OK";
00427
00428 if ((nc->getArgCount() < 2) || (!jumpMap.contains(nc->getArg(1))))
00429 return QString("ERROR: See 'help %1' for usage information")
00430 .arg(nc->getArg(0));
00431
00432 GetMythMainWindow()->JumpTo(jumpMap[nc->getArg(1)]);
00433
00434
00435
00436 QTime timer;
00437 timer.start();
00438 while ((timer.elapsed() < FE_SHORT_TO) &&
00439 (GetMythUI()->GetCurrentLocation().toLower() != nc->getArg(1)))
00440 usleep(10000);
00441
00442 return result;
00443 }
00444
00445 QString NetworkControl::processKey(NetworkCommand *nc)
00446 {
00447 QString result = "OK";
00448 QKeyEvent *event = NULL;
00449
00450 if (nc->getArgCount() < 2)
00451 return QString("ERROR: See 'help %1' for usage information")
00452 .arg(nc->getArg(0));
00453
00454 QObject *keyDest = NULL;
00455
00456 if (GetMythMainWindow())
00457 keyDest = GetMythMainWindow();
00458 else
00459 return QString("ERROR: Application has no main window!\n");
00460
00461 if (GetMythMainWindow()->currentWidget())
00462 keyDest = GetMythMainWindow()->currentWidget()->focusWidget();
00463
00464 int curToken = 1;
00465 int tokenLen = 0;
00466 while (curToken < nc->getArgCount())
00467 {
00468 tokenLen = nc->getArg(curToken).length();
00469
00470 if (nc->getArg(curToken) == "sleep")
00471 {
00472 sleep(1);
00473 }
00474 else if (keyMap.contains(nc->getArg(curToken)))
00475 {
00476 int keyCode = keyMap[nc->getArg(curToken)];
00477 QString keyText;
00478
00479 if (keyTextMap.contains(keyCode))
00480 keyText = keyTextMap[keyCode];
00481
00482 GetMythUI()->ResetScreensaver();
00483
00484 event = new QKeyEvent(QEvent::KeyPress, keyCode, Qt::NoModifier,
00485 keyText);
00486 QCoreApplication::postEvent(keyDest, event);
00487
00488 event = new QKeyEvent(QEvent::KeyRelease, keyCode, Qt::NoModifier,
00489 keyText);
00490 QCoreApplication::postEvent(keyDest, event);
00491 }
00492 else if (((tokenLen == 1) &&
00493 (nc->getArg(curToken)[0].isLetterOrNumber())) ||
00494 ((tokenLen >= 1) &&
00495 (nc->getArg(curToken).contains("+"))))
00496 {
00497 QKeySequence a(nc->getArg(curToken));
00498 int keyCode = a[0];
00499 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
00500
00501 if (tokenLen > 1)
00502 {
00503 QStringList tokenParts = nc->getArg(curToken).split('+');
00504
00505 int partNum = 0;
00506 while (partNum < (tokenParts.size() - 1))
00507 {
00508 if (tokenParts[partNum].toUpper() == "CTRL")
00509 modifiers |= Qt::ControlModifier;
00510 if (tokenParts[partNum].toUpper() == "SHIFT")
00511 modifiers |= Qt::ShiftModifier;
00512 if (tokenParts[partNum].toUpper() == "ALT")
00513 modifiers |= Qt::AltModifier;
00514 if (tokenParts[partNum].toUpper() == "META")
00515 modifiers |= Qt::MetaModifier;
00516
00517 partNum++;
00518 }
00519 }
00520 else
00521 {
00522 if (nc->getArg(curToken) == nc->getArg(curToken).toUpper())
00523 modifiers = Qt::ShiftModifier;
00524 }
00525
00526 GetMythUI()->ResetScreensaver();
00527
00528 event = new QKeyEvent(QEvent::KeyPress, keyCode, modifiers,
00529 nc->getArg(curToken));
00530 QCoreApplication::postEvent(keyDest, event);
00531
00532 event = new QKeyEvent(QEvent::KeyRelease, keyCode, modifiers,
00533 nc->getArg(curToken));
00534 QCoreApplication::postEvent(keyDest, event);
00535 }
00536 else
00537 return QString("ERROR: Invalid syntax at '%1', see 'help %2' for "
00538 "usage information")
00539 .arg(nc->getArg(curToken)).arg(nc->getArg(0));
00540
00541 curToken++;
00542 }
00543
00544 return result;
00545 }
00546
00547 QString NetworkControl::processPlay(NetworkCommand *nc, int clientID)
00548 {
00549 QString result = "OK";
00550 QString message;
00551
00552 if (nc->getArgCount() < 2)
00553 return QString("ERROR: See 'help %1' for usage information")
00554 .arg(nc->getArg(0));
00555
00556 if ((nc->getArgCount() >= 3) &&
00557 (is_abbrev("file", nc->getArg(1))))
00558 {
00559 if (GetMythUI()->GetCurrentLocation().toLower() != "mainmenu")
00560 {
00561 GetMythMainWindow()->JumpTo(jumpMap["mainmenu"]);
00562
00563 QTime timer;
00564 timer.start();
00565 while ((timer.elapsed() < FE_LONG_TO) &&
00566 (GetMythUI()->GetCurrentLocation().toLower() != "mainmenu"))
00567 usleep(10000);
00568 }
00569
00570 if (GetMythUI()->GetCurrentLocation().toLower() == "mainmenu")
00571 {
00572 QStringList args;
00573 args << nc->getFrom(2);
00574 MythEvent *me = new MythEvent(ACTION_HANDLEMEDIA, args);
00575 qApp->postEvent(GetMythMainWindow(), me);
00576 }
00577 else
00578 return QString("Unable to change to main menu to start playback!");
00579 }
00580 else if ((nc->getArgCount() >= 4) &&
00581 (is_abbrev("program", nc->getArg(1))) &&
00582 (nc->getArg(2).contains(QRegExp("^\\d+$"))) &&
00583 (nc->getArg(3).contains(QRegExp(
00584 "^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d$"))))
00585 {
00586 if (GetMythUI()->GetCurrentLocation().toLower() == "playback")
00587 {
00588 QString message = QString("NETWORK_CONTROL STOP");
00589 MythEvent me(message);
00590 gCoreContext->dispatch(me);
00591
00592 QTime timer;
00593 timer.start();
00594 while ((timer.elapsed() < FE_LONG_TO) &&
00595 (GetMythUI()->GetCurrentLocation().toLower() == "playback"))
00596 usleep(10000);
00597 }
00598
00599 if (GetMythUI()->GetCurrentLocation().toLower() != "playbackbox")
00600 {
00601 GetMythMainWindow()->JumpTo(jumpMap["playbackbox"]);
00602
00603 QTime timer;
00604 timer.start();
00605 while ((timer.elapsed() < 10000) &&
00606 (GetMythUI()->GetCurrentLocation().toLower() != "playbackbox"))
00607 usleep(10000);
00608
00609 timer.start();
00610 while ((timer.elapsed() < 10000) &&
00611 (!GetMythUI()->IsTopScreenInitialized()))
00612 usleep(10000);
00613 }
00614
00615 if (GetMythUI()->GetCurrentLocation().toLower() == "playbackbox")
00616 {
00617 QString action = "PLAY";
00618 if (nc->getArgCount() == 5 && nc->getArg(4) == "resume")
00619 action = "RESUME";
00620
00621 QString message = QString("NETWORK_CONTROL %1 PROGRAM %2 %3 %4")
00622 .arg(action).arg(nc->getArg(2))
00623 .arg(nc->getArg(3).toUpper()).arg(clientID);
00624
00625 result.clear();
00626 gotAnswer = false;
00627 QTime timer;
00628 timer.start();
00629
00630 MythEvent me(message);
00631 gCoreContext->dispatch(me);
00632
00633 while (timer.elapsed() < FE_LONG_TO && !gotAnswer)
00634 usleep(10000);
00635
00636 if (gotAnswer)
00637 result += answer;
00638 else
00639 result = "ERROR: Timed out waiting for reply from player";
00640
00641 }
00642 else
00643 {
00644 result = QString("ERROR: Unable to change to PlaybackBox from "
00645 "%1, cannot play requested file.")
00646 .arg(GetMythUI()->GetCurrentLocation());
00647 }
00648 }
00649 else if (is_abbrev("music", nc->getArg(1)))
00650 {
00651 #if 0
00652 if (GetMythUI()->GetCurrentLocation().toLower() != "playmusic")
00653 {
00654 return QString("ERROR: You are in %1 mode and this command is "
00655 "only for MythMusic")
00656 .arg(GetMythUI()->GetCurrentLocation());
00657 }
00658 #endif
00659
00660 QString hostname = gCoreContext->GetHostName();
00661
00662 if (nc->getArgCount() == 3)
00663 {
00664 if (is_abbrev("play", nc->getArg(2)))
00665 message = QString("MUSIC_COMMAND %1 PLAY").arg(hostname);
00666 else if (is_abbrev("pause", nc->getArg(2)))
00667 message = QString("MUSIC_COMMAND %1 PAUSE").arg(hostname);
00668 else if (is_abbrev("stop", nc->getArg(2)))
00669 message = QString("MUSIC_COMMAND %1 STOP").arg(hostname);
00670 else if (is_abbrev("getvolume", nc->getArg(2)))
00671 {
00672 gotAnswer = false;
00673
00674 MythEvent me(QString("MUSIC_COMMAND %1 GET_VOLUME").arg(hostname));
00675 gCoreContext->dispatch(me);
00676
00677 QTime timer;
00678 timer.start();
00679 while (timer.elapsed() < FE_SHORT_TO && !gotAnswer)
00680 {
00681 qApp->processEvents();
00682 usleep(10000);
00683 }
00684
00685 if (gotAnswer)
00686 return answer;
00687
00688 return "unknown";
00689 }
00690 else if (is_abbrev("getmeta", nc->getArg(2)))
00691 {
00692 gotAnswer = false;
00693
00694 MythEvent me(QString("MUSIC_COMMAND %1 GET_METADATA").arg(hostname));
00695 gCoreContext->dispatch(me);
00696
00697 QTime timer;
00698 timer.start();
00699 while (timer.elapsed() < FE_SHORT_TO && !gotAnswer)
00700 {
00701 qApp->processEvents();
00702 usleep(10000);
00703 }
00704
00705 if (gotAnswer)
00706 return answer;
00707
00708 return "unknown";
00709 }
00710 else
00711 return QString("ERROR: Invalid 'play music' command");
00712 }
00713 else if (nc->getArgCount() > 3)
00714 {
00715 if (is_abbrev("setvolume", nc->getArg(2)))
00716 message = QString("MUSIC_COMMAND %1 SET_VOLUME %2")
00717 .arg(hostname)
00718 .arg(nc->getArg(3));
00719 else if (is_abbrev("track", nc->getArg(2)))
00720 message = QString("MUSIC_COMMAND %1 PLAY_TRACK %2")
00721 .arg(hostname)
00722 .arg(nc->getArg(3));
00723 else if (is_abbrev("url", nc->getArg(2)))
00724 message = QString("MUSIC_COMMAND %1 PLAY_URL %2")
00725 .arg(hostname)
00726 .arg(nc->getArg(3));
00727 else if (is_abbrev("file", nc->getArg(2)))
00728 message = QString("MUSIC_COMMAND %1 PLAY_FILE '%2'")
00729 .arg(hostname)
00730 .arg(nc->getFrom(3));
00731 else
00732 return QString("ERROR: Invalid 'play music' command");
00733 }
00734 else
00735 return QString("ERROR: Invalid 'play music' command");
00736 }
00737
00738
00739 else if (GetMythUI()->GetCurrentLocation().toLower() != "playback")
00740 {
00741 return QString("ERROR: You are in %1 mode and this command is only "
00742 "for playback mode")
00743 .arg(GetMythUI()->GetCurrentLocation());
00744 }
00745 else if (is_abbrev("chanid", nc->getArg(1), 5))
00746 {
00747 if (nc->getArg(2).contains(QRegExp("^\\d+$")))
00748 message = QString("NETWORK_CONTROL CHANID %1").arg(nc->getArg(2));
00749 else
00750 return QString("ERROR: See 'help %1' for usage information")
00751 .arg(nc->getArg(0));
00752 }
00753 else if (is_abbrev("channel", nc->getArg(1), 5))
00754 {
00755 if (nc->getArgCount() < 3)
00756 return "ERROR: See 'help play' for usage information";
00757
00758 if (is_abbrev("up", nc->getArg(2)))
00759 message = "NETWORK_CONTROL CHANNEL UP";
00760 else if (is_abbrev("down", nc->getArg(2)))
00761 message = "NETWORK_CONTROL CHANNEL DOWN";
00762 else if (nc->getArg(2).contains(QRegExp("^[-\\.\\d_#]+$")))
00763 message = QString("NETWORK_CONTROL CHANNEL %1").arg(nc->getArg(2));
00764 else
00765 return QString("ERROR: See 'help %1' for usage information")
00766 .arg(nc->getArg(0));
00767 }
00768 else if (is_abbrev("seek", nc->getArg(1), 2))
00769 {
00770 if (nc->getArgCount() < 3)
00771 return QString("ERROR: See 'help %1' for usage information")
00772 .arg(nc->getArg(0));
00773
00774 if (is_abbrev("beginning", nc->getArg(2)))
00775 message = "NETWORK_CONTROL SEEK BEGINNING";
00776 else if (is_abbrev("forward", nc->getArg(2)))
00777 message = "NETWORK_CONTROL SEEK FORWARD";
00778 else if (is_abbrev("rewind", nc->getArg(2)) ||
00779 is_abbrev("backward", nc->getArg(2)))
00780 message = "NETWORK_CONTROL SEEK BACKWARD";
00781 else if (nc->getArg(2).contains(QRegExp("^\\d\\d:\\d\\d:\\d\\d$")))
00782 {
00783 int hours = nc->getArg(2).mid(0, 2).toInt();
00784 int minutes = nc->getArg(2).mid(3, 2).toInt();
00785 int seconds = nc->getArg(2).mid(6, 2).toInt();
00786 message = QString("NETWORK_CONTROL SEEK POSITION %1")
00787 .arg((hours * 3600) + (minutes * 60) + seconds);
00788 }
00789 else
00790 return QString("ERROR: See 'help %1' for usage information")
00791 .arg(nc->getArg(0));
00792 }
00793 else if (is_abbrev("speed", nc->getArg(1), 2))
00794 {
00795 if (nc->getArgCount() < 3)
00796 return QString("ERROR: See 'help %1' for usage information")
00797 .arg(nc->getArg(0));
00798
00799 QString token2 = nc->getArg(2).toLower();
00800 if ((token2.contains(QRegExp("^\\-*\\d+x$"))) ||
00801 (token2.contains(QRegExp("^\\-*\\d+\\/\\d+x$"))) ||
00802 (token2.contains(QRegExp("^\\-*\\d*\\.\\d+x$"))))
00803 message = QString("NETWORK_CONTROL SPEED %1").arg(token2);
00804 else if (is_abbrev("normal", token2))
00805 message = QString("NETWORK_CONTROL SPEED 1x");
00806 else if (is_abbrev("pause", token2))
00807 message = QString("NETWORK_CONTROL SPEED 0x");
00808 else
00809 return QString("ERROR: See 'help %1' for usage information")
00810 .arg(nc->getArg(0));
00811 }
00812 else if (is_abbrev("save", nc->getArg(1), 2))
00813 {
00814 if (is_abbrev("screenshot", nc->getArg(2), 2))
00815 return saveScreenshot(nc);
00816 }
00817 else if (is_abbrev("stop", nc->getArg(1), 2))
00818 message = QString("NETWORK_CONTROL STOP");
00819 else if (is_abbrev("volume", nc->getArg(1), 2))
00820 {
00821 if ((nc->getArgCount() < 3) ||
00822 (!nc->getArg(2).toLower().contains(QRegExp("^\\d+%?$"))))
00823 {
00824 return QString("ERROR: See 'help %1' for usage information")
00825 .arg(nc->getArg(0));
00826 }
00827
00828 message = QString("NETWORK_CONTROL VOLUME %1")
00829 .arg(nc->getArg(2).toLower());
00830 }
00831 else
00832 return QString("ERROR: See 'help %1' for usage information")
00833 .arg(nc->getArg(0));
00834
00835 if (!message.isEmpty())
00836 {
00837 MythEvent me(message);
00838 gCoreContext->dispatch(me);
00839 }
00840
00841 return result;
00842 }
00843
00844 QString NetworkControl::processQuery(NetworkCommand *nc)
00845 {
00846 QString result = "OK";
00847
00848 if (nc->getArgCount() < 2)
00849 return QString("ERROR: See 'help %1' for usage information")
00850 .arg(nc->getArg(0));
00851
00852 if (is_abbrev("location", nc->getArg(1)))
00853 {
00854 bool fullPath = false;
00855 bool mainStackOnly = true;
00856
00857 if (nc->getArgCount() > 2)
00858 fullPath = (nc->getArg(2).toLower() == "true" || nc->getArg(2) == "1");
00859 if (nc->getArgCount() > 3)
00860 mainStackOnly = (nc->getArg(3).toLower() == "true" || nc->getArg(3) == "1");
00861
00862 QString location = GetMythUI()->GetCurrentLocation(fullPath, mainStackOnly);
00863 result = location;
00864
00865
00866 if (location == "Playback")
00867 {
00868 result += " ";
00869 gotAnswer = false;
00870 QString message = QString("NETWORK_CONTROL QUERY POSITION");
00871 MythEvent me(message);
00872 gCoreContext->dispatch(me);
00873
00874 QTime timer;
00875 timer.start();
00876 while (timer.elapsed() < FE_SHORT_TO && !gotAnswer)
00877 usleep(10000);
00878
00879 if (gotAnswer)
00880 result += answer;
00881 else
00882 result = "ERROR: Timed out waiting for reply from player";
00883 }
00884 }
00885 else if (is_abbrev("verbose", nc->getArg(1)))
00886 {
00887 return verboseString;
00888 }
00889 else if (is_abbrev("liveTV", nc->getArg(1)))
00890 {
00891 if(nc->getArgCount() == 3)
00892 return listSchedule(nc->getArg(2));
00893 else
00894 return listSchedule();
00895 }
00896 else if (is_abbrev("version", nc->getArg(1)))
00897 {
00898 int dbSchema = gCoreContext->GetNumSetting("DBSchemaVer");
00899
00900 return QString("VERSION: %1/%2 %3 %4 QT/%5 DBSchema/%6")
00901 .arg(MYTH_SOURCE_VERSION)
00902 .arg(MYTH_SOURCE_PATH)
00903 .arg(MYTH_BINARY_VERSION)
00904 .arg(MYTH_PROTO_VERSION)
00905 .arg(QT_VERSION_STR)
00906 .arg(dbSchema);
00907
00908 }
00909 else if(is_abbrev("time", nc->getArg(1)))
00910 return QDateTime::currentDateTime().toString(Qt::ISODate);
00911 else if (is_abbrev("uptime", nc->getArg(1)))
00912 {
00913 QString str;
00914 time_t uptime;
00915
00916 if (getUptime(uptime))
00917 str = QString::number(uptime);
00918 else
00919 str = QString("Could not determine uptime.");
00920 return str;
00921 }
00922 else if (is_abbrev("load", nc->getArg(1)))
00923 {
00924 QString str;
00925 double loads[3];
00926
00927 if (getloadavg(loads,3) == -1)
00928 str = QString("getloadavg() failed");
00929 else
00930 str = QString("%1 %2 %3").arg(loads[0]).arg(loads[1]).arg(loads[2]);
00931 return str;
00932 }
00933 else if (is_abbrev("memstats", nc->getArg(1)))
00934 {
00935 QString str;
00936 int totalMB, freeMB, totalVM, freeVM;
00937
00938 if (getMemStats(totalMB, freeMB, totalVM, freeVM))
00939 str = QString("%1 %2 %3 %4")
00940 .arg(totalMB).arg(freeMB).arg(totalVM).arg(freeVM);
00941 else
00942 str = QString("Could not determine memory stats.");
00943 return str;
00944 }
00945 else if (is_abbrev("volume", nc->getArg(1)))
00946 {
00947 QString str = "0%";
00948
00949 QString location = GetMythUI()->GetCurrentLocation(false, false);
00950
00951 if (location != "Playback")
00952 return str;
00953
00954 gotAnswer = false;
00955 QString message = QString("NETWORK_CONTROL QUERY VOLUME");
00956 MythEvent me(message);
00957 gCoreContext->dispatch(me);
00958
00959 QTime timer;
00960 timer.start();
00961 while (timer.elapsed() < FE_SHORT_TO && !gotAnswer)
00962 usleep(10000);
00963
00964 if (gotAnswer)
00965 str = answer;
00966 else
00967 str = "ERROR: Timed out waiting for reply from player";
00968
00969 return str;
00970 }
00971 else if ((nc->getArgCount() == 4) &&
00972 is_abbrev("recording", nc->getArg(1)) &&
00973 (nc->getArg(2).contains(QRegExp("^\\d+$"))) &&
00974 (nc->getArg(3).contains(QRegExp(
00975 "^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d$"))))
00976 return listRecordings(nc->getArg(2), nc->getArg(3).toUpper());
00977 else if (is_abbrev("recordings", nc->getArg(1)))
00978 return listRecordings();
00979 else if (is_abbrev("channels", nc->getArg(1)))
00980 {
00981 if (nc->getArgCount() == 2)
00982 return listChannels(0, 0);
00983 else if (nc->getArgCount() == 4)
00984 return listChannels(nc->getArg(2).toLower().toUInt(),
00985 nc->getArg(3).toLower().toUInt());
00986 else
00987 return QString("ERROR: See 'help %1' for usage information "
00988 "(parameters mismatch)").arg(nc->getArg(0));
00989 }
00990 else
00991 return QString("ERROR: See 'help %1' for usage information")
00992 .arg(nc->getArg(0));
00993
00994 return result;
00995 }
00996
00997 QString NetworkControl::processSet(NetworkCommand *nc)
00998 {
00999 if (nc->getArgCount() == 1)
01000 return QString("ERROR: See 'help %1' for usage information")
01001 .arg(nc->getArg(0));
01002
01003 if (nc->getArg(1) == "verbose")
01004 {
01005 if (nc->getArgCount() > 3)
01006 return QString("ERROR: Separate filters with commas with no "
01007 "space: playback,audio\r\n See 'help %1' for usage "
01008 "information").arg(nc->getArg(0));
01009
01010 QString oldVerboseString = verboseString;
01011 QString result = "OK";
01012
01013 int pva_result = verboseArgParse(nc->getArg(2));
01014
01015 if (pva_result != 0 )
01016 result = "Failed";
01017
01018 result += "\r\n";
01019 result += " Previous filter: " + oldVerboseString + "\r\n";
01020 result += " New Filter: " + verboseString + "\r\n";
01021
01022 LOG(VB_GENERAL, LOG_NOTICE,
01023 QString("Verbose mask changed, new level is: %1")
01024 .arg(verboseString));
01025
01026 return result;
01027 }
01028
01029 return QString("ERROR: See 'help %1' for usage information")
01030 .arg(nc->getArg(0));
01031 }
01032
01033 QString NetworkControl::processHelp(NetworkCommand *nc)
01034 {
01035 QString command, helpText;
01036
01037 if (nc->getArgCount() >= 1)
01038 {
01039 if (is_abbrev("help", nc->getArg(0)))
01040 {
01041 if (nc->getArgCount() >= 2)
01042 command = nc->getArg(1);
01043 else
01044 command.clear();
01045 }
01046 else
01047 {
01048 command = nc->getArg(0);
01049 }
01050 }
01051
01052 if (is_abbrev("jump", command))
01053 {
01054 QMap<QString, QString>::Iterator it;
01055 helpText +=
01056 "Usage: jump JUMPPOINT\r\n"
01057 "\r\n"
01058 "Where JUMPPOINT is one of the following:\r\n";
01059
01060 for (it = jumpMap.begin(); it != jumpMap.end(); ++it)
01061 {
01062 helpText += it.key().leftJustified(20, ' ', true) + " - " +
01063 *it + "\r\n";
01064 }
01065 }
01066 else if (is_abbrev("key", command))
01067 {
01068 helpText +=
01069 "key LETTER - Send the letter key specified\r\n"
01070 "key NUMBER - Send the number key specified\r\n"
01071 "key CODE - Send one of the following key codes\r\n"
01072 "\r\n";
01073
01074 QMap<QString, int>::Iterator it;
01075 bool first = true;
01076 for (it = keyMap.begin(); it != keyMap.end(); ++it)
01077 {
01078 if (first)
01079 first = false;
01080 else
01081 helpText += ", ";
01082
01083 helpText += it.key();
01084 }
01085 helpText += "\r\n";
01086 }
01087 else if (is_abbrev("play", command))
01088 {
01089 helpText +=
01090 "play volume NUMBER% - Change volume to given percentage value\r\n"
01091 "play channel up - Change channel Up\r\n"
01092 "play channel down - Change channel Down\r\n"
01093 "play channel NUMBER - Change to a specific channel number\r\n"
01094 "play chanid NUMBER - Change to a specific channel id (chanid)\r\n"
01095 "play file FILENAME - Play FILENAME (FILENAME may be a file or a myth:// URL)\r\n"
01096 "play program CHANID yyyy-MM-ddThh:mm:ss\r\n"
01097 " - Play program with chanid & starttime\r\n"
01098 "play program CHANID yyyy-MM-ddThh:mm:ss resume\r\n"
01099 " - Resume program with chanid & starttime\r\n"
01100 "play save preview\r\n"
01101 " - Save preview image from current position\r\n"
01102 "play save preview FILENAME\r\n"
01103 " - Save preview image to FILENAME\r\n"
01104 "play save preview FILENAME WxH\r\n"
01105 " - Save preview image of size WxH\r\n"
01106 "play seek beginning - Seek to the beginning of the recording\r\n"
01107 "play seek forward - Skip forward in the video\r\n"
01108 "play seek backward - Skip backwards in the video\r\n"
01109 "play seek HH:MM:SS - Seek to a specific position\r\n"
01110 "play speed pause - Pause playback\r\n"
01111 "play speed normal - Playback at normal speed\r\n"
01112 "play speed 1x - Playback at normal speed\r\n"
01113 "play speed SPEEDx - Playback where SPEED must be a decimal\r\n"
01114 "play speed 1/8x - Playback at 1/8x speed\r\n"
01115 "play speed 1/4x - Playback at 1/4x speed\r\n"
01116 "play speed 1/3x - Playback at 1/3x speed\r\n"
01117 "play speed 1/2x - Playback at 1/2x speed\r\n"
01118 "play stop - Stop playback\r\n"
01119 "play music play - Resume playback (MythMusic)\r\n"
01120 "play music pause - Pause playback (MythMusic)\r\n"
01121 "play music stop - Stop Playback (MythMusic)\r\n"
01122 "play music setvolume N - Set volume to number (MythMusic)\r\n"
01123 "play music getvolume - Get current volume (MythMusic)\r\n"
01124 "play music getmeta - Get metadata for current track (MythMusic)\r\n"
01125 "play music file NAME - Play specified file (MythMusic)\r\n"
01126 "play music track N - Switch to specified track (MythMusic)\r\n"
01127 "play music url URL - Play specified URL (MythMusic)\r\n";
01128 }
01129 else if (is_abbrev("query", command))
01130 {
01131 helpText +=
01132 "query location - Query current screen or location\r\n"
01133 "query volume - Query the current playback volume\r\n"
01134 "query recordings - List currently available recordings\r\n"
01135 "query recording CHANID STARTTIME\r\n"
01136 " - List info about the specified program\r\n"
01137 "query liveTV - List current TV schedule\r\n"
01138 "query liveTV CHANID - Query current program for specified channel\r\n"
01139 "query load - List 1/5/15 load averages\r\n"
01140 "query memstats - List free and total, physical and swap memory\r\n"
01141 "query time - Query current time on frontend\r\n"
01142 "query uptime - Query machine uptime\r\n"
01143 "query verbose - Get current VERBOSE mask\r\n"
01144 "query version - Query Frontend version details\r\n"
01145 "query channels - Query available channels\r\n"
01146 "query channels START LIMIT - Query available channels from START and limit results to LIMIT lines\r\n";
01147 }
01148 else if (is_abbrev("set", command))
01149 {
01150 helpText +=
01151 "set verbose debug-mask - "
01152 "Change the VERBOSE mask to 'debug-mask'\r\n"
01153 " (i.e. 'set verbose playback,audio')\r\n"
01154 " use 'set verbose default' to revert\r\n"
01155 " back to the default level of\r\n";
01156 }
01157 else if (is_abbrev("screenshot", command))
01158 {
01159 helpText +=
01160 "screenshot - Takes a screenshot and saves it as screenshot.png\r\n"
01161 "screenshot WxH - Saves the screenshot as a WxH size image\r\n";
01162 }
01163 else if (command == "exit")
01164 {
01165 helpText +=
01166 "exit - Terminates session\r\n\r\n";
01167 }
01168 else if ((is_abbrev("message", command)))
01169 {
01170 helpText +=
01171 "message - Displays a simple text message popup\r\n";
01172 }
01173
01174 if (!helpText.isEmpty())
01175 return helpText;
01176
01177 if (!command.isEmpty())
01178 helpText += QString("Unknown command '%1'\r\n\r\n").arg(command);
01179
01180 helpText +=
01181 "Valid Commands:\r\n"
01182 "---------------\r\n"
01183 "jump - Jump to a specified location in Myth\r\n"
01184 "key - Send a keypress to the program\r\n"
01185 "play - Playback related commands\r\n"
01186 "query - Queries\r\n"
01187 "set - Changes\r\n"
01188 "screenshot - Capture screenshot\r\n"
01189 "message - Display a simple text message\r\n"
01190 "exit - Exit Network Control\r\n"
01191 "\r\n"
01192 "Type 'help COMMANDNAME' for help on any specific command.\r\n";
01193
01194 return helpText;
01195 }
01196
01197 QString NetworkControl::processMessage(NetworkCommand *nc)
01198 {
01199 if (nc->getArgCount() < 2)
01200 return QString("ERROR: See 'help %1' for usage information")
01201 .arg(nc->getArg(0));
01202
01203 QString message = nc->getCommand().remove(0, 7).trimmed();
01204 MythMainWindow *window = GetMythMainWindow();
01205 MythEvent* me = new MythEvent(MythEvent::MythUserMessage, message);
01206 qApp->postEvent(window, me);
01207 return QString("OK");
01208 }
01209
01210 void NetworkControl::notifyDataAvailable(void)
01211 {
01212 QCoreApplication::postEvent(
01213 this, new QEvent(kNetworkControlDataReadyEvent));
01214 }
01215
01216 void NetworkControl::sendReplyToClient(NetworkControlClient *ncc,
01217 QString &reply)
01218 {
01219 if (!clients.contains(ncc))
01220
01221
01222 return;
01223
01224 QRegExp crlfRegEx("\r\n$");
01225 QRegExp crlfcrlfRegEx("\r\n.*\r\n");
01226
01227 QTcpSocket *client = ncc->getSocket();
01228 QTextStream *clientStream = ncc->getTextStream();
01229
01230 if (client && clientStream && client->state() == QTcpSocket::ConnectedState)
01231 {
01232 *clientStream << reply;
01233
01234 if ((!reply.contains(crlfRegEx)) ||
01235 ( reply.contains(crlfcrlfRegEx)))
01236 *clientStream << "\r\n" << prompt;
01237
01238 clientStream->flush();
01239 client->flush();
01240 }
01241 }
01242
01243 void NetworkControl::customEvent(QEvent *e)
01244 {
01245 if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage)
01246 {
01247 MythEvent *me = (MythEvent *)e;
01248 QString message = me->Message();
01249
01250 if (message.left(13) == "MUSIC_CONTROL")
01251 {
01252 QStringList tokens = message.simplified().split(" ");
01253 if ((tokens.size() >= 4) &&
01254 (tokens[1] == "ANSWER") &&
01255 (tokens[2] == gCoreContext->GetHostName()))
01256 {
01257 answer = tokens[3];
01258 for (int i = 4; i < tokens.size(); i++)
01259 answer += QString(" ") + tokens[i];
01260 gotAnswer = true;
01261 }
01262
01263 }
01264 else if (message.left(15) == "NETWORK_CONTROL")
01265 {
01266 QStringList tokens = message.simplified().split(" ");
01267 if ((tokens.size() >= 3) &&
01268 (tokens[1] == "ANSWER"))
01269 {
01270 answer = tokens[2];
01271 for (int i = 3; i < tokens.size(); i++)
01272 answer += QString(" ") + tokens[i];
01273 gotAnswer = true;
01274 }
01275 else if ((tokens.size() >= 4) &&
01276 (tokens[1] == "RESPONSE"))
01277 {
01278
01279 answer = tokens[3];
01280 for (int i = 4; i < tokens.size(); i++)
01281 answer += QString(" ") + tokens[i];
01282 gotAnswer = true;
01283 }
01284 }
01285 }
01286 else if (e->type() == kNetworkControlDataReadyEvent)
01287 {
01288 NetworkCommand *nc;
01289 QString reply;
01290
01291 QMutexLocker locker(&clientLock);
01292 QMutexLocker nrLocker(&nrLock);
01293
01294 while (!networkControlReplies.isEmpty())
01295 {
01296 nc = networkControlReplies.front();
01297 networkControlReplies.pop_front();
01298
01299 reply = nc->getCommand();
01300
01301 NetworkControlClient * ncc = nc->getClient();
01302 if (ncc)
01303 {
01304 sendReplyToClient(ncc, reply);
01305 }
01306 else
01307 {
01308 QList<NetworkControlClient *>::const_iterator it;
01309 for (it = clients.begin(); it != clients.end(); ++it)
01310 {
01311 NetworkControlClient *ncc = *it;
01312 if (ncc)
01313 sendReplyToClient(ncc, reply);
01314 }
01315 }
01316 delete nc;
01317 }
01318 }
01319 else if (e->type() == NetworkControlCloseEvent::kEventType)
01320 {
01321 NetworkControlCloseEvent *ncce = static_cast<NetworkControlCloseEvent*>(e);
01322 NetworkControlClient *ncc = ncce->getClient();
01323
01324 deleteClient(ncc);
01325 }
01326 }
01327
01328 QString NetworkControl::listSchedule(const QString& chanID) const
01329 {
01330 QString result("");
01331 MSqlQuery query(MSqlQuery::InitCon());
01332 bool appendCRLF = true;
01333 QString queryStr("SELECT chanid, starttime, endtime, title, subtitle "
01334 "FROM program "
01335 "WHERE starttime < :START AND endtime > :END ");
01336
01337 if (!chanID.isEmpty())
01338 {
01339 queryStr += " AND chanid = :CHANID";
01340 appendCRLF = false;
01341 }
01342
01343 queryStr += " ORDER BY starttime, endtime, chanid";
01344
01345 query.prepare(queryStr);
01346 query.bindValue(":START", QDateTime::currentDateTime());
01347 query.bindValue(":END", QDateTime::currentDateTime());
01348 if (!chanID.isEmpty())
01349 {
01350 query.bindValue(":CHANID", chanID);
01351 }
01352
01353 if (query.exec())
01354 {
01355 while (query.next())
01356 {
01357 QString title = query.value(3).toString();
01358 QString subtitle = query.value(4).toString();
01359
01360 if (!subtitle.isEmpty())
01361 title += QString(" -\"%1\"").arg(subtitle);
01362 QByteArray atitle = title.toLocal8Bit();
01363
01364 result +=
01365 QString("%1 %2 %3 %4")
01366 .arg(QString::number(query.value(0).toInt())
01367 .rightJustified(5, ' '))
01368 .arg(query.value(1).toDateTime().toString(Qt::ISODate))
01369 .arg(query.value(2).toDateTime().toString(Qt::ISODate))
01370 .arg(atitle.constData());
01371
01372 if (appendCRLF)
01373 result += "\r\n";
01374 }
01375 }
01376 else
01377 {
01378 result = "ERROR: Unable to retrieve current schedule list.";
01379 }
01380 return result;
01381 }
01382
01383 QString NetworkControl::listRecordings(QString chanid, QString starttime)
01384 {
01385 QString result;
01386 MSqlQuery query(MSqlQuery::InitCon());
01387 QString queryStr;
01388 bool appendCRLF = true;
01389
01390 queryStr = "SELECT chanid, starttime, title, subtitle "
01391 "FROM recorded WHERE deletepending = 0 ";
01392
01393 if ((!chanid.isEmpty()) && (!starttime.isEmpty()))
01394 {
01395 queryStr += "AND chanid = " + chanid + " "
01396 "AND starttime = '" + starttime + "' ";
01397 appendCRLF = false;
01398 }
01399
01400 queryStr += "ORDER BY starttime, title;";
01401
01402 query.prepare(queryStr);
01403 if (query.exec())
01404 {
01405 QString episode, title, subtitle;
01406 while (query.next())
01407 {
01408 title = query.value(2).toString();
01409 subtitle = query.value(3).toString();
01410
01411 if (!subtitle.isEmpty())
01412 episode = QString("%1 -\"%2\"")
01413 .arg(title)
01414 .arg(subtitle);
01415 else
01416 episode = title;
01417
01418 result +=
01419 QString("%1 %2 %3").arg(query.value(0).toInt())
01420 .arg(query.value(1).toDateTime().toString(Qt::ISODate))
01421 .arg(episode);
01422
01423 if (appendCRLF)
01424 result += "\r\n";
01425 }
01426 }
01427 else
01428 result = "ERROR: Unable to retrieve recordings list.";
01429
01430 return result;
01431 }
01432
01433 QString NetworkControl::listChannels(const uint start, const uint limit) const
01434 {
01435 QString result;
01436 MSqlQuery query(MSqlQuery::InitCon());
01437 QString queryStr;
01438 uint cnt;
01439 uint maxcnt;
01440 uint sqlStart = start;
01441
01442
01443 if (sqlStart > 0)
01444 sqlStart--;
01445
01446 queryStr = "select chanid, callsign, name from channel where visible=1";
01447 queryStr += " ORDER BY callsign";
01448
01449 if (limit > 0)
01450 {
01451 QString limitStr = QString(" LIMIT %1,%2").arg(sqlStart).arg(limit);
01452 queryStr += limitStr;
01453 }
01454
01455 query.prepare(queryStr);
01456 if (!query.exec())
01457 {
01458 result = "ERROR: Unable to retrieve channel list.";
01459 return result;
01460 }
01461
01462 maxcnt = query.size();
01463 cnt = 0;
01464 if (maxcnt == 0)
01465 {
01466 result += QString("0:0 0 \"Invalid\" \"Invalid\"");
01467 return result;
01468 }
01469
01470 while (query.next())
01471 {
01472
01473
01474 cnt++;
01475 result += QString("%1:%2 %3 \"%4\" \"%5\"\r\n")
01476 .arg(cnt).arg(maxcnt).arg(query.value(0).toInt())
01477 .arg(query.value(1).toString())
01478 .arg(query.value(2).toString());
01479 }
01480
01481 return result;
01482 }
01483
01484 QString NetworkControl::saveScreenshot(NetworkCommand *nc)
01485 {
01486 int width = 0;
01487 int height = 0;
01488
01489 if (nc->getArgCount() == 2)
01490 {
01491 QStringList size = nc->getArg(1).split('x');
01492 if (size.size() == 2)
01493 {
01494 width = size[0].toInt();
01495 height = size[1].toInt();
01496 }
01497 }
01498
01499 MythMainWindow *window = GetMythMainWindow();
01500 QStringList args;
01501 if (width && height)
01502 {
01503 args << QString::number(width);
01504 args << QString::number(height);
01505 }
01506 MythEvent* me = new MythEvent(MythEvent::MythEventMessage,
01507 ACTION_SCREENSHOT, args);
01508 qApp->postEvent(window, me);
01509 return "OK";
01510 }
01511
01512 QString NetworkCommand::getFrom(int arg)
01513 {
01514 QString c = m_command;
01515 for(int i=0 ; i<arg ; i++) {
01516 QString arg = c.simplified().split(" ")[0];
01517 c = c.mid(arg.length()).trimmed();
01518 }
01519 return c;
01520 }
01521
01522
01523