|
MythTV
0.26-pre
|
00001 #include <unistd.h> 00002 00003 #include <QFileInfo> 00004 #include <QFile> 00005 #include <QDir> 00006 #include <QList> 00007 00008 #include "compat.h" 00009 #include "remoteutil.h" 00010 #include "programinfo.h" 00011 #include "mythcorecontext.h" 00012 #include "storagegroup.h" 00013 #include "mythevent.h" 00014 #include "mythsocket.h" 00015 00016 vector<ProgramInfo *> *RemoteGetRecordedList(int sort) 00017 { 00018 QString str = "QUERY_RECORDINGS "; 00019 if (sort < 0) 00020 str += "Descending"; 00021 else if (sort > 0) 00022 str += "Ascending"; 00023 else 00024 str += "Unsorted"; 00025 00026 QStringList strlist(str); 00027 00028 vector<ProgramInfo *> *info = new vector<ProgramInfo *>; 00029 00030 if (!RemoteGetRecordingList(*info, strlist)) 00031 { 00032 delete info; 00033 return NULL; 00034 } 00035 00036 return info; 00037 } 00038 00039 bool RemoteGetLoad(float load[3]) 00040 { 00041 QStringList strlist(QString("QUERY_LOAD")); 00042 00043 if (gCoreContext->SendReceiveStringList(strlist)) 00044 { 00045 load[0] = strlist[0].toFloat(); 00046 load[1] = strlist[1].toFloat(); 00047 load[2] = strlist[2].toFloat(); 00048 return true; 00049 } 00050 00051 return false; 00052 } 00053 00054 bool RemoteGetUptime(time_t &uptime) 00055 { 00056 QStringList strlist(QString("QUERY_UPTIME")); 00057 00058 if (!gCoreContext->SendReceiveStringList(strlist)) 00059 return false; 00060 00061 if (!strlist[0].at(0).isNumber()) 00062 return false; 00063 00064 if (sizeof(time_t) == sizeof(int)) 00065 uptime = strlist[0].toUInt(); 00066 else if (sizeof(time_t) == sizeof(long)) 00067 uptime = strlist[0].toULong(); 00068 else if (sizeof(time_t) == sizeof(long long)) 00069 uptime = strlist[0].toULongLong(); 00070 00071 return true; 00072 } 00073 00074 bool RemoteGetMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM) 00075 { 00076 QStringList strlist(QString("QUERY_MEMSTATS")); 00077 00078 if (gCoreContext->SendReceiveStringList(strlist)) 00079 { 00080 totalMB = strlist[0].toInt(); 00081 freeMB = strlist[1].toInt(); 00082 totalVM = strlist[2].toInt(); 00083 freeVM = strlist[3].toInt(); 00084 return true; 00085 } 00086 00087 return false; 00088 } 00089 00090 bool RemoteCheckFile(const ProgramInfo *pginfo, bool checkSlaves) 00091 { 00092 QStringList strlist("QUERY_CHECKFILE"); 00093 strlist << QString::number((int)checkSlaves); 00094 pginfo->ToStringList(strlist); 00095 00096 if ((!gCoreContext->SendReceiveStringList(strlist)) || 00097 (!strlist[0].toInt())) 00098 return false; 00099 00100 // Only modify the pathname if the recording file is available locally on 00101 // this host 00102 QString localpath = strlist[1]; 00103 QFile checkFile(localpath); 00104 if (checkFile.exists()) 00105 pginfo->SetPathname(localpath); 00106 00107 return true; 00108 } 00109 00110 bool RemoteDeleteRecording( 00111 uint chanid, const QDateTime &recstartts, bool forceMetadataDelete, 00112 bool forgetHistory) 00113 { 00114 bool result = true; 00115 QString cmd = 00116 QString("DELETE_RECORDING %1 %2 %3 %4") 00117 .arg(chanid) 00118 .arg(recstartts.toString(Qt::ISODate)) 00119 .arg(forceMetadataDelete ? "FORCE" : "NO_FORCE") 00120 .arg(forgetHistory ? "FORGET" : "NO_FORGET"); 00121 QStringList strlist(cmd); 00122 00123 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.empty()) 00124 result = false; 00125 else if (strlist[0].toInt() == -2) 00126 result = false; 00127 00128 if (!result) 00129 { 00130 LOG(VB_GENERAL, LOG_ALERT, 00131 QString("Failed to delete recording %1:%2") 00132 .arg(chanid).arg(recstartts.toString(Qt::ISODate))); 00133 } 00134 00135 return result; 00136 } 00137 00138 bool RemoteUndeleteRecording(uint chanid, const QDateTime &recstartts) 00139 { 00140 bool result = false; 00141 00142 #if 0 00143 if (!gCoreContext->GetNumSetting("AutoExpireInsteadOfDelete", 0)) 00144 return result; 00145 #endif 00146 00147 QStringList strlist(QString("UNDELETE_RECORDING")); 00148 strlist.push_back(QString::number(chanid)); 00149 strlist.push_back(recstartts.toString(Qt::ISODate)); 00150 00151 gCoreContext->SendReceiveStringList(strlist); 00152 00153 if (strlist[0].toInt() == 0) 00154 result = true; 00155 00156 return result; 00157 } 00158 00159 void RemoteGetAllScheduledRecordings(vector<ProgramInfo *> &scheduledlist) 00160 { 00161 QStringList strList(QString("QUERY_GETALLSCHEDULED")); 00162 RemoteGetRecordingList(scheduledlist, strList); 00163 } 00164 00165 void RemoteGetAllExpiringRecordings(vector<ProgramInfo *> &expiringlist) 00166 { 00167 QStringList strList(QString("QUERY_GETEXPIRING")); 00168 RemoteGetRecordingList(expiringlist, strList); 00169 } 00170 00171 uint RemoteGetRecordingList( 00172 vector<ProgramInfo *> &reclist, QStringList &strList) 00173 { 00174 if (!gCoreContext->SendReceiveStringList(strList)) 00175 return 0; 00176 00177 int numrecordings = strList[0].toInt(); 00178 if (numrecordings <= 0) 00179 return 0; 00180 00181 if (numrecordings * NUMPROGRAMLINES + 1 > (int)strList.size()) 00182 { 00183 LOG(VB_GENERAL, LOG_ERR, 00184 "RemoteGetRecordingList() list size appears to be incorrect."); 00185 return 0; 00186 } 00187 00188 uint reclist_initial_size = (uint) reclist.size(); 00189 QStringList::const_iterator it = strList.begin() + 1; 00190 for (int i = 0; i < numrecordings; i++) 00191 { 00192 ProgramInfo *pginfo = new ProgramInfo(it, strList.end()); 00193 reclist.push_back(pginfo); 00194 } 00195 00196 return ((uint) reclist.size()) - reclist_initial_size; 00197 } 00198 00199 vector<ProgramInfo *> *RemoteGetConflictList(const ProgramInfo *pginfo) 00200 { 00201 QString cmd = QString("QUERY_GETCONFLICTING"); 00202 QStringList strlist( cmd ); 00203 pginfo->ToStringList(strlist); 00204 00205 vector<ProgramInfo *> *retlist = new vector<ProgramInfo *>; 00206 00207 RemoteGetRecordingList(*retlist, strlist); 00208 return retlist; 00209 } 00210 00211 QDateTime RemoteGetPreviewLastModified(const ProgramInfo *pginfo) 00212 { 00213 QDateTime retdatetime; 00214 00215 QStringList strlist( "QUERY_PIXMAP_LASTMODIFIED" ); 00216 pginfo->ToStringList(strlist); 00217 00218 if (!gCoreContext->SendReceiveStringList(strlist)) 00219 return retdatetime; 00220 00221 if (!strlist.empty() && strlist[0] != "BAD") 00222 { 00223 uint timet = strlist[0].toUInt(); 00224 retdatetime.setTime_t(timet); 00225 } 00226 00227 return retdatetime; 00228 } 00229 00232 QDateTime RemoteGetPreviewIfModified( 00233 const ProgramInfo &pginfo, const QString &cachefile) 00234 { 00235 QString loc("RemoteGetPreviewIfModified: "); 00236 QDateTime cacheLastModified; 00237 QFileInfo cachefileinfo(cachefile); 00238 if (cachefileinfo.exists()) 00239 cacheLastModified = cachefileinfo.lastModified(); 00240 00241 QStringList strlist("QUERY_PIXMAP_GET_IF_MODIFIED"); 00242 strlist << ((cacheLastModified.isValid()) ? // unix secs, UTC 00243 QString::number(cacheLastModified.toTime_t()) : QString("-1")); 00244 strlist << QString::number(200 * 1024); // max size of preview file 00245 pginfo.ToStringList(strlist); 00246 00247 if (!gCoreContext->SendReceiveStringList(strlist) || 00248 strlist.empty() || strlist[0] == "ERROR") 00249 { 00250 LOG(VB_GENERAL, LOG_ERR, loc + "Remote error" + 00251 ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : "")); 00252 00253 return QDateTime(); 00254 } 00255 00256 if (strlist[0] == "WARNING") 00257 { 00258 LOG(VB_NETWORK, LOG_WARNING, loc + "Remote warning" + 00259 ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : "")); 00260 00261 return QDateTime(); 00262 } 00263 00264 QDateTime retdatetime; 00265 qlonglong timet = strlist[0].toLongLong(); 00266 if (timet >= 0) 00267 retdatetime.setTime_t(timet); 00268 00269 if (strlist.size() < 4) 00270 { 00271 return retdatetime; 00272 } 00273 00274 size_t length = strlist[1].toULongLong(); 00275 quint16 checksum16 = strlist[2].toUInt(); 00276 QByteArray data = QByteArray::fromBase64(strlist[3].toAscii()); 00277 if ((size_t) data.size() < length) 00278 { // (note data.size() may be up to 3 bytes longer after decoding 00279 LOG(VB_GENERAL, LOG_ERR, loc + 00280 QString("Preview size check failed %1 < %2") 00281 .arg(data.size()).arg(length)); 00282 return QDateTime(); 00283 } 00284 data.resize(length); 00285 00286 if (checksum16 != qChecksum(data.constData(), data.size())) 00287 { 00288 LOG(VB_GENERAL, LOG_ERR, loc + "Preview checksum failed"); 00289 return QDateTime(); 00290 } 00291 00292 QString pdir(cachefile.section("/", 0, -2)); 00293 QDir cfd(pdir); 00294 if (!cfd.exists() && !cfd.mkdir(pdir)) 00295 { 00296 LOG(VB_GENERAL, LOG_ERR, loc + 00297 QString("Unable to create remote cache directory '%1'") 00298 .arg(pdir)); 00299 00300 return QDateTime(); 00301 } 00302 00303 QFile file(cachefile); 00304 if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate)) 00305 { 00306 LOG(VB_GENERAL, LOG_ERR, loc + 00307 QString("Unable to open cached preview file for writing '%1'") 00308 .arg(cachefile)); 00309 00310 return QDateTime(); 00311 } 00312 00313 off_t offset = 0; 00314 size_t remaining = length; 00315 uint failure_cnt = 0; 00316 while ((remaining > 0) && (failure_cnt < 5)) 00317 { 00318 ssize_t written = file.write(data.data() + offset, remaining); 00319 if (written < 0) 00320 { 00321 failure_cnt++; 00322 usleep(50000); 00323 continue; 00324 } 00325 00326 failure_cnt = 0; 00327 offset += written; 00328 remaining -= written; 00329 } 00330 00331 if (remaining) 00332 { 00333 LOG(VB_GENERAL, LOG_ERR, loc + 00334 QString("Failed to write cached preview file '%1'") 00335 .arg(cachefile)); 00336 00337 file.resize(0); // in case unlink fails.. 00338 file.remove(); // closes fd 00339 return QDateTime(); 00340 } 00341 00342 file.close(); 00343 00344 return retdatetime; 00345 } 00346 00347 bool RemoteFillProgramInfo(ProgramInfo &pginfo, const QString &playbackhost) 00348 { 00349 QStringList strlist( "FILL_PROGRAM_INFO" ); 00350 strlist << playbackhost; 00351 pginfo.ToStringList(strlist); 00352 00353 if (gCoreContext->SendReceiveStringList(strlist)) 00354 { 00355 ProgramInfo tmp(strlist); 00356 if (tmp.HasPathname() || tmp.GetChanID()) 00357 { 00358 pginfo = tmp; 00359 return true; 00360 } 00361 } 00362 00363 return false; 00364 } 00365 00366 QStringList RemoteRecordings(void) 00367 { 00368 QStringList strlist("QUERY_ISRECORDING"); 00369 00370 if (!gCoreContext->SendReceiveStringList(strlist, false, false)) 00371 { 00372 QStringList empty; 00373 empty << "0" << "0"; 00374 return empty; 00375 } 00376 00377 return strlist; 00378 } 00379 00380 int RemoteGetRecordingMask(void) 00381 { 00382 int mask = 0; 00383 00384 QString cmd = "QUERY_ISRECORDING"; 00385 00386 QStringList strlist( cmd ); 00387 00388 if (!gCoreContext->SendReceiveStringList(strlist)) 00389 return mask; 00390 00391 if (strlist.empty()) 00392 return 0; 00393 00394 int recCount = strlist[0].toInt(); 00395 00396 for (int i = 0, j = 0; j < recCount; i++) 00397 { 00398 cmd = QString("QUERY_RECORDER %1").arg(i + 1); 00399 00400 strlist = QStringList( cmd ); 00401 strlist << "IS_RECORDING"; 00402 00403 if (gCoreContext->SendReceiveStringList(strlist) && !strlist.empty()) 00404 { 00405 if (strlist[0].toInt()) 00406 { 00407 mask |= 1<<i; 00408 j++; // count active recorder 00409 } 00410 } 00411 else 00412 { 00413 break; 00414 } 00415 } 00416 00417 return mask; 00418 } 00419 00420 int RemoteGetFreeRecorderCount(void) 00421 { 00422 QStringList strlist( "GET_FREE_RECORDER_COUNT" ); 00423 00424 if (!gCoreContext->SendReceiveStringList(strlist, true)) 00425 return 0; 00426 00427 if (strlist.empty()) 00428 return 0; 00429 00430 if (strlist[0] == "UNKNOWN_COMMAND") 00431 { 00432 LOG(VB_GENERAL, LOG_EMERG, 00433 "Unknown command GET_FREE_RECORDER_COUNT, upgrade your " 00434 "backend version."); 00435 return 0; 00436 } 00437 00438 return strlist[0].toInt(); 00439 } 00440 00441 bool RemoteGetFileList(QString host, QString path, QStringList* list, 00442 QString sgroup, bool fileNamesOnly) 00443 { 00444 00445 // Make sure the list is empty when we get started 00446 list->clear(); 00447 00448 if (sgroup.isEmpty()) 00449 sgroup = "Videos"; 00450 00451 *list << "QUERY_SG_GETFILELIST"; 00452 *list << host; 00453 *list << StorageGroup::GetGroupToUse(host, sgroup); 00454 *list << path; 00455 *list << QString::number(fileNamesOnly); 00456 00457 bool ok = false; 00458 00459 if (gCoreContext->IsMasterBackend()) 00460 { 00461 // since the master backend cannot connect back around to 00462 // itself, and the libraries do not have access to the list 00463 // of connected slave backends to query an existing connection 00464 // start up a new temporary connection directly to the slave 00465 // backend to query the file list 00466 QString ann = QString("ANN Playback %1 0") 00467 .arg(gCoreContext->GetHostName()); 00468 QString addr = gCoreContext->GetBackendServerIP(host); 00469 int port = gCoreContext->GetNumSettingOnHost("BackendServerPort", 00470 host, 6543); 00471 bool mismatch = false; 00472 00473 MythSocket *sock = gCoreContext->ConnectCommandSocket( 00474 addr, port, ann, &mismatch); 00475 if (sock) 00476 { 00477 ok = sock->SendReceiveStringList(*list); 00478 sock->DownRef(); 00479 } 00480 else 00481 list->clear(); 00482 } 00483 else 00484 ok = gCoreContext->SendReceiveStringList(*list); 00485 00486 // Should the SLAVE UNREACH test be here ? 00487 return ok; 00488 } 00489 00495 int RemoteCheckForRecording(const ProgramInfo *pginfo) 00496 { 00497 QStringList strlist( QString("CHECK_RECORDING") ); 00498 pginfo->ToStringList(strlist); 00499 00500 if (gCoreContext->SendReceiveStringList(strlist) && !strlist.empty()) 00501 return strlist[0].toInt(); 00502 00503 return 0; 00504 } 00505 00514 int RemoteGetRecordingStatus( 00515 const ProgramInfo *pginfo, int overrecsecs, int underrecsecs) 00516 { 00517 QDateTime curtime = QDateTime::currentDateTime(); 00518 00519 int retval = 0; 00520 00521 if (pginfo) 00522 { 00523 if (curtime >= pginfo->GetScheduledStartTime().addSecs(-underrecsecs) && 00524 curtime < pginfo->GetScheduledEndTime().addSecs(overrecsecs)) 00525 { 00526 if (curtime >= pginfo->GetScheduledStartTime() && 00527 curtime < pginfo->GetScheduledEndTime()) 00528 retval = 1; 00529 else if (curtime < pginfo->GetScheduledStartTime() && 00530 RemoteCheckForRecording(pginfo) > 0) 00531 retval = 2; 00532 else if (curtime > pginfo->GetScheduledEndTime() && 00533 RemoteCheckForRecording(pginfo) > 0) 00534 retval = 3; 00535 } 00536 } 00537 00538 return retval; 00539 } 00540 00544 vector<ProgramInfo *> *RemoteGetCurrentlyRecordingList(void) 00545 { 00546 QString str = "QUERY_RECORDINGS "; 00547 str += "Recording"; 00548 QStringList strlist( str ); 00549 00550 vector<ProgramInfo *> *reclist = new vector<ProgramInfo *>; 00551 vector<ProgramInfo *> *info = new vector<ProgramInfo *>; 00552 if (!RemoteGetRecordingList(*info, strlist)) 00553 { 00554 if (info) 00555 delete info; 00556 return reclist; 00557 } 00558 00559 ProgramInfo *p = NULL; 00560 vector<ProgramInfo *>::iterator it = info->begin(); 00561 // make sure whatever RemoteGetRecordingList() returned 00562 // only has rsRecording shows 00563 for ( ; it != info->end(); ++it) 00564 { 00565 p = *it; 00566 if (p->GetRecordingStatus() == rsRecording || 00567 (p->GetRecordingStatus() == rsRecorded && 00568 p->GetRecordingGroup() == "LiveTV")) 00569 { 00570 reclist->push_back(new ProgramInfo(*p)); 00571 } 00572 } 00573 00574 while (!info->empty()) 00575 { 00576 delete info->back(); 00577 info->pop_back(); 00578 } 00579 if (info) 00580 delete info; 00581 00582 return reclist; 00583 } 00584 00588 bool RemoteGetActiveBackends(QStringList *list) 00589 { 00590 list->clear(); 00591 *list << "QUERY_ACTIVE_BACKENDS"; 00592 00593 if (!gCoreContext->SendReceiveStringList(*list)) 00594 return false; 00595 00596 list->removeFirst(); 00597 return true; 00598 } 00599 00600 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1