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