00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <qfileinfo.h>
00020 #include <qdir.h>
00021
00022 #include <mythtv/mythcontext.h>
00023 #include <mythtv/mythdbcon.h>
00024 #include <mythtv/util.h>
00025
00026 #include "config.h"
00027 #include "constants.h"
00028 #include "galleryutil.h"
00029 #include "thumbgenerator.h"
00030
00031 #ifdef EXIF_SUPPORT
00032 #include <libexif/exif-data.h>
00033 #include <libexif/exif-entry.h>
00034
00035 #endif // EXIF_SUPPORT
00036
00037 #define LOC QString("GalleryUtil:")
00038 #define LOC_ERR QString("GalleryUtil, Error:")
00039
00040 static QFileInfo MakeUnique(const QFileInfo &dest);
00041 static QFileInfo MakeUniqueDirectory(const QFileInfo &dest);
00042 static bool FileCopy(const QFileInfo &src, const QFileInfo &dst);
00043 static bool FileMove(const QFileInfo &src, const QFileInfo &dst);
00044 static bool FileDelete(const QFileInfo &file);
00045
00046 bool GalleryUtil::isImage(const char* filePath)
00047 {
00048 QFileInfo fi(filePath);
00049 return !fi.isDir() && IMAGE_FILENAMES.find(fi.extension(FALSE)) != -1;
00050 }
00051
00052 bool GalleryUtil::isMovie(const char* filePath)
00053 {
00054 QFileInfo fi(filePath);
00055 return !fi.isDir() && MOVIE_FILENAMES.find(fi.extension(FALSE)) != -1;
00056 }
00057
00058 long GalleryUtil::GetNaturalRotation(const char* filePath)
00059 {
00060 long rotateAngle = 0;
00061
00062 try
00063 {
00064 #ifdef EXIF_SUPPORT
00065 char *exifvalue = new char[1024];
00066 ExifData *data = exif_data_new_from_file (filePath);
00067 if (data)
00068 {
00069 for (int i = 0; i < EXIF_IFD_COUNT; i++)
00070 {
00071 ExifEntry *entry = exif_content_get_entry (data->ifd[i],
00072 EXIF_TAG_ORIENTATION);
00073 if (entry)
00074 {
00075 #if NEW_LIB_EXIF
00076 exif_entry_get_value(entry, exifvalue, 1023);
00077 QString value = exifvalue;
00078 #else
00079 QString value = exif_entry_get_value(entry);
00080 #endif
00081 if (value == "left - bottom")
00082 {
00083 rotateAngle = -90;
00084 }
00085 else if (value == "right - top")
00086 {
00087 rotateAngle = 90;
00088 }
00089 break;
00090 }
00091 }
00092 exif_data_free(data);
00093 }
00094 else
00095 {
00096 VERBOSE(VB_IMPORTANT, LOC_ERR +
00097 QString("Could not load exif data from '%1'")
00098 .arg(filePath));
00099 }
00100
00101 delete [] exifvalue;
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128 #endif // EXIF_SUPPORT
00129 }
00130 catch (...)
00131 {
00132 VERBOSE(VB_IMPORTANT, LOC_ERR +
00133 QString("Failed to extract EXIF headers from '%1'")
00134 .arg(filePath));
00135 }
00136
00137 return rotateAngle;
00138 }
00139
00140 bool GalleryUtil::LoadDirectory(ThumbList& itemList, const QString& dir,
00141 int sortorder, bool recurse,
00142 ThumbDict *itemDict, ThumbGenerator* thumbGen)
00143 {
00144 QString blah = dir;
00145 QDir d(blah);
00146 QString currDir = d.absPath();
00147
00148 bool isGallery;
00149 const QFileInfoList* gList = d.entryInfoList("serial*.dat", QDir::Files);
00150 if (gList)
00151 isGallery = (gList->count() != 0);
00152 else
00153 isGallery = false;
00154
00155
00156 if (thumbGen)
00157 thumbGen->getThumbcacheDir(currDir);
00158
00159 d.setNameFilter(MEDIA_FILENAMES);
00160 d.setSorting(sortorder);
00161
00162 d.setMatchAllDirs(true);
00163 const QFileInfoList *list = d.entryInfoList();
00164 if (!list)
00165 return false;
00166
00167 QFileInfoListIterator it(*list);
00168 QFileInfo *fi;
00169
00170 if (thumbGen)
00171 {
00172 thumbGen->cancel();
00173 thumbGen->setDirectory(currDir, isGallery);
00174 }
00175
00176 while ((fi = it.current()) != 0)
00177 {
00178 ++it;
00179 if (fi->fileName() == "." || fi->fileName() == "..")
00180 continue;
00181
00182
00183 if (isGallery && (
00184 (fi->fileName().find(".thumb.") > 0) ||
00185 (fi->fileName().find(".sized.") > 0) ||
00186 (fi->fileName().find(".highlight.") > 0)))
00187 continue;
00188
00189 if (fi->isDir() && recurse)
00190 {
00191 GalleryUtil::LoadDirectory(
00192 itemList, QDir::cleanDirPath(fi->absFilePath()),
00193 sortorder, true, itemDict, thumbGen);
00194 }
00195 else
00196 {
00197 ThumbItem *item = new ThumbItem(
00198 fi->fileName(),
00199 QDir::cleanDirPath(fi->absFilePath()), fi->isDir());
00200
00201 itemList.append(item);
00202
00203 if (itemDict)
00204 itemDict->insert(item->GetName(), item);
00205
00206 if (thumbGen)
00207 thumbGen->addFile(item->GetName());
00208 }
00209 }
00210
00211 if (thumbGen && !thumbGen->running())
00212 {
00213 thumbGen->start();
00214 }
00215
00216 return isGallery;
00217 }
00218
00219 QString GalleryUtil::GetCaption(const QString &filePath)
00220 {
00221 QString caption("");
00222
00223 try
00224 {
00225 #ifdef EXIF_SUPPORT
00226 char *exifvalue = new char[1024];
00227 ExifData *data = exif_data_new_from_file (filePath);
00228 if (data)
00229 {
00230 for (int i = 0; i < EXIF_IFD_COUNT; i++)
00231 {
00232 ExifEntry *entry = exif_content_get_entry (data->ifd[i],
00233 EXIF_TAG_USER_COMMENT);
00234 if (entry)
00235 {
00236 #if NEW_LIB_EXIF
00237 exif_entry_get_value(entry, exifvalue, 1023);
00238 caption = exifvalue;
00239 #else
00240 caption = exif_entry_get_value(entry);
00241 #endif
00242
00243 if(!caption.isEmpty())
00244 break;
00245 }
00246
00247 entry = exif_content_get_entry (data->ifd[i],
00248 EXIF_TAG_IMAGE_DESCRIPTION);
00249 if (entry)
00250 {
00251 #if NEW_LIB_EXIF
00252 exif_entry_get_value(entry, exifvalue, 1023);
00253 caption = exifvalue;
00254 #else
00255 caption = exif_entry_get_value(entry);
00256 #endif
00257
00258 if(!caption.isEmpty())
00259 break;
00260 }
00261 }
00262 exif_data_free(data);
00263 }
00264 else
00265 {
00266 VERBOSE(VB_IMPORTANT, LOC_ERR +
00267 QString("Could not load exif data from '%1'")
00268 .arg(filePath));
00269 }
00270
00271 delete [] exifvalue;
00272 #endif // EXIF_SUPPORT
00273 }
00274 catch (...)
00275 {
00276 VERBOSE(VB_IMPORTANT, LOC_ERR +
00277 QString("Failed to extract EXIF headers from '%1'")
00278 .arg(filePath));
00279 }
00280
00281 return caption;
00282 }
00283
00284 bool GalleryUtil::Copy(const QFileInfo &src, QFileInfo &dst)
00285 {
00286 if (src.isDir())
00287 return CopyDirectory(src, dst);
00288
00289 dst = MakeUnique(dst);
00290
00291 if (!FileCopy(src, dst))
00292 return false;
00293
00294 MSqlQuery query(MSqlQuery::InitCon());
00295 query.prepare("INSERT INTO gallerymetadata (image, angle) "
00296 "SELECT :IMAGENEW , angle "
00297 "FROM gallerymetadata "
00298 "WHERE image = :IMAGEOLD");
00299 query.bindValue(":IMAGENEW", dst.absFilePath().utf8());
00300 query.bindValue(":IMAGEOLD", src.absFilePath().utf8());
00301 if (query.exec())
00302 return true;
00303
00304
00305 FileDelete(dst);
00306 return false;
00307 }
00308
00309 bool GalleryUtil::Move(const QFileInfo &src, QFileInfo &dst)
00310 {
00311 if (src.isDir())
00312 return MoveDirectory(src, dst);
00313
00314 dst = MakeUnique(dst);
00315
00316 if (!FileMove(src, dst))
00317 return false;
00318
00319 MSqlQuery query(MSqlQuery::InitCon());
00320 query.prepare("UPDATE gallerymetadata "
00321 "SET image = :IMAGENEW "
00322 "WHERE image = :IMAGEOLD");
00323 query.bindValue(":IMAGENEW", dst.absFilePath().utf8());
00324 query.bindValue(":IMAGEOLD", src.absFilePath().utf8());
00325 if (query.exec())
00326 return true;
00327
00328
00329 FileMove(dst, src);
00330 return false;
00331 }
00332
00333 bool GalleryUtil::Delete(const QFileInfo &file)
00334 {
00335 if (!file.exists())
00336 return false;
00337
00338 if (file.isDir())
00339 return DeleteDirectory(file);
00340
00341 MSqlQuery query(MSqlQuery::InitCon());
00342 query.prepare("DELETE FROM gallerymetadata "
00343 "WHERE image = :IMAGE ;");
00344 query.bindValue(":IMAGE", file.absFilePath().utf8());
00345 if (query.exec())
00346 return FileDelete(file);
00347
00348 return false;
00349 }
00350
00351 bool GalleryUtil::Rename(const QString &currDir, const QString &oldName,
00352 const QString &newName)
00353 {
00354
00355 QFileInfo fi(currDir + '/' + newName);
00356 if (fi.exists())
00357 return false;
00358
00359 fi.setFile(currDir + '/' + oldName);
00360 if (fi.isDir())
00361 return RenameDirectory(currDir, oldName, newName);
00362
00363
00364 QDir cdir(currDir);
00365 if (!cdir.rename(oldName, newName))
00366 return false;
00367
00368
00369 if (QFile::exists(currDir + "/.thumbcache/" + oldName))
00370 {
00371 QDir d(currDir + "/.thumbcache/");
00372 d.rename(oldName, newName);
00373 }
00374
00375 int prefixLen = gContext->GetSetting("GalleryDir").length();
00376 QString path = gContext->GetConfDir() + "/MythGallery";
00377 path += currDir.right(currDir.length() - prefixLen);
00378 path += QString("/.thumbcache/");
00379 if (QFile::exists(path + oldName))
00380 {
00381 QDir d(path);
00382 d.rename(oldName, newName);
00383 }
00384
00385
00386 MSqlQuery query(MSqlQuery::InitCon());
00387 query.prepare("UPDATE gallerymetadata "
00388 "SET image = :IMAGENEW "
00389 "WHERE image = :IMAGEOLD");
00390 query.bindValue(":IMAGENEW", QString(currDir + '/' + newName).utf8());
00391 query.bindValue(":IMAGEOLD", QString(currDir + '/' + oldName).utf8());
00392 if (query.exec())
00393 return true;
00394
00395
00396 cdir.rename(newName, oldName);
00397 return false;
00398 }
00399
00400 QSize GalleryUtil::ScaleToDest(const QSize &src, const QSize &dest, bool scaleMax)
00401 {
00402 QSize sz = src;
00403
00404
00405 double pixelAspect = MythGetPixelAspectRatio();
00406
00407
00408 double imageAspect = 1.0;
00409 if ((sz.width() > 0) && (sz.height() > 0))
00410 imageAspect = (double)sz.width() / (double)sz.height();
00411
00412 int scaleWidth;
00413 int scaleHeight;
00414 if (scaleMax)
00415 {
00416
00417 scaleWidth = dest.width();
00418 scaleHeight = (int)((float)dest.width() * pixelAspect / imageAspect);
00419 if (scaleHeight < dest.height())
00420 {
00421
00422 scaleWidth = (int)((float)dest.height() * imageAspect / pixelAspect);
00423 scaleHeight = dest.height();
00424 }
00425 }
00426 else
00427 {
00428
00429 scaleWidth = (int)((float)dest.height() * imageAspect / pixelAspect);
00430 scaleHeight = dest.height();
00431 if (scaleWidth > dest.width())
00432 {
00433
00434 scaleWidth = dest.width();
00435 scaleHeight = (int)((float)dest.width() * pixelAspect / imageAspect);
00436 }
00437 }
00438
00439 sz.scale(scaleWidth, scaleHeight, QSize::ScaleFree);
00440 return sz;
00441 }
00442
00443 bool GalleryUtil::CopyDirectory(const QFileInfo src, QFileInfo &dst)
00444 {
00445 QDir srcDir(src.absFilePath());
00446
00447 dst = MakeUniqueDirectory(dst);
00448 if (!dst.exists())
00449 {
00450 srcDir.mkdir(dst.absFilePath());
00451 dst.refresh();
00452 }
00453
00454 if (!dst.exists() || !dst.isDir())
00455 return false;
00456
00457 bool ok = true;
00458 QDir dstDir(dst.absFilePath());
00459 QFileInfoListIterator it(*srcDir.entryInfoList());
00460 for (; it.current(); ++it)
00461 {
00462 const QString fn = (it.current())->fileName();
00463 if (fn != "." && fn != "..")
00464 {
00465 QFileInfo dfi(dstDir, fn);
00466 ok &= Copy(*(it.current()), dfi);
00467 }
00468 }
00469
00470 return ok;
00471 }
00472
00473 bool GalleryUtil::MoveDirectory(const QFileInfo src, QFileInfo &dst)
00474 {
00475 QDir srcDir(src.absFilePath());
00476
00477 dst = MakeUniqueDirectory(dst);
00478 if (!dst.exists())
00479 {
00480 srcDir.mkdir(dst.absFilePath());
00481 dst.refresh();
00482 }
00483
00484 if (!dst.exists() || !dst.isDir())
00485 return false;
00486
00487 bool ok = true;
00488 QDir dstDir(dst.absFilePath());
00489 QFileInfoListIterator it(*srcDir.entryInfoList());
00490 for (; it.current(); ++it)
00491 {
00492 const QString fn = (it.current())->fileName();
00493 if (fn != "." && fn != "..")
00494 {
00495 QFileInfo dfi(dstDir, fn);
00496 ok &= Move(*(it.current()), dfi);
00497 }
00498 }
00499
00500 return ok && FileDelete(src);
00501 }
00502
00503 bool GalleryUtil::DeleteDirectory(const QFileInfo &dir)
00504 {
00505 if (!dir.exists())
00506 return false;
00507
00508 QDir srcDir(dir.absFilePath());
00509 QFileInfoListIterator it(*srcDir.entryInfoList());
00510 for (; it.current(); ++it)
00511 {
00512 const QString fn = (it.current())->fileName();
00513 if (fn != "." && fn != "..")
00514 Delete(*(it.current()));
00515 }
00516
00517 return FileDelete(dir);
00518 }
00519
00520 bool GalleryUtil::RenameDirectory(const QString &currDir, const QString &oldName,
00521 const QString &newName)
00522 {
00523
00524 QDir cdir(currDir);
00525 if (!cdir.rename(oldName, newName))
00526 return false;
00527
00528
00529 if (QFile::exists(currDir + "/.thumbcache/" + oldName))
00530 {
00531 QDir d(currDir + "/.thumbcache/");
00532 d.rename(oldName, newName);
00533 }
00534
00535
00536 int prefixLen = gContext->GetSetting("GalleryDir").length();
00537 QString path = gContext->GetConfDir() + "/MythGallery";
00538 path += currDir.right(currDir.length() - prefixLen) + '/';
00539 if (QFile::exists(path + oldName))
00540 {
00541 QDir d(path);
00542 d.rename(oldName, newName);
00543
00544
00545 path += QString(".thumbcache/");
00546 if (QFile::exists(path + oldName))
00547 {
00548 QDir d(path);
00549 d.rename(oldName, newName);
00550 }
00551 }
00552
00553
00554 MSqlQuery query(MSqlQuery::InitCon());
00555 query.prepare("SELECT image, angle FROM gallerymetadata "
00556 "WHERE image LIKE :IMAGEOLD");
00557 query.bindValue(":IMAGEOLD", QString(currDir + '/' + oldName + '%').utf8());
00558 if (query.exec())
00559 {
00560 while (query.next())
00561 {
00562 QString oldImage = query.value(0).asString();
00563 oldImage = QString::fromUtf8(oldImage);
00564 QString newImage = oldImage;
00565 newImage = newImage.replace(currDir + '/' + oldName,
00566 currDir + '/' + newName);
00567
00568 MSqlQuery subquery(MSqlQuery::InitCon());
00569 subquery.prepare("UPDATE gallerymetadata "
00570 "SET image = :IMAGENEW "
00571 "WHERE image = :IMAGEOLD");
00572 subquery.bindValue(":IMAGENEW", newImage);
00573 subquery.bindValue(":IMAGEOLD", oldImage);
00574 subquery.exec();
00575 }
00576 }
00577
00578 return true;
00579 }
00580
00581 static QFileInfo MakeUnique(const QFileInfo &dest)
00582 {
00583 QFileInfo newDest = dest;
00584
00585 for (uint i = 0; newDest.exists(); i++)
00586 {
00587 QString basename = QString("%1_%2.%3")
00588 .arg(dest.baseName(false)).arg(i).arg(dest.extension());
00589
00590 newDest.setFile(dest.dir(), basename);
00591
00592 VERBOSE(VB_GENERAL, LOC_ERR +
00593 QString("Need to find a new name for '%1' trying '%2'")
00594 .arg(dest.absFilePath()).arg(newDest.absFilePath()));
00595 }
00596
00597 return newDest;
00598 }
00599
00600 static QFileInfo MakeUniqueDirectory(const QFileInfo &dest)
00601 {
00602 QFileInfo newDest = dest;
00603
00604 for (uint i = 0; newDest.exists() && !newDest.isDir(); i++)
00605 {
00606 QString fullname = QString("%1_%2").arg(dest.absFilePath()).arg(i);
00607 newDest.setFile(fullname);
00608
00609 VERBOSE(VB_GENERAL, LOC_ERR +
00610 QString("Need to find a new name for '%1' trying '%2'")
00611 .arg(dest.absFilePath()).arg(newDest.absFilePath()));
00612 }
00613
00614 return newDest;
00615 }
00616
00617 static bool FileCopy(const QFileInfo &src, const QFileInfo &dst)
00618 {
00619 const int bufferSize = 16*1024;
00620
00621 QFile s(src.absFilePath());
00622 QFile d(dst.absFilePath());
00623 char buffer[bufferSize];
00624 int len;
00625
00626 if (!s.open(IO_ReadOnly))
00627 return false;
00628
00629 if (!d.open(IO_WriteOnly))
00630 {
00631 s.close();
00632 return false;
00633 }
00634
00635 len = s.readBlock(buffer, bufferSize);
00636 do
00637 {
00638 d.writeBlock(buffer, len);
00639 len = s.readBlock(buffer, bufferSize);
00640 } while (len > 0);
00641
00642 s.close();
00643 d.close();
00644
00645 return true;
00646 }
00647
00648 static bool FileMove(const QFileInfo &src, const QFileInfo &dst)
00649 {
00650
00651
00652 if (rename(src.absFilePath().local8Bit(),
00653 dst.absFilePath().local8Bit()) == 0)
00654 {
00655 return true;
00656 }
00657
00658
00659 if (errno == EXDEV)
00660 {
00661 if (FileCopy(src, dst))
00662 return FileDelete(src);
00663 }
00664
00665 return false;
00666 }
00667
00668 static bool FileDelete(const QFileInfo &file)
00669 {
00670 if (!file.isDir())
00671 return QFile::remove(file.absFilePath());
00672
00673
00674 QDir srcDir(file.absFilePath());
00675 QFileInfo tc(srcDir, ".thumbcache");
00676 GalleryUtil::Delete(tc);
00677
00678 srcDir.rmdir(srcDir.absPath());
00679
00680 return true;
00681 }