File indexing completed on 2024-04-28 08:44:15
0001 /* 0002 SPDX-FileCopyrightText: 2021 Julius Künzel <jk.kdedev@smartlab.uber.space> 0003 SPDX-FileCopyrightText: 2011 Jean-Baptiste Mardelle <jb@kdenlive.org> 0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "resourcewidget.hpp" 0008 #include "core.h" 0009 #include "kdenlivesettings.h" 0010 0011 #include "utils/KMessageBox_KdenliveCompat.h" 0012 #include <KFileItem> 0013 #include <KIO/FileCopyJob> 0014 #include <KIO/Global> 0015 #include <KLocalizedString> 0016 #include <KMessageBox> 0017 #include <KRecentDirs> 0018 #include <KSelectAction> 0019 #include <KSqueezedTextLabel> 0020 #include <QComboBox> 0021 #include <QDesktopServices> 0022 #include <QFileDialog> 0023 #include <QFontDatabase> 0024 #include <QIcon> 0025 #include <QInputDialog> 0026 #include <QMenu> 0027 #include <QProgressDialog> 0028 #include <QToolBar> 0029 #include <QtConcurrent> 0030 0031 #include <kcompletion_version.h> 0032 0033 ResourceWidget::ResourceWidget(QWidget *parent) 0034 : QWidget(parent) 0035 , m_showloadingWarning(true) 0036 { 0037 setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); 0038 setupUi(this); 0039 m_tmpThumbFile = new QTemporaryFile(this); 0040 0041 int iconHeight = int(QFontInfo(font()).pixelSize() * 3.5); 0042 m_iconSize = QSize(int(iconHeight * pCore->getCurrentDar()), iconHeight); 0043 0044 slider_zoom->setRange(0, 15); 0045 connect(slider_zoom, &QAbstractSlider::valueChanged, this, &ResourceWidget::slotSetIconSize); 0046 connect(button_zoomin, &QToolButton::clicked, this, [&]() { slider_zoom->setValue(qMin(slider_zoom->value() + 1, slider_zoom->maximum())); }); 0047 connect(button_zoomout, &QToolButton::clicked, this, [&]() { slider_zoom->setValue(qMax(slider_zoom->value() - 1, slider_zoom->minimum())); }); 0048 0049 message_line->hide(); 0050 0051 for (const QPair<QString, QString> &provider : ProvidersRepository::get()->getAllProviers()) { 0052 QIcon icon; 0053 switch (ProvidersRepository::get()->getProvider(provider.second)->type()) { 0054 case ProviderModel::AUDIO: 0055 icon = QIcon::fromTheme(QStringLiteral("player-volume")); 0056 break; 0057 case ProviderModel::VIDEO: 0058 icon = QIcon::fromTheme(QStringLiteral("camera-video")); 0059 break; 0060 case ProviderModel::IMAGE: 0061 icon = QIcon::fromTheme(QStringLiteral("camera-photo")); 0062 break; 0063 default: 0064 icon = QIcon(); 0065 } 0066 service_list->addItem(icon, provider.first, provider.second); 0067 } 0068 connect(service_list, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ResourceWidget::slotChangeProvider); 0069 loadConfig(); 0070 connect(provider_info, &KUrlLabel::leftClickedUrl, this, [&]() { slotOpenUrl(provider_info->url()); }); 0071 connect(label_license, &KUrlLabel::leftClickedUrl, this, [&]() { slotOpenUrl(label_license->url()); }); 0072 connect(search_text, &KLineEdit::returnKeyPressed, this, &ResourceWidget::slotStartSearch); 0073 connect(search_results, &QListWidget::currentRowChanged, this, &ResourceWidget::slotUpdateCurrentItem); 0074 connect(this, &ResourceWidget::gotPixmap, this, &ResourceWidget::slotShowPixmap); 0075 connect(button_preview, &QAbstractButton::clicked, this, [&]() { 0076 if (!m_currentProvider) { 0077 return; 0078 } 0079 0080 slotPreviewItem(); 0081 }); 0082 0083 connect(button_import, &QAbstractButton::clicked, this, [&]() { 0084 if (m_currentProvider->get()->downloadOAuth2()) { 0085 if (m_currentProvider->get()->requiresLogin()) { 0086 KMessageBox::information(this, i18n("Login is required to download this item.\nYou will be redirected to the login page now.")); 0087 } 0088 m_currentProvider->get()->authorize(); 0089 } else { 0090 if (m_currentItem->data(singleDownloadRole).toBool()) { 0091 if (m_currentItem->data(downloadRole).toString().isEmpty()) { 0092 m_currentProvider->get()->slotFetchFiles(m_currentItem->data(idRole).toString()); 0093 return; 0094 } else { 0095 slotSaveItem(); 0096 } 0097 } else { 0098 slotChooseVersion(m_currentItem->data(downloadRole).toStringList(), m_currentItem->data(downloadLabelRole).toStringList()); 0099 } 0100 } 0101 }); 0102 0103 page_number->setEnabled(false); 0104 0105 connect(page_number, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ResourceWidget::slotStartSearch); 0106 adjustSize(); 0107 } 0108 0109 ResourceWidget::~ResourceWidget() 0110 { 0111 saveConfig(); 0112 delete m_tmpThumbFile; 0113 } 0114 0115 /** 0116 * @brief ResourceWidget::saveConfig 0117 * Load current provider and zoom value from config file 0118 */ 0119 void ResourceWidget::loadConfig() 0120 { 0121 KSharedConfigPtr config = KSharedConfig::openConfig(); 0122 KConfigGroup resourceConfig(config, "OnlineResources"); 0123 slider_zoom->setValue(resourceConfig.readEntry("zoom", 7)); 0124 if (resourceConfig.readEntry("provider", service_list->itemText(0)).isEmpty()) { 0125 service_list->setCurrentIndex(0); 0126 } else { 0127 service_list->setCurrentItem(resourceConfig.readEntry("provider", service_list->itemText(0))); 0128 } 0129 slotChangeProvider(); 0130 } 0131 0132 /** 0133 * @brief ResourceWidget::saveConfig 0134 * Save current provider and zoom value to config file 0135 */ 0136 void ResourceWidget::saveConfig() 0137 { 0138 KSharedConfigPtr config = KSharedConfig::openConfig(); 0139 KConfigGroup resourceConfig(config, "OnlineResources"); 0140 resourceConfig.writeEntry(QStringLiteral("provider"), service_list->currentText()); 0141 resourceConfig.writeEntry(QStringLiteral("zoom"), slider_zoom->value()); 0142 config->sync(); 0143 } 0144 0145 /** 0146 * @brief ResourceWidget::blockUI 0147 * @param block 0148 * Block or unblock the online resource ui 0149 */ 0150 void ResourceWidget::blockUI(bool block) 0151 { 0152 buildin_box->setEnabled(!block); 0153 search_text->setEnabled(!block); 0154 service_list->setEnabled(!block); 0155 setCursor(block ? Qt::WaitCursor : Qt::ArrowCursor); 0156 } 0157 0158 /** 0159 * @brief ResourceWidget::slotChangeProvider 0160 * Set m_currentProvider to the current selected provider of the service_list and update ui 0161 */ 0162 void ResourceWidget::slotChangeProvider() 0163 { 0164 if (m_currentProvider != nullptr) { 0165 m_currentProvider->get()->disconnect(this); 0166 } 0167 0168 details_box->setEnabled(false); 0169 button_import->setEnabled(false); 0170 button_preview->setEnabled(false); 0171 info_browser->clear(); 0172 search_results->clear(); 0173 page_number->blockSignals(true); 0174 page_number->setValue(1); 0175 page_number->setMaximum(1); 0176 page_number->blockSignals(false); 0177 0178 if (service_list->currentData().toString().isEmpty()) { 0179 provider_info->clear(); 0180 buildin_box->setEnabled(false); 0181 return; 0182 } else { 0183 buildin_box->setEnabled(true); 0184 message_line->hide(); 0185 } 0186 0187 m_currentProvider = &ProvidersRepository::get()->getProvider(service_list->currentData().toString()); 0188 0189 provider_info->setText(i18n("Media provided by %1", m_currentProvider->get()->name())); 0190 provider_info->setUrl(m_currentProvider->get()->homepage()); 0191 connect(m_currentProvider->get(), &ProviderModel::searchDone, this, &ResourceWidget::slotSearchFinished); 0192 connect(m_currentProvider->get(), &ProviderModel::searchError, this, &ResourceWidget::slotDisplayError); 0193 0194 connect(m_currentProvider->get(), &ProviderModel::fetchedFiles, this, &ResourceWidget::slotChooseVersion); 0195 connect(m_currentProvider->get(), &ProviderModel::authenticated, this, &ResourceWidget::slotAccessTokenReceived); 0196 0197 // automatically kick of a search if we have search text and we switch services. 0198 if (!search_text->text().isEmpty()) { 0199 slotStartSearch(); 0200 } 0201 } 0202 0203 /** 0204 * @brief ResourceWidget::slotOpenUrl 0205 * @param url link to open in external browser 0206 * Open a url in a external browser 0207 */ 0208 void ResourceWidget::slotOpenUrl(const QString &url) 0209 { 0210 QDesktopServices::openUrl(QUrl(url)); 0211 } 0212 0213 /** 0214 * @brief ResourceWidget::slotStartSearch 0215 * Calls slotStartSearch on the object for the currently selected service. 0216 */ 0217 void ResourceWidget::slotStartSearch() 0218 { 0219 message_line->setText(i18nc("@info:status", "Search pending…")); 0220 message_line->setMessageType(KMessageWidget::Information); 0221 message_line->show(); 0222 0223 blockUI(true); 0224 details_box->setEnabled(false); 0225 button_import->setEnabled(false); 0226 button_preview->setEnabled(false); 0227 info_browser->clear(); 0228 search_results->clear(); 0229 m_currentProvider->get()->slotStartSearch(search_text->text(), page_number->value()); 0230 } 0231 0232 /** 0233 * @brief ResourceWidget::slotDisplayError 0234 * @param message the error text 0235 * Displays the items of list in the search_results ListView 0236 */ 0237 void ResourceWidget::slotDisplayError(const QString &message) 0238 { 0239 message_line->setText(i18n("Search failed! %1", message)); 0240 message_line->setMessageType(KMessageWidget::Error); 0241 message_line->show(); 0242 page_number->setEnabled(false); 0243 service_list->setEnabled(true); 0244 buildin_box->setEnabled(true); 0245 setCursor(Qt::ArrowCursor); 0246 } 0247 0248 /** 0249 * @brief ResourceWidget::slotSearchFinished 0250 * @param list list of the found items 0251 * @param pageCount number of found pages 0252 * Displays the items of list in the search_results ListView 0253 */ 0254 void ResourceWidget::slotSearchFinished(const QList<ResourceItemInfo> &list, int pageCount) 0255 { 0256 QMutexLocker lock(&m_imageLock); 0257 m_imagesUrl.clear(); 0258 if (list.isEmpty()) { 0259 message_line->setText(i18nc("@info", "No items found.")); 0260 message_line->setMessageType(KMessageWidget::Error); 0261 message_line->show(); 0262 blockUI(false); 0263 return; 0264 } 0265 0266 message_line->setMessageType(KMessageWidget::Information); 0267 message_line->show(); 0268 int count = 0; 0269 for (const ResourceItemInfo &item : qAsConst(list)) { 0270 message_line->setText(i18nc("@info:progress", "Parsing item %1 of %2…", count, list.count())); 0271 // if item has no name use "Created by Author", if item even has no author use "Unnamed" 0272 QListWidgetItem *listItem = new QListWidgetItem( 0273 item.name.isEmpty() ? (item.author.isEmpty() ? i18n("Unnamed") : i18nc("Created by author name", "Created by %1", item.author)) : item.name); 0274 if (!item.imageUrl.isEmpty()) { 0275 m_imagesUrl << item.imageUrl; 0276 } 0277 0278 listItem->setData(idRole, item.id); 0279 listItem->setData(nameRole, item.name); 0280 listItem->setData(filetypeRole, item.filetype); 0281 listItem->setData(descriptionRole, item.description); 0282 listItem->setData(imageRole, item.imageUrl); 0283 listItem->setData(previewRole, item.previewUrl); 0284 listItem->setData(authorUrl, item.authorUrl); 0285 listItem->setData(authorRole, item.author); 0286 listItem->setData(widthRole, item.width); 0287 listItem->setData(heightRole, item.height); 0288 listItem->setData(durationRole, item.duration); 0289 listItem->setData(urlRole, item.infoUrl); 0290 listItem->setData(licenseRole, item.license); 0291 if (item.downloadUrl.isEmpty() && item.downloadUrls.length() > 0) { 0292 listItem->setData(singleDownloadRole, false); 0293 listItem->setData(downloadRole, item.downloadUrls); 0294 listItem->setData(downloadLabelRole, item.downloadLabels); 0295 } else { 0296 listItem->setData(singleDownloadRole, true); 0297 listItem->setData(downloadRole, item.downloadUrl); 0298 } 0299 search_results->addItem(listItem); 0300 count++; 0301 } 0302 m_imagesUrl.removeDuplicates(); 0303 message_line->hide(); 0304 page_number->setMaximum(pageCount); 0305 page_number->setEnabled(true); 0306 blockUI(false); 0307 lock.unlock(); 0308 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0309 QtConcurrent::run(this, &ResourceWidget::slotLoadImages); 0310 #else 0311 QtConcurrent::run(&ResourceWidget::slotLoadImages, this); 0312 #endif 0313 } 0314 0315 void ResourceWidget::slotShowPixmap(const QString &url, const QPixmap &pixmap) 0316 { 0317 for (int i = 0; i < search_results->count(); i++) { 0318 auto item = search_results->item(i); 0319 if (item->data(imageRole).toString() == url) { 0320 item->setIcon(pixmap); 0321 break; 0322 } 0323 } 0324 } 0325 0326 void ResourceWidget::slotLoadImages() 0327 { 0328 if (!m_imageLock.tryLock()) { 0329 // Another request is loading, wait 0330 return; 0331 } 0332 if (m_imagesUrl.isEmpty()) { 0333 // No images to load 0334 m_imageLock.unlock(); 0335 return; 0336 } 0337 while (!m_imagesUrl.isEmpty()) { 0338 const QString url = m_imagesUrl.takeFirst(); 0339 m_imageLock.unlock(); 0340 QUrl img(url); 0341 m_tmpThumbFile->close(); 0342 if (m_tmpThumbFile->open()) { 0343 KIO::FileCopyJob *copyjob = KIO::file_copy(img, QUrl::fromLocalFile(m_tmpThumbFile->fileName()), -1, KIO::HideProgressInfo | KIO::Overwrite); 0344 if (copyjob->exec()) { 0345 QPixmap pic(m_tmpThumbFile->fileName()); 0346 Q_EMIT gotPixmap(url, pic); 0347 } 0348 } 0349 if (m_imagesUrl.isEmpty()) { 0350 break; 0351 } 0352 if (!m_imageLock.tryLock()) { 0353 break; 0354 } 0355 } 0356 } 0357 0358 /** 0359 * @brief ResourceWidget::slotUpdateCurrentItem 0360 * Set m_currentItem to the current selected item of the search_results ListView and 0361 * show its details within the details_box 0362 */ 0363 void ResourceWidget::slotUpdateCurrentItem() 0364 { 0365 details_box->setEnabled(false); 0366 button_import->setEnabled(false); 0367 button_preview->setEnabled(false); 0368 0369 // get the item the user selected 0370 m_currentItem = search_results->currentItem(); 0371 if (!m_currentItem) { 0372 return; 0373 } 0374 0375 if (m_currentProvider->get()->type() != ProviderModel::IMAGE && !m_currentItem->data(previewRole).toString().isEmpty()) { 0376 button_preview->show(); 0377 } else { 0378 button_preview->hide(); 0379 } 0380 0381 QString details = "<h3>" + m_currentItem->text(); 0382 0383 if (!m_currentItem->data(urlRole).toString().isEmpty()) { 0384 details += QStringLiteral(" <a href=\"%1\">%2</a>").arg(m_currentItem->data(urlRole).toString(), i18nc("the url link pointing to a web page", "link")); 0385 } 0386 0387 details.append(QStringLiteral("</h3>")); 0388 0389 if (!m_currentItem->data(authorUrl).toString().isEmpty()) { 0390 details += i18n("Created by <a href=\"%1\">", m_currentItem->data(authorUrl).toString()); 0391 if (!m_currentItem->data(authorRole).toString().isEmpty()) { 0392 details.append(m_currentItem->data(authorRole).toString()); 0393 } else { 0394 details.append(i18n("Author")); 0395 } 0396 details.append(QStringLiteral("</a><br />")); 0397 } else if (!m_currentItem->data(authorRole).toString().isEmpty()) { 0398 details.append(i18n("Created by %1", m_currentItem->data(authorRole).toString()) + QStringLiteral("<br />")); 0399 } else { 0400 details.append(QStringLiteral("<br />")); 0401 } 0402 0403 if (m_currentProvider->get()->type() != ProviderModel::AUDIO && m_currentItem->data(widthRole).toInt() != 0) { 0404 details.append(i18n("Size: %1 x %2", m_currentItem->data(widthRole).toInt(), m_currentItem->data(heightRole).toInt()) + QStringLiteral("<br />")); 0405 } 0406 if (m_currentItem->data(durationRole).toInt() != 0) { 0407 details.append(i18n("Duration: %1 sec", m_currentItem->data(durationRole).toInt()) + QStringLiteral("<br />")); 0408 } 0409 details.append(m_currentItem->data(descriptionRole).toString()); 0410 info_browser->setHtml(details); 0411 0412 label_license->setText(licenseNameFromUrl(m_currentItem->data(licenseRole).toString(), true)); 0413 label_license->setTipText(licenseNameFromUrl(m_currentItem->data(licenseRole).toString(), false)); 0414 label_license->setUseTips(true); 0415 label_license->setUrl(m_currentItem->data(licenseRole).toString()); 0416 0417 details_box->setEnabled(true); 0418 button_import->setEnabled(true); 0419 button_preview->setEnabled(true); 0420 } 0421 0422 /** 0423 * @brief ResourceWidget::licenseNameFromUrl 0424 * @param licenseUrl 0425 * @param shortName Whether the long name like "Attribution-NonCommercial-ShareAlike 3.0" or the short name like "CC BY-NC-SA 3.0" should be returned 0426 * @return the license name "Unnamed License" if url is not known. 0427 */ 0428 QString ResourceWidget::licenseNameFromUrl(const QString &licenseUrl, const bool shortName) 0429 { 0430 QString licenseName; 0431 QString licenseShortName; 0432 0433 if (licenseUrl.contains("creativecommons.org")) { 0434 if (licenseUrl.contains(QStringLiteral("/sampling+/"))) { 0435 licenseName = i18nc("Creative Commons License", "CC Sampling+"); 0436 } else if (licenseUrl.contains(QStringLiteral("/by/"))) { 0437 licenseName = i18nc("Creative Commons License", "Creative Commons Attribution"); 0438 licenseShortName = i18nc("Creative Commons License (short)", "CC BY"); 0439 } else if (licenseUrl.contains(QStringLiteral("/by-nd/"))) { 0440 licenseName = i18nc("Creative Commons License", "Creative Commons Attribution-NoDerivs"); 0441 licenseShortName = i18nc("Creative Commons License (short)", "CC BY-ND"); 0442 } else if (licenseUrl.contains(QStringLiteral("/by-nc-sa/"))) { 0443 licenseName = i18nc("Creative Commons License", "Creative Commons Attribution-NonCommercial-ShareAlike"); 0444 licenseShortName = i18nc("Creative Commons License (short)", "CC BY-NC-SA"); 0445 } else if (licenseUrl.contains(QStringLiteral("/by-sa/"))) { 0446 licenseName = i18nc("Creative Commons License", "Creative Commons Attribution-ShareAlike"); 0447 licenseShortName = i18nc("Creative Commons License (short)", "CC BY-SA"); 0448 } else if (licenseUrl.contains(QStringLiteral("/by-nc/"))) { 0449 licenseName = i18nc("Creative Commons License", "Creative Commons Attribution-NonCommercial"); 0450 licenseShortName = i18nc("Creative Commons License (short)", "CC BY-NC"); 0451 } else if (licenseUrl.contains(QStringLiteral("/by-nc-nd/"))) { 0452 licenseName = i18nc("Creative Commons License", "Creative Commons Attribution-NonCommercial-NoDerivs"); 0453 licenseShortName = i18nc("Creative Commons License (short)", "CC BY-NC-ND"); 0454 } else if (licenseUrl.contains(QLatin1String("/publicdomain/zero/"))) { 0455 licenseName = i18nc("Creative Commons License", "Creative Commons 0"); 0456 licenseShortName = i18nc("Creative Commons License (short)", "CC 0"); 0457 } else if (licenseUrl.endsWith(QLatin1String("/publicdomain")) || licenseUrl.contains(QLatin1String("openclipart.org/share"))) { 0458 licenseName = i18nc("License", "Public Domain"); 0459 } else { 0460 licenseShortName = i18nc("Short for: Unknown Creative Commons License", "Unknown CC License"); 0461 licenseName = i18n("Unknown Creative Commons License"); 0462 } 0463 0464 if (licenseUrl.contains(QStringLiteral("/1.0"))) { 0465 licenseName.append(QStringLiteral(" 1.0")); 0466 licenseShortName.append(QStringLiteral(" 1.0")); 0467 } else if (licenseUrl.contains(QStringLiteral("/2.0"))) { 0468 licenseName.append(QStringLiteral(" 2.0")); 0469 licenseShortName.append(QStringLiteral(" 2.0")); 0470 } else if (licenseUrl.contains(QStringLiteral("/2.5"))) { 0471 licenseName.append(QStringLiteral(" 2.5")); 0472 licenseShortName.append(QStringLiteral(" 2.5")); 0473 } else if (licenseUrl.contains(QStringLiteral("/3.0"))) { 0474 licenseName.append(QStringLiteral(" 3.0")); 0475 licenseShortName.append(QStringLiteral(" 3.0")); 0476 } else if (licenseUrl.contains(QStringLiteral("/4.0"))) { 0477 licenseName.append(QStringLiteral(" 4.0")); 0478 licenseShortName.append(QStringLiteral(" 4.0")); 0479 } 0480 } else if (licenseUrl.contains("pexels.com/license/")) { 0481 licenseName = i18n("Pexels License"); 0482 } else if (licenseUrl.contains("pixabay.com/service/license/")) { 0483 licenseName = i18n("Pixabay License"); 0484 ; 0485 } else { 0486 licenseName = i18n("Unknown License"); 0487 } 0488 0489 if (shortName && !licenseShortName.isEmpty()) { 0490 return licenseShortName; 0491 } else { 0492 return licenseName; 0493 } 0494 } 0495 0496 /** 0497 * @brief ResourceWidget::slotSetIconSize 0498 * @param size 0499 * Set the icon size for the search_results ListView 0500 */ 0501 void ResourceWidget::slotSetIconSize(int size) 0502 { 0503 if (!search_results) { 0504 return; 0505 } 0506 QSize zoom = m_iconSize; 0507 zoom = zoom * (size / 4.0); 0508 search_results->setIconSize(zoom); 0509 } 0510 0511 /** 0512 * @brief ResourceWidget::slotPreviewItem 0513 * Emits the previewClip signal if m_currentItem is valid to display a preview in the Clip Monitor 0514 */ 0515 void ResourceWidget::slotPreviewItem() 0516 { 0517 if (!m_currentItem) { 0518 return; 0519 } 0520 blockUI(true); 0521 const QString path = m_currentItem->data(previewRole).toString(); 0522 if (m_showloadingWarning && !QUrl::fromUserInput(path).isLocalFile()) { 0523 message_line->setText(i18n("It maybe takes a while until the preview is loaded")); 0524 message_line->setMessageType(KMessageWidget::Warning); 0525 message_line->show(); 0526 QTimer::singleShot(6000, message_line, &KMessageWidget::animatedHide); 0527 repaint(); 0528 // Only show this warning once 0529 m_showloadingWarning = false; 0530 } 0531 Q_EMIT previewClip(path, i18n("Online Resources Preview")); 0532 blockUI(false); 0533 } 0534 0535 /** 0536 * @brief ResourceWidget::slotChooseVersion 0537 * @param urls list of download urls pointing to the certain file version 0538 * @param labels list of labels for the certain file version (needs to have the same order than urls) 0539 * @param accessToken access token to pass through to slotSaveItem 0540 * Displays a dialog to let the user choose a file version (e.g. filetype, quality) if there are multiple versions 0541 * available 0542 */ 0543 void ResourceWidget::slotChooseVersion(const QStringList &urls, const QStringList &labels, const QString &accessToken) 0544 { 0545 if (urls.isEmpty() || labels.isEmpty()) { 0546 return; 0547 } 0548 if (urls.length() == 1) { 0549 slotSaveItem(urls.first(), accessToken); 0550 return; 0551 } 0552 bool ok; 0553 QString name = QInputDialog::getItem(this, i18nc("@title:window", "Choose File Version"), i18n("Please choose the version you want to download"), labels, 0, 0554 false, &ok); 0555 if (!ok || name.isEmpty()) { 0556 return; 0557 } 0558 slotSaveItem(urls.at(labels.indexOf(name)), accessToken); 0559 } 0560 0561 /** 0562 * @brief ResourceWidget::slotSaveItem 0563 * @param originalUrl url pointing to the download file 0564 * @param accessToken the access token (optional) 0565 * Opens a dialog for user to choose a save location and start the download of the file 0566 */ 0567 void ResourceWidget::slotSaveItem(const QString &originalUrl, const QString &accessToken) 0568 { 0569 QUrl saveUrl; 0570 0571 if (!m_currentItem) { 0572 return; 0573 } 0574 0575 QUrl srcUrl(originalUrl.isEmpty() ? m_currentItem->data(downloadRole).toString() : originalUrl); 0576 if (srcUrl.isEmpty()) { 0577 return; 0578 } 0579 0580 QString path = KRecentDirs::dir(QStringLiteral(":KdenliveOnlineResourceFolder")); 0581 QString ext; 0582 0583 if (path.isEmpty()) { 0584 path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); 0585 } 0586 if (!path.endsWith(QLatin1Char('/'))) { 0587 path.append(QLatin1Char('/')); 0588 } 0589 if (!srcUrl.fileName().isEmpty()) { 0590 path.append(srcUrl.fileName()); 0591 ext = "*." + srcUrl.fileName().section(QLatin1Char('.'), -1); 0592 } else if (!m_currentItem->data(filetypeRole).toString().isEmpty()) { 0593 ext = "*." + m_currentItem->data(filetypeRole).toString(); 0594 } else { 0595 if (m_currentProvider->get()->type() == ProviderModel::AUDIO) { 0596 ext = i18n("Audio") + QStringLiteral(" (*.wav *.mp3 *.ogg *.aif *.aiff *.m4a *.flac)") + QStringLiteral(";;"); 0597 } else if (m_currentProvider->get()->type() == ProviderModel::VIDEO) { 0598 ext = i18n("Video") + QStringLiteral(" (*.mp4 *.webm *.mpg *.mov *.avi *.mkv)") + QStringLiteral(";;"); 0599 } else if (m_currentProvider->get()->type() == ProviderModel::ProviderModel::IMAGE) { 0600 ext = i18n("Images") + QStringLiteral(" (*.png *.jpg *.jpeg *.svg") + QStringLiteral(";;"); 0601 } 0602 ext.append(i18n("All Files") + QStringLiteral(" (*)")); 0603 } 0604 if (path.endsWith(QLatin1Char('/'))) { 0605 if (m_currentItem->data(nameRole).toString().isEmpty()) { 0606 path.append(i18n("Unnamed")); 0607 } else { 0608 path.append(m_currentItem->data(nameRole).toString()); 0609 } 0610 path.append(srcUrl.fileName().section(QLatin1Char('.'), -1)); 0611 } 0612 0613 QString attribution; 0614 if (KMessageBox::questionTwoActions(this, 0615 i18n("Be aware that the usage of the resource is maybe restricted by license terms or law!\n" 0616 "Do you want to add license attribution to your Project Notes?"), 0617 QString(), KStandardGuiItem::add(), KGuiItem(i18nc("@action:button", "Continue without")), 0618 i18n("Remember this decision")) == KMessageBox::PrimaryAction) { 0619 attribution = i18nc("item name, item url, author name, license name, license url", 0620 "This video uses \"%1\" (%2) by \"%3\" licensed under %4. To view a copy of this license, visit %5", 0621 m_currentItem->data(nameRole).toString().isEmpty() ? i18n("Unnamed") : m_currentItem->data(nameRole).toString(), 0622 m_currentItem->data(urlRole).toString(), m_currentItem->data(authorRole).toString(), 0623 ResourceWidget::licenseNameFromUrl(m_currentItem->data(licenseRole).toString(), true), m_currentItem->data(licenseRole).toString()); 0624 attribution.append(QStringLiteral("<br/> ")); 0625 } 0626 0627 QString saveUrlstring = QFileDialog::getSaveFileName(this, QString(), path, ext); 0628 0629 // if user cancels save 0630 if (saveUrlstring.isEmpty()) { 0631 return; 0632 } 0633 0634 saveUrl = QUrl::fromLocalFile(saveUrlstring); 0635 0636 KIO::FileCopyJob *getJob = KIO::file_copy(srcUrl, saveUrl, -1, KIO::Overwrite); 0637 if (!accessToken.isEmpty()) { 0638 getJob->addMetaData("customHTTPHeader", QStringLiteral("Authorization: Bearer %1").arg(accessToken)); 0639 } 0640 KFileItem info(srcUrl); 0641 getJob->setSourceSize(info.size()); 0642 getJob->setProperty("attribution", attribution); 0643 getJob->setProperty("usedOAuth2", !accessToken.isEmpty()); 0644 0645 connect(getJob, &KJob::result, this, &ResourceWidget::slotGotFile); 0646 getJob->start(); 0647 } 0648 0649 /** 0650 * @brief ResourceWidget::slotGotFile 0651 * @param job 0652 * Finish the download by emitting addClip and if necessary addLicenseInfo 0653 * Enables the import button 0654 */ 0655 void ResourceWidget::slotGotFile(KJob *job) 0656 0657 { 0658 if (job->error() != 0) { 0659 const QString errTxt = job->errorString(); 0660 if (job->property("usedOAuth2").toBool()) { 0661 KMessageBox::error(this, i18n("%1 Try again.", errTxt), i18n("Error Loading Data")); 0662 } else { 0663 KMessageBox::error(this, errTxt, i18n("Error Loading Data")); 0664 } 0665 qCDebug(KDENLIVE_LOG) << "//file import job errored: " << errTxt; 0666 return; 0667 } 0668 auto *copyJob = static_cast<KIO::FileCopyJob *>(job); 0669 const QUrl filePath = copyJob->destUrl(); 0670 KRecentDirs::add(QStringLiteral(":KdenliveOnlineResourceFolder"), filePath.adjusted(QUrl::RemoveFilename).toLocalFile()); 0671 0672 KMessageBox::information(this, i18n("Resource saved to %1", filePath.toLocalFile()), i18n("Data Imported")); 0673 Q_EMIT addClip(filePath, QString()); 0674 0675 if (!copyJob->property("attribution").toString().isEmpty()) { 0676 Q_EMIT addLicenseInfo(copyJob->property("attribution").toString()); 0677 } 0678 } 0679 0680 /** 0681 * @brief ResourceWidget::slotAccessTokenReceived 0682 * @param accessToken - the access token obtained from the provider 0683 * Calls slotSaveItem or slotChooseVersion for auth protected files 0684 */ 0685 void ResourceWidget::slotAccessTokenReceived(const QString &accessToken) 0686 { 0687 if (!accessToken.isEmpty()) { 0688 if (m_currentItem->data(singleDownloadRole).toBool()) { 0689 if (m_currentItem->data(downloadRole).toString().isEmpty()) { 0690 m_currentProvider->get()->slotFetchFiles(m_currentItem->data(idRole).toString()); 0691 return; 0692 } else { 0693 slotSaveItem(QString(), accessToken); 0694 } 0695 } else { 0696 slotChooseVersion(m_currentItem->data(downloadRole).toStringList(), m_currentItem->data(downloadLabelRole).toStringList(), accessToken); 0697 } 0698 0699 } else { 0700 KMessageBox::error(this, i18n("Try importing again to obtain a new connection"), 0701 i18n("Error Getting Access Token from %1.", m_currentProvider->get()->name())); 0702 } 0703 }