|
MythTV
0.26-pre
|
00001 #include <algorithm> 00002 using namespace std; 00003 00004 #include <QCoreApplication> 00005 #include <QStringList> 00006 #include <QDateTime> 00007 #include <QFileInfo> 00008 #include <QDir> 00009 00010 #include "previewgeneratorqueue.h" 00011 #include "metadataimagehelper.h" 00012 #include "playbackboxhelper.h" 00013 #include "mythcorecontext.h" 00014 #include "filesysteminfo.h" 00015 #include "tvremoteutil.h" 00016 #include "storagegroup.h" 00017 #include "mythlogging.h" 00018 #include "programinfo.h" 00019 #include "remoteutil.h" 00020 #include "mythevent.h" 00021 #include "mythdirs.h" 00022 00023 #define LOC QString("PlaybackBoxHelper: ") 00024 #define LOC_WARN QString("PlaybackBoxHelper Warning: ") 00025 #define LOC_ERR QString("PlaybackBoxHelper Error: ") 00026 00027 class PBHEventHandler : public QObject 00028 { 00029 public: 00030 PBHEventHandler(PlaybackBoxHelper &pbh) : 00031 m_pbh(pbh), m_freeSpaceTimerId(0), m_checkAvailabilityTimerId(0) 00032 { 00033 StorageGroup::ClearGroupToUseCache(); 00034 } 00035 virtual bool event(QEvent*); // QObject 00036 void UpdateFreeSpaceEvent(void); 00037 AvailableStatusType CheckAvailability(const QStringList &slist); 00038 PlaybackBoxHelper &m_pbh; 00039 int m_freeSpaceTimerId; 00040 int m_checkAvailabilityTimerId; 00041 static const uint kUpdateFreeSpaceInterval; 00042 QMap<QString, QStringList> m_fileListCache; 00043 QHash<QString, QStringList> m_checkAvailability; 00044 }; 00045 00046 const uint PBHEventHandler::kUpdateFreeSpaceInterval = 15000; // 15 seconds 00047 00048 AvailableStatusType PBHEventHandler::CheckAvailability(const QStringList &slist) 00049 { 00050 QTime tm = QTime::currentTime(); 00051 00052 QStringList::const_iterator it = slist.begin(); 00053 ProgramInfo evinfo(it, slist.end()); 00054 QSet<CheckAvailabilityType> cats; 00055 for (; it != slist.end(); ++it) 00056 cats.insert((CheckAvailabilityType)(*it).toUInt()); 00057 00058 { 00059 QMutexLocker locker(&m_pbh.m_lock); 00060 QHash<QString, QStringList>::iterator it = 00061 m_checkAvailability.find(evinfo.MakeUniqueKey()); 00062 if (it != m_checkAvailability.end()) 00063 m_checkAvailability.erase(it); 00064 if (m_checkAvailability.empty() && m_checkAvailabilityTimerId) 00065 { 00066 killTimer(m_checkAvailabilityTimerId); 00067 m_checkAvailabilityTimerId = 0; 00068 } 00069 } 00070 00071 if (cats.empty()) 00072 return asFileNotFound; 00073 00074 AvailableStatusType availableStatus = asAvailable; 00075 if (!evinfo.HasPathname() && !evinfo.GetChanID()) 00076 availableStatus = asFileNotFound; 00077 else 00078 { 00079 // Note IsFileReadable() implicitly calls GetPlaybackURL 00080 // when necessary, we rely on this. 00081 if (!evinfo.IsFileReadable()) 00082 { 00083 LOG(VB_GENERAL, LOG_ERR, LOC + 00084 QString("CHECK_AVAILABILITY '%1' file not found") 00085 .arg(evinfo.GetPathname())); 00086 availableStatus = asFileNotFound; 00087 } 00088 else if (!evinfo.GetFilesize()) 00089 { 00090 evinfo.SetFilesize(evinfo.QueryFilesize()); 00091 if (!evinfo.GetFilesize()) 00092 { 00093 availableStatus = 00094 (evinfo.GetRecordingStatus() == rsRecording) ? 00095 asNotYetAvailable : asZeroByte; 00096 } 00097 } 00098 } 00099 00100 QStringList list; 00101 list.push_back(evinfo.MakeUniqueKey()); 00102 list.push_back(evinfo.GetPathname()); 00103 MythEvent *e0 = new MythEvent("SET_PLAYBACK_URL", list); 00104 QCoreApplication::postEvent(m_pbh.m_listener, e0); 00105 00106 list.clear(); 00107 list.push_back(evinfo.MakeUniqueKey()); 00108 list.push_back(QString::number((int)*cats.begin())); 00109 list.push_back(QString::number((int)availableStatus)); 00110 list.push_back(QString::number(evinfo.GetFilesize())); 00111 list.push_back(QString::number(tm.hour())); 00112 list.push_back(QString::number(tm.minute())); 00113 list.push_back(QString::number(tm.second())); 00114 list.push_back(QString::number(tm.msec())); 00115 00116 QSet<CheckAvailabilityType>::iterator cit = cats.begin(); 00117 for (; cit != cats.end(); ++cit) 00118 { 00119 if (*cit == kCheckForCache && cats.size() > 1) 00120 continue; 00121 list[1] = QString::number((int)*cit); 00122 MythEvent *e = new MythEvent("AVAILABILITY", list); 00123 QCoreApplication::postEvent(m_pbh.m_listener, e); 00124 } 00125 00126 return availableStatus; 00127 } 00128 00129 bool PBHEventHandler::event(QEvent *e) 00130 { 00131 if (e->type() == QEvent::Timer) 00132 { 00133 QTimerEvent *te = (QTimerEvent*)e; 00134 const int timer_id = te->timerId(); 00135 if (timer_id == m_freeSpaceTimerId) 00136 UpdateFreeSpaceEvent(); 00137 if (timer_id == m_checkAvailabilityTimerId) 00138 { 00139 QStringList slist; 00140 { 00141 QMutexLocker locker(&m_pbh.m_lock); 00142 QHash<QString, QStringList>::iterator it = 00143 m_checkAvailability.begin(); 00144 if (it != m_checkAvailability.end()) 00145 slist = *it; 00146 } 00147 00148 if (slist.size() >= 1 + NUMPROGRAMLINES) 00149 CheckAvailability(slist); 00150 } 00151 return true; 00152 } 00153 else if (e->type() == (QEvent::Type) MythEvent::MythEventMessage) 00154 { 00155 MythEvent *me = (MythEvent*)e; 00156 if (me->Message() == "UPDATE_FREE_SPACE") 00157 { 00158 UpdateFreeSpaceEvent(); 00159 return true; 00160 } 00161 else if (me->Message() == "STOP_RECORDING") 00162 { 00163 ProgramInfo pginfo(me->ExtraDataList()); 00164 if (pginfo.GetChanID()) 00165 RemoteStopRecording(&pginfo); 00166 return true; 00167 } 00168 else if (me->Message() == "DELETE_RECORDINGS") 00169 { 00170 QStringList successes; 00171 QStringList failures; 00172 QStringList list = me->ExtraDataList(); 00173 while (list.size() >= 4) 00174 { 00175 uint chanid = list[0].toUInt(); 00176 QDateTime recstartts = QDateTime::fromString( 00177 list[1], Qt::ISODate); 00178 bool forceDelete = list[2].toUInt(); 00179 bool forgetHistory = list[3].toUInt(); 00180 00181 bool ok = RemoteDeleteRecording( 00182 chanid, recstartts, forceDelete, forgetHistory); 00183 00184 QStringList &res = (ok) ? successes : failures; 00185 for (uint i = 0; i < 4; i++) 00186 { 00187 res.push_back(list.front()); 00188 list.pop_front(); 00189 } 00190 } 00191 if (!successes.empty()) 00192 { 00193 MythEvent *e = new MythEvent("DELETE_SUCCESSES", successes); 00194 QCoreApplication::postEvent(m_pbh.m_listener, e); 00195 } 00196 if (!failures.empty()) 00197 { 00198 MythEvent *e = new MythEvent("DELETE_FAILURES", failures); 00199 QCoreApplication::postEvent(m_pbh.m_listener, e); 00200 } 00201 00202 return true; 00203 } 00204 else if (me->Message() == "UNDELETE_RECORDINGS") 00205 { 00206 QStringList successes; 00207 QStringList failures; 00208 QStringList list = me->ExtraDataList(); 00209 while (list.size() >= 2) 00210 { 00211 uint chanid = list[0].toUInt(); 00212 QDateTime recstartts = QDateTime::fromString( 00213 list[1], Qt::ISODate); 00214 00215 bool ok = RemoteUndeleteRecording(chanid, recstartts); 00216 00217 QStringList &res = (ok) ? successes : failures; 00218 for (uint i = 0; i < 2; i++) 00219 { 00220 res.push_back(list.front()); 00221 list.pop_front(); 00222 } 00223 } 00224 if (!successes.empty()) 00225 { 00226 MythEvent *e = new MythEvent("UNDELETE_SUCCESSES", successes); 00227 QCoreApplication::postEvent(m_pbh.m_listener, e); 00228 } 00229 if (!failures.empty()) 00230 { 00231 MythEvent *e = new MythEvent("UNDELETE_FAILURES", failures); 00232 QCoreApplication::postEvent(m_pbh.m_listener, e); 00233 } 00234 00235 return true; 00236 } 00237 else if (me->Message() == "GET_PREVIEW") 00238 { 00239 QString token = me->ExtraData(0); 00240 bool check_avail = (bool) me->ExtraData(1).toInt(); 00241 QStringList list = me->ExtraDataList(); 00242 QStringList::const_iterator it = list.begin()+2; 00243 ProgramInfo evinfo(it, list.end()); 00244 if (!evinfo.HasPathname()) 00245 return true; 00246 00247 list.clear(); 00248 evinfo.ToStringList(list); 00249 list += QString::number(kCheckForCache); 00250 if (check_avail && (asAvailable != CheckAvailability(list))) 00251 return true; 00252 else if (asAvailable != evinfo.GetAvailableStatus()) 00253 return true; 00254 00255 // Now we can actually request the preview... 00256 PreviewGeneratorQueue::GetPreviewImage(evinfo, token); 00257 00258 return true; 00259 } 00260 else if (me->Message() == "CHECK_AVAILABILITY") 00261 { 00262 if (me->ExtraData(0) != QString::number(kCheckForCache)) 00263 { 00264 if (m_checkAvailabilityTimerId) 00265 killTimer(m_checkAvailabilityTimerId); 00266 m_checkAvailabilityTimerId = startTimer(0); 00267 } 00268 else if (!m_checkAvailabilityTimerId) 00269 m_checkAvailabilityTimerId = startTimer(50); 00270 } 00271 else if (me->Message() == "LOCATE_ARTWORK") 00272 { 00273 QString inetref = me->ExtraData(0); 00274 uint season = me->ExtraData(1).toUInt(); 00275 const VideoArtworkType type = (VideoArtworkType)me->ExtraData(2).toInt(); 00276 const QString pikey = me->ExtraData(3); 00277 const QString group = me->ExtraData(4); 00278 const QString cacheKey = QString("%1:%2:%3") 00279 .arg((int)type).arg(inetref).arg(season); 00280 00281 ArtworkMap map = GetArtwork(inetref, season); 00282 00283 ArtworkInfo info = map.value(type); 00284 00285 QString foundFile; 00286 00287 if (!info.url.isEmpty()) 00288 { 00289 foundFile = info.url; 00290 QMutexLocker locker(&m_pbh.m_lock); 00291 m_pbh.m_artworkFilenameCache[cacheKey] = foundFile; 00292 } 00293 00294 if (!foundFile.isEmpty()) 00295 { 00296 QStringList list = me->ExtraDataList(); 00297 list.push_back(foundFile); 00298 MythEvent *e = new MythEvent("FOUND_ARTWORK", list); 00299 QCoreApplication::postEvent(m_pbh.m_listener, e); 00300 } 00301 00302 return true; 00303 } 00304 } 00305 00306 return QObject::event(e); 00307 } 00308 00309 void PBHEventHandler::UpdateFreeSpaceEvent(void) 00310 { 00311 if (m_freeSpaceTimerId) 00312 killTimer(m_freeSpaceTimerId); 00313 m_pbh.UpdateFreeSpace(); 00314 m_freeSpaceTimerId = startTimer(kUpdateFreeSpaceInterval); 00315 } 00316 00318 00319 PlaybackBoxHelper::PlaybackBoxHelper(QObject *listener) : 00320 MThread("PlaybackBoxHelper"), 00321 m_listener(listener), m_eventHandler(new PBHEventHandler(*this)), 00322 // Free Space Tracking Variables 00323 m_freeSpaceTotalMB(0ULL), m_freeSpaceUsedMB(0ULL) 00324 { 00325 start(); 00326 m_eventHandler->moveToThread(qthread()); 00327 // Prime the pump so the disk free display starts updating 00328 ForceFreeSpaceUpdate(); 00329 } 00330 00331 PlaybackBoxHelper::~PlaybackBoxHelper() 00332 { 00333 exit(); 00334 wait(); 00335 00336 // delete the event handler 00337 delete m_eventHandler; 00338 m_eventHandler = NULL; 00339 } 00340 00341 void PlaybackBoxHelper::ForceFreeSpaceUpdate(void) 00342 { 00343 QCoreApplication::postEvent( 00344 m_eventHandler, new MythEvent("UPDATE_FREE_SPACE")); 00345 } 00346 00347 void PlaybackBoxHelper::StopRecording(const ProgramInfo &pginfo) 00348 { 00349 QStringList list; 00350 pginfo.ToStringList(list); 00351 MythEvent *e = new MythEvent("STOP_RECORDING", list); 00352 QCoreApplication::postEvent(m_eventHandler, e); 00353 } 00354 00355 void PlaybackBoxHelper::DeleteRecording( 00356 uint chanid, const QDateTime &recstartts, bool forceDelete, 00357 bool forgetHistory) 00358 { 00359 QStringList list; 00360 list.push_back(QString::number(chanid)); 00361 list.push_back(recstartts.toString(Qt::ISODate)); 00362 list.push_back((forceDelete) ? "1" : "0"); 00363 list.push_back((forgetHistory) ? "1" : "0"); 00364 DeleteRecordings(list); 00365 } 00366 00367 void PlaybackBoxHelper::DeleteRecordings(const QStringList &list) 00368 { 00369 MythEvent *e = new MythEvent("DELETE_RECORDINGS", list); 00370 QCoreApplication::postEvent(m_eventHandler, e); 00371 } 00372 00373 void PlaybackBoxHelper::UndeleteRecording( 00374 uint chanid, const QDateTime &recstartts) 00375 { 00376 QStringList list; 00377 list.push_back(QString::number(chanid)); 00378 list.push_back(recstartts.toString(Qt::ISODate)); 00379 MythEvent *e = new MythEvent("UNDELETE_RECORDINGS", list); 00380 QCoreApplication::postEvent(m_eventHandler, e); 00381 } 00382 00383 void PlaybackBoxHelper::UpdateFreeSpace(void) 00384 { 00385 QList<FileSystemInfo> fsInfos = FileSystemInfo::RemoteGetInfo(); 00386 00387 QMutexLocker locker(&m_lock); 00388 for (int i = 0; i < fsInfos.size(); i++) 00389 { 00390 if (fsInfos[i].getPath() == "TotalDiskSpace") 00391 { 00392 m_freeSpaceTotalMB = (uint64_t) (fsInfos[i].getTotalSpace() >> 10); 00393 m_freeSpaceUsedMB = (uint64_t) (fsInfos[i].getUsedSpace() >> 10); 00394 } 00395 } 00396 MythEvent *e = new MythEvent("UPDATE_USAGE_UI"); 00397 QCoreApplication::postEvent(m_listener, e); 00398 } 00399 00400 uint64_t PlaybackBoxHelper::GetFreeSpaceTotalMB(void) const 00401 { 00402 QMutexLocker locker(&m_lock); 00403 return m_freeSpaceTotalMB; 00404 } 00405 00406 uint64_t PlaybackBoxHelper::GetFreeSpaceUsedMB(void) const 00407 { 00408 QMutexLocker locker(&m_lock); 00409 return m_freeSpaceUsedMB; 00410 } 00411 00412 void PlaybackBoxHelper::CheckAvailability( 00413 const ProgramInfo &pginfo, CheckAvailabilityType cat) 00414 { 00415 QString catstr = QString::number((int)cat); 00416 QMutexLocker locker(&m_lock); 00417 QHash<QString, QStringList>::iterator it = 00418 m_eventHandler->m_checkAvailability.find(pginfo.MakeUniqueKey()); 00419 if (it == m_eventHandler->m_checkAvailability.end()) 00420 { 00421 QStringList list; 00422 pginfo.ToStringList(list); 00423 list += catstr; 00424 m_eventHandler->m_checkAvailability[pginfo.MakeUniqueKey()] = list; 00425 } 00426 else 00427 { 00428 (*it).push_back(catstr); 00429 } 00430 MythEvent *e = new MythEvent("CHECK_AVAILABILITY", QStringList(catstr)); 00431 QCoreApplication::postEvent(m_eventHandler, e); 00432 } 00433 00434 QString PlaybackBoxHelper::LocateArtwork( 00435 const QString &inetref, uint season, 00436 const VideoArtworkType type, 00437 const ProgramInfo *pginfo, 00438 const QString &groupname) 00439 { 00440 QString cacheKey = QString("%1:%2:%3") 00441 .arg((int)type).arg(inetref).arg(season); 00442 00443 QMutexLocker locker(&m_lock); 00444 00445 QHash<QString,QString>::const_iterator it = 00446 m_artworkFilenameCache.find(cacheKey); 00447 00448 if (it != m_artworkFilenameCache.end()) 00449 return *it; 00450 00451 QStringList list(inetref); 00452 list.push_back(QString::number(season)); 00453 list.push_back(QString::number(type)); 00454 list.push_back((pginfo)?pginfo->MakeUniqueKey():""); 00455 list.push_back(groupname); 00456 MythEvent *e = new MythEvent("LOCATE_ARTWORK", list); 00457 QCoreApplication::postEvent(m_eventHandler, e); 00458 00459 return QString(); 00460 } 00461 00462 QString PlaybackBoxHelper::GetPreviewImage( 00463 const ProgramInfo &pginfo, bool check_availability) 00464 { 00465 if (!check_availability && pginfo.GetAvailableStatus() != asAvailable) 00466 return QString(); 00467 00468 if (pginfo.GetAvailableStatus() == asPendingDelete) 00469 return QString(); 00470 00471 QString token = QString("%1:%2") 00472 .arg(pginfo.MakeUniqueKey()).arg(random()); 00473 00474 QStringList extra(token); 00475 extra.push_back(check_availability?"1":"0"); 00476 pginfo.ToStringList(extra); 00477 MythEvent *e = new MythEvent("GET_PREVIEW", extra); 00478 QCoreApplication::postEvent(m_eventHandler, e); 00479 00480 return token; 00481 }
1.7.6.1