00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <QApplication>
00024 #include <QImage>
00025 #include <QFileInfo>
00026 #include <QDir>
00027 #include <QEvent>
00028 #include <QImageReader>
00029 #include <QSet>
00030
00031
00032 #include <mythuihelper.h>
00033 #include <mythcontext.h>
00034 #include <mythdirs.h>
00035 #include <mthread.h>
00036 #include <mythmiscutil.h>
00037
00038
00039 #include "config.h"
00040 #include "thumbgenerator.h"
00041 #include "galleryutil.h"
00042 #include "mythsystem.h"
00043 #include "exitcodes.h"
00044 #include "mythlogging.h"
00045
00046 #ifdef DCRAW_SUPPORT
00047 #include "../dcrawplugin/dcrawformats.h"
00048 #include "../dcrawplugin/dcrawhandler.h"
00049 #endif // DCRAW_SUPPORT
00050
00051 #ifdef EXIF_SUPPORT
00052 #include <libexif/exif-data.h>
00053 #include <libexif/exif-entry.h>
00054 #endif
00055
00056 QEvent::Type ThumbGenEvent::kEventType =
00057 (QEvent::Type) QEvent::registerEventType();
00058
00059 ThumbGenerator::ThumbGenerator(QObject *parent, int w, int h) :
00060 MThread("ThumbGenerator"), m_parent(parent),
00061 m_isGallery(false), m_width(w), m_height(h), m_cancel(false)
00062 {
00063 }
00064
00065 ThumbGenerator::~ThumbGenerator()
00066 {
00067 cancel();
00068 wait();
00069 }
00070
00071 void ThumbGenerator::setSize(int w, int h)
00072 {
00073 m_width = w;
00074 m_height = h;
00075 }
00076
00077 void ThumbGenerator::setDirectory(const QString& directory, bool isGallery)
00078 {
00079 m_mutex.lock();
00080 m_directory = directory;
00081 m_isGallery = isGallery;
00082 m_mutex.unlock();
00083 }
00084
00085 void ThumbGenerator::addFile(const QString& filePath)
00086 {
00087
00088
00089 m_mutex.lock();
00090 m_fileList.append(filePath);
00091 m_mutex.unlock();
00092 }
00093
00094 void ThumbGenerator::cancel()
00095 {
00096 m_mutex.lock();
00097 m_fileList.clear();
00098 m_cancel = true;
00099 m_mutex.unlock();
00100 }
00101
00102 void ThumbGenerator::run()
00103 {
00104 RunProlog();
00105
00106 m_cancel = false;
00107 while (moreWork() && !m_cancel)
00108 {
00109 QString file, dir;
00110 bool isGallery;
00111
00112 m_mutex.lock();
00113 dir = m_directory;
00114 isGallery = m_isGallery;
00115 file = m_fileList.first();
00116 if (!m_fileList.isEmpty())
00117 m_fileList.pop_front();
00118 m_mutex.unlock();
00119 if (file.isEmpty())
00120 continue;
00121
00122 QString filePath = dir + QString("/") + file;
00123 QFileInfo fileInfo(filePath);
00124 if (!fileInfo.exists())
00125 continue;
00126
00127 if (isGallery)
00128 {
00129 if (fileInfo.isDir())
00130 isGallery = checkGalleryDir(fileInfo);
00131 else
00132 isGallery = checkGalleryFile(fileInfo);
00133 }
00134
00135 if (!isGallery)
00136 {
00137 QString cachePath = QString("%1%2.jpg").arg(getThumbcacheDir(dir))
00138 .arg(file);
00139 QFileInfo cacheInfo(cachePath);
00140
00141 if (cacheInfo.exists() &&
00142 cacheInfo.lastModified() >= fileInfo.lastModified())
00143 {
00144 continue;
00145 }
00146 else
00147 {
00148
00149 QImage image;
00150
00151
00152 if (cacheInfo.exists())
00153 QFile::remove(cachePath);
00154
00155 if (fileInfo.isDir())
00156 loadDir(image, fileInfo);
00157 else
00158 loadFile(image, fileInfo);
00159
00160 if (image.isNull())
00161 continue;
00162
00163
00164 if (GalleryUtil::IsMovie(fileInfo.filePath()))
00165 {
00166 QString screenshotPath = QString("%1%2-screenshot.jpg")
00167 .arg(getThumbcacheDir(dir))
00168 .arg(file);
00169 image.save(screenshotPath, "JPEG", 95);
00170 }
00171
00172 image = image.scaled(m_width,m_height,
00173 Qt::KeepAspectRatio, Qt::SmoothTransformation);
00174 image.save(cachePath, "JPEG", 95);
00175
00176
00177 ThumbData *td = new ThumbData;
00178 td->directory = dir;
00179 td->fileName = file;
00180 td->thumb = image.copy();
00181
00182
00183 QApplication::postEvent(m_parent, new ThumbGenEvent(td));
00184
00185 }
00186 }
00187 }
00188
00189 RunEpilog();
00190 }
00191
00192 bool ThumbGenerator::moreWork()
00193 {
00194 bool result;
00195 m_mutex.lock();
00196 result = !m_fileList.isEmpty();
00197 m_mutex.unlock();
00198 return result;
00199 }
00200
00201 bool ThumbGenerator::checkGalleryDir(const QFileInfo& fi)
00202 {
00203
00204 QDir subdir(fi.absoluteFilePath(), "*.highlight.*", QDir::Name,
00205 QDir::Files);
00206
00207
00208 if (subdir.count() > 0)
00209 {
00210
00211 QString path(subdir.entryInfoList().begin()->absoluteFilePath());
00212 QImageReader testread(path);
00213 return testread.canRead();
00214 }
00215 else
00216 return false;
00217 }
00218
00219 bool ThumbGenerator::checkGalleryFile(const QFileInfo& fi)
00220 {
00221
00222
00223 QString fn = fi.fileName();
00224 int firstDot = fn.indexOf('.');
00225 if (firstDot > 0)
00226 {
00227 fn.insert(firstDot, ".thumb");
00228 QFileInfo galThumb(fi.absolutePath() + "/" + fn);
00229 if (galThumb.exists())
00230 {
00231 QImageReader testread(galThumb.absoluteFilePath());
00232 return testread.canRead();
00233 }
00234 else
00235 return false;
00236 }
00237 return false;
00238 }
00239
00240 void ThumbGenerator::loadDir(QImage& image, const QFileInfo& fi)
00241 {
00242 QDir dir(fi.absoluteFilePath());
00243 dir.setFilter(QDir::Files);
00244
00245 QFileInfoList list = dir.entryInfoList();
00246
00247 for (QFileInfoList::const_iterator it = list.begin();
00248 it != list.end() && !m_cancel; ++it)
00249 {
00250 const QFileInfo *f = &(*it);
00251 QImageReader testread(f->absoluteFilePath());
00252 if (testread.canRead())
00253 {
00254 loadFile(image, *f);
00255 return;
00256 }
00257 }
00258
00259
00260 if (m_cancel)
00261 return;
00262
00263
00264
00265 dir.setFilter(QDir::Dirs);
00266 QFileInfoList dirlist = dir.entryInfoList();
00267 if (dirlist.isEmpty())
00268 return;
00269
00270 for (QFileInfoList::const_iterator it = dirlist.begin();
00271 it != dirlist.end() && image.isNull() && !m_cancel; ++it)
00272 {
00273 const QFileInfo *f = &(*it);
00274
00275 if (f->fileName() == "." || f->fileName() == "..")
00276 continue;
00277
00278 loadDir(image, *f);
00279 }
00280 }
00281
00282 void ThumbGenerator::loadFile(QImage& image, const QFileInfo& fi)
00283 {
00284 static int sequence = 0;
00285
00286 if (GalleryUtil::IsMovie(fi.filePath()))
00287 {
00288 bool thumbnailCreated = false;
00289 QDir tmpDir("/tmp/mythgallery");
00290 if (!tmpDir.exists())
00291 {
00292 if (!tmpDir.mkdir(tmpDir.absolutePath()))
00293 {
00294 LOG(VB_GENERAL, LOG_ERR,
00295 "Unable to create temp dir for movie thumbnail creation: " +
00296 tmpDir.absolutePath());
00297 }
00298 }
00299
00300 if (tmpDir.exists())
00301 {
00302 QString thumbFile = QString("%1.png")
00303 .arg(++sequence,8,10,QChar('0'));
00304
00305 QString cmd = "mythpreviewgen";
00306 QStringList args;
00307 args << logPropagateArgs.split(" ", QString::SkipEmptyParts);
00308 args << "--infile" << '"' + fi.absoluteFilePath() + '"';
00309 args << "--outfile" << '"' + tmpDir.filePath(thumbFile) + '"';
00310
00311 MythSystem ms(cmd, args, kMSRunShell);
00312 ms.SetDirectory(tmpDir.absolutePath());
00313 ms.Run();
00314 if (ms.Wait() == GENERIC_EXIT_OK)
00315 {
00316 QFileInfo thumb(tmpDir.filePath(thumbFile));
00317 if (thumb.exists())
00318 {
00319 QImage img(thumb.absoluteFilePath());
00320 image = img;
00321 thumbnailCreated = true;
00322 }
00323 }
00324 }
00325
00326 if (!thumbnailCreated)
00327 {
00328 QImage *img = GetMythUI()->LoadScaleImage("gallery-moviethumb.png");
00329 if (img)
00330 {
00331 image = *img;
00332 }
00333 }
00334 }
00335 else
00336 {
00337 #ifdef EXIF_SUPPORT
00338
00339 ExifData *ed = exif_data_new_from_file(fi.absoluteFilePath()
00340 .toLocal8Bit().constData());
00341 if (ed && ed->data)
00342 {
00343 image.loadFromData(ed->data, ed->size);
00344 }
00345
00346 if (ed)
00347 exif_data_free(ed);
00348
00349 if (image.width() > m_width && image.height() > m_height)
00350 return;
00351 #endif
00352
00353 #ifdef DCRAW_SUPPORT
00354 QString extension = fi.suffix();
00355 QSet<QString> dcrawFormats = DcrawFormats::getFormats();
00356 int rotateAngle;
00357
00358 if (dcrawFormats.contains(extension) &&
00359 (rotateAngle = DcrawHandler::loadThumbnail(&image,
00360 fi.absoluteFilePath())) != -1 &&
00361 image.width() > m_width && image.height() > m_height)
00362 {
00363 if (rotateAngle != 0)
00364 {
00365 QMatrix matrix;
00366 matrix.rotate(rotateAngle);
00367 image = image.transformed(matrix);
00368 }
00369
00370 return;
00371 }
00372 #endif
00373
00374 image.load(fi.absoluteFilePath());
00375 }
00376 }
00377
00378
00379 QString ThumbGenerator::getThumbcacheDir(const QString& inDir)
00380 {
00381 QString galleryDir = gCoreContext->GetSetting("GalleryDir");
00382
00383
00384
00385
00386 QString aPath = inDir + QString("/.thumbcache/");
00387 QDir dir(aPath);
00388 if (gCoreContext->GetNumSetting("GalleryThumbnailLocation") &&
00389 !dir.exists() && inDir.startsWith(galleryDir))
00390 {
00391 dir.mkpath(aPath);
00392 }
00393
00394 if (!gCoreContext->GetNumSetting("GalleryThumbnailLocation") ||
00395 !dir.exists() || !inDir.startsWith(galleryDir))
00396 {
00397
00398
00399 int prefixLen = galleryDir.length();
00400 QString location = "";
00401 if (prefixLen < inDir.length())
00402 location = QString("%1/")
00403 .arg(inDir.right(inDir.length() - prefixLen));
00404 aPath = QString("%1/MythGallery/%2").arg(GetConfDir())
00405 .arg(location);
00406 dir.setPath(aPath);
00407 dir.mkpath(aPath);
00408 }
00409
00410 return aPath;
00411 }
00412
00413
00414
00415