00001 #include <qapplication.h>
00002 #include <qsqldatabase.h>
00003 #include <qdatetime.h>
00004 #include <qfile.h>
00005 #include <qdir.h>
00006 #include <qurl.h>
00007 #include <qthread.h>
00008 #include <qwaitcondition.h>
00009 #include <qregexp.h>
00010
00011 #include <cstdlib>
00012 #include <cerrno>
00013 #include <math.h>
00014 #include <unistd.h>
00015 #include <fcntl.h>
00016 #include "../../config.h"
00017 #ifdef HAVE_SYS_SOUNDCARD_H
00018 #include <sys/soundcard.h>
00019 #elif HAVE_SOUNDCARD_H
00020 #include <soundcard.h>
00021 #endif
00022 #ifndef USING_MINGW
00023 #include <sys/ioctl.h>
00024 #endif
00025
00026 #include <list>
00027 #include <iostream>
00028 using namespace std;
00029
00030 #include <sys/stat.h>
00031 #ifdef __linux__
00032 # include <sys/vfs.h>
00033 #else // if !__linux__
00034 # include <sys/param.h>
00035 # ifndef USING_MINGW
00036 # include <sys/mount.h>
00037 # endif // USING_MINGW
00038 #endif // !__linux__
00039
00040 #include "libmyth/exitcodes.h"
00041 #include "libmyth/mythcontext.h"
00042 #include "libmyth/util.h"
00043 #include "libmyth/mythdbcon.h"
00044
00045 #include "mainserver.h"
00046 #include "scheduler.h"
00047 #include "backendutil.h"
00048 #include "programinfo.h"
00049 #include "jobqueue.h"
00050 #include "autoexpire.h"
00051 #include "previewgenerator.h"
00052 #include "storagegroup.h"
00053 #include "compat.h"
00054
00058 #define PRT_TIMEOUT 10
00059
00060 #define PRT_STARTUP_THREAD_COUNT 5
00061
00062 namespace {
00063
00064 int delete_file_immediately(const QString &filename,
00065 bool followLinks, bool checkexists)
00066 {
00067
00068 QFile checkFile(filename);
00069 int success1, success2;
00070
00071 VERBOSE(VB_FILE, QString("About to delete file: %1").arg(filename));
00072 success1 = true;
00073 success2 = true;
00074 if (followLinks)
00075 {
00076 QFileInfo finfo(filename);
00077 if (finfo.isSymLink())
00078 {
00079 QString linktext = finfo.readLink();
00080 if (linktext.left(1) != "/")
00081 linktext = finfo.dirPath(true) + "/" + finfo.readLink();
00082
00083 QFile target(linktext);
00084 if (!(success1 = target.remove()))
00085 {
00086 VERBOSE(VB_IMPORTANT, QString("Error deleting '%1' -> '%2': %3")
00087 .arg(filename).arg(linktext.local8Bit())
00088 .arg(strerror(errno)));
00089 }
00090 }
00091 }
00092 if ((!checkexists || checkFile.exists()) &&
00093 !(success2 = checkFile.remove()))
00094 {
00095 VERBOSE(VB_IMPORTANT, QString("Error deleting '%1': %2")
00096 .arg(filename).arg(strerror(errno)));
00097 }
00098 return success1 && success2 ? 0 : -1;
00099 }
00100
00101 };
00102
00103 QMutex MainServer::truncate_and_close_lock;
00104
00105 class ProcessRequestThread : public QThread
00106 {
00107 public:
00108 ProcessRequestThread(MainServer *ms) { parent = ms; }
00109
00110 void setup(MythSocket *sock)
00111 {
00112 lock.lock();
00113 socket = sock;
00114 socket->UpRef();
00115 waitCond.wakeOne();
00116 lock.unlock();
00117 }
00118
00119 void killit(void)
00120 {
00121 lock.lock();
00122 threadlives = false;
00123 waitCond.wakeOne();
00124 lock.unlock();
00125 }
00126
00127 virtual void run()
00128 {
00129 threadlives = true;
00130
00131 lock.lock();
00132
00133
00134
00135 waitCond.wakeOne();
00136
00137 while (1)
00138 {
00139 waitCond.wait(&lock);
00140
00141 if (!threadlives)
00142 break;
00143
00144 if (!socket)
00145 continue;
00146
00147 parent->ProcessRequest(socket);
00148 socket->DownRef();
00149 socket = NULL;
00150 parent->MarkUnused(this);
00151 }
00152
00153 lock.unlock();
00154 }
00155
00156 QMutex lock;
00157 QWaitCondition waitCond;
00158
00159 private:
00160 MainServer *parent;
00161
00162 MythSocket *socket;
00163
00164 bool threadlives;
00165 };
00166
00167 MainServer::MainServer(bool master, int port,
00168 QMap<int, EncoderLink *> *tvList,
00169 Scheduler *sched, AutoExpire *expirer)
00170 {
00171 m_sched = sched;
00172 m_expirer = expirer;
00173
00174 ismaster = master;
00175 masterServer = NULL;
00176
00177 encoderList = tvList;
00178 AutoExpire::Update(true);
00179
00180 for (int i = 0; i < PRT_STARTUP_THREAD_COUNT; i++)
00181 {
00182 ProcessRequestThread *prt = new ProcessRequestThread(this);
00183 prt->lock.lock();
00184 prt->start();
00185 prt->waitCond.wait(&prt->lock);
00186 prt->lock.unlock();
00187 threadPool.push_back(prt);
00188 }
00189
00190 masterBackendOverride = gContext->GetNumSetting("MasterBackendOverride", 0);
00191
00192 mythserver = new MythServer(port);
00193 if (!mythserver->ok())
00194 {
00195 VERBOSE(VB_IMPORTANT, QString("Failed to bind port %1. Exiting.")
00196 .arg(port));
00197 exit(BACKEND_BUGGY_EXIT_NO_BIND_MAIN);
00198 }
00199
00200 connect(mythserver, SIGNAL(newConnect(MythSocket *)),
00201 SLOT(newConnection(MythSocket *)));
00202
00203 gContext->addListener(this);
00204
00205 if (!ismaster)
00206 {
00207 masterServerReconnect = new QTimer(this);
00208 connect(masterServerReconnect, SIGNAL(timeout()), this,
00209 SLOT(reconnectTimeout()));
00210 masterServerReconnect->start(1000, true);
00211 }
00212
00213 deferredDeleteTimer = new QTimer(this);
00214 connect(deferredDeleteTimer, SIGNAL(timeout()), this,
00215 SLOT(deferredDeleteSlot()));
00216 deferredDeleteTimer->start(30 * 1000);
00217
00218 autoexpireUpdateTimer = new QTimer(this);
00219 connect(autoexpireUpdateTimer, SIGNAL(timeout()), this,
00220 SLOT(autoexpireUpdate()));
00221
00222 if (sched)
00223 sched->SetMainServer(this);
00224 }
00225
00226 MainServer::~MainServer()
00227 {
00228 delete mythserver;
00229
00230 if (masterServerReconnect)
00231 delete masterServerReconnect;
00232 if (deferredDeleteTimer)
00233 delete deferredDeleteTimer;
00234 }
00235
00236 void MainServer::autoexpireUpdate(void)
00237 {
00238 AutoExpire::Update(false);
00239 }
00240
00241 void MainServer::newConnection(MythSocket *socket)
00242 {
00243 socket->setCallbacks(this);
00244 }
00245
00246 void MainServer::readyRead(MythSocket *sock)
00247 {
00248 PlaybackSock *testsock = getPlaybackBySock(sock);
00249 if (testsock && testsock->isExpectingReply())
00250 {
00251 return;
00252 }
00253
00254 readReadyLock.lock();
00255
00256 ProcessRequestThread *prt = NULL;
00257 threadPoolLock.lock();
00258 if (threadPool.empty())
00259 {
00260 VERBOSE(VB_IMPORTANT, "Waiting for a process request thread..");
00261 threadPoolCond.wait(&threadPoolLock, PRT_TIMEOUT);
00262 }
00263 if (!threadPool.empty())
00264 {
00265 prt = threadPool.back();
00266 threadPool.pop_back();
00267 }
00268 else
00269 {
00270 VERBOSE(VB_IMPORTANT, "Adding a new process request thread");
00271 prt = new ProcessRequestThread(this);
00272 prt->lock.lock();
00273 prt->start();
00274 prt->waitCond.wait(&prt->lock);
00275 prt->lock.unlock();
00276 }
00277 threadPoolLock.unlock();
00278
00279 prt->setup(sock);
00280
00281 readReadyLock.unlock();
00282 }
00283
00284 void MainServer::ProcessRequest(MythSocket *sock)
00285 {
00286 sock->Lock();
00287
00288 if (sock->bytesAvailable() > 0)
00289 {
00290 ProcessRequestWork(sock);
00291 }
00292
00293 sock->Unlock();
00294 }
00295
00296 void MainServer::ProcessRequestWork(MythSocket *sock)
00297 {
00298 QStringList listline;
00299 if (!sock->readStringList(listline))
00300 return;
00301
00302 QString line = listline[0];
00303
00304 line = line.simplifyWhiteSpace();
00305 QStringList tokens = QStringList::split(" ", line);
00306 QString command = tokens[0];
00307
00308 if (command == "MYTH_PROTO_VERSION")
00309 {
00310 if (tokens.size() < 2)
00311 VERBOSE(VB_IMPORTANT, "Bad MYTH_PROTO_VERSION command");
00312 else
00313 HandleVersion(sock,tokens[1]);
00314 return;
00315 }
00316 else if (command == "ANN")
00317 {
00318 if (tokens.size() < 3 || tokens.size() > 5)
00319 VERBOSE(VB_IMPORTANT, "Bad ANN query");
00320 else
00321 HandleAnnounce(listline, tokens, sock);
00322 return;
00323 }
00324 else if (command == "DONE")
00325 {
00326 HandleDone(sock);
00327 return;
00328 }
00329
00330 PlaybackSock *pbs = getPlaybackBySock(sock);
00331 if (!pbs)
00332 {
00333 VERBOSE(VB_IMPORTANT, "unknown socket");
00334 return;
00335 }
00336
00337
00338 pbs->UpRef();
00339
00340 if (command == "QUERY_RECORDINGS")
00341 {
00342 if (tokens.size() != 2)
00343 VERBOSE(VB_IMPORTANT, "Bad QUERY_RECORDINGS query");
00344 else
00345 HandleQueryRecordings(tokens[1], pbs);
00346 }
00347 else if (command == "QUERY_RECORDING")
00348 {
00349 HandleQueryRecording(tokens, pbs);
00350 }
00351 else if (command == "QUERY_FREE_SPACE")
00352 {
00353 HandleQueryFreeSpace(pbs, false);
00354 }
00355 else if (command == "QUERY_FREE_SPACE_LIST")
00356 {
00357 HandleQueryFreeSpace(pbs, true);
00358 }
00359 else if (command == "QUERY_FREE_SPACE_SUMMARY")
00360 {
00361 HandleQueryFreeSpaceSummary(pbs);
00362 }
00363 else if (command == "QUERY_LOAD")
00364 {
00365 HandleQueryLoad(pbs);
00366 }
00367 else if (command == "QUERY_UPTIME")
00368 {
00369 HandleQueryUptime(pbs);
00370 }
00371 else if (command == "QUERY_MEMSTATS")
00372 {
00373 HandleQueryMemStats(pbs);
00374 }
00375 else if (command == "QUERY_CHECKFILE")
00376 {
00377 HandleQueryCheckFile(listline, pbs);
00378 }
00379 else if (command == "QUERY_GUIDEDATATHROUGH")
00380 {
00381 HandleQueryGuideDataThrough(pbs);
00382 }
00383 else if (command == "STOP_RECORDING")
00384 {
00385 HandleStopRecording(listline, pbs);
00386 }
00387 else if (command == "CHECK_RECORDING")
00388 {
00389 HandleCheckRecordingActive(listline, pbs);
00390 }
00391 else if (command == "DELETE_RECORDING")
00392 {
00393 HandleDeleteRecording(listline, pbs, false);
00394 }
00395 else if (command == "FORCE_DELETE_RECORDING")
00396 {
00397 HandleDeleteRecording(listline, pbs, true);
00398 }
00399 else if (command == "UNDELETE_RECORDING")
00400 {
00401 HandleUndeleteRecording(listline, pbs);
00402 }
00403 else if (command == "RESCHEDULE_RECORDINGS")
00404 {
00405 if (tokens.size() != 2)
00406 VERBOSE(VB_IMPORTANT, "Bad RESCHEDULE_RECORDINGS request");
00407 else
00408 HandleRescheduleRecordings(tokens[1].toInt(), pbs);
00409 }
00410 else if (command == "FORGET_RECORDING")
00411 {
00412 HandleForgetRecording(listline, pbs);
00413 }
00414 else if (command == "QUERY_GETALLPENDING")
00415 {
00416 if (tokens.size() == 1)
00417 HandleGetPendingRecordings(pbs);
00418 else if (tokens.size() == 2)
00419 HandleGetPendingRecordings(pbs, tokens[1]);
00420 else
00421 HandleGetPendingRecordings(pbs, tokens[1], tokens[2].toInt());
00422 }
00423 else if (command == "QUERY_GETALLSCHEDULED")
00424 {
00425 HandleGetScheduledRecordings(pbs);
00426 }
00427 else if (command == "QUERY_GETCONFLICTING")
00428 {
00429 HandleGetConflictingRecordings(listline, pbs);
00430 }
00431 else if (command == "QUERY_GETEXPIRING")
00432 {
00433 HandleGetExpiringRecordings(pbs);
00434 }
00435 else if (command == "GET_FREE_RECORDER")
00436 {
00437 HandleGetFreeRecorder(pbs);
00438 }
00439 else if (command == "GET_FREE_RECORDER_COUNT")
00440 {
00441 HandleGetFreeRecorderCount(pbs);
00442 }
00443 else if (command == "GET_FREE_RECORDER_LIST")
00444 {
00445 HandleGetFreeRecorderList(pbs);
00446 }
00447 else if (command == "GET_NEXT_FREE_RECORDER")
00448 {
00449 HandleGetNextFreeRecorder(listline, pbs);
00450 }
00451 else if (command == "QUERY_RECORDER")
00452 {
00453 if (tokens.size() != 2)
00454 VERBOSE(VB_IMPORTANT, "Bad QUERY_RECORDER");
00455 else
00456 HandleRecorderQuery(listline, tokens, pbs);
00457 }
00458 else if (command == "SET_NEXT_LIVETV_DIR")
00459 {
00460 if (tokens.size() != 3)
00461 VERBOSE(VB_IMPORTANT, "Bad SET_NEXT_LIVETV_DIR");
00462 else
00463 HandleSetNextLiveTVDir(tokens, pbs);
00464 }
00465 else if (command == "SET_CHANNEL_INFO")
00466 {
00467 HandleSetChannelInfo(listline, pbs);
00468 }
00469 else if (command == "QUERY_REMOTEENCODER")
00470 {
00471 if (tokens.size() != 2)
00472 VERBOSE(VB_IMPORTANT, "Bad QUERY_REMOTEENCODER");
00473 else
00474 HandleRemoteEncoder(listline, tokens, pbs);
00475 }
00476 else if (command == "GET_RECORDER_FROM_NUM")
00477 {
00478 HandleGetRecorderFromNum(listline, pbs);
00479 }
00480 else if (command == "GET_RECORDER_NUM")
00481 {
00482 HandleGetRecorderNum(listline, pbs);
00483 }
00484 else if (command == "QUERY_FILETRANSFER")
00485 {
00486 if (tokens.size() != 2)
00487 VERBOSE(VB_IMPORTANT, "Bad QUERY_FILETRANSFER");
00488 else
00489 HandleFileTransferQuery(listline, tokens, pbs);
00490 }
00491 else if (command == "QUERY_GENPIXMAP")
00492 {
00493 HandleGenPreviewPixmap(listline, pbs);
00494 }
00495 else if (command == "QUERY_PIXMAP_LASTMODIFIED")
00496 {
00497 HandlePixmapLastModified(listline, pbs);
00498 }
00499 else if (command == "QUERY_ISRECORDING")
00500 {
00501 HandleIsRecording(listline, pbs);
00502 }
00503 else if (command == "MESSAGE")
00504 {
00505 HandleMessage(listline, pbs);
00506 }
00507 else if (command == "FILL_PROGRAM_INFO")
00508 {
00509 HandleFillProgramInfo(listline, pbs);
00510 }
00511 else if (command == "LOCK_TUNER")
00512 {
00513 HandleLockTuner(pbs);
00514 }
00515 else if (command == "FREE_TUNER")
00516 {
00517 if (tokens.size() != 2)
00518 VERBOSE(VB_IMPORTANT, "Bad FREE_TUNER query");
00519 else
00520 HandleFreeTuner(tokens[1].toInt(), pbs);
00521 }
00522 else if (command == "QUERY_IS_ACTIVE_BACKEND")
00523 {
00524 if (tokens.size() != 1)
00525 VERBOSE(VB_IMPORTANT, "Bad QUERY_IS_ACTIVE_BACKEND");
00526 else
00527 HandleIsActiveBackendQuery(listline, pbs);
00528 }
00529 else if (command == "QUERY_COMMBREAK")
00530 {
00531 if (tokens.size() != 3)
00532 VERBOSE(VB_IMPORTANT, "Bad QUERY_COMMBREAK");
00533 else
00534 HandleCommBreakQuery(tokens[1], tokens[2], pbs);
00535 }
00536 else if (command == "QUERY_CUTLIST")
00537 {
00538 if (tokens.size() != 3)
00539 VERBOSE(VB_IMPORTANT, "Bad QUERY_CUTLIST");
00540 else
00541 HandleCutlistQuery(tokens[1], tokens[2], pbs);
00542 }
00543 else if (command == "QUERY_BOOKMARK")
00544 {
00545 if (tokens.size() != 3)
00546 VERBOSE(VB_IMPORTANT, "Bad QUERY_BOOKMARK");
00547 else
00548 HandleBookmarkQuery(tokens[1], tokens[2], pbs);
00549 }
00550 else if (command == "SET_BOOKMARK")
00551 {
00552 if (tokens.size() != 5)
00553 VERBOSE(VB_IMPORTANT, "Bad SET_BOOKMARK");
00554 else
00555 HandleSetBookmark(tokens, pbs);
00556 }
00557 else if (command == "QUERY_SETTING")
00558 {
00559 if (tokens.size() != 3)
00560 VERBOSE(VB_IMPORTANT, "Bad QUERY_SETTING");
00561 else
00562 HandleSettingQuery(tokens, pbs);
00563 }
00564 else if (command == "SET_SETTING")
00565 {
00566 if (tokens.size() != 4)
00567 VERBOSE(VB_IMPORTANT, "Bad SET_SETTING");
00568 else
00569 HandleSetSetting(tokens, pbs);
00570 }
00571 else if (command == "ALLOW_SHUTDOWN")
00572 {
00573 if (tokens.size() != 1)
00574 VERBOSE(VB_IMPORTANT, "Bad ALLOW_SHUTDOWN");
00575 else
00576 HandleBlockShutdown(false, pbs);
00577 }
00578 else if (command == "BLOCK_SHUTDOWN")
00579 {
00580 if (tokens.size() != 1)
00581 VERBOSE(VB_IMPORTANT, "Bad BLOCK_SHUTDOWN");
00582 else
00583 HandleBlockShutdown(true, pbs);
00584 }
00585 else if (command == "SHUTDOWN_NOW")
00586 {
00587 if (tokens.size() != 1)
00588 VERBOSE(VB_IMPORTANT, "Bad SHUTDOWN_NOW query");
00589 else if (!ismaster)
00590 {
00591 QString halt_cmd = listline[1];
00592 if (!halt_cmd.isEmpty())
00593 {
00594 VERBOSE(VB_IMPORTANT, "Going down now as of Mainserver request!");
00595 system(halt_cmd.ascii());
00596 }
00597 else
00598 VERBOSE(VB_IMPORTANT,
00599 "WARNING: Recieved an empty SHUTDOWN_NOW query!");
00600 }
00601 }
00602 else if (command == "BACKEND_MESSAGE")
00603 {
00604 QString message = listline[1];
00605 QStringList extra = listline[2];
00606 for (uint i = 3; i < listline.size(); i++)
00607 extra << listline[i];
00608 MythEvent me(message, extra);
00609 gContext->dispatch(me);
00610 }
00611 else if (command == "REFRESH_BACKEND")
00612 {
00613 VERBOSE(VB_IMPORTANT,"Reloading backend settings");
00614 HandleBackendRefresh(sock);
00615 }
00616 else if (command == "OK")
00617 {
00618 VERBOSE(VB_IMPORTANT, "Got 'OK' out of sequence.");
00619 }
00620 else if (command == "UNKNOWN_COMMAND")
00621 {
00622 VERBOSE(VB_IMPORTANT, "Got 'UNKNOWN_COMMAND' out of sequence.");
00623 }
00624 else
00625 {
00626 VERBOSE(VB_IMPORTANT, "Unknown command: " + command);
00627
00628 MythSocket *pbssock = pbs->getSocket();
00629
00630 QStringList strlist;
00631 strlist << "UNKNOWN_COMMAND";
00632
00633 SendResponse(pbssock, strlist);
00634 }
00635
00636
00637 pbs->DownRef();
00638 }
00639
00640 void MainServer::MarkUnused(ProcessRequestThread *prt)
00641 {
00642 threadPoolLock.lock();
00643 threadPool.push_back(prt);
00644 threadPoolLock.unlock();
00645 }
00646
00647 void MainServer::customEvent(QCustomEvent *e)
00648 {
00649 QStringList broadcast;
00650 bool sendstuff = false;
00651
00652 if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage)
00653 {
00654 MythEvent *me = (MythEvent *)e;
00655
00656 if (me->Message().left(11) == "AUTO_EXPIRE")
00657 {
00658 QStringList tokens = QStringList::split(" ", me->Message());
00659 if (tokens.size() != 3)
00660 {
00661 VERBOSE(VB_IMPORTANT, "Bad AUTO_EXPIRE message");
00662 return;
00663 }
00664
00665 QDateTime startts = QDateTime::fromString(tokens[2], Qt::ISODate);
00666 ProgramInfo *pinfo = ProgramInfo::GetProgramFromRecorded(tokens[1],
00667 startts);
00668 if (pinfo)
00669 {
00670
00671
00672 if (pinfo->recgroup != "LiveTV" &&
00673 pinfo->recgroup != "Deleted" &&
00674 (gContext->GetNumSetting("RerecordWatched", 0) ||
00675 !(pinfo->getProgramFlags() & FL_WATCHED)))
00676 {
00677 pinfo->ForgetHistory();
00678 }
00679 DoHandleDeleteRecording(pinfo, NULL, false, true);
00680 }
00681 else
00682 {
00683 cerr << "Cannot find program info for '" << me->Message()
00684 << "', while attempting to Auto-Expire." << endl;
00685 }
00686
00687 return;
00688 }
00689
00690 if (me->Message().left(21) == "QUERY_NEXT_LIVETV_DIR" && m_sched)
00691 {
00692 QStringList tokens = QStringList::split(" ", me->Message());
00693 if (tokens.size() != 2)
00694 {
00695 VERBOSE(VB_IMPORTANT, QString("Bad %1 message").arg(tokens[0]));
00696 return;
00697 }
00698
00699 m_sched->GetNextLiveTVDir(tokens[1].toInt());
00700 return;
00701 }
00702
00703 if ((me->Message().left(16) == "DELETE_RECORDING") ||
00704 (me->Message().left(22) == "FORCE_DELETE_RECORDING"))
00705 {
00706 QStringList tokens = QStringList::split(" ", me->Message());
00707 if (tokens.size() != 3)
00708 {
00709 VERBOSE(VB_IMPORTANT, QString("Bad %1 message").arg(tokens[0]));
00710 return;
00711 }
00712
00713 QDateTime startts = QDateTime::fromString(tokens[2], Qt::ISODate);
00714 ProgramInfo *pinfo = ProgramInfo::GetProgramFromRecorded(tokens[1],
00715 startts);
00716 if (pinfo)
00717 {
00718 if (tokens[0] == "FORCE_DELETE_RECORDING")
00719 DoHandleDeleteRecording(pinfo, NULL, true);
00720 else
00721 DoHandleDeleteRecording(pinfo, NULL, false);
00722 }
00723 else
00724 {
00725 VERBOSE(VB_IMPORTANT,
00726 QString("Cannot find program info for '%1' while "
00727 "attempting to delete.").arg(me->Message()));
00728 }
00729
00730 return;
00731 }
00732
00733 if (me->Message().left(21) == "RESCHEDULE_RECORDINGS" && m_sched)
00734 {
00735 QStringList tokens = QStringList::split(" ", me->Message());
00736 if (tokens.size() != 2)
00737 {
00738 VERBOSE(VB_IMPORTANT, "Bad RESCHEDULE_RECORDINGS message");
00739 return;
00740 }
00741
00742 int recordid = tokens[1].toInt();
00743 m_sched->Reschedule(recordid);
00744 return;
00745 }
00746
00747 if (me->Message().left(23) == "SCHEDULER_ADD_RECORDING" && m_sched)
00748 {
00749 ProgramInfo pi;
00750 QStringList list = me->ExtraDataList();
00751 if (!pi.FromStringList(list, 0))
00752 {
00753 VERBOSE(VB_IMPORTANT, "Bad SCHEDULER_ADD_RECORDING message");
00754 return;
00755 }
00756
00757 m_sched->AddRecording(pi);
00758 return;
00759 }
00760
00761 if (me->Message().left(23) == "UPDATE_RECORDING_STATUS" && m_sched)
00762 {
00763 QStringList tokens = QStringList::split(" ", me->Message());
00764 if (tokens.size() != 6)
00765 {
00766 VERBOSE(VB_IMPORTANT, "Bad UPDATE_RECORDING_STATUS message");
00767 return;
00768 }
00769
00770 int cardid = tokens[1].toInt();
00771 QString chanid = tokens[2];
00772 QDateTime startts = QDateTime::fromString(tokens[3], Qt::ISODate);
00773 RecStatusType recstatus = RecStatusType(tokens[4].toInt());
00774 QDateTime recendts = QDateTime::fromString(tokens[5], Qt::ISODate);
00775 m_sched->UpdateRecStatus(cardid, chanid, startts,
00776 recstatus, recendts);
00777 return;
00778 }
00779
00780 if (me->Message().left(13) == "LIVETV_EXITED")
00781 {
00782 QString chainid = me->ExtraData();
00783 LiveTVChain *chain = GetExistingChain(chainid);
00784 if (chain)
00785 DeleteChain(chain);
00786
00787 return;
00788 }
00789
00790 if (me->Message() == "CLEAR_SETTINGS_CACHE")
00791 gContext->ClearSettingsCache();
00792
00793 if (me->Message().left(14) == "RESET_IDLETIME" && m_sched)
00794 m_sched->ResetIdleTime();
00795
00796 if (me->Message().left(6) == "LOCAL_")
00797 return;
00798
00799 broadcast = "BACKEND_MESSAGE";
00800 broadcast << me->Message();
00801 broadcast += me->ExtraDataList();
00802 sendstuff = true;
00803 }
00804
00805 if (sendstuff)
00806 {
00807 readReadyLock.lock();
00808
00809 bool sendGlobal = false;
00810 if (ismaster && broadcast[1].left(7) == "GLOBAL_")
00811 {
00812 broadcast[1].replace(QRegExp("GLOBAL_"), "LOCAL_");
00813 MythEvent me(broadcast[1], broadcast[2]);
00814 gContext->dispatch(me);
00815
00816 sendGlobal = true;
00817 }
00818
00819 QPtrList<PlaybackSock> sentSet;
00820
00821
00822 vector<PlaybackSock *> localPBSList;
00823 sockListLock.lock();
00824 vector<PlaybackSock *>::iterator iter = playbackList.begin();
00825 for (; iter != playbackList.end(); iter++)
00826 {
00827 PlaybackSock *pbs = (*iter);
00828 pbs->UpRef();
00829 localPBSList.push_back(pbs);
00830 }
00831 sockListLock.unlock();
00832
00833 for (iter = localPBSList.begin(); iter != localPBSList.end(); iter++)
00834 {
00835 PlaybackSock *pbs = (*iter);
00836
00837 if (sentSet.containsRef(pbs) || pbs->IsDisconnected())
00838 continue;
00839
00840 sentSet.append(pbs);
00841
00842 bool reallysendit = false;
00843
00844 if (broadcast[1] == "CLEAR_SETTINGS_CACHE")
00845 {
00846 if ((ismaster) &&
00847 (pbs->isSlaveBackend() || pbs->wantsEvents()))
00848 reallysendit = true;
00849 }
00850 else if (sendGlobal)
00851 {
00852 if (pbs->isSlaveBackend())
00853 reallysendit = true;
00854 }
00855 else if (pbs->wantsEvents())
00856 {
00857 reallysendit = true;
00858 }
00859
00860 MythSocket *sock = pbs->getSocket();
00861 sock->UpRef();
00862
00863 if (reallysendit)
00864 {
00865 sock->Lock();
00866 sock->writeStringList(broadcast);
00867 sock->Unlock();
00868 }
00869
00870 sock->DownRef();
00871 }
00872
00873
00874 for (iter = localPBSList.begin(); iter != localPBSList.end(); iter++)
00875 {
00876 PlaybackSock *pbs = (*iter);
00877 pbs->DownRef();
00878 }
00879
00880 readReadyLock.unlock();
00881 }
00882 }
00883
00892 void MainServer::HandleVersion(MythSocket *socket, QString version)
00893 {
00894 QStringList retlist;
00895 if (version != MYTH_PROTO_VERSION)
00896 {
00897 VERBOSE(VB_GENERAL,
00898 "MainServer::HandleVersion - Client speaks protocol version "
00899 + version + " but we speak " + MYTH_PROTO_VERSION + "!");
00900 retlist << "REJECT" << MYTH_PROTO_VERSION;
00901 socket->writeStringList(retlist);
00902 HandleDone(socket);
00903 return;
00904 }
00905
00906 retlist << "ACCEPT" << MYTH_PROTO_VERSION;
00907 socket->writeStringList(retlist);
00908 }
00909
00921 void MainServer::HandleAnnounce(QStringList &slist, QStringList commands,
00922 MythSocket *socket)
00923 {
00924 QStringList retlist = "OK";
00925
00926 sockListLock.lock();
00927 vector<PlaybackSock *>::iterator iter = playbackList.begin();
00928 for (; iter != playbackList.end(); iter++)
00929 {
00930 PlaybackSock *pbs = (*iter);
00931 if (pbs->getSocket() == socket)
00932 {
00933 sockListLock.unlock();
00934 VERBOSE(VB_IMPORTANT, QString("Client %1 is trying to announce a socket "
00935 "multiple times.")
00936 .arg(commands[2]));
00937 socket->writeStringList(retlist);
00938 return;
00939 }
00940 }
00941 sockListLock.unlock();
00942
00943 if (commands[1] == "Playback" || commands[1] == "Monitor")
00944 {
00945
00946
00947
00948 bool wantevents = commands[3].toInt();
00949 VERBOSE(VB_GENERAL, QString("MainServer::HandleAnnounce %1")
00950 .arg(commands[1]));
00951 VERBOSE(VB_IMPORTANT, QString("adding: %1 as a client (events: %2)")
00952 .arg(commands[2]).arg(wantevents));
00953 PlaybackSock *pbs = new PlaybackSock(this, socket, commands[2], wantevents);
00954 pbs->setBlockShutdown(commands[1] == "Playback");
00955 sockListLock.lock();
00956 playbackList.push_back(pbs);
00957 sockListLock.unlock();
00958 }
00959 else if (commands[1] == "SlaveBackend")
00960 {
00961 VERBOSE(VB_IMPORTANT, QString("adding: %1 as a slave backend server")
00962 .arg(commands[2]));
00963 PlaybackSock *pbs = new PlaybackSock(this, socket, commands[2], false);
00964 pbs->setAsSlaveBackend();
00965 pbs->setIP(commands[3]);
00966
00967 if (m_sched)
00968 {
00969 ProgramInfo pinfo;
00970 ProgramList slavelist;
00971 QStringList::const_iterator sit = slist.at(1);
00972 while (sit != slist.end())
00973 {
00974 if (!pinfo.FromStringList(sit, slist.end()))
00975 break;
00976 slavelist.append(new ProgramInfo(pinfo));
00977 }
00978 m_sched->SlaveConnected(slavelist);
00979 }
00980
00981 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
00982 for (; iter != encoderList->end(); ++iter)
00983 {
00984 EncoderLink *elink = iter.data();
00985 if (elink->GetHostName() == commands[2])
00986 elink->SetSocket(pbs);
00987 }
00988
00989 if (m_sched)
00990 m_sched->Reschedule(0);
00991
00992 QString message = QString("LOCAL_SLAVE_BACKEND_ONLINE %2")
00993 .arg(commands[2]);
00994 MythEvent me(message);
00995 gContext->dispatch(me);
00996
00997 pbs->setBlockShutdown(false);
00998
00999 sockListLock.lock();
01000 playbackList.push_back(pbs);
01001 sockListLock.unlock();
01002
01003 autoexpireUpdateTimer->start(1000, true);
01004 }
01005 else if (commands[1] == "FileTransfer")
01006 {
01007 VERBOSE(VB_GENERAL, "MainServer::HandleAnnounce FileTransfer");
01008 VERBOSE(VB_IMPORTANT, QString("adding: %1 as a remote file transfer")
01009 .arg(commands[2]));
01010 QUrl qurl = slist[1];
01011 QString filename = LocalFilePath(qurl);
01012
01013 FileTransfer *ft = NULL;
01014 bool usereadahead = true;
01015 int retries = -1;
01016 if (commands.size() >= 5)
01017 {
01018 usereadahead = commands[3].toInt();
01019 retries = commands[4].toInt();
01020 }
01021
01022 if (retries >= 0)
01023 ft = new FileTransfer(filename, socket, usereadahead, retries);
01024 else
01025 ft = new FileTransfer(filename, socket);
01026
01027 sockListLock.lock();
01028 fileTransferList.push_back(ft);
01029 sockListLock.unlock();
01030
01031 retlist << QString::number(socket->socket());
01032 ft->UpRef();
01033 encodeLongLong(retlist, ft->GetFileSize());
01034 ft->DownRef();
01035 }
01036
01037 socket->writeStringList(retlist);
01038 }
01039
01045 void MainServer::HandleDone(MythSocket *socket)
01046 {
01047 socket->close();
01048 }
01049
01050 void MainServer::SendResponse(MythSocket *socket, QStringList &commands)
01051 {
01052 if (getPlaybackBySock(socket) || getFileTransferBySock(socket))
01053 {
01054 socket->writeStringList(commands);
01055 }
01056 else
01057 {
01058 VERBOSE(VB_IMPORTANT, "SendResponse: Unable to write to client socket,"
01059 " as it's no longer there");
01060 }
01061 }
01062
01070 void MainServer::HandleQueryRecordings(QString type, PlaybackSock *pbs)
01071 {
01072 MythSocket *pbssock = pbs->getSocket();
01073 QString playbackhost = pbs->getHostname();
01074
01075 QString fs_db_name = "";
01076
01077 QDateTime rectime = QDateTime::currentDateTime().addSecs(
01078 -gContext->GetNumSetting("RecordOverTime"));
01079 RecIter ri;
01080 RecList schedList;
01081 if (m_sched)
01082 m_sched->getAllPending(&schedList);
01083
01084 QString ip = gContext->GetSetting("BackendServerIP");
01085 QString port = gContext->GetSetting("BackendServerPort");
01086
01087 QMap<QString, int> inUseMap;
01088 QString inUseKey;
01089 QString inUseForWhat;
01090 QDateTime oneHourAgo = QDateTime::currentDateTime().addSecs(-61 * 60);
01091
01092 MSqlQuery query(MSqlQuery::InitCon());
01093
01094 query.prepare("SELECT DISTINCT chanid, starttime, recusage "
01095 " FROM inuseprograms WHERE lastupdatetime >= :ONEHOURAGO ;");
01096 query.bindValue(":ONEHOURAGO", oneHourAgo);
01097
01098 if (query.exec() && query.isActive() && query.size() > 0)
01099 while (query.next())
01100 {
01101 inUseKey = query.value(0).toString() + " " +
01102 query.value(1).toDateTime().toString(Qt::ISODate);
01103 inUseForWhat = query.value(2).toString();
01104
01105 if (!inUseMap.contains(inUseKey))
01106 inUseMap[inUseKey] = 0;
01107
01108 if ((inUseForWhat == "player") ||
01109 (inUseForWhat == "preview player") ||
01110 (inUseForWhat == "PIP player"))
01111 inUseMap[inUseKey] = inUseMap[inUseKey] | FL_INUSEPLAYING;
01112 else if (inUseForWhat == "recorder")
01113 inUseMap[inUseKey] = inUseMap[inUseKey] | FL_INUSERECORDING;
01114 }
01115
01116
01117 QString thequery =
01118 "SELECT recorded.chanid,recorded.starttime,recorded.endtime,"
01119 "recorded.title,recorded.subtitle,recorded.description,"
01120 "recorded.hostname,channum,name,callsign,commflagged,cutlist,"
01121 "recorded.autoexpire,editing,bookmark,recorded.category,"
01122 "recorded.recgroup,record.dupin,record.dupmethod,"
01123 "recorded.recordid,channel.outputfilters,"
01124 "recorded.seriesid,recorded.programid,recorded.filesize, "
01125 "recorded.lastmodified, recorded.findid, "
01126 "recorded.originalairdate, recorded.playgroup, "
01127 "recorded.basename, recorded.progstart, "
01128 "recorded.progend, recorded.stars, "
01129 "recordedprogram.audioprop+0, recordedprogram.videoprop+0, "
01130 "recordedprogram.subtitletypes+0, transcoded, "
01131 "recorded.recpriority, watched, recorded.preserve, "
01132 "recorded.storagegroup "
01133 "FROM recorded "
01134 "LEFT JOIN record ON recorded.recordid = record.recordid "
01135 "LEFT JOIN channel ON recorded.chanid = channel.chanid "
01136 "LEFT JOIN recordedprogram ON "
01137 " ( recorded.chanid = recordedprogram.chanid AND "
01138 " recorded.progstart = recordedprogram.starttime ) "
01139 "WHERE ( recorded.deletepending = 0 OR "
01140 " DATE_ADD(recorded.lastmodified, INTERVAL 5 MINUTE) <= NOW() "
01141 " ) ";
01142
01143 if (type == "Recording")
01144 thequery += "AND recorded.endtime >= NOW() AND "
01145 "recorded.starttime <= NOW()";
01146
01147 thequery += "ORDER BY recorded.starttime";
01148
01149 if (type == "Delete")
01150 thequery += " DESC";
01151
01152 QString chanorder = gContext->GetSetting("ChannelOrdering", "channum");
01153 if (chanorder != "channum")
01154 thequery += ", " + chanorder;
01155 else
01156 thequery += ",atsc_major_chan,atsc_minor_chan,channum,callsign";
01157
01158 QStringList outputlist;
01159 QString fileprefix = gContext->GetFilePrefix();
01160 QMap<QString, QString> backendIpMap;
01161 QMap<QString, QString> backendPortMap;
01162
01163 query.prepare(thequery);
01164
01165 if (!query.exec() || !query.isActive())
01166 {
01167 MythContext::DBError("ProgramList::FromRecorded", query);
01168 outputlist << "0";
01169 }
01170 else
01171 {
01172 outputlist << QString::number(query.size());
01173
01174 while (query.next())
01175 {
01176 ProgramInfo *proginfo = new ProgramInfo;
01177
01178 proginfo->chanid = query.value(0).toString();
01179 proginfo->startts = query.value(29).toDateTime();
01180 proginfo->endts = query.value(30).toDateTime();
01181 proginfo->recstartts = query.value(1).toDateTime();
01182 proginfo->recendts = query.value(2).toDateTime();
01183 proginfo->title = QString::fromUtf8(query.value(3).toString());
01184 proginfo->subtitle = QString::fromUtf8(query.value(4).toString());
01185 proginfo->description = QString::fromUtf8(query.value(5).toString());
01186 proginfo->hostname = query.value(6).toString();
01187
01188 proginfo->dupin = RecordingDupInType(query.value(17).toInt());
01189 proginfo->dupmethod = RecordingDupMethodType(query.value(18).toInt());
01190 proginfo->recordid = query.value(19).toInt();
01191 proginfo->chanOutputFilters = query.value(20).toString();
01192 proginfo->seriesid = query.value(21).toString();
01193 proginfo->programid = query.value(22).toString();
01194 proginfo->filesize = stringToLongLong(query.value(23).toString());
01195 proginfo->lastmodified =
01196 QDateTime::fromString(query.value(24).toString(),
01197 Qt::ISODate);
01198 proginfo->findid = query.value(25).toInt();
01199
01200 if (query.value(26).isNull() ||
01201 query.value(26).toString().isEmpty())
01202 {
01203 proginfo->originalAirDate = QDate::QDate (0, 1, 1);
01204 proginfo->hasAirDate = false;
01205 }
01206 else
01207 {
01208 proginfo->originalAirDate =
01209 QDate::fromString(query.value(26).toString(),Qt::ISODate);
01210
01211 if (proginfo->originalAirDate > QDate(1940, 1, 1))
01212 proginfo->hasAirDate = true;
01213 else
01214 proginfo->hasAirDate = false;
01215 }
01216
01217 proginfo->pathname = QString::fromUtf8(query.value(28).toString());
01218
01219 if (proginfo->hostname.isEmpty() || proginfo->hostname.isNull())
01220 proginfo->hostname = gContext->GetHostName();
01221
01222 if (!query.value(7).toString().isEmpty())
01223 {
01224 proginfo->chanstr = query.value(7).toString();
01225 proginfo->channame = QString::fromUtf8(query.value(8).toString());
01226 proginfo->chansign = QString::fromUtf8(query.value(9).toString());
01227 }
01228 else
01229 {
01230 proginfo->chanstr = "#" + proginfo->chanid;
01231 proginfo->channame = "#" + proginfo->chanid;
01232 proginfo->chansign = "#" + proginfo->chanid;
01233 }
01234
01235
01236 int flags = 0;
01237 flags |= (query.value(10).toInt() == 1) ? FL_COMMFLAG : 0;
01238 flags |= (query.value(11).toInt() == 1) ? FL_CUTLIST : 0;
01239 flags |= query.value(12).toInt() ? FL_AUTOEXP : 0;
01240 flags |= (query.value(14).toInt() == 1) ? FL_BOOKMARK : 0;
01241 flags |= (query.value(35).toInt() == TRANSCODING_COMPLETE) ?
01242 FL_TRANSCODED : 0;
01243 flags |= (query.value(37).toInt() == 1) ? FL_WATCHED : 0;
01244 flags |= (query.value(38).toInt() == 1) ? FL_PRESERVED : 0;
01245
01246 inUseKey = query.value(0).toString() + " " +
01247 query.value(1).toDateTime().toString(Qt::ISODate);
01248 if (inUseMap.contains(inUseKey))
01249 flags |= inUseMap[inUseKey];
01250
01251 if (query.value(13).toInt())
01252 {
01253 flags |= FL_EDITING;
01254 }
01255 else if (query.value(10).toInt() == COMM_FLAG_PROCESSING)
01256 {
01257 if (JobQueue::IsJobRunning(JOB_COMMFLAG, proginfo))
01258 flags |= FL_EDITING;
01259 else
01260 proginfo->SetCommFlagged(COMM_FLAG_NOT_FLAGGED);
01261 }
01262
01263 proginfo->audioproperties = query.value(32).toInt();
01264 proginfo->videoproperties = query.value(33).toInt();
01265 proginfo->subtitleType = query.value(34).toInt();
01266
01267 proginfo->programflags = flags;
01268
01269 proginfo->category = QString::fromUtf8(query.value(15).toString());
01270
01271 proginfo->recgroup = QString::fromUtf8(query.value(16).toString());
01272 proginfo->playgroup = QString::fromUtf8(query.value(27).toString());
01273 proginfo->storagegroup =
01274 QString::fromUtf8(query.value(39).toString());
01275
01276 proginfo->recpriority = query.value(36).toInt();
01277
01278 proginfo->recstatus = rsRecorded;
01279 if (proginfo->recendts > rectime)
01280 {
01281 for (ri = schedList.begin(); ri != schedList.end(); ri++)
01282 {
01283 if ((*ri) && (*ri)->recstatus == rsRecording &&
01284 proginfo->chanid == (*ri)->chanid &&
01285 proginfo->recstartts == (*ri)->recstartts)
01286 {
01287 proginfo->recstatus = rsRecording;
01288 break;
01289 }
01290 }
01291 }
01292
01293 proginfo->stars = query.value(31).toDouble();
01294
01295 PlaybackSock *slave = NULL;
01296
01297 if (proginfo->hostname != gContext->GetHostName())
01298 slave = getSlaveByHostname(proginfo->hostname);
01299
01300 if ((proginfo->hostname == gContext->GetHostName()) ||
01301 (!slave && masterBackendOverride))
01302 {
01303 proginfo->pathname = QString("myth://") + ip + ":" + port
01304 + "/" + proginfo->pathname;
01305 if (proginfo->filesize == 0)
01306 {
01307 QString tmpURL = GetPlaybackURL(proginfo);
01308 QFile checkFile(tmpURL);
01309 if (tmpURL != "" && checkFile.exists())
01310 {
01311 struct stat st;
01312 if (stat(tmpURL.ascii(), &st) == 0)
01313 {
01314 proginfo->filesize = st.st_size;
01315
01316 if (proginfo->recendts < QDateTime::currentDateTime())
01317 proginfo->SetFilesize(proginfo->filesize);
01318 }
01319 }
01320 }
01321 }
01322 else if (!slave)
01323 {
01324 proginfo->pathname = GetPlaybackURL(proginfo);
01325 if (proginfo->pathname == "")
01326 {
01327 VERBOSE(VB_IMPORTANT,
01328 "MainServer::HandleQueryRecordings()"
01329 "\n\t\t\tCouldn't find backend for: " +
01330 QString("\n\t\t\t%1 : \"%2\"")
01331 .arg(proginfo->title).arg(proginfo->subtitle));
01332
01333 proginfo->filesize = 0;
01334 proginfo->pathname = "file not found";
01335 }
01336 }
01337 else
01338 {
01339 if (proginfo->filesize == 0)
01340 {
01341 slave->FillProgramInfo(proginfo, playbackhost);
01342
01343 if (proginfo->recendts < QDateTime::currentDateTime())
01344 proginfo->SetFilesize(proginfo->filesize);
01345 }
01346 else
01347 {
01348 ProgramInfo *p = proginfo;
01349 if (!backendIpMap.contains(p->hostname))
01350 backendIpMap[p->hostname] =
01351 gContext->GetSettingOnHost("BackendServerIp",
01352 p->hostname);
01353 if (!backendPortMap.contains(p->hostname))
01354 backendPortMap[p->hostname] =
01355 gContext->GetSettingOnHost("BackendServerPort",
01356 p->hostname);
01357 p->pathname = QString("myth://") +
01358 backendIpMap[p->hostname] + ":" +
01359 backendPortMap[p->hostname] + "/" +
01360 p->pathname;
01361 }
01362 }
01363
01364 if (slave)
01365 slave->DownRef();
01366
01367 proginfo->ToStringList(outputlist);
01368
01369 delete proginfo;
01370 }
01371 }
01372
01373 for (ri = schedList.begin(); ri != schedList.end(); ri++)
01374 delete (*ri);
01375
01376 SendResponse(pbssock, outputlist);
01377 }
01378
01384 void MainServer::HandleQueryRecording(QStringList &slist, PlaybackSock *pbs)
01385 {
01386 MythSocket *pbssock = pbs->getSocket();
01387 QString command = slist[1].upper();
01388 ProgramInfo *pginfo = NULL;
01389
01390 if (command == "BASENAME")
01391 {
01392 pginfo = ProgramInfo::GetProgramFromBasename(slist[2]);
01393 }
01394 else if (command == "TIMESLOT")
01395 {
01396 pginfo = ProgramInfo::GetProgramFromRecorded(slist[2], slist[3]);
01397 }
01398
01399 QStringList strlist;
01400
01401 if (pginfo)
01402 {
01403 strlist << "OK";
01404 pginfo->ToStringList(strlist);
01405 delete pginfo;
01406 }
01407 else
01408 {
01409 strlist << "ERROR";
01410 }
01411
01412 SendResponse(pbssock, strlist);
01413 }
01414
01415 void MainServer::HandleFillProgramInfo(QStringList &slist, PlaybackSock *pbs)
01416 {
01417 MythSocket *pbssock = pbs->getSocket();
01418
01419 QString playbackhost = slist[1];
01420
01421 ProgramInfo *pginfo = new ProgramInfo();
01422 pginfo->FromStringList(slist, 2);
01423
01424 QString lpath = GetPlaybackURL(pginfo);
01425 QString ip = gContext->GetSetting("BackendServerIP");
01426 QString port = gContext->GetSetting("BackendServerPort");
01427
01428 if (playbackhost == gContext->GetHostName())
01429 pginfo->pathname = lpath;
01430 else
01431 pginfo->pathname = QString("myth://") + ip + ":" + port
01432 + "/" + pginfo->GetRecordBasename();
01433
01434 struct stat st;
01435
01436 long long size = 0;
01437 if (stat(lpath.ascii(), &st) == 0)
01438 size = st.st_size;
01439
01440 pginfo->filesize = size;
01441
01442 QStringList strlist;
01443
01444 pginfo->ToStringList(strlist);
01445
01446 delete pginfo;
01447
01448 SendResponse(pbssock, strlist);
01449 }
01450
01451 void *MainServer::SpawnDeleteThread(void *param)
01452 {
01453 DeleteStruct *ds = (DeleteStruct *)param;
01454
01455 MainServer *ms = ds->ms;
01456 ms->DoDeleteThread(ds);
01457
01458 delete ds;
01459
01460 return NULL;
01461 }
01462
01463 void MainServer::DoDeleteThread(const DeleteStruct *ds)
01464 {
01465
01466
01467 sleep(3);
01468 usleep(rand()%2000);
01469
01470 deletelock.lock();
01471
01472 QString logInfo = QString("chanid %1 at %2")
01473 .arg(ds->chanid).arg(ds->recstartts.toString());
01474
01475 QString name = QString("deleteThread%1%2").arg(getpid()).arg(rand());
01476 QFile checkFile(ds->filename);
01477
01478 if (!MSqlQuery::testDBConnection())
01479 {
01480 QString msg = QString("ERROR opening database connection for Delete "
01481 "Thread for chanid %1 recorded at %2. Program "
01482 "will NOT be deleted.")
01483 .arg(ds->chanid).arg(ds->recstartts.toString());
01484 VERBOSE(VB_GENERAL, msg);
01485 gContext->LogEntry("mythbackend", LP_ERROR, "Delete Recording",
01486 QString("Unable to open database connection for %1. "
01487 "Program will NOT be deleted.")
01488 .arg(logInfo));
01489
01490 deletelock.unlock();
01491 return;
01492 }
01493
01494 ProgramInfo *pginfo;
01495 pginfo = ProgramInfo::GetProgramFromRecorded(ds->chanid,
01496 ds->recstartts);
01497 if (pginfo == NULL)
01498 {
01499 QString msg = QString("ERROR retrieving program info when trying to "
01500 "delete program for chanid %1 recorded at %2. "
01501 "Recording will NOT be deleted.")
01502 .arg(ds->chanid).arg(ds->recstartts.toString());
01503 VERBOSE(VB_GENERAL, msg);
01504 gContext->LogEntry("mythbackend", LP_ERROR, "Delete Recording",
01505 QString("Unable to retrieve program info for %1. "
01506 "Program will NOT be deleted.")
01507 .arg(logInfo));
01508
01509 deletelock.unlock();
01510 return;
01511 }
01512
01513
01514 if ((!checkFile.exists()) &&
01515 (pginfo->filesize > 0) &&
01516 (!ds->forceMetadataDelete))
01517 {
01518 VERBOSE(VB_IMPORTANT, QString("ERROR when trying to delete file: %1. File "
01519 "doesn't exist. Database metadata"
01520 "will not be removed.")
01521 .arg(ds->filename));
01522 gContext->LogEntry("mythbackend", LP_WARNING, "Delete Recording",
01523 QString("File %1 does not exist for %2 when trying "
01524 "to delete recording.")
01525 .arg(ds->filename).arg(logInfo));
01526
01527 pginfo->SetDeleteFlag(false);
01528 delete pginfo;
01529
01530 MythEvent me("RECORDING_LIST_CHANGE");
01531 gContext->dispatch(me);
01532
01533 deletelock.unlock();
01534 return;
01535 }
01536
01537 JobQueue::DeleteAllJobs(ds->chanid, ds->recstartts);
01538
01539 LiveTVChain *tvchain = GetChainWithRecording(pginfo);
01540 if (tvchain)
01541 tvchain->DeleteProgram(pginfo);
01542
01543 bool followLinks = gContext->GetNumSetting("DeletesFollowLinks", 0);
01544 bool slowDeletes = gContext->GetNumSetting("TruncateDeletesSlowly", 0);
01545 int fd = -1;
01546 off_t size = 0;
01547 bool errmsg = false;
01548
01549
01550 if (slowDeletes)
01551 {
01552
01553
01554 struct stat st;
01555 if (stat(ds->filename.ascii(), &st) == 0)
01556 size = st.st_size;
01557 fd = DeleteFile(ds->filename, followLinks);
01558
01559 if ((fd < 0) && checkFile.exists())
01560 errmsg = true;
01561 }
01562 else
01563 {
01564 delete_file_immediately(ds->filename, followLinks, false);
01565 sleep(2);
01566 if (checkFile.exists())
01567 errmsg = true;
01568 }
01569
01570 if (errmsg)
01571 {
01572 VERBOSE(VB_IMPORTANT,
01573 QString("Error deleting file: %1. Keeping metadata in database.")
01574 .arg(ds->filename));
01575 gContext->LogEntry("mythbackend", LP_WARNING, "Delete Recording",
01576 QString("File %1 for %2 could not be deleted.")
01577 .arg(ds->filename).arg(logInfo));
01578
01579 pginfo->SetDeleteFlag(false);
01580 delete pginfo;
01581
01582 MythEvent me("RECORDING_LIST_CHANGE");
01583 gContext->dispatch(me);
01584
01585 deletelock.unlock();
01586 return;
01587 }
01588
01589
01590
01591 QFileInfo fInfo( ds->filename );
01592 QString nameFilter = fInfo.fileName() + "*.png";
01593
01594
01595
01596 nameFilter.replace(QRegExp("( |;)"), "?");
01597 QDir dir ( fInfo.dirPath(), nameFilter );
01598
01599 for (uint nIdx = 0; nIdx < dir.count(); nIdx++)
01600 {
01601 QString sFileName = QString( "%1/%2" )
01602 .arg( fInfo.dirPath() )
01603 .arg( dir[ nIdx ] );
01604
01605 delete_file_immediately( sFileName, followLinks, true);
01606 }
01607
01608 DoDeleteInDB(ds);
01609
01610 if (pginfo->recgroup != "LiveTV")
01611 ScheduledRecording::signalChange(0);
01612
01613 deletelock.unlock();
01614
01615 if (slowDeletes && fd != -1)
01616 TruncateAndClose(pginfo, fd, ds->filename, size);
01617
01618 delete pginfo;
01619 }
01620
01621 void MainServer::DoDeleteInDB(const DeleteStruct *ds)
01622 {
01623 QString logInfo = QString("chanid %1 at %2")
01624 .arg(ds->chanid).arg(ds->recstartts.toString());
01625
01626 MSqlQuery query(MSqlQuery::InitCon());
01627 query.prepare("DELETE FROM recorded WHERE chanid = :CHANID AND "
01628 "title = :TITLE AND starttime = :STARTTIME;");
01629 query.bindValue(":CHANID", ds->chanid);
01630 query.bindValue(":TITLE", ds->title.utf8());
01631 query.bindValue(":STARTTIME", ds->recstartts);
01632
01633 if (!query.exec() || !query.isActive())
01634 {
01635 MythContext::DBError("Recorded program deletion", query);
01636 gContext->LogEntry("mythbackend", LP_ERROR, "Delete Recording",
01637 QString("Error deleting recorded table for %1.")
01638 .arg(logInfo));
01639 }
01640
01641 sleep(1);
01642
01643
01644 QString msg = QString("RECORDING_LIST_CHANGE DELETE %1 %2")
01645 .arg(ds->chanid)
01646 .arg(ds->recstartts.toString(Qt::ISODate));
01647 MythEvent me(msg);
01648 gContext->dispatch(me);
01649
01650
01651 sleep(3);
01652
01653 query.prepare("DELETE FROM recordedmarkup "
01654 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
01655 query.bindValue(":CHANID", ds->chanid);
01656 query.bindValue(":STARTTIME", ds->recstartts);
01657 query.exec();
01658
01659 if (!query.isActive())
01660 {
01661 MythContext::DBError("Recorded program delete recordedmarkup",
01662 query);
01663 gContext->LogEntry("mythbackend", LP_ERROR, "Delete Recording",
01664 QString("Error deleting recordedmarkup for %1.")
01665 .arg(logInfo));
01666 }
01667
01668 query.prepare("DELETE FROM recordedseek "
01669 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
01670 query.bindValue(":CHANID", ds->chanid);
01671 query.bindValue(":STARTTIME", ds->recstartts);
01672 query.exec();
01673
01674 if (!query.isActive())
01675 {
01676 MythContext::DBError("Recorded program delete recordedseek",
01677 query);
01678 gContext->LogEntry("mythbackend", LP_ERROR, "Delete Recording",
01679 QString("Error deleting recordedseek for %1.")
01680 .arg(logInfo));
01681 }
01682 }
01683
01693 int MainServer::DeleteFile(const QString &filename, bool followLinks)
01694 {
01695 QFileInfo finfo(filename);
01696 int fd = -1, err = 0;
01697 QString linktext = "";
01698
01699 VERBOSE(VB_FILE, QString("About to unlink/delete file: '%1'")
01700 .arg(filename));
01701
01702 QString errmsg = QString("Delete Error '%1'").arg(filename.local8Bit());
01703 if (finfo.isSymLink())
01704 {
01705 linktext = finfo.readLink();
01706 if (linktext.left(1) != "/")
01707 linktext = finfo.dirPath(true) + "/" + finfo.readLink();
01708
01709 errmsg += QString(" -> '%2'").arg(linktext.local8Bit());
01710 }
01711
01712 if (followLinks && finfo.isSymLink())
01713 {
01714 fd = OpenAndUnlink(linktext);
01715 if (fd >= 0)
01716 err = unlink(filename.local8Bit());
01717 }
01718 else if (!finfo.isSymLink())
01719 {
01720 fd = OpenAndUnlink(filename);
01721 }
01722 else
01723 {
01724 err = unlink(filename.local8Bit());
01725 if (err == 0)
01726 return -1;
01727 }
01728
01729 if (fd < 0)
01730 VERBOSE(VB_IMPORTANT, errmsg + ENO);
01731
01732 return fd;
01733 }
01734
01744 int MainServer::OpenAndUnlink(const QString &filename)
01745 {
01746 QString msg = QString("Error deleting '%1'").arg(filename.local8Bit());
01747 int fd = open(filename.local8Bit(), O_WRONLY);
01748
01749 if (fd == -1)
01750 {
01751 VERBOSE(VB_IMPORTANT, msg + " could not open " + ENO);
01752 return -1;
01753 }
01754
01755 if (unlink(filename.local8Bit()))
01756 {
01757 VERBOSE(VB_IMPORTANT, msg + " could not unlink " + ENO);
01758 close(fd);
01759 return -1;
01760 }
01761
01762 return fd;
01763 }
01764
01773 bool MainServer::TruncateAndClose(ProgramInfo *pginfo, int fd,
01774 const QString &filename, off_t fsize)
01775 {
01776 QMutexLocker locker(&truncate_and_close_lock);
01777
01778 pginfo->pathname = filename;
01779 pginfo->MarkAsInUse(true, "truncatingdelete");
01780
01781 int cards = 5;
01782
01783 MSqlQuery query(MSqlQuery::InitCon());
01784 query.prepare("SELECT COUNT(cardid) FROM capturecard;");
01785 if (query.exec() && query.isActive() && query.size() && query.next())
01786 cards = query.value(0).toInt();
01787
01788
01789 const size_t sleep_time = 500;
01790 const size_t min_tps = (size_t) (cards * 1.2 * (19400000LL / 8));
01791 const size_t increment = (size_t) (min_tps * (sleep_time * 0.001f));
01792
01793
01794 gContext->SaveSetting("TruncateIncrement", increment);
01795
01796 VERBOSE(VB_FILE,
01797 QString("Truncating '%1' by %2 MB every %3 milliseconds")
01798 .arg(filename)
01799 .arg(increment / (1024.0 * 1024.0), 0, 'f', 2)
01800 .arg(sleep_time));
01801
01802 int count = 0;
01803 while (fsize > 0)
01804 {
01805
01806
01807
01808 int err = ftruncate(fd, fsize);
01809 if (err)
01810 {
01811 VERBOSE(VB_IMPORTANT, QString("Error truncating '%1'")
01812 .arg(filename) + ENO);
01813 pginfo->MarkAsInUse(false);
01814 return 0 == close(fd);
01815 }
01816
01817 fsize -= increment;
01818
01819 if ((count % 100) == 0)
01820 pginfo->UpdateInUseMark(true);
01821
01822 count++;
01823
01824 usleep(sleep_time * 1000);
01825 }
01826
01827 bool ok = (0 == close(fd));
01828
01829 pginfo->MarkAsInUse(false);
01830
01831 VERBOSE(VB_FILE, QString("Finished truncating '%1'").arg(filename));
01832
01833 return ok;
01834 }
01835
01836 void MainServer::HandleCheckRecordingActive(QStringList &slist,
01837 PlaybackSock *pbs)
01838 {
01839 MythSocket *pbssock = NULL;
01840 if (pbs)
01841 pbssock = pbs->getSocket();
01842
01843 ProgramInfo *pginfo = new ProgramInfo();
01844 pginfo->FromStringList(slist, 1);
01845
01846 int result = 0;
01847
01848 if (ismaster && pginfo->hostname != gContext->GetHostName())
01849 {
01850 PlaybackSock *slave = getSlaveByHostname(pginfo->hostname);
01851 if (slave)
01852 {
01853 result = slave->CheckRecordingActive(pginfo);
01854 slave->DownRef();
01855 }
01856 }
01857 else
01858 {
01859 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
01860 for (; iter != encoderList->end(); ++iter)
01861 {
01862 EncoderLink *elink = iter.data();
01863
01864 if (elink->IsLocal() && elink->MatchesRecording(pginfo))
01865 result = iter.key();
01866 }
01867 }
01868
01869 QStringList outputlist = QString::number(result);
01870 if (pbssock)
01871 SendResponse(pbssock, outputlist);
01872
01873 delete pginfo;
01874 return;
01875 }
01876
01877 void MainServer::HandleStopRecording(QStringList &slist, PlaybackSock *pbs)
01878 {
01879 ProgramInfo *pginfo = new ProgramInfo();
01880 pginfo->FromStringList(slist, 1);
01881
01882 DoHandleStopRecording(pginfo, pbs);
01883 }
01884
01885 void MainServer::DoHandleStopRecording(ProgramInfo *pginfo, PlaybackSock *pbs)
01886 {
01887 MythSocket *pbssock = NULL;
01888 if (pbs)
01889 pbssock = pbs->getSocket();
01890
01891 if (ismaster && pginfo->hostname != gContext->GetHostName())
01892 {
01893 PlaybackSock *slave = getSlaveByHostname(pginfo->hostname);
01894
01895 int num = -1;
01896
01897 if (slave)
01898 {
01899 num = slave->StopRecording(pginfo);
01900
01901 if (num > 0)
01902 {
01903 (*encoderList)[num]->StopRecording();
01904 pginfo->recstatus = rsRecorded;
01905 if (m_sched)
01906 m_sched->UpdateRecStatus(pginfo);
01907 }
01908 if (pbssock)
01909 {
01910 QStringList outputlist = "0";
01911 SendResponse(pbssock, outputlist);
01912 }
01913
01914 delete pginfo;
01915 slave->DownRef();
01916 return;
01917 }
01918 else
01919 {
01920
01921
01922
01923
01924 pginfo->recstatus = rsRecorded;
01925 if (m_sched)
01926 m_sched->UpdateRecStatus(pginfo);
01927 }
01928
01929 }
01930
01931 int recnum = -1;
01932
01933 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
01934 for (; iter != encoderList->end(); ++iter)
01935 {
01936 EncoderLink *elink = iter.data();
01937
01938 if (elink->IsLocal() && elink->MatchesRecording(pginfo))
01939 {
01940 recnum = iter.key();
01941
01942 elink->StopRecording();
01943
01944 while (elink->IsBusyRecording() ||
01945 elink->GetState() == kState_ChangingState)
01946 {
01947 usleep(100);
01948 }
01949
01950 if (ismaster)
01951 {
01952 pginfo->recstatus = rsRecorded;
01953 if (m_sched)
01954 m_sched->UpdateRecStatus(pginfo);
01955 }
01956 }
01957 }
01958
01959 if (pbssock)
01960 {
01961 QStringList outputlist = QString::number(recnum);
01962 SendResponse(pbssock, outputlist);
01963 }
01964
01965 delete pginfo;
01966 }
01967
01968 void MainServer::HandleDeleteRecording(QStringList &slist, PlaybackSock *pbs,
01969 bool forceMetadataDelete)
01970 {
01971 ProgramInfo *pginfo = new ProgramInfo();
01972 pginfo->FromStringList(slist, 1);
01973
01974 DoHandleDeleteRecording(pginfo, pbs, forceMetadataDelete);
01975 }
01976
01977 void MainServer::DoHandleDeleteRecording(ProgramInfo *pginfo, PlaybackSock *pbs,
01978 bool forceMetadataDelete, bool expirer)
01979 {
01980 int resultCode = -1;
01981 MythSocket *pbssock = NULL;
01982 if (pbs)
01983 pbssock = pbs->getSocket();
01984
01985 bool justexpire = expirer ? false :
01986 (gContext->GetNumSetting("AutoExpireInsteadOfDelete") &&
01987 (pginfo->recgroup != "Deleted") && (pginfo->recgroup != "LiveTV"));
01988
01989 QString filename = GetPlaybackURL(pginfo, false);
01990 if (filename == "")
01991 {
01992 VERBOSE(VB_IMPORTANT,
01993 QString("ERROR when trying to delete file for %1 @ %2. Unable "
01994 "to determine filename of recording.")
01995 .arg(pginfo->chanid)
01996 .arg(pginfo->recstartts.toString(Qt::ISODate)));
01997
01998 if (pbssock)
01999 {
02000 resultCode = -2;
02001 QStringList outputlist = QString::number(resultCode);
02002 SendResponse(pbssock, outputlist);
02003 }
02004 delete pginfo;
02005 return;
02006 }
02007
02008 if (justexpire && !forceMetadataDelete && pginfo->filesize > (1024 * 1024) )
02009 {
02010 pginfo->ApplyRecordRecGroupChange("Deleted");
02011 pginfo->SetAutoExpire(kDeletedAutoExpire);
02012 pginfo->UpdateLastDelete(true);
02013 if (pginfo->recstatus == rsRecording)
02014 DoHandleStopRecording(pginfo, NULL);
02015 else
02016 delete pginfo;
02017 QStringList outputlist = QString::number(0);
02018 SendResponse(pbssock, outputlist);
02019 MythEvent me("RECORDING_LIST_CHANGE");
02020 gContext->dispatch(me);
02021 return;
02022 }
02023
02024
02025
02026 if (ismaster && pginfo->hostname != gContext->GetHostName())
02027 {
02028 PlaybackSock *slave = getSlaveByHostname(pginfo->hostname);
02029
02030 int num = -1;
02031
02032 if (slave)
02033 {
02034 num = slave->DeleteRecording(pginfo, forceMetadataDelete);
02035
02036 if (num > 0)
02037 {
02038 (*encoderList)[num]->StopRecording();
02039 pginfo->recstatus = rsRecorded;
02040 if (m_sched)
02041 m_sched->UpdateRecStatus(pginfo);
02042 }
02043
02044 if (pbssock)
02045 {
02046 QStringList outputlist = QString::number(num);
02047 SendResponse(pbssock, outputlist);
02048 }
02049
02050 delete pginfo;
02051 slave->DownRef();
02052 return;
02053 }
02054 }
02055
02056
02057
02058
02059 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
02060 for (; iter != encoderList->end(); ++iter)
02061 {
02062 EncoderLink *elink = iter.data();
02063
02064 if (elink->IsLocal() && elink->MatchesRecording(pginfo))
02065 {
02066 resultCode = iter.key();
02067
02068 elink->StopRecording();
02069
02070 while (elink->IsBusyRecording() ||
02071 elink->GetState() == kState_ChangingState)
02072 {
02073 usleep(100);
02074 }
02075
02076 if (ismaster)
02077 {
02078 pginfo->recstatus = rsRecorded;
02079 if (m_sched)
02080 m_sched->UpdateRecStatus(pginfo);
02081 }
02082 }
02083 }
02084
02085 QFile checkFile(filename);
02086 bool fileExists = checkFile.exists();
02087 if (!fileExists)
02088 {
02089 QFile checkFileUTF8(QString::fromUtf8(filename));
02090 if (fileExists = checkFileUTF8.exists())
02091 filename = QString::fromUtf8(filename);
02092 }
02093
02094
02095
02096
02097
02098 if ((fileExists) || (pginfo->filesize == 0) || (forceMetadataDelete))
02099 {
02100 DeleteStruct *ds = new DeleteStruct;
02101 ds->ms = this;
02102 ds->filename = filename;
02103 ds->title = pginfo->title;
02104 ds->chanid = pginfo->chanid;
02105 ds->recstartts = pginfo->recstartts;
02106 ds->recendts = pginfo->recendts;
02107 ds->forceMetadataDelete = forceMetadataDelete;
02108
02109 pginfo->SetDeleteFlag(true);
02110
02111 pthread_t deleteThread;
02112 pthread_attr_t attr;
02113 pthread_attr_init(&attr);
02114 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02115 pthread_create(&deleteThread, &attr, SpawnDeleteThread, ds);
02116 pthread_attr_destroy(&attr);
02117 }
02118 else
02119 {
02120 QString logInfo = QString("chanid %1 at %2")
02121 .arg(pginfo->chanid)
02122 .arg(pginfo->recstartts.toString());
02123 VERBOSE(VB_IMPORTANT,
02124 QString("ERROR when trying to delete file: %1. File doesn't "
02125 "exist. Database metadata will not be removed.")
02126 .arg(filename));
02127 gContext->LogEntry("mythbackend", LP_WARNING, "Delete Recording",
02128 QString("File %1 does not exist for %2 when trying "
02129 "to delete recording.")
02130 .arg(filename).arg(logInfo));
02131 resultCode = -2;
02132 }
02133
02134 if (pbssock)
02135 {
02136 QStringList outputlist = QString::number(resultCode);
02137 SendResponse(pbssock, outputlist);
02138 }
02139
02140
02141 if ((fileExists) || (pginfo->filesize == 0) || (forceMetadataDelete))
02142 {
02143 QString msg = QString("RECORDING_LIST_CHANGE DELETE %1 %2")
02144 .arg(pginfo->chanid)
02145 .arg(pginfo->recstartts.toString(Qt::ISODate));
02146 MythEvent me(msg);
02147 gContext->dispatch(me);
02148 }
02149
02150 delete pginfo;
02151 }
02152
02153 void MainServer::HandleUndeleteRecording(QStringList &slist, PlaybackSock *pbs)
02154 {
02155 ProgramInfo *pginfo = new ProgramInfo();
02156 pginfo->FromStringList(slist, 1);
02157
02158 DoHandleUndeleteRecording(pginfo, pbs);
02159 }
02160
02161 void MainServer::DoHandleUndeleteRecording(ProgramInfo *pginfo, PlaybackSock *pbs)
02162 {
02163 bool ret = -1;
02164 bool undelete_possible =
02165 gContext->GetNumSetting("AutoExpireInsteadOfDelete", 0);
02166 MythSocket *pbssock = NULL;
02167 if (pbs)
02168 pbssock = pbs->getSocket();
02169
02170 if (undelete_possible)
02171 {
02172 pginfo->ApplyRecordRecGroupChange("Default");
02173 pginfo->UpdateLastDelete(false);
02174 pginfo->SetAutoExpire(kDisableAutoExpire);
02175 delete pginfo;
02176 ret = 0;
02177 MythEvent me("RECORDING_LIST_CHANGE");
02178 gContext->dispatch(me);
02179 }
02180
02181 QStringList outputlist = QString::number(ret);
02182 SendResponse(pbssock, outputlist);
02183 }
02184
02185 void MainServer::HandleRescheduleRecordings(int recordid, PlaybackSock *pbs)
02186 {
02187 QStringList result;
02188 if (m_sched)
02189 {
02190 m_sched->Reschedule(recordid);
02191 result = QString::number(1);
02192 }
02193 else
02194 result = QString::number(0);
02195
02196 if (pbs)
02197 {
02198 MythSocket *pbssock = pbs->getSocket();
02199 if (pbssock)
02200 SendResponse(pbssock, result);
02201 }
02202 }
02203
02204 void MainServer::HandleForgetRecording(QStringList &slist, PlaybackSock *pbs)
02205 {
02206 ProgramInfo *pginfo = new ProgramInfo();
02207 pginfo->FromStringList(slist, 1);
02208
02209 MythSocket *pbssock = NULL;
02210 if (pbs)
02211 pbssock = pbs->getSocket();
02212
02213 pginfo->ForgetHistory();
02214
02215 if (pbssock)
02216 {
02217 QStringList outputlist = QString::number(0);
02218 SendResponse(pbssock, outputlist);
02219 }
02220
02221 delete pginfo;
02222
02223 }
02224
02234 void MainServer::HandleQueryFreeSpace(PlaybackSock *pbs, bool allHosts)
02235 {
02236 QStringList strlist;
02237
02238 BackendQueryDiskSpace(strlist, encoderList, allHosts, allHosts);
02239
02240 SendResponse(pbs->getSocket(), strlist);
02241 }
02242
02248 void MainServer::HandleQueryFreeSpaceSummary(PlaybackSock *pbs)
02249 {
02250 QStringList fullStrList;
02251 QStringList strList;
02252
02253 BackendQueryDiskSpace(fullStrList, encoderList, true, true);
02254
02255
02256 unsigned int index = fullStrList.size() - 4;
02257 strList << fullStrList[index++];
02258 strList << fullStrList[index++];
02259 strList << fullStrList[index++];
02260 strList << fullStrList[index++];
02261
02262 SendResponse(pbs->getSocket(), strList);
02263 }
02264
02271 void MainServer::HandleQueryLoad(PlaybackSock *pbs)
02272 {
02273 MythSocket *pbssock = pbs->getSocket();
02274
02275 QStringList strlist;
02276
02277 double loads[3];
02278 if (getloadavg(loads,3) == -1)
02279 strlist << "getloadavg() failed";
02280 else
02281 strlist << QString::number(loads[0])
02282 << QString::number(loads[1])
02283 << QString::number(loads[2]);
02284
02285 SendResponse(pbssock, strlist);
02286 }
02287
02293 void MainServer::HandleQueryUptime(PlaybackSock *pbs)
02294 {
02295 MythSocket *pbssock = pbs->getSocket();
02296 QStringList strlist;
02297 time_t uptime;
02298
02299 if (getUptime(uptime))
02300 strlist << QString::number(uptime);
02301 else
02302 strlist << "Could not determine uptime.";
02303
02304 SendResponse(pbssock, strlist);
02305 }
02306
02312 void MainServer::HandleQueryMemStats(PlaybackSock *pbs)
02313 {
02314 MythSocket *pbssock = pbs->getSocket();
02315 QStringList strlist;
02316 int totalMB, freeMB, totalVM, freeVM;
02317
02318 if (getMemStats(totalMB, freeMB, totalVM, freeVM))
02319 strlist << QString::number(totalMB) << QString::number(freeMB)
02320 << QString::number(totalVM) << QString::number(freeVM);
02321 else
02322 strlist << "Could not determine memory stats.";
02323
02324 SendResponse(pbssock, strlist);
02325 }
02326
02331 void MainServer::HandleQueryCheckFile(QStringList &slist, PlaybackSock *pbs)
02332 {
02333 MythSocket *pbssock = pbs->getSocket();
02334 bool checkSlaves = slist[1].toInt();
02335
02336 ProgramInfo *pginfo = new ProgramInfo();
02337 pginfo->FromStringList(slist, 2);
02338
02339 int exists = 0;
02340
02341 if ((ismaster) &&
02342 (pginfo->hostname != gContext->GetHostName()) &&
02343 (checkSlaves))
02344 {
02345 PlaybackSock *slave = getSlaveByHostname(pginfo->hostname);
02346
02347 if (slave)
02348 {
02349 exists = slave->CheckFile(pginfo);
02350 slave->DownRef();
02351
02352 QStringList outputlist = QString::number(exists);
02353 if (exists)
02354 outputlist << pginfo->pathname;
02355 else
02356 outputlist << "";
02357
02358 SendResponse(pbssock, outputlist);
02359 delete pginfo;
02360 return;
02361 }
02362 }
02363
02364 QString pburl = GetPlaybackURL(pginfo);
02365 QFile checkFile(pburl);
02366
02367 if (checkFile.exists() == true)
02368 exists = 1;
02369
02370 QStringList strlist = QString::number(exists);
02371 if (exists)
02372 strlist << pburl;
02373 else
02374 strlist << "";
02375 SendResponse(pbssock, strlist);
02376
02377 delete pginfo;
02378 }
02379
02380 void MainServer::getGuideDataThrough(QDateTime &GuideDataThrough)
02381 {
02382 MSqlQuery query(MSqlQuery::InitCon());
02383 query.prepare("SELECT MAX(endtime) FROM program WHERE manualid = 0;");
02384
02385 if (query.exec() && query.isActive() && query.size())
02386 {
02387 query.next();
02388 if (query.isValid())
02389 GuideDataThrough = QDateTime::fromString(query.value(0).toString(),
02390 Qt::ISODate);
02391 }
02392 }
02393
02394 void MainServer::HandleQueryGuideDataThrough(PlaybackSock *pbs)
02395 {
02396 QDateTime GuideDataThrough;
02397 MythSocket *pbssock = pbs->getSocket();
02398 QStringList strlist;
02399
02400 getGuideDataThrough(GuideDataThrough);
02401
02402 if (GuideDataThrough.isNull())
02403 strlist << QString("0000-00-00 00:00");
02404 else
02405 strlist << QDateTime(GuideDataThrough).toString("yyyy-MM-dd hh:mm");
02406
02407 SendResponse(pbssock, strlist);
02408 }
02409
02410 void MainServer::HandleGetPendingRecordings(PlaybackSock *pbs,
02411 QString tmptable, int recordid)
02412 {
02413 MythSocket *pbssock = pbs->getSocket();
02414
02415 QStringList strList;
02416
02417 if (m_sched)
02418 {
02419 if (tmptable == "")
02420 m_sched->getAllPending(strList);
02421 else
02422 {
02423 Scheduler *sched = new Scheduler(false, encoderList,
02424 tmptable, m_sched);
02425 sched->FillRecordListFromDB(recordid);
02426 sched->getAllPending(strList);
02427 delete sched;
02428
02429 if (recordid > 0)
02430 {
02431 MSqlQuery query(MSqlQuery::InitCon());
02432 query.prepare("SELECT NULL FROM record "
02433 "WHERE recordid = :RECID;");
02434 query.bindValue(":RECID", recordid);
02435
02436 if (query.exec() && query.isActive() && query.size())
02437 {
02438 ScheduledRecording *record = new ScheduledRecording();
02439 record->loadByID(recordid);
02440 if (record->getSearchType() == kManualSearch)
02441 HandleRescheduleRecordings(recordid, NULL);
02442 record->deleteLater();
02443 }
02444 query.prepare("DELETE FROM program WHERE manualid = :RECID;");
02445 query.bindValue(":RECID", recordid);
02446 query.exec();
02447 }
02448 }
02449 }
02450 else
02451 {
02452 strList << QString::number(0);
02453 strList << QString::number(0);
02454 }
02455
02456 SendResponse(pbssock, strList);
02457 }
02458
02459 void MainServer::HandleGetScheduledRecordings(PlaybackSock *pbs)
02460 {
02461 MythSocket *pbssock = pbs->getSocket();
02462
02463 QStringList strList;
02464
02465 if (m_sched)
02466 m_sched->getAllScheduled(strList);
02467 else
02468 strList << QString::number(0);
02469
02470 SendResponse(pbssock, strList);
02471 }
02472
02473 void MainServer::HandleGetConflictingRecordings(QStringList &slist,
02474 PlaybackSock *pbs)
02475 {
02476 MythSocket *pbssock = pbs->getSocket();
02477
02478 ProgramInfo *pginfo = new ProgramInfo();
02479 pginfo->FromStringList(slist, 1);
02480
02481 QStringList strlist;
02482
02483 if (m_sched)
02484 m_sched->getConflicting(pginfo, strlist);
02485 else
02486 strlist << QString::number(0);
02487
02488 SendResponse(pbssock, strlist);
02489
02490 delete pginfo;
02491 }
02492
02493 void MainServer::HandleGetExpiringRecordings(PlaybackSock *pbs)
02494 {
02495 MythSocket *pbssock = pbs->getSocket();
02496
02497 QStringList strList;
02498
02499 if (m_expirer)
02500 m_expirer->GetAllExpiring(strList);
02501 else
02502 strList << QString::number(0);
02503
02504 SendResponse(pbssock, strList);
02505 }
02506
02507 void MainServer::HandleLockTuner(PlaybackSock *pbs)
02508 {
02509 MythSocket *pbssock = pbs->getSocket();
02510 QString pbshost = pbs->getHostname();
02511
02512 QStringList strlist;
02513 int retval;
02514
02515 EncoderLink *encoder = NULL;
02516 QString enchost;
02517
02518 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
02519 for (; iter != encoderList->end(); ++iter)
02520 {
02521 EncoderLink *elink = iter.data();
02522
02523 if (elink->IsLocal())
02524 enchost = gContext->GetHostName();
02525 else
02526 enchost = elink->GetHostName();
02527
02528 if ((enchost == pbshost) &&
02529 (elink->IsConnected()) &&
02530 (!elink->IsBusy()) &&
02531 (!elink->IsTunerLocked()))
02532 {
02533 encoder = elink;
02534 break;
02535 }
02536 }
02537
02538 if (encoder)
02539 {
02540 retval = encoder->LockTuner();
02541
02542 if (retval != -1)
02543 {
02544 QString msg = QString("Cardid %1 LOCKed for external use on %2.")
02545 .arg(retval).arg(pbshost);
02546 VERBOSE(VB_GENERAL, msg);
02547
02548 MSqlQuery query(MSqlQuery::InitCon());
02549 query.prepare("SELECT videodevice, audiodevice, "
02550 "vbidevice "
02551 "FROM capturecard "
02552 "WHERE cardid = :CARDID ;");
02553 query.bindValue(":CARDID", retval);
02554
02555 if (query.exec() && query.isActive() && query.size())
02556 {
02557
02558 query.next();
02559 strlist << QString::number(retval)
02560 << query.value(0).toString()
02561 << query.value(1).toString()
02562 << query.value(2).toString();
02563
02564 if (m_sched)
02565 m_sched->Reschedule(0);
02566
02567 SendResponse(pbssock, strlist);
02568 return;
02569 }
02570 else
02571 {
02572 cerr << "mainserver.o: Failed querying the db for a videodevice"
02573 << endl;
02574 }
02575 }
02576 else
02577 {
02578
02579 strlist << "-2" << "" << "" << "";
02580 SendResponse(pbssock, strlist);
02581 return;
02582 }
02583 }
02584
02585 strlist << "-1" << "" << "" << "";
02586 SendResponse(pbssock, strlist);
02587 }
02588
02589 void MainServer::HandleFreeTuner(int cardid, PlaybackSock *pbs)
02590 {
02591 MythSocket *pbssock = pbs->getSocket();
02592 QStringList strlist;
02593 EncoderLink *encoder = NULL;
02594
02595 QMap<int, EncoderLink *>::Iterator iter = encoderList->find(cardid);
02596 if (iter == encoderList->end())
02597 {
02598 VERBOSE(VB_IMPORTANT, "MainServer::HandleFreeTuner() " +
02599 QString("Unknown encoder: %1").arg(cardid));
02600 strlist << "FAILED";
02601 }
02602 else
02603 {
02604 encoder = iter.data();
02605 encoder->FreeTuner();
02606
02607 QString msg = QString("Cardid %1 FREED from external use on %2.")
02608 .arg(cardid).arg(pbs->getHostname());
02609 VERBOSE(VB_GENERAL, msg);
02610
02611 if (m_sched)
02612 m_sched->Reschedule(0);
02613
02614 strlist << "OK";
02615 }
02616
02617 SendResponse(pbssock, strlist);
02618 }
02619
02620 void MainServer::HandleGetFreeRecorder(PlaybackSock *pbs)
02621 {
02622 MythSocket *pbssock = pbs->getSocket();
02623 QString pbshost = pbs->getHostname();
02624
02625 QStringList strlist;
02626 int retval = -1;
02627
02628 EncoderLink *encoder = NULL;
02629 QString enchost;
02630
02631 bool lastcard = false;
02632
02633 if (gContext->GetSetting("LastFreeCard", "0") == "1")
02634 lastcard = true;
02635
02636 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
02637 for (; iter != encoderList->end(); ++iter)
02638 {
02639 EncoderLink *elink = iter.data();
02640
02641 if (!lastcard)
02642 {
02643 if (elink->IsLocal())
02644 enchost = gContext->GetHostName();
02645 else
02646 enchost = elink->GetHostName();
02647
02648 if (enchost == pbshost && elink->IsConnected() &&
02649 !elink->IsBusy() && !elink->IsTunerLocked())
02650 {
02651 encoder = elink;
02652 retval = iter.key();
02653 VERBOSE(VB_RECORD, QString("Card %1 is local.")
02654 .arg(iter.key()));
02655 break;
02656 }
02657 }
02658
02659 if ((retval == -1 || lastcard) && elink->IsConnected() &&
02660 !elink->IsBusy() && !elink->IsTunerLocked())
02661 {
02662 encoder = elink;
02663 retval = iter.key();
02664 }
02665 VERBOSE(VB_RECORD, QString("Checking card %1. Best card so far %2")
02666 .arg(iter.key()).arg(retval));
02667 }
02668
02669 strlist << QString::number(retval);
02670
02671 if (encoder)
02672 {
02673 if (encoder->IsLocal())
02674 {
02675 strlist << gContext->GetSetting("BackendServerIP");
02676 strlist << gContext->GetSetting("BackendServerPort");
02677 }
02678 else
02679 {
02680 strlist << gContext->GetSettingOnHost("BackendServerIP",
02681 encoder->GetHostName(),
02682 "nohostname");
02683 strlist << gContext->GetSettingOnHost("BackendServerPort",
02684 encoder->GetHostName(), "-1");
02685 }
02686 }
02687 else
02688 {
02689 strlist << "nohost";
02690 strlist << "-1";
02691 }
02692
02693 SendResponse(pbssock, strlist);
02694 }
02695
02696 void MainServer::HandleGetFreeRecorderCount(PlaybackSock *pbs)
02697 {
02698 MythSocket *pbssock = pbs->getSocket();
02699
02700 QStringList strlist;
02701 int count = 0;
02702
02703 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
02704 for (; iter != encoderList->end(); ++iter)
02705 {
02706 EncoderLink *elink = iter.data();
02707
02708 if ((elink->IsConnected()) &&
02709 (!elink->IsBusy()) &&
02710 (!elink->IsTunerLocked()))
02711 {
02712 count++;
02713 }
02714 }
02715
02716 strlist << QString::number(count);
02717
02718 SendResponse(pbssock, strlist);
02719 }
02720
02721 void MainServer::HandleGetFreeRecorderList(PlaybackSock *pbs)
02722 {
02723 MythSocket *pbssock = pbs->getSocket();
02724
02725 QStringList strlist;
02726
02727 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
02728 for (; iter != encoderList->end(); ++iter)
02729 {
02730 EncoderLink *elink = iter.data();
02731
02732 if ((elink->IsConnected()) &&
02733 (!elink->IsBusy()) &&
02734 (!elink->IsTunerLocked()))
02735 {
02736 strlist << QString::number(iter.key());
02737 }
02738 }
02739
02740 if (strlist.size() == 0)
02741 strlist << "0";
02742
02743 SendResponse(pbssock, strlist);
02744 }
02745
02746 void MainServer::HandleGetNextFreeRecorder(QStringList &slist,
02747 PlaybackSock *pbs)
02748 {
02749 MythSocket *pbssock = pbs->getSocket();
02750 QString pbshost = pbs->getHostname();
02751
02752 QStringList strlist;
02753 int retval = -1;
02754 int currrec = slist[1].toInt();
02755
02756 EncoderLink *encoder = NULL;
02757 QString enchost;
02758
02759 VERBOSE(VB_RECORD, QString("Getting next free recorder after : %1")
02760 .arg(currrec));
02761
02762
02763 QMap<int, EncoderLink *>::Iterator iter, curr = encoderList->find(currrec);
02764
02765 if (currrec > 0 && curr != encoderList->end())
02766 {
02767
02768 for (iter = curr;;)
02769 {
02770 EncoderLink *elink;
02771
02772
02773 if (++iter == encoderList->end())
02774 {
02775 iter = encoderList->begin();
02776 }
02777
02778 elink = iter.data();
02779
02780 if ((retval == -1) &&
02781 (elink->IsConnected()) &&
02782 (!elink->IsBusy()) &&
02783 (!elink->IsTunerLocked()))
02784 {
02785 encoder = elink;
02786 retval = iter.key();
02787 }
02788
02789
02790 if (iter == curr)
02791 break;
02792 }
02793 }
02794 else
02795 {
02796 HandleGetFreeRecorder(pbs);
02797 return;
02798 }
02799
02800
02801 strlist << QString::number(retval);
02802
02803 if (encoder)
02804 {
02805 if (encoder->IsLocal())
02806 {
02807 strlist << gContext->GetSetting("BackendServerIP");
02808 strlist << gContext->GetSetting("BackendServerPort");
02809 }
02810 else
02811 {
02812 strlist << gContext->GetSettingOnHost("BackendServerIP",
02813 encoder->GetHostName(),
02814 "nohostname");
02815 strlist << gContext->GetSettingOnHost("BackendServerPort",
02816 encoder->GetHostName(), "-1");
02817 }
02818 }
02819 else
02820 {
02821 strlist << "nohost";
02822 strlist << "-1";
02823 }
02824
02825 SendResponse(pbssock, strlist);
02826 }
02827
02828 static QString cleanup(const QString &str)
02829 {
02830 if (str == " ")
02831 return "";
02832 return str;
02833 }
02834
02835 static QString make_safe(const QString &str)
02836 {
02837 if (str.isEmpty())
02838 return " ";
02839 return str;
02840 }
02841
02842 void MainServer::HandleRecorderQuery(QStringList &slist, QStringList &commands,
02843 PlaybackSock *pbs)
02844 {
02845 MythSocket *pbssock = pbs->getSocket();
02846
02847 int recnum = commands[1].toInt();
02848
02849 QMap<int, EncoderLink *>::Iterator iter = encoderList->find(recnum);
02850 if (iter == encoderList->end())
02851 {
02852 VERBOSE(VB_IMPORTANT, "MainServer::HandleRecorderQuery() " +
02853 QString("Unknown encoder: %1").arg(recnum));
02854 QStringList retlist = "bad";
02855 SendResponse(pbssock, retlist);
02856 return;
02857 }
02858
02859 QString command = slist[1];
02860
02861 QStringList retlist;
02862
02863 EncoderLink *enc = iter.data();
02864 if (!enc->IsConnected())
02865 {
02866 VERBOSE(VB_IMPORTANT," MainServer::HandleRecorderQuery() " +
02867 QString("Command %1 for unconnected encoder %2")
02868 .arg(command).arg(recnum));
02869 retlist << "bad";
02870 SendResponse(pbssock, retlist);
02871 return;
02872 }
02873
02874 if (command == "IS_RECORDING")
02875 {
02876 retlist << QString::number((int)enc->IsReallyRecording());
02877 }
02878 else if (command == "GET_FRAMERATE")
02879 {
02880 retlist << QString::number(enc->GetFramerate());
02881 }
02882 else if (command == "GET_FRAMES_WRITTEN")
02883 {
02884 long long value = enc->GetFramesWritten();
02885 encodeLongLong(retlist, value);
02886 }
02887 else if (command == "GET_FILE_POSITION")
02888 {
02889 long long value = enc->GetFilePosition();
02890 encodeLongLong(retlist, value);
02891 }
02892 else if (command == "GET_MAX_BITRATE")
02893 {
02894 long long value = enc->GetMaxBitrate();
02895 encodeLongLong(retlist, value);
02896 }
02897 else if (command == "GET_CURRENT_RECORDING")
02898 {
02899 ProgramInfo *info = enc->GetRecording();
02900 info->ToStringList(retlist);
02901 delete info;
02902 }
02903 else if (command == "GET_KEYFRAME_POS")
02904 {
02905 long long desired = decodeLongLong(slist, 2);
02906
02907 long long value = enc->GetKeyframePosition(desired);
02908 encodeLongLong(retlist, value);
02909 }
02910 else if (command == "FILL_POSITION_MAP")
02911 {
02912 int start = slist[2].toInt();
02913 int end = slist[3].toInt();
02914
02915 for (int keynum = start; keynum <= end; keynum++)
02916 {
02917 long long value = enc->GetKeyframePosition(keynum);
02918 if (value != -1)
02919 {
02920 encodeLongLong(retlist, keynum);
02921 encodeLongLong(retlist, value);
02922 }
02923 }
02924
02925 if (!retlist.size())
02926 retlist << "ok";
02927 }
02928 else if (command == "GET_RECORDING")
02929 {
02930 ProgramInfo *pginfo = enc->GetRecording();
02931 if (pginfo)
02932 {
02933 pginfo->ToStringList(retlist);
02934 delete pginfo;
02935 }
02936 else
02937 {
02938 ProgramInfo dummy;
02939 dummy.ToStringList(retlist);
02940 }
02941 }
02942 else if (command == "FRONTEND_READY")
02943 {
02944 enc->FrontendReady();
02945 retlist << "ok";
02946 }
02947 else if (command == "CANCEL_NEXT_RECORDING")
02948 {
02949 QString cancel = slist[2];
02950 VERBOSE(VB_IMPORTANT, "Received: CANCEL_NEXT_RECORDING "<<cancel);
02951 enc->CancelNextRecording(cancel == "1");
02952 retlist << "ok";
02953 }
02954 else if (command == "SPAWN_LIVETV")
02955 {
02956 QString chainid = slist[2];
02957 LiveTVChain *chain = GetExistingChain(chainid);
02958 if (!chain)
02959 {
02960 chain = new LiveTVChain();
02961 chain->LoadFromExistingChain(chainid);
02962 AddToChains(chain);
02963 }
02964
02965 chain->SetHostSocket(pbssock);
02966
02967 enc->SpawnLiveTV(chain, slist[3].toInt(), slist[4]);
02968 retlist << "ok";
02969 }
02970 else if (command == "STOP_LIVETV")
02971 {
02972 QString chainid = enc->GetChainID();
02973 enc->StopLiveTV();
02974
02975 LiveTVChain *chain = GetExistingChain(chainid);
02976 if (chain)
02977 {
02978 chain->DelHostSocket(pbssock);
02979 if (chain->HostSocketCount() == 0)
02980 {
02981 DeleteChain(chain);
02982 }
02983 }
02984
02985 retlist << "ok";
02986 }
02987 else if (command == "PAUSE")
02988 {
02989 enc->PauseRecorder();
02990 retlist << "ok";
02991 }
02992 else if (command == "FINISH_RECORDING")
02993 {
02994 enc->FinishRecording();
02995 retlist << "ok";
02996 }
02997 else if (command == "SET_LIVE_RECORDING")
02998 {
02999 int recording = slist[2].toInt();
03000 enc->SetLiveRecording(recording);
03001 retlist << "ok";
03002 }
03003 else if (command == "GET_FREE_INPUTS")
03004 {
03005 vector<uint> excluded_cardids;
03006 for (uint i = 2; i < slist.size(); i++)
03007 excluded_cardids.push_back(slist[i].toUInt());
03008
03009 vector<InputInfo> inputs = enc->GetFreeInputs(excluded_cardids);
03010
03011 if (inputs.empty())
03012 retlist << "EMPTY_LIST";
03013 else
03014 {
03015 for (uint i = 0; i < inputs.size(); i++)
03016 inputs[i].ToStringList(retlist);
03017 }
03018 }
03019 else if (command == "GET_INPUT")
03020 {
03021 QString ret = enc->GetInput();
03022 ret = (ret.isEmpty()) ? "UNKNOWN" : ret;
03023 retlist << ret;
03024 }
03025 else if (command == "SET_INPUT")
03026 {
03027 QString input = slist[2];
03028 QString ret = enc->SetInput(input);
03029 ret = (ret.isEmpty()) ? "UNKNOWN" : ret;
03030 retlist << ret;
03031 }
03032 else if (command == "TOGGLE_CHANNEL_FAVORITE")
03033 {
03034 enc->ToggleChannelFavorite();
03035 retlist << "ok";
03036 }
03037 else if (command == "CHANGE_CHANNEL")
03038 {
03039 int direction = slist[2].toInt();
03040 enc->ChangeChannel(direction);
03041 retlist << "ok";
03042 }
03043 else if (command == "SET_CHANNEL")
03044 {
03045 QString name = slist[2];
03046 enc->SetChannel(name);
03047 retlist << "ok";
03048 }
03049 else if (command == "SET_SIGNAL_MONITORING_RATE")
03050 {
03051 int rate = slist[2].toInt();
03052 int notifyFrontend = slist[3].toInt();
03053 int oldrate = enc->SetSignalMonitoringRate(rate, notifyFrontend);
03054 retlist << QString::number(oldrate);
03055 }
03056 else if (command == "GET_COLOUR")
03057 {
03058 int ret = enc->GetPictureAttribute(kPictureAttribute_Colour);
03059 retlist << QString::number(ret);
03060 }
03061 else if (command == "GET_CONTRAST")
03062 {
03063 int ret = enc->GetPictureAttribute(kPictureAttribute_Contrast);
03064 retlist << QString::number(ret);
03065 }
03066 else if (command == "GET_BRIGHTNESS")
03067 {
03068 int ret = enc->GetPictureAttribute(kPictureAttribute_Brightness);
03069 retlist << QString::number(ret);
03070 }
03071 else if (command == "GET_HUE")
03072 {
03073 int ret = enc->GetPictureAttribute(kPictureAttribute_Hue);
03074 retlist << QString::number(ret);
03075 }
03076 else if (command == "CHANGE_COLOUR")
03077 {
03078 int type = slist[2].toInt();
03079 bool up = slist[3].toInt();
03080 int ret = enc->ChangePictureAttribute(
03081 (PictureAdjustType) type, kPictureAttribute_Colour, up);
03082 retlist << QString::number(ret);
03083 }
03084 else if (command == "CHANGE_CONTRAST")
03085 {
03086 int type = slist[2].toInt();
03087 bool up = slist[3].toInt();
03088 int ret = enc->ChangePictureAttribute(
03089 (PictureAdjustType) type, kPictureAttribute_Contrast, up);
03090 retlist << QString::number(ret);
03091 }
03092 else if (command == "CHANGE_BRIGHTNESS")
03093 {
03094 int type= slist[2].toInt();
03095 bool up = slist[3].toInt();
03096 int ret = enc->ChangePictureAttribute(
03097 (PictureAdjustType) type, kPictureAttribute_Brightness, up);
03098 retlist << QString::number(ret);
03099 }
03100 else if (command == "CHANGE_HUE")
03101 {
03102 int type= slist[2].toInt();
03103 bool up = slist[3].toInt();
03104 int ret = enc->ChangePictureAttribute(
03105 (PictureAdjustType) type, kPictureAttribute_Hue, up);
03106 retlist << QString::number(ret);
03107 }
03108 else if (command == "CHECK_CHANNEL")
03109 {
03110 QString name = slist[2];
03111 retlist << QString::number((int)(enc->CheckChannel(name)));
03112 }
03113 else if (command == "SHOULD_SWITCH_CARD")
03114 {
03115 QString chanid = slist[2];
03116 retlist << QString::number((int)(enc->ShouldSwitchToAnotherCard(chanid)));
03117 }
03118 else if (command == "CHECK_CHANNEL_PREFIX")
03119 {
03120 QString needed_spacer = QString::null;
03121 QString prefix = slist[2];
03122 uint is_complete_valid_channel_on_rec = 0;
03123 bool is_extra_char_useful = false;
03124
03125 bool match = enc->CheckChannelPrefix(
03126 prefix, is_complete_valid_channel_on_rec,
03127 is_extra_char_useful, needed_spacer);
03128
03129 retlist << QString::number((int)match);
03130 retlist << QString::number(is_complete_valid_channel_on_rec);
03131 retlist << QString::number((int)is_extra_char_useful);
03132 retlist << ((needed_spacer.isEmpty()) ? QString("X") : needed_spacer);
03133 }
03134 else if (command == "GET_NEXT_PROGRAM_INFO")
03135 {
03136 QString channelname = slist[2];
03137 QString chanid = slist[3];
03138 int direction = slist[4].toInt();
03139 QString starttime = slist[5];
03140
03141 QString title = "", subtitle = "", desc = "", category = "";
03142 QString endtime = "", callsign = "", iconpath = "";
03143 QString seriesid = "", programid = "";
03144
03145 enc->GetNextProgram(direction,
03146 title, subtitle, desc, category, starttime,
03147 endtime, callsign, iconpath, channelname, chanid,
03148 seriesid, programid);
03149
03150 retlist << make_safe(title);
03151 retlist << make_safe(subtitle);
03152 retlist << make_safe(desc);
03153 retlist << make_safe(category);
03154 retlist << make_safe(starttime);
03155 retlist << make_safe(endtime);
03156 retlist << make_safe(callsign);
03157 retlist << make_safe(iconpath);
03158 retlist << make_safe(channelname);
03159 retlist << make_safe(chanid);
03160 retlist << make_safe(seriesid);
03161 retlist << make_safe(programid);
03162 }
03163 else if (command == "GET_CHANNEL_INFO")
03164 {
03165 uint chanid = slist[2].toUInt();
03166 uint sourceid = 0;
03167 QString callsign = "", channum = "", channame = "", xmltv = "";
03168
03169 enc->GetChannelInfo(chanid, sourceid,
03170 callsign, channum, channame, xmltv);
03171
03172 retlist << QString::number(chanid);
03173 retlist << QString::number(sourceid);
03174 retlist << make_safe(callsign);
03175 retlist << make_safe(channum);
03176 retlist << make_safe(channame);
03177 retlist << make_safe(xmltv);
03178 }
03179 else
03180 {
03181 VERBOSE(VB_IMPORTANT, QString("Unknown command: %1").arg(command));
03182 retlist << "ok";
03183 }
03184
03185 SendResponse(pbssock, retlist);
03186 }
03187
03188 void MainServer::HandleSetNextLiveTVDir(QStringList &commands,
03189 PlaybackSock *pbs)
03190 {
03191 MythSocket *pbssock = pbs->getSocket();
03192
03193 int recnum = commands[1].toInt();
03194
03195 QMap<int, EncoderLink *>::Iterator iter = encoderList->find(recnum);
03196 if (iter == encoderList->end())
03197 {
03198 VERBOSE(VB_IMPORTANT, "MainServer::HandleSetNextLiveTVDir() " +
03199 QString("Unknown encoder: %1").arg(recnum));
03200 QStringList retlist = "bad";
03201 SendResponse(pbssock, retlist);
03202 return;
03203 }
03204
03205 EncoderLink *enc = iter.data();
03206 enc->SetNextLiveTVDir(commands[2]);
03207
03208 QStringList retlist = "OK";
03209 SendResponse(pbssock, retlist);
03210 }
03211
03212 void MainServer::HandleSetChannelInfo(QStringList &slist, PlaybackSock *pbs)
03213 {
03214 bool ok = true;
03215 MythSocket *pbssock = pbs->getSocket();
03216 uint chanid = slist[1].toUInt();
03217 uint sourceid = slist[2].toUInt();
03218 QString oldcnum = cleanup(slist[3]);
03219 QString callsign = cleanup(slist[4]);
03220 QString channum = cleanup(slist[5]);
03221 QString channame = cleanup(slist[6]);
03222 QString xmltv = cleanup(slist[7]);
03223
03224 QStringList retlist;
03225 if (!chanid || !sourceid)
03226 {
03227 retlist << "0";
03228 SendResponse(pbssock, retlist);
03229 return;
03230 }
03231
03232 QMap<int, EncoderLink *>::iterator it = encoderList->begin();
03233 for (; it != encoderList->end(); ++it)
03234 {
03235 if (*it)
03236 {
03237 ok &= (*it)->SetChannelInfo(chanid, sourceid, oldcnum,
03238 callsign, channum, channame, xmltv);
03239 }
03240 }
03241
03242 retlist << ((ok) ? "1" : "0");
03243 SendResponse(pbssock, retlist);
03244 }
03245
03246 void MainServer::HandleRemoteEncoder(QStringList &slist, QStringList &commands,
03247 PlaybackSock *pbs)
03248 {
03249 MythSocket *pbssock = pbs->getSocket();
03250
03251 int recnum = commands[1].toInt();
03252 QStringList retlist;
03253
03254 QMap<int, EncoderLink *>::Iterator iter = encoderList->find(recnum);
03255 if (iter == encoderList->end())
03256 {
03257 VERBOSE(VB_IMPORTANT, "MainServer: " +
03258 QString("HandleRemoteEncoder(cmd %1) ").arg(slist[1]) +
03259 QString("Unknown encoder: %1").arg(recnum));
03260 retlist << QString::number((int) kState_Error);
03261 SendResponse(pbssock, retlist);
03262 return;
03263 }
03264
03265 EncoderLink *enc = iter.data();
03266
03267 QString command = slist[1];
03268
03269 if (command == "GET_STATE")
03270 {
03271 retlist << QString::number((int)enc->GetState());
03272 }
03273 else if (command == "GET_FLAGS")
03274 {
03275 retlist << QString::number(enc->GetFlags());
03276 }
03277 else if (command == "IS_BUSY")
03278 {
03279 int time_buffer = (slist.size() >= 3) ? slist[2].toInt() : 5;
03280 TunedInputInfo busy_input;
03281 retlist << QString::number((int)enc->IsBusy(&busy_input, time_buffer));
03282 busy_input.ToStringList(retlist);
03283 }
03284 else if (command == "MATCHES_RECORDING")
03285 {
03286 ProgramInfo *pginfo = new ProgramInfo();
03287 pginfo->FromStringList(slist, 2);
03288
03289 retlist << QString::number((int)enc->MatchesRecording(pginfo));
03290
03291 delete pginfo;
03292 }
03293 else if (command == "START_RECORDING")
03294 {
03295 ProgramInfo *pginfo = new ProgramInfo();
03296 pginfo->FromStringList(slist, 2);
03297
03298 retlist << QString::number(enc->StartRecording(pginfo));
03299
03300 delete pginfo;
03301 }
03302 else if (command == "RECORD_PENDING")
03303 {
03304 ProgramInfo *pginfo = new ProgramInfo();
03305 int secsleft = slist[2].toInt();
03306 int haslater = slist[3].toInt();
03307 pginfo->FromStringList(slist, 4);
03308
03309 enc->RecordPending(pginfo, secsleft, haslater);
03310
03311 retlist << "OK";
03312 delete pginfo;
03313 }
03314 else if (command == "CANCEL_NEXT_RECORDING")
03315 {
03316 bool cancel = (bool) slist[2].toInt();
03317 enc->CancelNextRecording(cancel);
03318 retlist << "OK";
03319 }
03320 else if (command == "STOP_RECORDING")
03321 {
03322 enc->StopRecording();
03323 retlist << "OK";
03324 }
03325 else if (command == "GET_MAX_BITRATE")
03326 {
03327 long long value = enc->GetMaxBitrate();
03328 encodeLongLong(retlist, value);
03329 }
03330 else if (command == "GET_CURRENT_RECORDING")
03331 {
03332 ProgramInfo *info = enc->GetRecording();
03333 info->ToStringList(retlist);
03334 delete info;
03335 }
03336 else if (command == "GET_FREE_INPUTS")
03337 {
03338 vector<uint> excluded_cardids;
03339 for (uint i = 2; i < slist.size(); i++)
03340 excluded_cardids.push_back(slist[i].toUInt());
03341
03342 vector<InputInfo> inputs = enc->GetFreeInputs(excluded_cardids);
03343
03344 if (inputs.empty())
03345 retlist << "EMPTY_LIST";
03346 else
03347 {
03348 for (uint i = 0; i < inputs.size(); i++)
03349 inputs[i].ToStringList(retlist);
03350 }
03351 }
03352
03353 SendResponse(pbssock, retlist);
03354 }
03355
03356 void MainServer::HandleIsActiveBackendQuery(QStringList &slist,
03357 PlaybackSock *pbs)
03358 {
03359 QStringList retlist;
03360 QString queryhostname = slist[1];
03361
03362 if (gContext->GetHostName() != queryhostname)
03363 {
03364 PlaybackSock *slave = getSlaveByHostname(queryhostname);
03365 if (slave != NULL)
03366 {
03367 retlist << "TRUE";
03368 slave->DownRef();
03369 }
03370 else
03371 retlist << "FALSE";
03372 }
03373 else
03374 retlist << "TRUE";
03375
03376 SendResponse(pbs->getSocket(), retlist);
03377 }
03378
03379
03380 void MainServer::HandleCutMapQuery(const QString &chanid,
03381 const QString &starttime,
03382 PlaybackSock *pbs, bool commbreak)
03383 {
03384 MythSocket *pbssock = NULL;
03385 if (pbs)
03386 pbssock = pbs->getSocket();
03387
03388 QMap<long long, int> markMap;
03389 QMap<long long, int>::Iterator it;
03390 QDateTime startdt;
03391 startdt.setTime_t((uint)atoi(starttime));
03392 QStringList retlist;
03393 int rowcnt = 0;
03394
03395 ProgramInfo *pginfo = ProgramInfo::GetProgramFromRecorded(chanid,
03396 startdt);
03397
03398 if (pginfo)
03399 {
03400 if (commbreak)
03401 pginfo->GetCommBreakList(markMap);
03402 else
03403 pginfo->GetCutList(markMap);
03404
03405 for (it = markMap.begin(); it != markMap.end(); ++it)
03406 {
03407 rowcnt++;
03408 QString intstr = QString("%1").arg(it.data());
03409 retlist << intstr;
03410 encodeLongLong(retlist,it.key());
03411 }
03412 }
03413
03414 if (rowcnt > 0)
03415 retlist.prepend(QString("%1").arg(rowcnt));
03416 else
03417 retlist << "-1";
03418
03419 if (pbssock)
03420 SendResponse(pbssock, retlist);
03421
03422 return;
03423 }
03424
03425 void MainServer::HandleCommBreakQuery(const QString &chanid,
03426 const QString &starttime,
03427 PlaybackSock *pbs)
03428 {
03429
03430
03431
03432
03433
03434
03435
03436
03437 return HandleCutMapQuery(chanid, starttime, pbs, true);
03438 }
03439
03440 void MainServer::HandleCutlistQuery(const QString &chanid,
03441 const QString &starttime,
03442 PlaybackSock *pbs)
03443 {
03444
03445
03446
03447
03448
03449
03450
03451
03452 return HandleCutMapQuery(chanid, starttime, pbs, false);
03453 }
03454
03455
03456 void MainServer::HandleBookmarkQuery(const QString &chanid,
03457 const QString &starttime,
03458 PlaybackSock *pbs)
03459
03460
03461
03462
03463
03464
03465 {
03466 MythSocket *pbssock = NULL;
03467 if (pbs)
03468 pbssock = pbs->getSocket();
03469
03470 QDateTime startdt;
03471 startdt.setTime_t((uint)atoi(starttime));
03472 QStringList retlist;
03473 long long bookmark = 0;
03474
03475 ProgramInfo *pginfo = ProgramInfo::GetProgramFromRecorded(chanid,
03476 startdt);
03477 if (pginfo)
03478 bookmark = pginfo->GetBookmark();
03479
03480 encodeLongLong(retlist,bookmark);
03481
03482 if (pbssock)
03483 SendResponse(pbssock, retlist);
03484
03485 return;
03486 }
03487
03488
03489 void MainServer::HandleSetBookmark(QStringList &tokens,
03490 PlaybackSock *pbs)
03491 {
03492
03493
03494
03495
03496
03497
03498
03499 MythSocket *pbssock = NULL;
03500 if (pbs)
03501 pbssock = pbs->getSocket();
03502
03503 QString chanid = tokens[1];
03504 QString starttime = tokens[2];
03505 QStringList bookmarklist;
03506 bookmarklist << tokens[3];
03507 bookmarklist << tokens[4];
03508
03509 QDateTime startdt;
03510 startdt.setTime_t((uint)atoi(starttime));
03511 QStringList retlist;
03512 long long bookmark = decodeLongLong(bookmarklist, 0);
03513
03514 ProgramInfo *pginfo = ProgramInfo::GetProgramFromRecorded(chanid,
03515 startdt);
03516 if (pginfo)
03517 {
03518 pginfo->SetBookmark(bookmark);
03519 retlist << "OK";
03520 }
03521 else
03522 retlist << "FAILED";
03523
03524 if (pbssock)
03525 SendResponse(pbssock, retlist);
03526
03527 return;
03528 }
03529
03530 void MainServer::HandleSettingQuery(QStringList &tokens, PlaybackSock *pbs)
03531 {
03532
03533
03534
03535 MythSocket *pbssock = NULL;
03536 if (pbs)
03537 pbssock = pbs->getSocket();
03538
03539 QString hostname = tokens[1];
03540 QString setting = tokens[2];
03541 QStringList retlist;
03542
03543 QString retvalue = gContext->GetSettingOnHost(setting, hostname, "-1");
03544
03545 retlist << retvalue;
03546 if (pbssock)
03547 SendResponse(pbssock, retlist);
03548
03549 return;
03550 }
03551
03552 void MainServer::HandleSetSetting(QStringList &tokens,
03553 PlaybackSock *pbs)
03554 {
03555
03556 MythSocket *pbssock = NULL;
03557 if (pbs)
03558 pbssock = pbs->getSocket();
03559
03560 QString hostname = tokens[1];
03561 QString setting = tokens[2];
03562 QString svalue = tokens[3];
03563 QStringList retlist;
03564
03565 if (gContext->SaveSettingOnHost(setting, svalue, hostname))
03566 retlist << "OK";
03567 else
03568 retlist << "ERROR";
03569
03570 if (pbssock)
03571 SendResponse(pbssock, retlist);
03572
03573 return;
03574 }
03575
03576 void MainServer::HandleFileTransferQuery(QStringList &slist,
03577 QStringList &commands,
03578 PlaybackSock *pbs)
03579 {
03580 MythSocket *pbssock = pbs->getSocket();
03581
03582 int recnum = commands[1].toInt();
03583
03584 QStringList retlist;
03585 FileTransfer *ft = getFileTransferByID(recnum);
03586 if (!ft)
03587 {
03588 VERBOSE(VB_IMPORTANT, QString("Unknown file transfer socket: %1")
03589 .arg(recnum));
03590 retlist << QString("ERROR: Unknown file transfer socket: %1")
03591 .arg(recnum);
03592 SendResponse(pbssock, retlist);
03593 return;
03594 }
03595
03596 QString command = slist[1];
03597
03598 ft->UpRef();
03599
03600 if (command == "IS_OPEN")
03601 {
03602 bool isopen = ft->isOpen();
03603
03604 retlist << QString::number(isopen);
03605 }
03606 else if (command == "DONE")
03607 {
03608 ft->Stop();
03609 retlist << "ok";
03610 }
03611 else if (command == "REQUEST_BLOCK")
03612 {
03613 int size = slist[2].toInt();
03614
03615 retlist << QString::number(ft->RequestBlock(size));
03616 }
03617 else if (command == "SEEK")
03618 {
03619 long long pos = decodeLongLong(slist, 2);
03620 int whence = slist[4].toInt();
03621 long long curpos = decodeLongLong(slist, 5);
03622
03623 long long ret = ft->Seek(curpos, pos, whence);
03624 encodeLongLong(retlist, ret);
03625 }
03626 else if (command == "SET_TIMEOUT")
03627 {
03628 bool fast = slist[2].toInt();
03629 ft->SetTimeout(fast);
03630 retlist << "ok";
03631 }
03632 else
03633 {
03634 VERBOSE(VB_IMPORTANT, QString("Unknown command: %1").arg(command));
03635 retlist << "ok";
03636 }
03637
03638 ft->DownRef();
03639
03640 SendResponse(pbssock, retlist);
03641 }
03642
03643 void MainServer::HandleGetRecorderNum(QStringList &slist, PlaybackSock *pbs)
03644 {
03645 MythSocket *pbssock = pbs->getSocket();
03646
03647 int retval = -1;
03648
03649 ProgramInfo *pginfo = new ProgramInfo();
03650 pginfo->FromStringList(slist, 1);
03651
03652 EncoderLink *encoder = NULL;
03653
03654 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
03655 for (; iter != encoderList->end(); ++iter)
03656 {
03657 EncoderLink *elink = iter.data();
03658
03659 if (elink->IsConnected() && elink->MatchesRecording(pginfo))
03660 {
03661 retval = iter.key();
03662 encoder = elink;
03663 }
03664 }
03665
03666 QStringList strlist = QString::number(retval);
03667
03668 if (encoder)
03669 {
03670 if (encoder->IsLocal())
03671 {
03672 strlist << gContext->GetSetting("BackendServerIP");
03673 strlist << gContext->GetSetting("BackendServerPort");
03674 }
03675 else
03676 {
03677 strlist << gContext->GetSettingOnHost("BackendServerIP",
03678 encoder->GetHostName(),
03679 "nohostname");
03680 strlist << gContext->GetSettingOnHost("BackendServerPort",
03681 encoder->GetHostName(), "-1");
03682 }
03683 }
03684 else
03685 {
03686 strlist << "nohost";
03687 strlist << "-1";
03688 }
03689
03690 SendResponse(pbssock, strlist);
03691 delete pginfo;
03692 }
03693
03694 void MainServer::HandleGetRecorderFromNum(QStringList &slist,
03695 PlaybackSock *pbs)
03696 {
03697 MythSocket *pbssock = pbs->getSocket();
03698
03699 int recordernum = slist[1].toInt();
03700 EncoderLink *encoder = NULL;
03701 QStringList strlist;
03702
03703 QMap<int, EncoderLink *>::Iterator iter = encoderList->find(recordernum);
03704
03705 if (iter != encoderList->end())
03706 encoder = iter.data();
03707
03708 if (encoder && encoder->IsConnected())
03709 {
03710 if (encoder->IsLocal())
03711 {
03712 strlist << gContext->GetSetting("BackendServerIP");
03713 strlist << gContext->GetSetting("BackendServerPort");
03714 }
03715 else
03716 {
03717 strlist << gContext->GetSettingOnHost("BackendServerIP",
03718 encoder->GetHostName(),
03719 "nohostname");
03720 strlist << gContext->GetSettingOnHost("BackendServerPort",
03721 encoder->GetHostName(), "-1");
03722 }
03723 }
03724 else
03725 {
03726 strlist << "nohost";
03727 strlist << "-1";
03728 }
03729
03730 SendResponse(pbssock, strlist);
03731 }
03732
03733 void MainServer::HandleMessage(QStringList &slist, PlaybackSock *pbs)
03734 {
03735 MythSocket *pbssock = pbs->getSocket();
03736
03737 QString message = slist[1];
03738
03739 MythEvent me(message);
03740 gContext->dispatch(me);
03741
03742 QStringList retlist = "OK";
03743
03744 SendResponse(pbssock, retlist);
03745 }
03746
03747 void MainServer::HandleIsRecording(QStringList &slist, PlaybackSock *pbs)
03748 {
03749 (void)slist;
03750
03751 MythSocket *pbssock = pbs->getSocket();
03752 int RecordingsInProgress = 0;
03753 int LiveTVRecordingsInProgress = 0;
03754 QStringList retlist;
03755
03756 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
03757 for (; iter != encoderList->end(); ++iter)
03758 {
03759 EncoderLink *elink = iter.data();
03760 if (elink->IsBusyRecording()) {
03761 RecordingsInProgress++;
03762
03763 ProgramInfo *info = elink->GetRecording();
03764 if (info && info->recgroup == "LiveTV")
03765 LiveTVRecordingsInProgress++;
03766
03767 delete info;
03768 }
03769 }
03770
03771 retlist << QString::number(RecordingsInProgress);
03772 retlist << QString::number(LiveTVRecordingsInProgress);
03773
03774 SendResponse(pbssock, retlist);
03775 }
03776
03777 void MainServer::HandleGenPreviewPixmap(QStringList &slist, PlaybackSock *pbs)
03778 {
03779 MythSocket *pbssock = pbs->getSocket();
03780
03781 bool time_fmt_sec = true;
03782 long long time = -1;
03783 QString outputfile = QString::null;
03784 int width = -1;
03785 int height = -1;
03786 bool has_extra_data = false;
03787
03788 QStringList::const_iterator it = slist.at(1);
03789 QStringList::const_iterator end = slist.end();
03790 ProgramInfo *pginfo = new ProgramInfo();
03791 bool ok = pginfo->FromStringList(it, end);
03792 if (!ok)
03793 {
03794 VERBOSE(VB_IMPORTANT, "MainServer: Failed to parse pixmap request.");
03795 QStringList outputlist = "BAD";
03796 outputlist += "ERROR_INVALID_REQUEST";
03797 SendResponse(pbssock, outputlist);
03798 }
03799 if (it != slist.end())
03800 (time_fmt_sec = ((*it).lower() == "s")), it++;
03801 if (it != slist.end())
03802 time = decodeLongLong(slist, it);
03803 if (it != slist.end())
03804 (outputfile = *it), it++;
03805 outputfile = (outputfile == "<EMPTY>") ? QString::null : outputfile;
03806 if (it != slist.end())
03807 {
03808 width = (*it).toInt(&ok); it++;
03809 width = (ok) ? width : -1;
03810 }
03811 if (it != slist.end())
03812 {
03813 height = (*it).toInt(&ok); it++;
03814 height = (ok) ? height : -1;
03815 has_extra_data = true;
03816 }
03817 QSize outputsize = QSize(width, height);
03818
03819 if (has_extra_data)
03820 {
03821 VERBOSE(VB_PLAYBACK, "HandleGenPreviewPixmap got extra data\n\t\t\t"
03822 << QString("%1%2 %3x%4 '%5'")
03823 .arg(time).arg(time_fmt_sec?"s":"f")
03824 .arg(width).arg(height).arg(outputfile));
03825 }
03826
03827 pginfo->pathname = GetPlaybackURL(pginfo);
03828
03829 if ((ismaster) &&
03830 (pginfo->hostname != gContext->GetHostName()) &&
03831 ((!masterBackendOverride) ||
03832 (pginfo->pathname.left(1) != "/")))
03833 {
03834 PlaybackSock *slave = getSlaveByHostname(pginfo->hostname);
03835
03836 if (slave)
03837 {
03838 QStringList outputlist = "OK";
03839 if (has_extra_data)
03840 {
03841 outputlist = slave->GenPreviewPixmap(
03842 pginfo, time_fmt_sec, time, outputfile, outputsize);
03843 }
03844 else
03845 {
03846 outputlist = slave->GenPreviewPixmap(pginfo);
03847 }
03848
03849 slave->DownRef();
03850 SendResponse(pbssock, outputlist);
03851 delete pginfo;
03852 return;
03853 }
03854 VERBOSE(VB_IMPORTANT, "MainServer::HandleGenPreviewPixmap()"
03855 "\n\t\t\tCouldn't find backend for: " +
03856 QString("\n\t\t\t%1 : \"%2\"")
03857 .arg(pginfo->title).arg(pginfo->subtitle));
03858 }
03859
03860 if (pginfo->pathname.left(1) != "/")
03861 {
03862 VERBOSE(VB_IMPORTANT, "MainServer: HandleGenPreviewPixmap: Unable to "
03863 "find file locally, unable to make preview image.");
03864 QStringList outputlist = "BAD";
03865 outputlist += "ERROR_NOFILE";
03866 SendResponse(pbssock, outputlist);
03867 delete pginfo;
03868 return;
03869 }
03870
03871 PreviewGenerator *previewgen = new PreviewGenerator(pginfo);
03872 if (has_extra_data)
03873 {
03874 previewgen->SetOutputSize(outputsize);
03875 previewgen->SetOutputFilename(outputfile);
03876 previewgen->SetPreviewTime(time, time_fmt_sec);
03877 }
03878 ok = previewgen->Run();
03879 previewgen->deleteLater();
03880
03881 if (ok)
03882 {
03883 QStringList outputlist = "OK";
03884 if (!outputfile.isEmpty())
03885 outputlist += outputfile;
03886 SendResponse(pbssock, outputlist);
03887 }
03888 else
03889 {
03890 VERBOSE(VB_IMPORTANT, "MainServer: Failed to make preview image.");
03891 QStringList outputlist = "BAD";
03892 outputlist += "ERROR_UNKNOWN";
03893 SendResponse(pbssock, outputlist);
03894 }
03895
03896 delete pginfo;
03897 }
03898
03899 void MainServer::HandlePixmapLastModified(QStringList &slist, PlaybackSock *pbs)
03900 {
03901 MythSocket *pbssock = pbs->getSocket();
03902
03903 ProgramInfo *pginfo = new ProgramInfo();
03904 pginfo->FromStringList(slist, 1);
03905 pginfo->pathname = GetPlaybackURL(pginfo);
03906
03907 QDateTime lastmodified;
03908 QStringList strlist;
03909
03910 if ((ismaster) &&
03911 (pginfo->hostname != gContext->GetHostName()) &&
03912 ((!masterBackendOverride) ||
03913 (pginfo->pathname.left(1) != "/")))
03914 {
03915 PlaybackSock *slave = getSlaveByHostname(pginfo->hostname);
03916
03917 if (slave)
03918 {
03919 QDateTime slavetime = slave->PixmapLastModified(pginfo);
03920 slave->DownRef();
03921
03922 strlist = (slavetime.isValid()) ?
03923 QString::number(slavetime.toTime_t()) : "BAD";
03924
03925 SendResponse(pbssock, strlist);
03926 delete pginfo;
03927 return;
03928 }
03929 else
03930 {
03931 VERBOSE(VB_IMPORTANT, QString("MainServer::HandlePixmapLastModified()"
03932 " - Couldn't find backend for: %1 : \"%2\"").arg(pginfo->title)
03933 .arg(pginfo->subtitle));
03934 }
03935 }
03936
03937 if (pginfo->pathname.left(1) != "/")
03938 {
03939 VERBOSE(VB_IMPORTANT, "MainServer: HandlePixmapLastModified: Unable to "
03940 "find file locally, unable to get last modified date.");
03941 QStringList outputlist = "BAD";
03942 SendResponse(pbssock, outputlist);
03943 delete pginfo;
03944 return;
03945 }
03946
03947 QString filename = pginfo->pathname + ".png";
03948
03949 QFileInfo finfo(filename);
03950
03951 if (finfo.exists())
03952 {
03953 lastmodified = finfo.lastModified();
03954 strlist = QString::number(lastmodified.toTime_t());
03955 }
03956 else
03957 strlist = "BAD";
03958
03959 SendResponse(pbssock, strlist);
03960 delete pginfo;
03961 }
03962
03963 void MainServer::HandleBackendRefresh(MythSocket *socket)
03964 {
03965 gContext->RefreshBackendConfig();
03966
03967 QStringList retlist = "OK";
03968 SendResponse(socket, retlist);
03969 }
03970
03971 void MainServer::HandleBlockShutdown(bool blockShutdown, PlaybackSock *pbs)
03972 {
03973 pbs->setBlockShutdown(blockShutdown);
03974
03975 MythSocket *socket = pbs->getSocket();
03976 QStringList retlist = "OK";
03977 SendResponse(socket, retlist);
03978 }
03979
03980 void MainServer::deferredDeleteSlot(void)
03981 {
03982 QMutexLocker lock(&deferredDeleteLock);
03983
03984 if (deferredDeleteList.size() == 0)
03985 return;
03986
03987 DeferredDeleteStruct dds = deferredDeleteList.front();
03988 while (dds.ts.secsTo(QDateTime::currentDateTime()) > 30)
03989 {
03990 delete dds.sock;
03991 deferredDeleteList.pop_front();
03992 if (deferredDeleteList.size() == 0)
03993 return;
03994 dds = deferredDeleteList.front();
03995 }
03996 }
03997
03998 void MainServer::DeletePBS(PlaybackSock *sock)
03999 {
04000 DeferredDeleteStruct dds;
04001 dds.sock = sock;
04002 dds.ts = QDateTime::currentDateTime();
04003
04004 QMutexLocker lock(&deferredDeleteLock);
04005 deferredDeleteList.push_back(dds);
04006 }
04007
04008 void MainServer::connectionClosed(MythSocket *socket)
04009 {
04010 sockListLock.lock();
04011
04012 vector<PlaybackSock *>::iterator it = playbackList.begin();
04013 for (; it != playbackList.end(); ++it)
04014 {
04015 PlaybackSock *pbs = (*it);
04016 MythSocket *sock = pbs->getSocket();
04017 if (sock == socket && pbs == masterServer)
04018 {
04019 playbackList.erase(it);
04020 sockListLock.unlock();
04021 masterServer->DownRef();
04022 masterServer = NULL;
04023 masterServerReconnect->start(1000, true);
04024 return;
04025 }
04026 else if (sock == socket)
04027 {
04028 if (ismaster && pbs->isSlaveBackend())
04029 {
04030 VERBOSE(VB_IMPORTANT,QString("Slave backend: %1 no longer connected")
04031 .arg(pbs->getHostname()));
04032
04033 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
04034 for (; iter != encoderList->end(); ++iter)
04035 {
04036 EncoderLink *elink = iter.data();
04037 if (elink->GetSocket() == pbs)
04038 {
04039 elink->SetSocket(NULL);
04040 if (m_sched)
04041 m_sched->SlaveDisconnected(elink->GetCardID());
04042 }
04043 }
04044 if (m_sched)
04045 m_sched->Reschedule(0);
04046
04047 QString message = QString("LOCAL_SLAVE_BACKEND_OFFLINE %2")
04048 .arg(pbs->getHostname());
04049 MythEvent me(message);
04050 gContext->dispatch(me);
04051 }
04052
04053 LiveTVChain *chain;
04054 if ((chain = GetExistingChain(sock)))
04055 {
04056 chain->DelHostSocket(sock);
04057 if (chain->HostSocketCount() == 0)
04058 {
04059 QMap<int, EncoderLink *>::iterator i = encoderList->begin();
04060 for (; i != encoderList->end(); ++i)
04061 {
04062 EncoderLink *enc = i.data();
04063 if (enc->IsLocal())
04064 {
04065 while (enc->GetState() == kState_ChangingState)
04066 usleep(500);
04067
04068 if (enc->IsBusy() &&
04069 enc->GetChainID() == chain->GetID())
04070 {
04071 enc->StopLiveTV();
04072 }
04073 }
04074 }
04075 DeleteChain(chain);
04076 }
04077 }
04078
04079 pbs->SetDisconnected();
04080 playbackList.erase(it);
04081 sockListLock.unlock();
04082
04083 PlaybackSock *testsock = getPlaybackBySock(socket);
04084 if (testsock)
04085 VERBOSE(VB_IMPORTANT, "Playback sock still exists?");
04086
04087 pbs->DownRef();
04088 return;
04089 }
04090 }
04091
04092 vector<FileTransfer *>::iterator ft = fileTransferList.begin();
04093 for (; ft != fileTransferList.end(); ++ft)
04094 {
04095 MythSocket *sock = (*ft)->getSocket();
04096 if (sock == socket)
04097 {
04098 (*ft)->DownRef();
04099 fileTransferList.erase(ft);
04100 sockListLock.unlock();
04101 return;
04102 }
04103 }
04104
04105 sockListLock.unlock();
04106
04107 VERBOSE(VB_IMPORTANT, "Unknown socket closing");
04108 }
04109
04110 PlaybackSock *MainServer::getSlaveByHostname(QString &hostname)
04111 {
04112 if (!ismaster)
04113 return NULL;
04114
04115 sockListLock.lock();
04116
04117 vector<PlaybackSock *>::iterator iter = playbackList.begin();
04118 for (; iter != playbackList.end(); iter++)
04119 {
04120 PlaybackSock *pbs = (*iter);
04121 if (pbs->isSlaveBackend() &&
04122 ((pbs->getHostname() == hostname) || (pbs->getIP() == hostname)))
04123 {
04124 sockListLock.unlock();
04125 pbs->UpRef();
04126 return pbs;
04127 }
04128 }
04129
04130 sockListLock.unlock();
04131
04132 return NULL;
04133 }
04134
04135 PlaybackSock *MainServer::getPlaybackBySock(MythSocket *sock)
04136 {
04137 PlaybackSock *retval = NULL;
04138
04139 sockListLock.lock();
04140
04141 vector<PlaybackSock *>::iterator it = playbackList.begin();
04142 for (; it != playbackList.end(); ++it)
04143 {
04144 if (sock == (*it)->getSocket())
04145 {
04146 retval = (*it);
04147 break;
04148 }
04149 }
04150
04151 sockListLock.unlock();
04152
04153 return retval;
04154 }
04155
04156 FileTransfer *MainServer::getFileTransferByID(int id)
04157 {
04158 FileTransfer *retval = NULL;
04159
04160 sockListLock.lock();
04161
04162 vector<FileTransfer *>::iterator it = fileTransferList.begin();
04163 for (; it != fileTransferList.end(); ++it)
04164 {
04165 if (id == (*it)->getSocket()->socket())
04166 {
04167 retval = (*it);
04168 break;
04169 }
04170 }
04171
04172 sockListLock.unlock();
04173
04174 return retval;
04175 }
04176
04177 FileTransfer *MainServer::getFileTransferBySock(MythSocket *sock)
04178 {
04179 FileTransfer *retval = NULL;
04180
04181 sockListLock.lock();
04182
04183 vector<FileTransfer *>::iterator it = fileTransferList.begin();
04184 for (; it != fileTransferList.end(); ++it)
04185 {
04186 if (sock == (*it)->getSocket())
04187 {
04188 retval = (*it);
04189 break;
04190 }
04191 }
04192
04193 sockListLock.unlock();
04194
04195 return retval;
04196 }
04197
04198 LiveTVChain *MainServer::GetExistingChain(QString id)
04199 {
04200 QMutexLocker lock(&liveTVChainsLock);
04201
04202 LiveTVChain *chain;
04203
04204 QPtrListIterator<LiveTVChain> it(liveTVChains);
04205 while ((chain = it.current()) != 0)
04206 {
04207 ++it;
04208 if (chain->GetID() == id)
04209 return chain;
04210 }
04211
04212 return NULL;
04213 }
04214
04215 LiveTVChain *MainServer::GetExistingChain(MythSocket *sock)
04216 {
04217 QMutexLocker lock(&liveTVChainsLock);
04218
04219 LiveTVChain *chain;
04220
04221 QPtrListIterator<LiveTVChain> it(liveTVChains);
04222 while ((chain = it.current()) != 0)
04223 {
04224 ++it;
04225 if (chain->IsHostSocket(sock))
04226 return chain;
04227 }
04228
04229 return NULL;
04230 }
04231
04232 LiveTVChain *MainServer::GetChainWithRecording(ProgramInfo *pginfo)
04233 {
04234 QMutexLocker lock(&liveTVChainsLock);
04235
04236 LiveTVChain *chain;
04237
04238 QPtrListIterator<LiveTVChain> it(liveTVChains);
04239 while ((chain = it.current()) != 0)
04240 {
04241 ++it;
04242 if (chain->ProgramIsAt(pginfo) >= 0)
04243 return chain;
04244 }
04245
04246 return NULL;
04247 }
04248
04249 void MainServer::AddToChains(LiveTVChain *chain)
04250 {
04251 liveTVChains.append(chain);
04252 }
04253
04254 void MainServer::DeleteChain(LiveTVChain *chain)
04255 {
04256 QMutexLocker lock(&liveTVChainsLock);
04257
04258 while (liveTVChains.removeRef(chain))
04259 ;
04260
04261 delete chain;
04262 }
04263
04264 QString MainServer::LocalFilePath(QUrl &url)
04265 {
04266 QString lpath = url.path();
04267
04268 if (lpath.section('/', -2, -2) == "channels")
04269 {
04270
04271 QString querytext;
04272
04273 QString file = lpath.section('/', -1);
04274 lpath = "";
04275
04276 MSqlQuery query(MSqlQuery::InitCon());
04277 query.prepare("SELECT icon FROM channel WHERE icon LIKE :FILENAME ;");
04278 query.bindValue(":FILENAME", QString("%") + file);
04279
04280 if (query.exec() && query.isActive() && query.size())
04281 {
04282 query.next();
04283 lpath = query.value(0).toString();
04284 }
04285 else
04286 {
04287 MythContext::DBError("Icon path", query);
04288 }
04289 }
04290 else
04291 {
04292 lpath = lpath.section('/', -1);
04293
04294 QString fpath = lpath;
04295 if (fpath.right(4) == ".png")
04296 fpath = fpath.left(fpath.length() - 4);
04297
04298 ProgramInfo *pginfo = ProgramInfo::GetProgramFromBasename(fpath);
04299 if (pginfo)
04300 {
04301 QString pburl = GetPlaybackURL(pginfo);
04302 if (pburl.left(1) == "/")
04303 {
04304 lpath = pburl.section('/', 0, -2) + "/" + lpath;
04305 VERBOSE(VB_FILE, QString("Local file path: %1").arg(lpath));
04306 }
04307 else
04308 {
04309 VERBOSE(VB_IMPORTANT,
04310 QString("ERROR: LocalFilePath unable to find local "
04311 "path for '%1', found '%2' instead.")
04312 .arg(lpath).arg(pburl));
04313 lpath = "";
04314 }
04315
04316 delete pginfo;
04317 }
04318 else if (!lpath.isEmpty())
04319 {
04320
04321 QString opath = lpath;
04322 lpath = QFileInfo(lpath).fileName();
04323 StorageGroup sgroup;
04324 QString tmpFile = sgroup.FindRecordingFile(lpath);
04325 if (!tmpFile.isEmpty())
04326 {
04327 lpath = tmpFile;
04328 VERBOSE(VB_FILE,
04329 QString("LocalFilePath(%1 '%2')").arg(url).arg(opath)
04330 <<", found file through exhaustive search "
04331 <<QString("at '%1'").arg(lpath));
04332 }
04333 else
04334 {
04335 VERBOSE(VB_IMPORTANT, "ERROR: LocalFilePath "
04336 <<QString("unable to find local path for '%1'.")
04337 .arg(opath));
04338 lpath = "";
04339 }
04340 }
04341 else
04342 {
04343 lpath = "";
04344 }
04345 }
04346
04347 return QDeepCopy<QString>(lpath);
04348 }
04349
04350 void MainServer::reconnectTimeout(void)
04351 {
04352 MythSocket *masterServerSock = new MythSocket();
04353
04354 QString server = gContext->GetSetting("MasterServerIP", "127.0.0.1");
04355 int port = gContext->GetNumSetting("MasterServerPort", 6543);
04356
04357 VERBOSE(VB_IMPORTANT, QString("Connecting to master server: %1:%2")
04358 .arg(server).arg(port));
04359
04360 if (!masterServerSock->connect(server, port))
04361 {
04362 VERBOSE(VB_IMPORTANT, "Connection to master server timed out.");
04363 masterServerReconnect->start(1000, true);
04364 masterServerSock->DownRef();
04365 return;
04366 }
04367
04368 if (masterServerSock->state() != MythSocket::Connected)
04369 {
04370 VERBOSE(VB_IMPORTANT, "Could not connect to master server.");
04371 masterServerReconnect->start(1000, true);
04372 masterServerSock->DownRef();
04373 return;
04374 }
04375
04376 VERBOSE(VB_IMPORTANT, "Connected successfully");
04377
04378 QString str = QString("ANN SlaveBackend %1 %2")
04379 .arg(gContext->GetHostName())
04380 .arg(gContext->GetSetting("BackendServerIP"));
04381
04382 masterServerSock->Lock();
04383
04384 QStringList strlist = str;
04385
04386 QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
04387 for (; iter != encoderList->end(); ++iter)
04388 {
04389 EncoderLink *elink = iter.data();
04390 elink->CancelNextRecording(true);
04391 ProgramInfo *pinfo = elink->GetRecording();
04392 pinfo->ToStringList(strlist);
04393 delete pinfo;
04394 }
04395
04396 masterServerSock->writeStringList(strlist);
04397 masterServerSock->readStringList(strlist);
04398 masterServerSock->setCallbacks(this);
04399
04400 masterServer = new PlaybackSock(this, masterServerSock, server, true);
04401 sockListLock.lock();
04402 playbackList.push_back(masterServer);
04403 sockListLock.unlock();
04404
04405 masterServerSock->Unlock();
04406
04407 autoexpireUpdateTimer->start(1000, true);
04408 }
04409
04410
04411
04412 bool MainServer::isClientConnected()
04413 {
04414 bool foundClient = false;
04415
04416 sockListLock.lock();
04417
04418 foundClient |= (fileTransferList.size() > 0);
04419
04420 if ((playbackList.size() > 0) && !foundClient)
04421 {
04422 vector<PlaybackSock *>::iterator it = playbackList.begin();
04423 for (; !foundClient && (it != playbackList.end()); ++it)
04424 {
04425
04426
04427 if (!(*it)->isSlaveBackend() && (*it)->getBlockShutdown())
04428 foundClient = true;
04429 }
04430 }
04431
04432 sockListLock.unlock();
04433
04434 return (foundClient);
04435 }
04436
04437
04438 void MainServer::ShutSlaveBackendsDown(QString &haltcmd)
04439 {
04440 QStringList bcast = "SHUTDOWN_NOW";
04441 bcast << haltcmd;
04442
04443 sockListLock.lock();
04444
04445 if (playbackList.size() > 0)
04446 {
04447 vector<PlaybackSock *>::iterator it = playbackList.begin();
04448 for (; it != playbackList.end(); ++it)
04449 {
04450 if ((*it)->isSlaveBackend())
04451 (*it)->getSocket()->writeStringList(bcast);
04452 }
04453 }
04454
04455 sockListLock.unlock();
04456 }
04457
04458