MythTV  0.26-pre
metadataimagedownload.cpp
Go to the documentation of this file.
00001 // qt
00002 #include <QCoreApplication>
00003 #include <QImage>
00004 #include <QFileInfo>
00005 #include <QDir>
00006 #include <QEvent>
00007 
00008 // myth
00009 #include "mythcorecontext.h"
00010 #include "mythuihelper.h"
00011 #include "mythdirs.h"
00012 #include "httpcomms.h"
00013 #include "storagegroup.h"
00014 #include "metadataimagedownload.h"
00015 #include "remotefile.h"
00016 #include "mythdownloadmanager.h"
00017 #include "mythlogging.h"
00018 
00019 QEvent::Type ImageDLEvent::kEventType =
00020     (QEvent::Type) QEvent::registerEventType();
00021 
00022 QEvent::Type ImageDLFailureEvent::kEventType =
00023     (QEvent::Type) QEvent::registerEventType();
00024 
00025 QEvent::Type ThumbnailDLEvent::kEventType =
00026     (QEvent::Type) QEvent::registerEventType();
00027 
00028 MetadataImageDownload::MetadataImageDownload(QObject *parent) :
00029     MThread("MetadataImageDownload")
00030 {
00031     m_parent = parent;
00032 }
00033 
00034 MetadataImageDownload::~MetadataImageDownload()
00035 {
00036     cancel();
00037     wait();
00038 }
00039 
00040 void MetadataImageDownload::addThumb(QString title,
00041                                      QString url, QVariant data)
00042 {
00043     m_mutex.lock();
00044     ThumbnailData *id = new ThumbnailData();
00045     id->title = title;
00046     id->data = data;
00047     id->url = url;
00048     m_thumbnailList.append(id);
00049     if (!isRunning())
00050         start();
00051     m_mutex.unlock();
00052 }
00053 
00054 void MetadataImageDownload::addDownloads(MetadataLookup *lookup)
00055 {
00056     m_mutex.lock();
00057     m_downloadList.append(lookup);
00058     if (!isRunning())
00059         start();
00060     m_mutex.unlock();
00061 }
00062 
00063 void MetadataImageDownload::cancel()
00064 {
00065     m_mutex.lock();
00066     qDeleteAll(m_thumbnailList);
00067     m_thumbnailList.clear();
00068     qDeleteAll(m_downloadList);
00069     m_downloadList.clear();
00070     m_mutex.unlock();
00071 }
00072 
00073 void MetadataImageDownload::run()
00074 {
00075     RunProlog();
00076 
00077     // Always handle thumbnails first, they're higher priority.
00078     ThumbnailData *thumb;
00079     while ((thumb = moreThumbs()) != NULL)
00080     {
00081         QString sFilename = getDownloadFilename(thumb->title, thumb->url);
00082 
00083         bool exists = QFile::exists(sFilename);
00084         if (!exists && !thumb->url.isEmpty())
00085             GetMythDownloadManager()->download(thumb->url, sFilename);
00086 
00087         // inform parent we have thumbnail ready for it
00088         if (QFile::exists(sFilename) && m_parent)
00089         {
00090             LOG(VB_GENERAL, LOG_DEBUG,
00091                     QString("Threaded Image Thumbnail Download: %1")
00092                     .arg(sFilename));
00093             thumb->url = sFilename;
00094             QCoreApplication::postEvent(m_parent,
00095                            new ThumbnailDLEvent(thumb));
00096         }
00097         else
00098             delete thumb;
00099     }
00100 
00101     MetadataLookup *lookup;
00102     while ((lookup = moreDownloads()) != NULL)
00103     {
00104         DownloadMap downloads = lookup->GetDownloads();
00105         DownloadMap downloaded;
00106 
00107         for (DownloadMap::iterator i = downloads.begin();
00108                 i != downloads.end(); ++i)
00109         {
00110             VideoArtworkType type = i.key();
00111             ArtworkInfo info = i.value();
00112             QString filename = getDownloadFilename( type, lookup,
00113                                    info.url );
00114             if (lookup->GetHost().isEmpty())
00115             {
00116                 QString path = getLocalWritePath(lookup->GetType(), type);
00117                 QDir dirPath(path);
00118                 if (!dirPath.exists())
00119                     if (!dirPath.mkpath(path))
00120                     {
00121                         LOG(VB_GENERAL, LOG_ERR,
00122                             QString("Metadata Image Download: Unable to create "
00123                                     "path %1, aborting download.").arg(path));
00124                         QCoreApplication::postEvent(m_parent,
00125                                     new ImageDLFailureEvent(lookup));
00126                         continue;
00127                     }
00128                 QString finalfile = path + "/" + filename;
00129                 QString oldurl = info.url;
00130                 info.url = finalfile;
00131                 if (!QFile::exists(finalfile) || lookup->GetAllowOverwrites())
00132                 {
00133                     QFile dest_file(finalfile);
00134                     if (dest_file.exists())
00135                     {
00136                         QFileInfo fi(finalfile);
00137                         GetMythUI()->RemoveFromCacheByFile(fi.fileName());
00138                         dest_file.remove();
00139                     }
00140 
00141                     LOG(VB_GENERAL, LOG_INFO,
00142                         QString("Metadata Image Download: %1 ->%2")
00143                          .arg(oldurl).arg(finalfile));
00144                     QByteArray *download = new QByteArray();
00145                     GetMythDownloadManager()->download(oldurl, download);
00146 
00147                     QImage testImage;
00148                     bool didLoad = testImage.loadFromData(*download);
00149                     if (!didLoad)
00150                     {
00151                         LOG(VB_GENERAL, LOG_ERR,
00152                             QString("Tried to write %1, but it appears to be "
00153                                     "an HTML redirect (filesize %2).")
00154                                 .arg(oldurl).arg(download->size()));
00155                         delete download;
00156                         download = NULL;
00157                         QCoreApplication::postEvent(m_parent,
00158                                     new ImageDLFailureEvent(lookup));
00159                         continue;
00160                     }
00161 
00162                     if (dest_file.open(QIODevice::WriteOnly))
00163                     {
00164                         off_t size = dest_file.write(*download,
00165                                                      download->size());
00166                         if (size != download->size())
00167                         {
00168                             LOG(VB_GENERAL, LOG_ERR,
00169                                 QString("Image Download: Error Writing Image "
00170                                         "to file: %1").arg(finalfile));
00171                             QCoreApplication::postEvent(m_parent,
00172                                         new ImageDLFailureEvent(lookup));
00173                         }
00174                         else
00175                             downloaded.insert(type, info);
00176                     }
00177 
00178                     delete download;
00179                 }
00180                 else
00181                     downloaded.insert(type, info);
00182             }
00183             else
00184             {
00185                 QString path = getStorageGroupURL(type, lookup->GetHost());
00186                 QString finalfile = path + filename;
00187                 QString oldurl = info.url;
00188                 info.url = finalfile;
00189                 bool exists = false;
00190                 bool onMaster = false;
00191                 QString resolvedFN;
00192                 if ((lookup->GetHost().toLower() == gCoreContext->GetHostName().toLower()) ||
00193                     (gCoreContext->IsThisHost(lookup->GetHost())))
00194                 {
00195                     StorageGroup sg;
00196                     resolvedFN = sg.FindFile(filename);
00197                     exists = QFile::exists(resolvedFN);
00198                     if (!exists)
00199                     {
00200                         resolvedFN = getLocalStorageGroupPath(type,
00201                                                  lookup->GetHost()) + "/" + filename;
00202                     }
00203                     onMaster = true;
00204                 }
00205                 else
00206                     exists = RemoteFile::Exists(finalfile);
00207 
00208                 if (!exists || lookup->GetAllowOverwrites())
00209                 {
00210 
00211                     if (exists && !onMaster)
00212                     {
00213                         QFileInfo fi(finalfile);
00214                         GetMythUI()->RemoveFromCacheByFile(fi.fileName());
00215                         RemoteFile::DeleteFile(finalfile);
00216                     }
00217                     else if (exists)
00218                         QFile::remove(resolvedFN);
00219 
00220                     LOG(VB_GENERAL, LOG_INFO,
00221                         QString("Metadata Image Download: %1 -> %2")
00222                             .arg(oldurl).arg(finalfile));
00223                     QByteArray *download = new QByteArray();
00224                     GetMythDownloadManager()->download(oldurl, download);
00225 
00226                     QImage testImage;
00227                     bool didLoad = testImage.loadFromData(*download);
00228                     if (!didLoad)
00229                     {
00230                         LOG(VB_GENERAL, LOG_ERR,
00231                             QString("Tried to write %1, but it appears to be "
00232                                     "an HTML redirect or corrupt file "
00233                                     "(filesize %2).")
00234                                 .arg(oldurl).arg(download->size()));
00235                         delete download;
00236                         download = NULL;
00237                         QCoreApplication::postEvent(m_parent,
00238                                     new ImageDLFailureEvent(lookup));
00239                         continue;
00240                     }
00241 
00242                     if (!onMaster)
00243                     {
00244                         RemoteFile *outFile = new RemoteFile(finalfile, true);
00245                         if (!outFile->isOpen())
00246                         {
00247                             LOG(VB_GENERAL, LOG_ERR,
00248                                 QString("Image Download: Failed to open "
00249                                         "remote file (%1) for write.  Does "
00250                                         "Storage Group Exist?")
00251                                         .arg(finalfile));
00252                             delete outFile;
00253                             outFile = NULL;
00254                             QCoreApplication::postEvent(m_parent,
00255                                         new ImageDLFailureEvent(lookup));
00256                         }
00257                         else
00258                         {
00259                             off_t written = outFile->Write(*download,
00260                                                            download->size());
00261                             if (written != download->size())
00262                             {
00263                                 LOG(VB_GENERAL, LOG_ERR,
00264                                     QString("Image Download: Error Writing Image "
00265                                             "to file: %1").arg(finalfile));
00266                                 QCoreApplication::postEvent(m_parent,
00267                                         new ImageDLFailureEvent(lookup));
00268                             }
00269                             else
00270                                 downloaded.insert(type, info);
00271                             delete outFile;
00272                             outFile = NULL;
00273                         }
00274                     }
00275                     else
00276                     {
00277                         QFile dest_file(resolvedFN);
00278                         if (dest_file.open(QIODevice::WriteOnly))
00279                         {
00280                             off_t size = dest_file.write(*download,
00281                                                          download->size());
00282                             if (size != download->size())
00283                             {
00284                                 LOG(VB_GENERAL, LOG_ERR,
00285                                     QString("Image Download: Error Writing Image "
00286                                             "to file: %1").arg(finalfile));
00287                                 QCoreApplication::postEvent(m_parent,
00288                                             new ImageDLFailureEvent(lookup));
00289                             }
00290                             else
00291                                 downloaded.insert(type, info);
00292                         }
00293                     }
00294 
00295                     delete download;
00296                 }
00297                 else
00298                     downloaded.insert(type, info);
00299             }
00300         }
00301         lookup->SetDownloads(downloaded);
00302         QCoreApplication::postEvent(m_parent, new ImageDLEvent(lookup));
00303     }
00304 
00305     RunEpilog();
00306 }
00307 
00308 ThumbnailData* MetadataImageDownload::moreThumbs()
00309 {
00310     ThumbnailData *ret = NULL;
00311     m_mutex.lock();
00312     if (!m_thumbnailList.isEmpty())
00313         ret = m_thumbnailList.takeFirst();
00314     m_mutex.unlock();
00315     return ret;
00316 }
00317 
00318 MetadataLookup* MetadataImageDownload::moreDownloads()
00319 {
00320     MetadataLookup *ret = NULL;
00321     m_mutex.lock();
00322     if (!m_downloadList.isEmpty())
00323         ret = m_downloadList.takeFirst();
00324     m_mutex.unlock();
00325     return ret;
00326 }
00327 
00328 QString getDownloadFilename(QString title, QString url)
00329 {
00330     QString fileprefix = GetConfDir();
00331 
00332     QDir dir(fileprefix);
00333     if (!dir.exists())
00334         dir.mkdir(fileprefix);
00335 
00336     fileprefix += "/thumbcache";
00337 
00338     dir = QDir(fileprefix);
00339     if (!dir.exists())
00340         dir.mkdir(fileprefix);
00341 
00342     QByteArray titlearr(title.toLatin1());
00343     quint16 titleChecksum = qChecksum(titlearr.data(), titlearr.length());
00344     QByteArray urlarr(url.toLatin1());
00345     quint16 urlChecksum = qChecksum(urlarr.data(), urlarr.length());
00346     QUrl qurl(url);
00347     QString ext = QFileInfo(qurl.path()).suffix();
00348     QString basefilename = QString("thumbnail_%1_%2.%3")
00349                            .arg(QString::number(urlChecksum))
00350                            .arg(QString::number(titleChecksum)).arg(ext);
00351 
00352     QString outputfile = QString("%1/%2").arg(fileprefix).arg(basefilename);
00353 
00354     return outputfile;
00355 }
00356 
00357 QString getDownloadFilename(VideoArtworkType type, MetadataLookup *lookup,
00358                             QString url)
00359 {
00360     QString basefilename;
00361     QString title;
00362     QString inter;
00363     uint tracknum = lookup->GetTrackNumber();
00364     uint season = lookup->GetSeason();
00365     uint episode = lookup->GetEpisode();
00366     QString system = lookup->GetSystem();
00367     if (season > 0 || episode > 0)
00368     {
00369         title = lookup->GetTitle();
00370         if (title.contains("/"))
00371             title.replace("/", "-");
00372         if (title.contains("?"))
00373             title.replace("?", "");
00374         if (title.contains("*"))
00375             title.replace("*", "");
00376         inter = QString(" Season %1").arg(QString::number(season));
00377         if (type == kArtworkScreenshot)
00378             inter += QString("x%1").arg(QString::number(episode));
00379     }
00380     else if (lookup->GetType() == kMetadataVideo ||
00381              lookup->GetType() == kMetadataRecording)
00382         title = lookup->GetInetref();
00383     else if (lookup->GetType() == kMetadataGame)
00384         title = QString("%1 (%2)").arg(lookup->GetTitle())
00385                     .arg(lookup->GetSystem());
00386 
00387     if (tracknum > 0)
00388         inter = QString(" Track %1").arg(QString::number(tracknum));
00389     else if (!system.isEmpty())
00390         inter = QString(" (%1)").arg(system);
00391 
00392     QString suffix;
00393     QUrl qurl(url);
00394     QString ext = QFileInfo(qurl.path()).suffix();
00395 
00396     if (type == kArtworkCoverart)
00397         suffix = "_coverart";
00398     else if (type == kArtworkFanart)
00399         suffix = "_fanart";
00400     else if (type == kArtworkBanner)
00401         suffix = "_banner";
00402     else if (type == kArtworkScreenshot)
00403         suffix = "_screenshot";
00404     else if (type == kArtworkPoster)
00405         suffix = "_poster";
00406     else if (type == kArtworkBackCover)
00407         suffix = "_backcover";
00408     else if (type == kArtworkInsideCover)
00409         suffix = "_insidecover";
00410     else if (type == kArtworkCDImage)
00411         suffix = "_cdimage";
00412 
00413     basefilename = title + inter + suffix + "." + ext;
00414 
00415     return basefilename;
00416 }
00417 
00418 QString getLocalWritePath(MetadataType metadatatype, VideoArtworkType type)
00419 {
00420     QString ret;
00421 
00422     if (metadatatype == kMetadataVideo)
00423     {
00424         if (type == kArtworkCoverart)
00425             ret = gCoreContext->GetSetting("VideoArtworkDir");
00426         else if (type == kArtworkFanart)
00427             ret = gCoreContext->GetSetting("mythvideo.fanartDir");
00428         else if (type == kArtworkBanner)
00429             ret = gCoreContext->GetSetting("mythvideo.bannerDir");
00430         else if (type == kArtworkScreenshot)
00431             ret = gCoreContext->GetSetting("mythvideo.screenshotDir");
00432     }
00433     else if (metadatatype == kMetadataMusic)
00434     {
00435     }
00436     else if (metadatatype == kMetadataGame)
00437     {
00438         if (type == kArtworkCoverart)
00439             ret = gCoreContext->GetSetting("mythgame.boxartdir");
00440         else if (type == kArtworkFanart)
00441             ret = gCoreContext->GetSetting("mythgame.fanartdir");
00442         else if (type == kArtworkScreenshot)
00443             ret = gCoreContext->GetSetting("mythgame.screenshotdir");
00444     }
00445 
00446     return ret;
00447 }
00448 
00449 QString getStorageGroupURL(VideoArtworkType type, QString host)
00450 {
00451     QString sgroup;
00452     QString ip = gCoreContext->GetSettingOnHost("BackendServerIP", host);
00453     uint port = gCoreContext->GetSettingOnHost("BackendServerPort",
00454                                                host).toUInt();
00455 
00456     if (type == kArtworkCoverart)
00457         sgroup = "Coverart";
00458     else if (type == kArtworkFanart)
00459         sgroup = "Fanart";
00460     else if (type == kArtworkBanner)
00461         sgroup = "Banners";
00462     else if (type == kArtworkScreenshot)
00463         sgroup = "Screenshots";
00464     else
00465         sgroup = "Default";
00466 
00467     return gCoreContext->GenMythURL(ip,port,"",sgroup);
00468 }
00469 
00470 QString getLocalStorageGroupPath(VideoArtworkType type, QString host)
00471 {
00472     QString path;
00473 
00474     StorageGroup sg;
00475 
00476     if (type == kArtworkCoverart)
00477         sg.Init("Coverart", host);
00478     else if (type == kArtworkFanart)
00479         sg.Init("Fanart", host);
00480     else if (type == kArtworkBanner)
00481         sg.Init("Banners", host);
00482     else if (type == kArtworkScreenshot)
00483         sg.Init("Screenshots", host);
00484     else
00485         sg.Init("Default", host);
00486 
00487     path = sg.FindNextDirMostFree();
00488 
00489     return path;
00490 }
00491 
00492 void cleanThumbnailCacheDir()
00493 {
00494     QString cache = QString("%1/thumbcache")
00495                .arg(GetConfDir());
00496     QDir cacheDir(cache);
00497     QStringList thumbs = cacheDir.entryList(QDir::Files);
00498 
00499     for (QStringList::const_iterator i = thumbs.end() - 1;
00500             i != thumbs.begin() - 1; --i)
00501     {
00502         QString filename = QString("%1/%2").arg(cache).arg(*i);
00503         QFileInfo fi(filename);
00504         QDateTime lastmod = fi.lastModified();
00505         if (lastmod.addDays(2) < QDateTime::currentDateTime())
00506         {
00507             LOG(VB_GENERAL, LOG_DEBUG, QString("Deleting file %1")
00508                   .arg(filename));
00509             QFile::remove(filename);
00510         }
00511     }
00512 }
00513 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends