File indexing completed on 2025-01-05 03:59:46
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2003-01-21 0007 * Description : Gphoto2 camera interface 0008 * 0009 * SPDX-FileCopyrightText: 2003-2005 by Renchi Raju <renchi dot raju at gmail dot com> 0010 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2006-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "gpcamera.h" 0018 0019 // C ANSI includes 0020 0021 extern "C" 0022 { 0023 #ifndef Q_CC_MSVC 0024 # include <unistd.h> 0025 # include <utime.h> 0026 #else 0027 # include <sys/utime.h> 0028 #endif 0029 } 0030 0031 // C++ includes 0032 0033 #include <cstdio> 0034 #include <iostream> 0035 0036 // Qt includes 0037 0038 #include <QString> 0039 #include <QStringList> 0040 #include <QImage> 0041 #include <QPixmap> 0042 #include <QFile> 0043 #include <QScopedPointer> 0044 #include <QDateTime> 0045 #include <QTextDocument> 0046 #include <QCryptographicHash> 0047 0048 // KDE includes 0049 0050 #include <klocalizedstring.h> 0051 0052 // Local includes 0053 0054 #include "digikam_debug.h" 0055 #include "digikam_config.h" 0056 #include "dfileoperations.h" 0057 #include "dmetadata.h" 0058 0059 //#define GPHOTO2_DEBUG 1 0060 0061 #ifdef HAVE_GPHOTO2 0062 0063 // LibGphoto2 includes 0064 0065 extern "C" 0066 { 0067 #include <gphoto2.h> 0068 } 0069 0070 #endif // HAVE_GPHOTO2 0071 0072 namespace Digikam 0073 { 0074 0075 class Q_DECL_HIDDEN GPStatus 0076 { 0077 public: 0078 0079 GPStatus() 0080 { 0081 0082 #ifdef HAVE_GPHOTO2 0083 0084 context = gp_context_new(); 0085 cancel = false; 0086 gp_context_set_cancel_func(context, cancel_func, nullptr); 0087 0088 # ifdef GPHOTO2_DEBUG 0089 0090 gp_context_set_progress_funcs(context, start_func, update_func, stop_func, 0); 0091 gp_context_set_error_func(context, error_func, 0); 0092 gp_context_set_status_func(context, status_func, 0); 0093 0094 # endif // GPHOTO2_DEBUG 0095 0096 #endif // HAVE_GPHOTO2 0097 0098 } 0099 0100 ~GPStatus() 0101 { 0102 0103 #ifdef HAVE_GPHOTO2 0104 0105 gp_context_unref(context); 0106 cancel = false; 0107 0108 #endif // HAVE_GPHOTO2 0109 0110 } 0111 0112 static bool cancel; 0113 0114 #ifdef HAVE_GPHOTO2 0115 0116 GPContext* context; 0117 0118 static GPContextFeedback cancel_func(GPContext*, void*) 0119 { 0120 return (cancel ? GP_CONTEXT_FEEDBACK_CANCEL : 0121 GP_CONTEXT_FEEDBACK_OK); 0122 } 0123 0124 # ifdef GPHOTO2_DEBUG 0125 0126 static void error_func(GPContext*, const char* msg, void*) 0127 { 0128 qCDebug(DIGIKAM_IMPORTUI_LOG) << "error:" << msg; 0129 } 0130 0131 static void status_func(GPContext*, const char* msg, void*) 0132 { 0133 qCDebug(DIGIKAM_IMPORTUI_LOG) << "status:" << msg; 0134 } 0135 0136 static unsigned int start_func(GPContext*, float target, const char *text, void *data) 0137 { 0138 Q_UNUSED(data); 0139 qCDebug(DIGIKAM_IMPORTUI_LOG) << "start:" << target << "- text:" << text; 0140 return 0; 0141 0142 } 0143 0144 static void update_func(GPContext*, unsigned int id, float target, void *data) 0145 { 0146 Q_UNUSED(data); 0147 qCDebug(DIGIKAM_IMPORTUI_LOG) << "update:" << id << "- target:" << target; 0148 } 0149 0150 static void stop_func(GPContext*, unsigned int id, void *data) 0151 { 0152 Q_UNUSED(data); 0153 qCDebug(DIGIKAM_IMPORTUI_LOG) << "stop:" << id; 0154 } 0155 0156 # endif // GPHOTO2_DEBUG 0157 0158 #endif // HAVE_GPHOTO2 0159 0160 }; 0161 0162 bool GPStatus::cancel = false; 0163 0164 // --------------------------------------------------------------------------- 0165 0166 class Q_DECL_HIDDEN GPCamera::Private 0167 { 0168 0169 public: 0170 0171 explicit Private() 0172 0173 #ifdef HAVE_GPHOTO2 0174 0175 : cameraInitialized(false), 0176 camera (nullptr), 0177 status (nullptr) 0178 0179 #endif // HAVE_GPHOTO2 0180 0181 { 0182 } 0183 0184 #ifdef HAVE_GPHOTO2 0185 0186 bool cameraInitialized; 0187 0188 Camera* camera; 0189 0190 CameraAbilities cameraAbilities; 0191 0192 GPStatus* status; 0193 0194 #endif // HAVE_GPHOTO2 0195 0196 }; 0197 0198 // --------------------------------------------------------------------------- 0199 0200 GPCamera::GPCamera(const QString& title, const QString& model, 0201 const QString& port, const QString& path) 0202 : DKCamera(title, model, port, path), 0203 d (new Private) 0204 { 0205 } 0206 0207 GPCamera::~GPCamera() 0208 { 0209 0210 #ifdef HAVE_GPHOTO2 0211 0212 if (d->status) 0213 { 0214 gp_context_unref(d->status->context); 0215 d->status = nullptr; 0216 } 0217 0218 if (d->camera) 0219 { 0220 gp_camera_unref(d->camera); 0221 d->camera = nullptr; 0222 } 0223 0224 #endif // HAVE_GPHOTO2 0225 0226 delete d; 0227 } 0228 0229 DKCamera::CameraDriverType GPCamera::cameraDriverType() 0230 { 0231 return DKCamera::GPhotoDriver; 0232 } 0233 0234 QByteArray GPCamera::cameraMD5ID() 0235 { 0236 QByteArray md5data; 0237 0238 #ifdef HAVE_GPHOTO2 0239 0240 QString camData; 0241 0242 // We don't use camera title from digiKam settings panel to compute MD5 fingerprint, 0243 // because it can be changed by users between session. 0244 0245 camData.append(model()); 0246 0247 // TODO is it really necessary to have a path here? I think model+filename+size+ctime should be enough to give unique fingerprint 0248 // while still allowing you to move files around in the camera if needed 0249 0250 camData.append(path()); 0251 QCryptographicHash md5(QCryptographicHash::Md5); 0252 md5.addData(camData.toUtf8()); 0253 md5data = md5.result().toHex(); 0254 0255 #endif // HAVE_GPHOTO2 0256 0257 return md5data; 0258 } 0259 0260 bool GPCamera::doConnect() 0261 { 0262 0263 #ifdef HAVE_GPHOTO2 0264 0265 int errorCode; 0266 0267 // -- first step - setup the camera -------------------- 0268 0269 if (d->camera) 0270 { 0271 gp_camera_unref(d->camera); 0272 d->camera = nullptr; 0273 } 0274 0275 CameraAbilitiesList* abilList = nullptr; 0276 GPPortInfoList* infoList = nullptr; 0277 GPPortInfo info; 0278 0279 gp_camera_new(&d->camera); 0280 0281 delete d->status; 0282 d->status = nullptr; 0283 d->status = new GPStatus(); 0284 0285 gp_abilities_list_new(&abilList); 0286 gp_abilities_list_load(abilList, d->status->context); 0287 gp_port_info_list_new(&infoList); 0288 gp_port_info_list_load(infoList); 0289 0290 int modelNum = gp_abilities_list_lookup_model(abilList, m_model.toLatin1().constData()); 0291 int portNum = gp_port_info_list_lookup_path(infoList, m_port.toLatin1().constData()); 0292 0293 gp_abilities_list_get_abilities(abilList, modelNum, &d->cameraAbilities); 0294 0295 errorCode = gp_camera_set_abilities(d->camera, d->cameraAbilities); 0296 0297 if (errorCode != GP_OK) 0298 { 0299 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to set camera Abilities!"; 0300 printGphotoErrorDescription(errorCode); 0301 gp_camera_unref(d->camera); 0302 d->camera = nullptr; 0303 gp_abilities_list_free(abilList); 0304 gp_port_info_list_free(infoList); 0305 return false; 0306 } 0307 0308 if (m_model != QLatin1String("Directory Browse")) 0309 { 0310 gp_port_info_list_get_info(infoList, portNum, &info); 0311 errorCode = gp_camera_set_port_info(d->camera, info); 0312 0313 if (errorCode != GP_OK) 0314 { 0315 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to set camera port!"; 0316 printGphotoErrorDescription(errorCode); 0317 gp_camera_unref(d->camera); 0318 d->camera = nullptr; 0319 gp_abilities_list_free(abilList); 0320 gp_port_info_list_free(infoList); 0321 return false; 0322 } 0323 } 0324 0325 gp_abilities_list_free(abilList); 0326 gp_port_info_list_free(infoList); 0327 0328 if (d->cameraAbilities.file_operations & 0329 GP_FILE_OPERATION_PREVIEW) 0330 { 0331 m_thumbnailSupport = true; 0332 } 0333 0334 if (d->cameraAbilities.file_operations & 0335 GP_FILE_OPERATION_DELETE) 0336 { 0337 m_deleteSupport = true; 0338 } 0339 0340 if (d->cameraAbilities.folder_operations & 0341 GP_FOLDER_OPERATION_PUT_FILE) 0342 { 0343 m_uploadSupport = true; 0344 } 0345 0346 if (d->cameraAbilities.folder_operations & 0347 GP_FOLDER_OPERATION_MAKE_DIR) 0348 { 0349 m_mkDirSupport = true; 0350 } 0351 0352 if (d->cameraAbilities.folder_operations & 0353 GP_FOLDER_OPERATION_REMOVE_DIR) 0354 { 0355 m_delDirSupport = true; 0356 } 0357 0358 if (d->cameraAbilities.operations & 0359 GP_OPERATION_CAPTURE_IMAGE) 0360 { 0361 m_captureImageSupport = true; 0362 } 0363 0364 if (d->cameraAbilities.operations & 0365 GP_OPERATION_CAPTURE_PREVIEW) 0366 { 0367 m_captureImagePreviewSupport = true; 0368 } 0369 0370 // -- Try and initialize the camera to see if its connected ----------------- 0371 0372 errorCode = gp_camera_init(d->camera, d->status->context); 0373 0374 if (errorCode != GP_OK) 0375 { 0376 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to initialize camera!"; 0377 printGphotoErrorDescription(errorCode); 0378 gp_camera_unref(d->camera); 0379 d->camera = nullptr; 0380 return false; 0381 } 0382 0383 d->cameraInitialized = true; 0384 0385 return true; 0386 0387 #else 0388 0389 return false; 0390 0391 #endif // HAVE_GPHOTO2 0392 0393 } 0394 0395 void GPCamera::cancel() 0396 { 0397 0398 #ifdef HAVE_GPHOTO2 0399 0400 /* 0401 TODO what to do on cancel, if there's nothing to cancel? 0402 0403 if (!d->status) 0404 { 0405 return; 0406 } 0407 */ 0408 0409 d->status->cancel = true; 0410 0411 #endif // HAVE_GPHOTO2 0412 0413 } 0414 0415 bool GPCamera::getFreeSpace(qint64& bytesSize, qint64& bytesAvail) 0416 { 0417 0418 #ifdef HAVE_GPHOTO2 0419 0420 // NOTE: This method depends of libgphoto2 2.4.0 0421 0422 int nrofsinfos; 0423 CameraStorageInformation* sinfos = nullptr; 0424 0425 d->status->cancel = false; 0426 int errorCode = gp_camera_get_storageinfo(d->camera, &sinfos, &nrofsinfos, d->status->context); 0427 0428 if (errorCode != GP_OK) 0429 { 0430 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Getting storage information not supported for this camera!"; 0431 printGphotoErrorDescription(errorCode); 0432 return false; 0433 } 0434 0435 for (int i = 0 ; i < nrofsinfos ; ++i) 0436 { 0437 if (sinfos[i].fields & GP_STORAGEINFO_FILESYSTEMTYPE) 0438 { 0439 switch (sinfos[i].fstype) 0440 { 0441 case GP_STORAGEINFO_FST_UNDEFINED: 0442 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage fstype: undefined"; 0443 break; 0444 case GP_STORAGEINFO_FST_GENERICFLAT: 0445 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage fstype: flat, all in one directory"; 0446 break; 0447 case GP_STORAGEINFO_FST_GENERICHIERARCHICAL: 0448 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage fstype: generic tree hierarchy"; 0449 break; 0450 case GP_STORAGEINFO_FST_DCF: 0451 qCDebug(DIGIKAM_IMPORTUI_LOG) << "DCIM style storage"; 0452 break; 0453 } 0454 } 0455 0456 if (sinfos[i].fields & GP_STORAGEINFO_LABEL) 0457 { 0458 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage label: " << QString::fromUtf8(sinfos[i].label); 0459 } 0460 0461 if (sinfos[i].fields & GP_STORAGEINFO_DESCRIPTION) 0462 { 0463 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage description: " << QString::fromUtf8(sinfos[i].description); 0464 } 0465 0466 if (sinfos[i].fields & GP_STORAGEINFO_BASE) 0467 { 0468 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage base-dir: " << QString::fromUtf8(sinfos[i].basedir); 0469 0470 // TODO in order for this to work, the upload dialog needs to be fixed. 0471 /* 0472 if (nrofsinfos == 1) 0473 { 0474 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Only one storage, so setting storage directory to" << sinfos[i].basedir; 0475 m_path = QString(sinfos[i].basedir); 0476 } 0477 */ 0478 } 0479 0480 if (sinfos[i].fields & GP_STORAGEINFO_ACCESS) 0481 { 0482 switch (sinfos[i].access) 0483 { 0484 case GP_STORAGEINFO_AC_READWRITE: 0485 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage access: R/W"; 0486 break; 0487 0488 case GP_STORAGEINFO_AC_READONLY: 0489 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage access: RO"; 0490 break; 0491 0492 case GP_STORAGEINFO_AC_READONLY_WITH_DELETE: 0493 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage access: RO + Del"; 0494 break; 0495 0496 default: 0497 break; 0498 } 0499 } 0500 0501 if (sinfos[i].fields & GP_STORAGEINFO_STORAGETYPE) 0502 { 0503 switch (sinfos[i].type) 0504 { 0505 case GP_STORAGEINFO_ST_FIXED_ROM: 0506 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage type: fixed ROM"; 0507 break; 0508 0509 case GP_STORAGEINFO_ST_REMOVABLE_ROM: 0510 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage type: removable ROM"; 0511 break; 0512 0513 case GP_STORAGEINFO_ST_FIXED_RAM: 0514 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage type: fixed RAM"; 0515 break; 0516 0517 case GP_STORAGEINFO_ST_REMOVABLE_RAM: 0518 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage type: removable RAM"; 0519 break; 0520 0521 case GP_STORAGEINFO_ST_UNKNOWN: 0522 default: 0523 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage type: unknown"; 0524 break; 0525 } 0526 } 0527 0528 if (sinfos[i].fields & GP_STORAGEINFO_MAXCAPACITY) 0529 { 0530 bytesSize += sinfos[i].capacitykbytes * 1024; 0531 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage capacity: " << bytesSize; 0532 } 0533 0534 if (sinfos[i].fields & GP_STORAGEINFO_FREESPACEKBYTES) 0535 { 0536 bytesAvail += sinfos[i].freekbytes * 1024; 0537 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Storage free-space: " << bytesAvail; 0538 } 0539 } 0540 0541 return true; 0542 0543 #else 0544 0545 Q_UNUSED(bytesSize); 0546 Q_UNUSED(bytesAvail); 0547 return false; 0548 0549 #endif // HAVE_GPHOTO2 0550 0551 } 0552 0553 bool GPCamera::getPreview(QImage& preview) 0554 { 0555 0556 #ifdef HAVE_GPHOTO2 0557 0558 int errorCode; 0559 CameraFile* cfile = nullptr; 0560 const char* data = nullptr; 0561 unsigned long int size; 0562 0563 d->status->cancel = false; 0564 gp_file_new(&cfile); 0565 0566 errorCode = gp_camera_capture_preview(d->camera, cfile, d->status->context); 0567 0568 if (errorCode != GP_OK) 0569 { 0570 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to initialize camera preview mode!"; 0571 printGphotoErrorDescription(errorCode); 0572 gp_file_unref(cfile); 0573 return false; 0574 } 0575 0576 errorCode = gp_file_get_data_and_size(cfile, &data, &size); 0577 0578 if (errorCode != GP_OK) 0579 { 0580 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get preview from camera!"; 0581 printGphotoErrorDescription(errorCode); 0582 gp_file_unref(cfile); 0583 return false; 0584 } 0585 0586 preview.loadFromData((const uchar*)data, (uint)size); 0587 0588 gp_file_unref(cfile); 0589 return true; 0590 0591 #else 0592 0593 Q_UNUSED(preview); 0594 return false; 0595 0596 #endif // HAVE_GPHOTO2 0597 0598 } 0599 0600 bool GPCamera::capture(CamItemInfo& itemInfo) 0601 { 0602 0603 #ifdef HAVE_GPHOTO2 0604 0605 int errorCode; 0606 CameraFilePath path; 0607 0608 d->status->cancel = false; 0609 errorCode = gp_camera_capture(d->camera, GP_CAPTURE_IMAGE, &path, d->status->context); 0610 0611 if (errorCode != GP_OK) 0612 { 0613 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to take camera capture!"; 0614 printGphotoErrorDescription(errorCode); 0615 return false; 0616 } 0617 0618 // Get new camera item information. 0619 0620 itemInfo.folder = QString::fromUtf8(path.folder); 0621 itemInfo.name = QString::fromUtf8(path.name); 0622 0623 CameraFileInfo info; 0624 errorCode = gp_camera_file_get_info(d->camera, QFile::encodeName(itemInfo.folder).constData(), 0625 QFile::encodeName(itemInfo.name).constData(), &info, 0626 d->status->context); 0627 0628 if (errorCode != GP_OK) 0629 { 0630 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get camera item information!"; 0631 printGphotoErrorDescription(errorCode); 0632 return false; 0633 } 0634 0635 itemInfo.ctime = QDateTime(); 0636 itemInfo.mime = QString(); 0637 itemInfo.size = -1; 0638 itemInfo.width = -1; 0639 itemInfo.height = -1; 0640 itemInfo.downloaded = CamItemInfo::DownloadUnknown; 0641 itemInfo.readPermissions = -1; 0642 itemInfo.writePermissions = -1; 0643 0644 /* The mime type returned by Gphoto2 is dummy with all RAW files. 0645 if (info.file.fields & GP_FILE_INFO_TYPE) 0646 itemInfo.mime = info.file.type;*/ 0647 0648 itemInfo.mime = mimeType(itemInfo.name.section(QLatin1Char('.'), -1).toLower()); 0649 0650 if (info.file.fields & GP_FILE_INFO_MTIME) 0651 { 0652 itemInfo.ctime = QDateTime::fromSecsSinceEpoch(info.file.mtime); 0653 } 0654 0655 if (info.file.fields & GP_FILE_INFO_SIZE) 0656 { 0657 itemInfo.size = info.file.size; 0658 } 0659 0660 if (info.file.fields & GP_FILE_INFO_WIDTH) 0661 { 0662 itemInfo.width = info.file.width; 0663 } 0664 0665 if (info.file.fields & GP_FILE_INFO_HEIGHT) 0666 { 0667 itemInfo.height = info.file.height; 0668 } 0669 0670 if (info.file.fields & GP_FILE_INFO_STATUS) 0671 { 0672 if (info.file.status == GP_FILE_STATUS_DOWNLOADED) 0673 { 0674 itemInfo.downloaded = CamItemInfo::DownloadedYes; 0675 } 0676 else 0677 { 0678 itemInfo.downloaded = CamItemInfo::DownloadedNo; 0679 } 0680 } 0681 0682 if (info.file.fields & GP_FILE_INFO_PERMISSIONS) 0683 { 0684 if (info.file.permissions & GP_FILE_PERM_READ) 0685 { 0686 itemInfo.readPermissions = 1; 0687 } 0688 else 0689 { 0690 itemInfo.readPermissions = 0; 0691 } 0692 0693 if (info.file.permissions & GP_FILE_PERM_DELETE) 0694 { 0695 itemInfo.writePermissions = 1; 0696 } 0697 else 0698 { 0699 itemInfo.writePermissions = 0; 0700 } 0701 } 0702 0703 return true; 0704 0705 #else 0706 0707 Q_UNUSED(itemInfo); 0708 return false; 0709 0710 #endif // HAVE_GPHOTO2 0711 0712 } 0713 0714 bool GPCamera::getFolders(const QString& folder) 0715 { 0716 0717 #ifdef HAVE_GPHOTO2 0718 0719 int errorCode; 0720 CameraList* clist = nullptr; 0721 gp_list_new(&clist); 0722 0723 d->status->cancel = false; 0724 errorCode = gp_camera_folder_list_folders(d->camera, QFile::encodeName(folder).constData(), clist, d->status->context); 0725 0726 if (errorCode != GP_OK) 0727 { 0728 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get folders list from camera!"; 0729 printGphotoErrorDescription(errorCode); 0730 gp_list_unref(clist); 0731 return false; 0732 } 0733 0734 QStringList subFolderList; 0735 int count = gp_list_count(clist); 0736 0737 if (count < 1) 0738 { 0739 return true; 0740 } 0741 0742 for (int i = 0 ; i < count ; ++i) 0743 { 0744 const char* subFolder = nullptr; 0745 errorCode = gp_list_get_name(clist, i, &subFolder); 0746 0747 if (errorCode != GP_OK) 0748 { 0749 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get folder name from camera!"; 0750 printGphotoErrorDescription(errorCode); 0751 gp_list_unref(clist); 0752 return false; 0753 } 0754 0755 subFolderList.append(folder + QFile::decodeName(subFolder) + QLatin1Char('/')); 0756 } 0757 0758 gp_list_unref(clist); 0759 0760 Q_EMIT signalFolderList(subFolderList); 0761 0762 return true; 0763 0764 #else 0765 0766 Q_UNUSED(folder); 0767 return false; 0768 0769 #endif // HAVE_GPHOTO2 0770 0771 } 0772 0773 // TODO unused, remove? 0774 bool GPCamera::getItemsList(const QString& folder, QStringList& itemsList) 0775 { 0776 0777 #ifdef HAVE_GPHOTO2 0778 0779 int errorCode; 0780 CameraList* clist = nullptr; 0781 const char* cname = nullptr; 0782 0783 gp_list_new(&clist); 0784 0785 d->status->cancel = false; 0786 errorCode = gp_camera_folder_list_files(d->camera, QFile::encodeName(folder).constData(), clist, d->status->context); 0787 0788 if (errorCode != GP_OK) 0789 { 0790 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get folder files list from camera!"; 0791 printGphotoErrorDescription(errorCode); 0792 gp_list_unref(clist); 0793 return false; 0794 } 0795 0796 int count = gp_list_count(clist); 0797 0798 for (int i = 0 ; i < count ; ++i) 0799 { 0800 errorCode = gp_list_get_name(clist, i, &cname); 0801 0802 if (errorCode != GP_OK) 0803 { 0804 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get file name from camera!"; 0805 printGphotoErrorDescription(errorCode); 0806 gp_list_unref(clist); 0807 return false; 0808 } 0809 0810 itemsList.append(QFile::decodeName(cname)); 0811 } 0812 0813 gp_list_unref(clist); 0814 0815 return true; 0816 0817 #else 0818 0819 Q_UNUSED(folder); 0820 Q_UNUSED(itemsList); 0821 0822 return false; 0823 0824 #endif // HAVE_GPHOTO2 0825 0826 } 0827 0828 bool GPCamera::getItemsInfoList(const QString& folder, bool useMetadata, CamItemInfoList& items) 0829 { 0830 0831 #ifdef HAVE_GPHOTO2 0832 0833 int errorCode; 0834 CameraList* clist = nullptr; 0835 const char* cname = nullptr; 0836 0837 gp_list_new(&clist); 0838 0839 d->status->cancel = false; 0840 errorCode = gp_camera_folder_list_files(d->camera, QFile::encodeName(folder).constData(), clist, d->status->context); 0841 0842 if (errorCode != GP_OK) 0843 { 0844 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get folder files list from camera!"; 0845 printGphotoErrorDescription(errorCode); 0846 gp_list_unref(clist); 0847 return false; 0848 } 0849 0850 int count = gp_list_count(clist); 0851 0852 for (int i = 0 ; i < count ; ++i) 0853 { 0854 errorCode = gp_list_get_name(clist, i, &cname); 0855 0856 if (errorCode != GP_OK) 0857 { 0858 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get file name from camera!"; 0859 printGphotoErrorDescription(errorCode); 0860 gp_list_unref(clist); 0861 return false; 0862 } 0863 0864 // TODO for further speed-up, getItemInfoInternal call could be called separately when needed 0865 CamItemInfo info; 0866 getItemInfoInternal(folder, QFile::decodeName(cname), info, useMetadata); 0867 items.append(info); 0868 } 0869 0870 gp_list_unref(clist); 0871 0872 return true; 0873 0874 #else 0875 0876 Q_UNUSED(folder); 0877 Q_UNUSED(useMetadata); 0878 Q_UNUSED(items); 0879 0880 return false; 0881 0882 #endif // HAVE_GPHOTO2 0883 0884 } 0885 0886 void GPCamera::getItemInfo(const QString& folder, const QString& itemName, CamItemInfo& info, bool useMetadata) 0887 { 0888 0889 #ifdef HAVE_GPHOTO2 0890 0891 getItemInfoInternal(folder, itemName, info, useMetadata); 0892 0893 #else 0894 Q_UNUSED(folder); 0895 Q_UNUSED(itemName); 0896 Q_UNUSED(info); 0897 Q_UNUSED(useMetadata); 0898 0899 #endif // HAVE_GPHOTO2 0900 0901 } 0902 0903 void GPCamera::getItemInfoInternal(const QString& folder, const QString& itemName, CamItemInfo& info, bool useMetadata) 0904 { 0905 0906 #ifdef HAVE_GPHOTO2 0907 0908 info.folder = folder; 0909 info.name = itemName; 0910 d->status->cancel = false; 0911 info.previewPossible = false; 0912 0913 CameraFileInfo cfinfo; 0914 gp_camera_file_get_info(d->camera, QFile::encodeName(info.folder).constData(), 0915 QFile::encodeName(info.name).constData(), &cfinfo, d->status->context); 0916 0917 // if preview has size field, it's a valid preview most likely, otherwise we'll skip it later on 0918 0919 if (cfinfo.preview.fields & GP_FILE_INFO_SIZE) 0920 { 0921 info.previewPossible = true; 0922 } 0923 0924 if (cfinfo.file.fields & GP_FILE_INFO_STATUS) 0925 { 0926 if (cfinfo.file.status == GP_FILE_STATUS_DOWNLOADED) 0927 { 0928 info.downloaded = CamItemInfo::DownloadedYes; 0929 } 0930 } 0931 0932 if (cfinfo.file.fields & GP_FILE_INFO_SIZE) 0933 { 0934 info.size = cfinfo.file.size; 0935 } 0936 0937 if (cfinfo.file.fields & GP_FILE_INFO_PERMISSIONS) 0938 { 0939 if (cfinfo.file.permissions & GP_FILE_PERM_READ) 0940 { 0941 info.readPermissions = 1; 0942 } 0943 else 0944 { 0945 info.readPermissions = 0; 0946 } 0947 0948 if (cfinfo.file.permissions & GP_FILE_PERM_DELETE) 0949 { 0950 info.writePermissions = 1; 0951 } 0952 else 0953 { 0954 info.writePermissions = 0; 0955 } 0956 } 0957 0958 /* 0959 The mime type returned by Gphoto2 is dummy with all RAW files. 0960 0961 if (cfinfo.file.fields & GP_FILE_INFO_TYPE) 0962 { 0963 info.mime = cfinfo.file.type; 0964 } 0965 */ 0966 0967 info.mime = mimeType(info.name.section(QLatin1Char('.'), -1).toLower()); 0968 0969 if (!info.mime.isEmpty()) 0970 { 0971 if (useMetadata) 0972 { 0973 // Try to use file metadata 0974 QScopedPointer<DMetadata> meta(new DMetadata); 0975 getMetadata(folder, itemName, *meta); 0976 fillItemInfoFromMetadata(info, *meta); 0977 0978 // Fall back to camera file system info 0979 0980 if (info.ctime.isNull()) 0981 { 0982 if (cfinfo.file.fields & GP_FILE_INFO_MTIME) 0983 { 0984 info.ctime = QDateTime::fromSecsSinceEpoch(cfinfo.file.mtime); 0985 } 0986 else 0987 { 0988 info.ctime = QDateTime::currentDateTime(); 0989 } 0990 0991 info.ctime.setTimeSpec(Qt::UTC); 0992 } 0993 } 0994 else 0995 { 0996 // Only use properties provided by camera. 0997 if (cfinfo.file.fields & GP_FILE_INFO_MTIME) 0998 { 0999 info.ctime = QDateTime::fromSecsSinceEpoch(cfinfo.file.mtime); 1000 } 1001 else 1002 { 1003 info.ctime = QDateTime::currentDateTime(); 1004 } 1005 1006 info.ctime.setTimeSpec(Qt::UTC); 1007 1008 if (cfinfo.file.fields & GP_FILE_INFO_WIDTH) 1009 { 1010 info.width = cfinfo.file.width; 1011 } 1012 1013 if (cfinfo.file.fields & GP_FILE_INFO_HEIGHT) 1014 { 1015 info.height = cfinfo.file.height; 1016 } 1017 } 1018 } 1019 1020 #else 1021 1022 Q_UNUSED(folder); 1023 Q_UNUSED(itemName); 1024 Q_UNUSED(info); 1025 Q_UNUSED(useMetadata); 1026 1027 #endif // HAVE_GPHOTO2 1028 1029 } 1030 1031 bool GPCamera::getThumbnail(const QString& folder, const QString& itemName, QImage& thumbnail) 1032 { 1033 #ifdef HAVE_GPHOTO2 1034 1035 int errorCode; 1036 CameraFile* cfile = nullptr; 1037 const char* data = nullptr; 1038 unsigned long int size; 1039 1040 gp_file_new(&cfile); 1041 1042 d->status->cancel = false; 1043 errorCode = gp_camera_file_get(d->camera, QFile::encodeName(folder).constData(), 1044 QFile::encodeName(itemName).constData(), 1045 GP_FILE_TYPE_PREVIEW, 1046 cfile, d->status->context); 1047 1048 if (errorCode != GP_OK) 1049 { 1050 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get camera item!" << folder << itemName; 1051 printGphotoErrorDescription(errorCode); 1052 gp_file_unref(cfile); 1053 return false; 1054 } 1055 1056 errorCode = gp_file_get_data_and_size(cfile, &data, &size); 1057 1058 if (errorCode != GP_OK) 1059 { 1060 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get thumbnail from camera item!" << folder << itemName; 1061 printGphotoErrorDescription(errorCode); 1062 gp_file_unref(cfile); 1063 return false; 1064 } 1065 1066 thumbnail.loadFromData((const uchar*) data, (uint) size); 1067 1068 gp_file_unref(cfile); 1069 return !thumbnail.isNull(); 1070 1071 #else 1072 1073 Q_UNUSED(folder); 1074 Q_UNUSED(itemName); 1075 Q_UNUSED(thumbnail); 1076 1077 return false; 1078 1079 #endif // HAVE_GPHOTO2 1080 1081 } 1082 1083 bool GPCamera::getMetadata(const QString& folder, const QString& itemName, DMetadata& meta) 1084 { 1085 #ifdef HAVE_GPHOTO2 1086 1087 int errorCode; 1088 CameraFile* cfile = nullptr; 1089 const char* data = nullptr; 1090 unsigned long int size; 1091 1092 gp_file_new(&cfile); 1093 1094 d->status->cancel = false; 1095 errorCode = gp_camera_file_get(d->camera, QFile::encodeName(folder).constData(), 1096 QFile::encodeName(itemName).constData(), 1097 GP_FILE_TYPE_EXIF, 1098 cfile, d->status->context); 1099 1100 if (errorCode != GP_OK) 1101 { 1102 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get camera item!"; 1103 printGphotoErrorDescription(errorCode); 1104 gp_file_unref(cfile); 1105 return false; 1106 } 1107 1108 errorCode = gp_file_get_data_and_size(cfile, &data, &size); 1109 1110 if (errorCode != GP_OK) 1111 { 1112 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get Exif data from camera item!"; 1113 printGphotoErrorDescription(errorCode); 1114 gp_file_unref(cfile); 1115 return false; 1116 } 1117 1118 QByteArray exifData(data, size); 1119 1120 gp_file_unref(cfile); 1121 1122 // Sometimes, GPhoto2 drivers return complete APP1 JFIF section. Exiv2 cannot 1123 // decode (yet) exif metadata from APP1. We will find Exif header to get data at this place 1124 // to please with Exiv2... 1125 1126 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Size of Exif metadata from camera = " << exifData.size(); 1127 1128 if (!exifData.isEmpty()) 1129 { 1130 char exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; 1131 int i = exifData.indexOf(*exifHeader); 1132 1133 if (i != -1) 1134 { 1135 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Exif header found at position " << i; 1136 i = i + sizeof(exifHeader); 1137 QByteArray data; 1138 data.resize(exifData.size() - i); 1139 memcpy(data.data(), exifData.data() + i, data.size()); 1140 meta.setExif(data); 1141 return true; 1142 } 1143 } 1144 1145 return false; 1146 1147 #else 1148 1149 Q_UNUSED(folder); 1150 Q_UNUSED(itemName); 1151 Q_UNUSED(meta); 1152 1153 return false; 1154 1155 #endif // HAVE_GPHOTO2 1156 } 1157 1158 bool GPCamera::downloadItem(const QString& folder, const QString& itemName, 1159 const QString& saveFile) 1160 { 1161 #ifdef HAVE_GPHOTO2 1162 1163 int errorCode; 1164 CameraFile* cfile = nullptr; 1165 1166 d->status->cancel = false; 1167 QFile file(saveFile); 1168 1169 if (!file.open(QIODevice::ReadWrite)) 1170 { 1171 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to open file" << file.fileName() << file.errorString(); 1172 return false; 1173 } 1174 1175 // dup fd, passing fd control to gphoto2 later 1176 int handle = dup(file.handle()); 1177 1178 if (handle == -1) 1179 { 1180 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to dup file descriptor"; 1181 return false; 1182 } 1183 1184 errorCode = gp_file_new_from_fd(&cfile, handle); 1185 1186 if (errorCode != GP_OK) 1187 { 1188 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get camera item!"; 1189 printGphotoErrorDescription(errorCode); 1190 return false; 1191 } 1192 1193 errorCode = gp_camera_file_get(d->camera, QFile::encodeName(folder).constData(), 1194 QFile::encodeName(itemName).constData(), 1195 GP_FILE_TYPE_NORMAL, cfile, 1196 d->status->context); 1197 1198 if (errorCode != GP_OK) 1199 { 1200 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get camera item!"; 1201 printGphotoErrorDescription(errorCode); 1202 gp_file_unref(cfile); 1203 return false; 1204 } 1205 1206 time_t mtime; 1207 errorCode = gp_file_get_mtime(cfile, &mtime); 1208 1209 file.close(); 1210 1211 if ((errorCode == GP_OK) && mtime) 1212 { 1213 DFileOperations::setModificationTime(saveFile, QDateTime::fromSecsSinceEpoch(mtime)); 1214 } 1215 1216 gp_file_unref(cfile); 1217 1218 return true; 1219 1220 #else 1221 1222 Q_UNUSED(folder); 1223 Q_UNUSED(itemName); 1224 Q_UNUSED(saveFile); 1225 1226 return false; 1227 1228 #endif // HAVE_GPHOTO2 1229 1230 } 1231 1232 bool GPCamera::setLockItem(const QString& folder, const QString& itemName, bool lock) 1233 { 1234 #ifdef HAVE_GPHOTO2 1235 1236 int errorCode; 1237 1238 d->status->cancel = false; 1239 CameraFileInfo info; 1240 errorCode = gp_camera_file_get_info(d->camera, QFile::encodeName(folder).constData(), 1241 QFile::encodeName(itemName).constData(), &info, d->status->context); 1242 1243 if (errorCode != GP_OK) 1244 { 1245 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get camera item properties!"; 1246 printGphotoErrorDescription(errorCode); 1247 return false; 1248 } 1249 1250 if (info.file.fields & GP_FILE_INFO_PERMISSIONS) 1251 { 1252 if (lock) 1253 { 1254 // Lock the file to set read only flag 1255 info.file.permissions = (CameraFilePermissions)GP_FILE_PERM_READ; 1256 } 1257 else 1258 { 1259 // Unlock the file to set read/write flag 1260 info.file.permissions = (CameraFilePermissions)(GP_FILE_PERM_READ | GP_FILE_PERM_DELETE); 1261 } 1262 } 1263 1264 // Some gphoto2 drivers need to have only the right flag at on to process properties update in camera. 1265 info.file.fields = GP_FILE_INFO_PERMISSIONS; 1266 info.preview.fields = GP_FILE_INFO_NONE; 1267 info.audio.fields = GP_FILE_INFO_NONE; 1268 1269 errorCode = gp_camera_file_set_info(d->camera, QFile::encodeName(folder).constData(), 1270 QFile::encodeName(itemName).constData(), info, d->status->context); 1271 1272 if (errorCode != GP_OK) 1273 { 1274 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to set camera item lock properties!"; 1275 printGphotoErrorDescription(errorCode); 1276 return false; 1277 } 1278 1279 return true; 1280 1281 #else 1282 1283 Q_UNUSED(folder); 1284 Q_UNUSED(itemName); 1285 Q_UNUSED(lock); 1286 1287 return false; 1288 1289 #endif // HAVE_GPHOTO2 1290 1291 } 1292 1293 bool GPCamera::deleteItem(const QString& folder, const QString& itemName) 1294 { 1295 #ifdef HAVE_GPHOTO2 1296 1297 d->status->cancel = false; 1298 int errorCode = gp_camera_file_delete(d->camera, QFile::encodeName(folder).constData(), 1299 QFile::encodeName(itemName).constData(), 1300 d->status->context); 1301 1302 if (errorCode != GP_OK) 1303 { 1304 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to delete camera item!"; 1305 printGphotoErrorDescription(errorCode); 1306 return false; 1307 } 1308 1309 return true; 1310 1311 #else 1312 1313 Q_UNUSED(folder); 1314 Q_UNUSED(itemName); 1315 1316 return false; 1317 1318 #endif // HAVE_GPHOTO2 1319 1320 } 1321 1322 // TODO fix to go through all folders 1323 // TODO this was never even used. 1324 bool GPCamera::deleteAllItems(const QString& folder) 1325 { 1326 #ifdef HAVE_GPHOTO2 1327 1328 int errorCode; 1329 QStringList folderList; 1330 1331 d->status->cancel = false; 1332 errorCode = gp_camera_folder_delete_all(d->camera, QFile::encodeName(folder).constData(), 1333 d->status->context); 1334 1335 if (errorCode != GP_OK) 1336 { 1337 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to delete camera folder!"; 1338 printGphotoErrorDescription(errorCode); 1339 return false; 1340 } 1341 1342 return true; 1343 1344 #else 1345 1346 Q_UNUSED(folder); 1347 1348 return false; 1349 1350 #endif // HAVE_GPHOTO2 1351 1352 } 1353 1354 bool GPCamera::uploadItem(const QString& folder, const QString& itemName, const QString& localFile, CamItemInfo& itemInfo) 1355 { 1356 #ifdef HAVE_GPHOTO2 1357 1358 int errorCode; 1359 CameraFile* cfile = nullptr; 1360 errorCode = gp_file_new(&cfile); 1361 d->status->cancel = false; 1362 1363 if (errorCode != GP_OK) 1364 { 1365 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to init new camera file instance!"; 1366 printGphotoErrorDescription(errorCode); 1367 return false; 1368 } 1369 1370 errorCode = gp_file_open(cfile, QFile::encodeName(localFile).constData()); 1371 1372 if (errorCode != GP_OK) 1373 { 1374 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to open file!"; 1375 printGphotoErrorDescription(errorCode); 1376 gp_file_unref(cfile); 1377 return false; 1378 } 1379 1380 errorCode = gp_file_set_name(cfile, QFile::encodeName(itemName).constData()); 1381 1382 if (errorCode != GP_OK) 1383 { 1384 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to rename item from camera!"; 1385 printGphotoErrorDescription(errorCode); 1386 gp_file_unref(cfile); 1387 return false; 1388 } 1389 1390 # ifdef HAVE_GPHOTO25 1391 1392 errorCode = gp_camera_folder_put_file(d->camera, 1393 QFile::encodeName(folder).constData(), 1394 QFile::encodeName(itemName).constData(), 1395 GP_FILE_TYPE_NORMAL, 1396 cfile, 1397 d->status->context); 1398 # else 1399 1400 errorCode = gp_camera_folder_put_file(d->camera, 1401 QFile::encodeName(folder).constData(), 1402 cfile, 1403 d->status->context); 1404 # endif 1405 1406 if (errorCode != GP_OK) 1407 { 1408 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to upload item to camera!"; 1409 printGphotoErrorDescription(errorCode); 1410 gp_file_unref(cfile); 1411 return false; 1412 } 1413 1414 // Get new camera item information. 1415 1416 itemInfo.name = itemName; 1417 itemInfo.folder = folder; 1418 1419 CameraFileInfo info; 1420 errorCode = gp_camera_file_get_info(d->camera, QFile::encodeName(folder).constData(), 1421 QFile::encodeName(itemName).constData(), &info, d->status->context); 1422 1423 if (errorCode != GP_OK) 1424 { 1425 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get camera item information!"; 1426 printGphotoErrorDescription(errorCode); 1427 gp_file_unref(cfile); 1428 return false; 1429 } 1430 1431 itemInfo.ctime = QDateTime(); 1432 itemInfo.mime = QString(); 1433 itemInfo.size = -1; 1434 itemInfo.width = -1; 1435 itemInfo.height = -1; 1436 itemInfo.downloaded = CamItemInfo::DownloadUnknown; 1437 itemInfo.readPermissions = -1; 1438 itemInfo.writePermissions = -1; 1439 1440 /* The mime type returned by Gphoto2 is dummy with all RAW files. 1441 if (info.file.fields & GP_FILE_INFO_TYPE) 1442 itemInfo.mime = info.file.type; 1443 */ 1444 1445 itemInfo.mime = mimeType(itemInfo.name.section(QLatin1Char('.'), -1).toLower()); 1446 1447 if (info.file.fields & GP_FILE_INFO_MTIME) 1448 { 1449 itemInfo.ctime = QDateTime::fromSecsSinceEpoch(info.file.mtime); 1450 } 1451 1452 if (info.file.fields & GP_FILE_INFO_SIZE) 1453 { 1454 itemInfo.size = info.file.size; 1455 } 1456 1457 if (info.file.fields & GP_FILE_INFO_WIDTH) 1458 { 1459 itemInfo.width = info.file.width; 1460 } 1461 1462 if (info.file.fields & GP_FILE_INFO_HEIGHT) 1463 { 1464 itemInfo.height = info.file.height; 1465 } 1466 1467 if (info.file.fields & GP_FILE_INFO_STATUS) 1468 { 1469 if (info.file.status == GP_FILE_STATUS_DOWNLOADED) 1470 { 1471 itemInfo.downloaded = CamItemInfo::DownloadedYes; 1472 } 1473 else 1474 { 1475 itemInfo.downloaded = CamItemInfo::DownloadedNo; 1476 } 1477 } 1478 1479 if (info.file.fields & GP_FILE_INFO_PERMISSIONS) 1480 { 1481 if (info.file.permissions & GP_FILE_PERM_READ) 1482 { 1483 itemInfo.readPermissions = 1; 1484 } 1485 else 1486 { 1487 itemInfo.readPermissions = 0; 1488 } 1489 1490 if (info.file.permissions & GP_FILE_PERM_DELETE) 1491 { 1492 itemInfo.writePermissions = 1; 1493 } 1494 else 1495 { 1496 itemInfo.writePermissions = 0; 1497 } 1498 } 1499 1500 gp_file_unref(cfile); 1501 1502 return true; 1503 1504 #else 1505 1506 Q_UNUSED(folder); 1507 Q_UNUSED(itemName); 1508 Q_UNUSED(localFile); 1509 Q_UNUSED(itemInfo); 1510 1511 return false; 1512 1513 #endif // HAVE_GPHOTO2 1514 1515 } 1516 1517 bool GPCamera::cameraSummary(QString& summary) 1518 { 1519 #ifdef HAVE_GPHOTO2 1520 1521 int errorCode; 1522 CameraText sum; 1523 1524 d->status->cancel = false; 1525 errorCode = gp_camera_get_summary(d->camera, &sum, d->status->context); 1526 1527 if (errorCode != GP_OK) 1528 { 1529 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get camera summary!"; 1530 printGphotoErrorDescription(errorCode); 1531 return false; 1532 } 1533 1534 // we do not expect title/model/etc. to contain newlines, 1535 // so we just escape HTML characters 1536 summary = i18nc("@info List of device properties", 1537 "Title: \"%1\"\n" 1538 "Model: \"%2\"\n" 1539 "Port: \"%3\"\n" 1540 "Path: \"%4\"\n\n", 1541 title(), model(), port(), path()); 1542 1543 summary += i18nc("@info List of supported device operations", 1544 "Thumbnails: \"%1\"\n" 1545 "Capture image: \"%2\"\n" 1546 "Delete items: \"%3\"\n" 1547 "Upload items: \"%4\"\n" 1548 "Create directories: \"%5\"\n" 1549 "Delete Directories: \"%6\"\n\n", 1550 thumbnailSupport() ? i18nc("@info: gphoto backend feature", "yes") : i18nc("@info: gphoto backend feature", "no"), 1551 captureImageSupport() ? i18nc("@info: gphoto backend feature", "yes") : i18nc("@info: gphoto backend feature", "no"), 1552 deleteSupport() ? i18nc("@info: gphoto backend feature", "yes") : i18nc("@info: gphoto backend feature", "no"), 1553 uploadSupport() ? i18nc("@info: gphoto backend feature", "yes") : i18nc("@info: gphoto backend feature", "no"), 1554 mkDirSupport() ? i18nc("@info: gphoto backend feature", "yes") : i18nc("@info: gphoto backend feature", "no"), 1555 delDirSupport() ? i18nc("@info: gphoto backend feature", "yes") : i18nc("@info: gphoto backend feature", "no")); 1556 1557 // here we need to make sure whitespace and newlines 1558 // are converted to HTML properly 1559 summary.append(Qt::convertFromPlainText(QString::fromLocal8Bit(sum.text), Qt::WhiteSpacePre)); 1560 1561 return true; 1562 1563 #else 1564 1565 Q_UNUSED(summary); 1566 1567 return false; 1568 1569 #endif // HAVE_GPHOTO2 1570 1571 } 1572 1573 bool GPCamera::cameraManual(QString& manual) 1574 { 1575 #ifdef HAVE_GPHOTO2 1576 1577 int errorCode; 1578 CameraText man; 1579 1580 d->status->cancel = false; 1581 errorCode = gp_camera_get_manual(d->camera, &man, d->status->context); 1582 1583 if (errorCode != GP_OK) 1584 { 1585 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get camera manual!"; 1586 printGphotoErrorDescription(errorCode); 1587 return false; 1588 } 1589 1590 // I guess manual is plain text and not HTML? 1591 // Can't test it. (Michael G. Hansen) 1592 manual = Qt::convertFromPlainText(QString::fromLocal8Bit(man.text), Qt::WhiteSpacePre); 1593 1594 return true; 1595 1596 #else 1597 1598 Q_UNUSED(manual); 1599 1600 return false; 1601 1602 #endif // HAVE_GPHOTO2 1603 1604 } 1605 1606 bool GPCamera::cameraAbout(QString& about) 1607 { 1608 #ifdef HAVE_GPHOTO2 1609 1610 int errorCode; 1611 CameraText abt; 1612 1613 d->status->cancel = false; 1614 errorCode = gp_camera_get_about(d->camera, &abt, d->status->context); 1615 1616 if (errorCode != GP_OK) 1617 { 1618 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get information about camera!"; 1619 printGphotoErrorDescription(errorCode); 1620 return false; 1621 } 1622 1623 // here we need to make sure whitespace and newlines 1624 // are converted to HTML properly 1625 about = Qt::convertFromPlainText(QString::fromLocal8Bit(abt.text), Qt::WhiteSpacePre); 1626 about.append(QString::fromUtf8("<br/><br/>To report problems about this driver, please contact " 1627 "the gphoto2 team at:<br/><br/>http://gphoto.org/bugs")); // krazy:exclude=insecurenet 1628 1629 return true; 1630 1631 #else 1632 1633 Q_UNUSED(about); 1634 1635 return false; 1636 1637 #endif // HAVE_GPHOTO2 1638 1639 } 1640 1641 // -- Static methods --------------------------------------------------------------------- 1642 1643 void GPCamera::printGphotoErrorDescription(int errorCode) 1644 { 1645 #ifdef HAVE_GPHOTO2 1646 1647 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Libgphoto2 error: " << gp_result_as_string(errorCode) 1648 << " (" << errorCode << ")"; 1649 1650 #else 1651 1652 Q_UNUSED(errorCode); 1653 1654 #endif // HAVE_GPHOTO2 1655 1656 } 1657 1658 void GPCamera::getSupportedCameras(int& count, QStringList& clist) 1659 { 1660 1661 #ifdef HAVE_GPHOTO2 1662 1663 clist.clear(); 1664 count = 0; 1665 1666 CameraAbilities abil; 1667 CameraAbilitiesList* abilList = nullptr; 1668 GPContext* context = nullptr; 1669 context = gp_context_new(); 1670 1671 gp_abilities_list_new(&abilList); 1672 gp_abilities_list_load(abilList, context); 1673 1674 count = gp_abilities_list_count(abilList); 1675 1676 if (count < 0) 1677 { 1678 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get list of cameras!"; 1679 printGphotoErrorDescription(count); 1680 gp_context_unref(context); 1681 return; 1682 } 1683 else 1684 { 1685 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Number of cameras supported:" << count; 1686 1687 for (int i = 0 ; i < count ; ++i) 1688 { 1689 gp_abilities_list_get_abilities(abilList, i, &abil); 1690 const char* cname = abil.model; 1691 clist.append(QString::fromLocal8Bit(cname)); 1692 } 1693 } 1694 1695 gp_abilities_list_free(abilList); 1696 gp_context_unref(context); 1697 1698 #else 1699 1700 Q_UNUSED(count); 1701 Q_UNUSED(clist); 1702 qCDebug(DIGIKAM_IMPORTUI_LOG) << "GPCamera::getSupportedCamerasNumber(): function not available!"; 1703 1704 #endif // HAVE_GPHOTO2 1705 1706 } 1707 1708 void GPCamera::getSupportedPorts(QStringList& plist) 1709 { 1710 1711 #ifdef HAVE_GPHOTO2 1712 1713 GPPortInfoList* list = nullptr; 1714 GPPortInfo info; 1715 1716 plist.clear(); 1717 1718 gp_port_info_list_new(&list); 1719 gp_port_info_list_load(list); 1720 1721 int numPorts = gp_port_info_list_count(list); 1722 1723 if (numPorts < 0) 1724 { 1725 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get list of ports!"; 1726 printGphotoErrorDescription(numPorts); 1727 gp_port_info_list_free(list); 1728 return; 1729 } 1730 else 1731 { 1732 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Number of ports supported:" << numPorts; 1733 1734 for (int i = 0 ; i < numPorts ; ++i) 1735 { 1736 gp_port_info_list_get_info(list, i, &info); 1737 1738 # ifdef HAVE_GPHOTO25 1739 1740 char* xpath = nullptr; 1741 gp_port_info_get_name (info, &xpath); 1742 plist.append(QString::fromUtf8(xpath)); 1743 1744 # else 1745 1746 plist.append(QString::fromUtf8(info.path)); 1747 1748 # endif 1749 1750 } 1751 } 1752 1753 gp_port_info_list_free(list); 1754 1755 #else 1756 1757 Q_UNUSED(plist); 1758 qCDebug(DIGIKAM_IMPORTUI_LOG) << "GPCamera::getSupportedPorts(): function not available!"; 1759 1760 #endif // HAVE_GPHOTO2 1761 1762 } 1763 1764 void GPCamera::getCameraSupportedPorts(const QString& model, QStringList& plist) 1765 { 1766 1767 #ifdef HAVE_GPHOTO2 1768 1769 int i = 0; 1770 plist.clear(); 1771 1772 CameraAbilities abilities; 1773 CameraAbilitiesList* abilList = nullptr; 1774 GPContext* context = nullptr; 1775 context = gp_context_new(); 1776 1777 gp_abilities_list_new(&abilList); 1778 gp_abilities_list_load(abilList, context); 1779 i = gp_abilities_list_lookup_model(abilList, model.toLocal8Bit().data()); 1780 gp_abilities_list_get_abilities(abilList, i, &abilities); 1781 gp_abilities_list_free(abilList); 1782 1783 if (abilities.port & GP_PORT_SERIAL) 1784 { 1785 plist.append(QLatin1String("serial")); 1786 } 1787 1788 if (abilities.port & GP_PORT_PTPIP) 1789 { 1790 plist.append(QLatin1String("ptpip")); 1791 } 1792 1793 if (abilities.port & GP_PORT_USB) 1794 { 1795 plist.append(QLatin1String("usb")); 1796 } 1797 1798 gp_context_unref(context); 1799 1800 #else 1801 1802 Q_UNUSED(model); 1803 Q_UNUSED(plist); 1804 qCDebug(DIGIKAM_IMPORTUI_LOG) << "GPCamera::getCameraSupportedPorts(): function not available!"; 1805 1806 #endif // HAVE_GPHOTO2 1807 1808 } 1809 1810 int GPCamera::autoDetect(QString& model, QString& port) 1811 { 1812 1813 #ifdef HAVE_GPHOTO2 1814 1815 CameraList* camList = nullptr; 1816 CameraAbilitiesList* abilList = nullptr; 1817 GPPortInfoList* infoList = nullptr; 1818 const char* camModel_ = nullptr, *camPort_ = nullptr; 1819 GPContext* context = nullptr; 1820 context = gp_context_new(); 1821 1822 gp_list_new(&camList); 1823 1824 gp_abilities_list_new(&abilList); 1825 gp_abilities_list_load(abilList, context); 1826 gp_port_info_list_new(&infoList); 1827 gp_port_info_list_load(infoList); 1828 gp_abilities_list_detect(abilList, infoList, camList, context); 1829 gp_abilities_list_free(abilList); 1830 gp_port_info_list_free(infoList); 1831 1832 gp_context_unref(context); 1833 1834 int count = gp_list_count(camList); 1835 1836 if (count <= 0) 1837 { 1838 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to autodetect camera!"; 1839 printGphotoErrorDescription(count); 1840 gp_list_free(camList); 1841 return -1; 1842 } 1843 1844 camModel_ = nullptr; 1845 camPort_ = nullptr; 1846 1847 for (int i = 0 ; i < count ; ++i) 1848 { 1849 if (gp_list_get_name(camList, i, &camModel_) != GP_OK) 1850 { 1851 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to autodetect camera!"; 1852 gp_list_free(camList); 1853 return -1; 1854 } 1855 1856 if (gp_list_get_value(camList, i, &camPort_) != GP_OK) 1857 { 1858 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to autodetect camera!"; 1859 gp_list_free(camList); 1860 return -1; 1861 } 1862 1863 if (camModel_ && camPort_) 1864 { 1865 model = QLatin1String(camModel_); 1866 port = QLatin1String(camPort_); 1867 gp_list_free(camList); 1868 return 0; 1869 } 1870 } 1871 1872 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to autodetect camera!"; 1873 gp_list_free(camList); 1874 1875 #else 1876 1877 Q_UNUSED(model); 1878 Q_UNUSED(port); 1879 qCDebug(DIGIKAM_IMPORTUI_LOG) << "GPCamera::autoDetect(): function not available!"; 1880 1881 #endif // HAVE_GPHOTO2 1882 1883 return -1; 1884 } 1885 1886 bool GPCamera::findConnectedUsbCamera(int vendorId, int productId, QString& model, QString& port) 1887 { 1888 1889 #ifdef HAVE_GPHOTO2 1890 1891 CameraAbilitiesList* abilList = nullptr; 1892 GPPortInfoList* list = nullptr; 1893 GPContext* context = nullptr; 1894 CameraList* camList = nullptr; 1895 bool success = false; 1896 // get name and port of detected camera 1897 const char* model_str = nullptr; 1898 const char* port_str = nullptr; 1899 context = gp_context_new(); 1900 1901 // get list of all ports 1902 gp_port_info_list_new(&list); 1903 gp_port_info_list_load(list); 1904 1905 gp_abilities_list_new(&abilList); 1906 // get list of all supported cameras 1907 gp_abilities_list_load(abilList, context); 1908 1909 // autodetect all cameras, then match the list to the passed in USB ids 1910 gp_list_new (&camList); 1911 gp_abilities_list_detect(abilList, list, camList, context); 1912 gp_context_unref(context); 1913 1914 int count = gp_list_count(camList); 1915 int cnt = 0; 1916 1917 for (int i = 0 ; i < count ; ++i) 1918 { 1919 const char* xmodel = nullptr; 1920 gp_list_get_name(camList, i, &xmodel); 1921 int model = gp_abilities_list_lookup_model (abilList, xmodel); 1922 CameraAbilities ab; 1923 gp_abilities_list_get_abilities(abilList, model, &ab); 1924 1925 if (ab.port != GP_PORT_USB) 1926 continue; 1927 1928 /* KDE provides us USB Vendor and Product, but we might just 1929 * have covered this via a class match. Check class matched 1930 * cameras also for matchingo USB vendor/product id 1931 */ 1932 if (ab.usb_vendor == 0) 1933 { 1934 int ret; 1935 GPPortInfo info; 1936 const char* xport = nullptr; 1937 GPPort* gpport = nullptr; 1938 1939 /* get the port path so we only look at this bus position */ 1940 gp_list_get_value(camList, i, &xport); 1941 ret = gp_port_info_list_lookup_path (list, xport); 1942 1943 if (ret < GP_OK) /* should not happen */ 1944 continue; 1945 1946 /* get the lowlevel port info for the path 1947 */ 1948 gp_port_info_list_get_info(list, ret, &info); 1949 1950 /* open lowlevel driver interface briefly to search */ 1951 gp_port_new(&gpport); 1952 gp_port_set_info(gpport, info); 1953 1954 /* And now call into the lowlevel usb driver to see if the bus position 1955 * has that specific vendor/product id 1956 */ 1957 if (gp_port_usb_find_device(gpport, vendorId, productId) == GP_OK) 1958 { 1959 ab.usb_vendor = vendorId; 1960 ab.usb_product = productId; 1961 } 1962 1963 gp_port_free (gpport); 1964 } 1965 1966 if (ab.usb_vendor != vendorId) 1967 continue; 1968 1969 if (ab.usb_product != productId) 1970 continue; 1971 1972 /* keep it, and continue iterating, in case we find another one 1973 */ 1974 gp_list_get_name (camList, i, &model_str); 1975 gp_list_get_value(camList, i, &port_str); 1976 1977 cnt++; 1978 } 1979 1980 gp_port_info_list_free(list); 1981 gp_abilities_list_free(abilList); 1982 1983 if (cnt > 0) 1984 { 1985 if (cnt > 1) 1986 { 1987 qCWarning(DIGIKAM_IMPORTUI_LOG) << "More than one camera detected on port " << port 1988 << ". Due to restrictions in the GPhoto2 API, " 1989 << "only the first camera is used."; 1990 } 1991 1992 model = QLatin1String(model_str); 1993 port = QLatin1String(port_str); 1994 success = true; 1995 } 1996 else 1997 { 1998 qCDebug(DIGIKAM_IMPORTUI_LOG) << "Failed to get information for the listed camera"; 1999 } 2000 2001 gp_list_free(camList); 2002 return success; 2003 2004 #else 2005 2006 Q_UNUSED(vendorId); 2007 Q_UNUSED(productId); 2008 Q_UNUSED(model); 2009 Q_UNUSED(port); 2010 qCDebug(DIGIKAM_IMPORTUI_LOG) << "GPCamera::findConnectedUsbCamera(): function not available!"; 2011 2012 return false; 2013 2014 #endif // HAVE_GPHOTO2 2015 2016 } 2017 2018 } // namespace Digikam 2019 2020 #include "moc_gpcamera.cpp"