resourcewidget.cpp 26.8 KB
Newer Older
Julius Künzel's avatar
Julius Künzel committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/***************************************************************************
 *   Copyright (C) 2021 by Julius Künzel (jk.kdedev@smartlab.uber.space)   *
 *   Copyright (C) 2011 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
 *   This file is part of Kdenlive. See www.kdenlive.org.                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) version 3 or any later version accepted by the       *
 *   membership of KDE e.V. (or its successor approved  by the membership  *
 *   of KDE e.V.), which shall act as a proxy defined in Section 14 of     *
 *   version 3 of the license.                                             *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.*
 ***************************************************************************/

23
24
25
26
27
28
29
30
#include "resourcewidget.hpp"
#include "core.h"
#include "kdenlivesettings.h"

#include <klocalizedstring.h>
#include <KSqueezedTextLabel>
#include <QFileDialog>
#include <QFontDatabase>
Julius Künzel's avatar
Julius Künzel committed
31
#include <QIcon>
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <QInputDialog>
#include <QMenu>
#include <QProgressDialog>
#include <QToolBar>
#include <QComboBox>
#include <KFileItem>
#include <KMessageBox>
#include <KRecentDirs>
#include <KRun>
#include <KSelectAction>

ResourceWidget::ResourceWidget(QWidget *parent)
    : QWidget(parent)
{
    setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
    setupUi(this);
48
    m_tmpThumbFile = new QTemporaryFile(this);
49

Vincent Pinon's avatar
Vincent Pinon committed
50
51
    int iconHeight = int(QFontInfo(font()).pixelSize() * 3.5);
    m_iconSize = QSize(int(iconHeight * pCore->getCurrentDar()), iconHeight);
52
53
54

    slider_zoom->setRange(0, 15);
    connect(slider_zoom, &QAbstractSlider::valueChanged, this, &ResourceWidget::slotSetIconSize);
Julius Künzel's avatar
Julius Künzel committed
55
    connect(button_zoomin, &QToolButton::clicked, this, [&]() {
56
57
58
        slider_zoom->setValue(qMin(slider_zoom->value() + 1, slider_zoom->maximum())); });
    connect(button_zoomout, &QToolButton::clicked, this, [&]() { 
        slider_zoom->setValue(qMax(slider_zoom->value() - 1, slider_zoom->minimum())); });
59

60
    message_line->hide();
61

62
    for (const QPair<QString, QString> &provider : ProvidersRepository::get()->getAllProviers()) {
Julius Künzel's avatar
Julius Künzel committed
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
        QIcon icon;
        switch(ProvidersRepository::get()->getProvider(provider.second)->type()){
        case ProviderModel::AUDIO:
            icon = QIcon::fromTheme(QStringLiteral("player-volume"));
            break;
        case ProviderModel::VIDEO:
            icon = QIcon::fromTheme(QStringLiteral("camera-video"));
            break;
        case ProviderModel::IMAGE:
            icon = QIcon::fromTheme(QStringLiteral("camera-photo"));
            break;
        default:
            icon = QIcon();
        }
        service_list->addItem(icon, provider.first, provider.second);
78
79
    }
    connect(service_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChangeProvider()));
Julius Künzel's avatar
Julius Künzel committed
80
    loadConfig();
81
    connect(provider_info, SIGNAL(leftClickedUrl(const QString&)), this, SLOT(slotOpenUrl(const QString&)));
Julius Künzel's avatar
Julius Künzel committed
82
    connect(label_license, SIGNAL(leftClickedUrl(const QString&)), this, SLOT(slotOpenUrl(const QString&)));
83
84
85
86
87
88
89
90
91
92
93
94
    connect(search_text, SIGNAL(returnPressed()), this, SLOT(slotStartSearch()));
    connect(search_results, &QListWidget::currentRowChanged, this, &ResourceWidget::slotUpdateCurrentItem);
    connect(button_preview, &QAbstractButton::clicked, this, [&](){
            if (!m_currentProvider) {
                return;
            }

            slotPreviewItem();
        });

    connect(button_import, &QAbstractButton::clicked, this, [&](){
        if(m_currentProvider->get()->downloadOAuth2()) {
Julius Künzel's avatar
Julius Künzel committed
95
96
97
98
            if(m_currentProvider->get()->requiresLogin()) {
                KMessageBox::information(this, i18n("Login is required to download this item.\nYou will be redirected to the login page now."));
            }
            m_currentProvider->get()->authorize();
99
100
101
102
103
104
105
106
107
        } else {
            if(m_currentItem->data(singleDownloadRole).toBool()) {
                if(m_currentItem->data(downloadRole).toString().isEmpty()) {
                    m_currentProvider->get()->slotFetchFiles(m_currentItem->data(idRole).toString());
                    return;
                } else {
                    slotSaveItem();
                }
            } else {
Julius Künzel's avatar
Julius Künzel committed
108
                slotChooseVersion(m_currentItem->data(downloadRole).toStringList(), m_currentItem->data(downloadLabelRole).toStringList());
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
            }
        }

    });

    page_number->setEnabled(false);

    connect(page_number, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ResourceWidget::slotStartSearch);
    adjustSize();
}

ResourceWidget::~ResourceWidget()
{
    saveConfig();
    delete m_tmpThumbFile;
}

Julius Künzel's avatar
Julius Künzel committed
126
127
128
129
/**
 * @brief ResourceWidget::saveConfig
 * Load current provider and zoom value from config file
 */
130
131
132
133
134
void ResourceWidget::loadConfig()
{
    KSharedConfigPtr config = KSharedConfig::openConfig();
    KConfigGroup resourceConfig(config, "OnlineResources");
    slider_zoom->setValue(resourceConfig.readEntry("zoom", 7));
135
136
137
138
139
    if(resourceConfig.readEntry("provider", service_list->itemText(0)).isEmpty()) {
        service_list->setCurrentIndex(0);
    } else {
        service_list->setCurrentItem(resourceConfig.readEntry("provider", service_list->itemText(0)));
    }
Julius Künzel's avatar
Julius Künzel committed
140
    slotChangeProvider();
141
}
Julius Künzel's avatar
Julius Künzel committed
142

143
144
/**
 * @brief ResourceWidget::saveConfig
Julius Künzel's avatar
Julius Künzel committed
145
 * Save current provider and zoom value to config file
146
147
148
149
150
151
152
153
154
155
 */
void ResourceWidget::saveConfig()
{
    KSharedConfigPtr config = KSharedConfig::openConfig();
    KConfigGroup resourceConfig(config, "OnlineResources");
    resourceConfig.writeEntry(QStringLiteral("provider"), service_list->currentText());
    resourceConfig.writeEntry(QStringLiteral("zoom"), slider_zoom->value());
    config->sync();
}

Julius Künzel's avatar
Julius Künzel committed
156
157
158
159
160
/**
 * @brief ResourceWidget::slotChangeProvider
 * @param block
 * Block or unblock the online resource ui
 */
161
162
163
void ResourceWidget::blockUI(bool block)
{
    buildin_box->setEnabled(!block);
164
    search_text->setEnabled(!block);
165
166
167
168
    service_list->setEnabled(!block);
    setCursor(block ? Qt::WaitCursor : Qt::ArrowCursor);
}

Julius Künzel's avatar
Julius Künzel committed
169
170
171
172
/**
 * @brief ResourceWidget::slotChangeProvider
 * Set m_currentProvider to the current selected provider of the service_list and update ui
 */
173
174
175
176
177
void ResourceWidget::slotChangeProvider()
{
    if(m_currentProvider != nullptr) {
        m_currentProvider->get()->disconnect(this);
    }
178

179
180
181
182
183
184
185
186
187
188
    details_box->setEnabled(false);
    button_import->setEnabled(false);
    button_preview->setEnabled(false);
    info_browser->clear();
    search_results->clear();
    page_number->blockSignals(true);
    page_number->setValue(1);
    page_number->setMaximum(1);
    page_number->blockSignals(false);

189
    if(service_list->currentData().toString().isEmpty()) {
190
191
        provider_info->clear();
        buildin_box->setEnabled(false);
192
        return;
193
194
195
    } else {
        buildin_box->setEnabled(true);
        message_line->hide();
196
197
    }

198
199
    m_currentProvider = &ProvidersRepository::get()->getProvider(service_list->currentData().toString());

Julius Künzel's avatar
Julius Künzel committed
200
201
202
203
204
    provider_info->setText(i18n("Media provided by %1", m_currentProvider->get()->name()));
    provider_info->setUrl(m_currentProvider->get()->homepage());
    connect(m_currentProvider->get(), &ProviderModel::searchDone, this, &ResourceWidget::slotSearchFinished);
    connect(m_currentProvider->get(), &ProviderModel::searchError, this, [&](const QString &msg){
        message_line->setText(i18n("Search failed! %1", msg));
205
        message_line->setMessageType(KMessageWidget::Error);
Julius Künzel's avatar
Julius Künzel committed
206
207
208
209
210
211
212
213
214
215
216
217
        message_line->show();
        page_number->setEnabled(false);
        service_list->setEnabled(true);
        buildin_box->setEnabled(true);
        setCursor(Qt::ArrowCursor);
    });
    connect(m_currentProvider->get(), &ProviderModel::fetchedFiles, this, &ResourceWidget::slotChooseVersion);
    connect(m_currentProvider->get(), &ProviderModel::authenticated, this, &ResourceWidget::slotAccessTokenReceived);

    // automatically kick of a search if we have search text and we switch services.
    if (!search_text->text().isEmpty()) {
        slotStartSearch();
218
219
220
    }
}

Julius Künzel's avatar
Julius Künzel committed
221
222
223
224
225
/**
 * @brief ResourceWidget::slotOpenUrl
 * @param url link to open in external browser
 * Open a url in a external browser
 */
226
227
228
229
230
231
232
233
234
235
236
void ResourceWidget::slotOpenUrl(const QString &url)
{
    new KRun(QUrl(url), this);
}

/**
 * @brief ResourceWidget::slotStartSearch
 * Calls slotStartSearch on the object for the currently selected service.
 */
void ResourceWidget::slotStartSearch()
{
237
    message_line->setText(i18nc("@info:status", "Search pending..."));
238
239
240
241
242
    message_line->setMessageType(KMessageWidget::Information);
    message_line->show();

    blockUI(true);
    details_box->setEnabled(false);
243
244
    button_import->setEnabled(false);
    button_preview->setEnabled(false);
245
246
247
248
249
    info_browser->clear();
    search_results->clear();
    m_currentProvider->get()->slotStartSearch(search_text->text(), page_number->value());
}

Julius Künzel's avatar
Julius Künzel committed
250
251
252
253
254
255
/**
 * @brief ResourceWidget::slotSearchFinished
 * @param list list of the found items
 * @param pageCount number of found pages
 * Displays the items of list in the search_results ListView
 */
256
257
void ResourceWidget::slotSearchFinished(QList<ResourceItemInfo> &list, const int pageCount) {

Julius Künzel's avatar
Julius Künzel committed
258
259
    if(list.isEmpty()) {
        message_line->setText(i18nc("@info", "No items found."));
260
        message_line->setMessageType(KMessageWidget::Error);
Julius Künzel's avatar
Julius Künzel committed
261
262
263
264
265
        message_line->show();
        blockUI(false);
        return;
    }

266
267
268
269
    message_line->setMessageType(KMessageWidget::Information);
    message_line->show();
    int count = 0;
    for (const ResourceItemInfo &item: qAsConst(list)) {
Julius Künzel's avatar
Julius Künzel committed
270
271
272
        message_line->setText(i18nc("@info:progress", "Parsing item %1 of %2...", count, list.count()));
        // if item has no name use "Created by Author", if item even has no author use "Unnamed"
        QListWidgetItem *listItem = new QListWidgetItem(item.name.isEmpty() ? (item.author.isEmpty() ? i18n("Unnamed") : i18nc("Created by author name", "Created by %1", item.author)) : item.name);
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
        if(!item.imageUrl.isEmpty()) {
            QUrl img(item.imageUrl);
            if (img.isEmpty()) {
                return;
            }
            m_tmpThumbFile->close();
            if (m_tmpThumbFile->open()) {
                KIO::FileCopyJob *copyjob = KIO::file_copy(img, QUrl::fromLocalFile(m_tmpThumbFile->fileName()), -1, KIO::HideProgressInfo | KIO::Overwrite);
                if(copyjob->exec()) {
                    QPixmap pic(m_tmpThumbFile->fileName());
                    listItem->setIcon(pic);
                }
            }
        }

        listItem->setData(idRole,item.id);
        listItem->setData(nameRole, item.name);
        listItem->setData(filetypeRole, item.filetype);
        listItem->setData(descriptionRole,item.description);
        listItem->setData(imageRole,item.imageUrl);
        listItem->setData(previewRole, item.previewUrl);
        listItem->setData(authorUrl, item.authorUrl);
        listItem->setData(authorRole, item.author);
        listItem->setData(widthRole, item.width);
        listItem->setData(heightRole, item.height);
        listItem->setData(durationRole, item.duration);
        listItem->setData(urlRole, item.infoUrl);
        listItem->setData(licenseRole, item.license);
        if(item.downloadUrl.isEmpty() && item.downloadUrls.length() > 0) {
            listItem->setData(singleDownloadRole, false);
            listItem->setData(downloadRole, item.downloadUrls);
            listItem->setData(downloadLabelRole, item.downloadLabels);
        } else {
            listItem->setData(singleDownloadRole, true);
            listItem->setData(downloadRole, item.downloadUrl);
        }
        search_results->addItem(listItem);
        count++;
    }
312
    message_line->hide();
313
314
315
316
317
318
    page_number->setMaximum(pageCount);
    page_number->setEnabled(true);
    blockUI(false);
}

/**
Julius Künzel's avatar
Julius Künzel committed
319
320
321
 * @brief ResourceWidget::slotUpdateCurrentItem
 * Set m_currentItem to the current selected item of the search_results ListView and
 * show its details within the details_box
322
323
324
325
 */
void ResourceWidget::slotUpdateCurrentItem()
{
    details_box->setEnabled(false);
326
327
    button_import->setEnabled(false);
    button_preview->setEnabled(false);
328

Julius Künzel's avatar
Julius Künzel committed
329
330
    // get the item the user selected
    m_currentItem = search_results->currentItem();
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
    if (!m_currentItem) {
        return;
    }

    if(m_currentProvider->get()->type() != ProviderModel::IMAGE && !m_currentItem->data(previewRole).toString().isEmpty()) {
        button_preview->show();
    } else {
        button_preview->hide();
    }

    QString details = "<h3>" + m_currentItem->text();

    if (!m_currentItem->data(urlRole).toString().isEmpty()) {
        details += QStringLiteral(" <a href=\"%1\">%2</a>").arg(m_currentItem->data(urlRole).toString(), i18nc("the url link pointing to a web page", "link"));
    }

    details.append(QStringLiteral("</h3>"));

    if (!m_currentItem->data(authorUrl).toString().isEmpty()) {
        details += i18n("Created by <a href=\"%1\">", m_currentItem->data(authorUrl).toString());
        if (!m_currentItem->data(authorRole).toString().isEmpty()) {
            details.append(m_currentItem->data(authorRole).toString());
        } else {
            details.append(i18n("Author"));
        }
        details.append(QStringLiteral("</a><br />"));
    } else if (!m_currentItem->data(authorRole).toString().isEmpty()) {
        details.append(i18n("Created by %1", m_currentItem->data(authorRole).toString()) + QStringLiteral("<br />"));
    } else {
        details.append(QStringLiteral("<br />"));
    }

    if (m_currentProvider->get()->type() != ProviderModel::AUDIO && m_currentItem->data(widthRole).toInt() != 0) {
        details.append(i18n("Size: %1 x %2", m_currentItem->data(widthRole).toInt(), m_currentItem->data(heightRole).toInt()) + QStringLiteral("<br />"));
    }
    if (m_currentItem->data(durationRole).toInt() != 0) {
        details.append(i18n("Duration: %1 sec", m_currentItem->data(durationRole).toInt()) + QStringLiteral("<br />"));
    }
    details.append(m_currentItem->data(descriptionRole).toString());
    info_browser->setHtml(details);

Julius Künzel's avatar
Julius Künzel committed
372
373
374
375
    label_license->setText(licenseNameFromUrl(m_currentItem->data(licenseRole).toString(), true));
    label_license->setTipText(licenseNameFromUrl(m_currentItem->data(licenseRole).toString(), false));
    label_license->setUseTips(true);
    label_license->setUrl(m_currentItem->data(licenseRole).toString());
376
377

    details_box->setEnabled(true);
378
379
    button_import->setEnabled(true);
    button_preview->setEnabled(true);
380
381
382
}

/**
Julius Künzel's avatar
Julius Künzel committed
383
 * @brief ResourceWidget::licenseNameFromUrl
384
 * @param licenseUrl
385
 * @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
Julius Künzel's avatar
Julius Künzel committed
386
 * @return the license name "Unnamed License" if url is not known.
387
388
389
390
391
392
 */
QString ResourceWidget::licenseNameFromUrl(const QString &licenseUrl, const bool shortName)
{
    QString licenseName;
    QString licenseShortName;

Julius Künzel's avatar
Julius Künzel committed
393
394
395
396
397
398
399
400
401
402
403
    if(licenseUrl.contains("creativecommons.org")) {
        if (licenseUrl.contains(QStringLiteral("/sampling+/"))) {
            licenseName = i18nc("Creative Commons License", "CC Sampling+");
        } else if (licenseUrl.contains(QStringLiteral("/by/"))) {
            licenseName = i18nc("Creative Commons License", "Creative Commons Attribution");
            licenseShortName = i18nc("Creative Commons License (short)", "CC BY");
        } else if (licenseUrl.contains(QStringLiteral("/by-nd/"))) {
            licenseName = i18nc("Creative Commons License", "Creative Commons Attribution-NoDerivs");
            licenseShortName = i18nc("Creative Commons License (short)", "CC BY-ND");
        } else if (licenseUrl.contains(QStringLiteral("/by-nc-sa/"))) {
            licenseName = i18nc("Creative Commons License", "Creative Commons Attribution-NonCommercial-ShareAlike");
404
            licenseShortName = i18nc("Creative Commons License (short)", "CC BY-NC-SA");
Julius Künzel's avatar
Julius Künzel committed
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
        } else if (licenseUrl.contains(QStringLiteral("/by-sa/"))) {
            licenseName = i18nc("Creative Commons License", "Creative Commons Attribution-ShareAlike");
            licenseShortName = i18nc("Creative Commons License (short)", "CC BY-SA");
        } else if (licenseUrl.contains(QStringLiteral("/by-nc/"))) {
            licenseName = i18nc("Creative Commons License", "Creative Commons Attribution-NonCommercial");
            licenseShortName = i18nc("Creative Commons License (short)", "CC BY-NC");
        } else if (licenseUrl.contains(QStringLiteral("/by-nc-nd/"))) {
            licenseName = i18nc("Creative Commons License", "Creative Commons Attribution-NonCommercial-NoDerivs");
            licenseShortName = i18nc("Creative Commons License (short)", "CC BY-NC-ND");
        } else if (licenseUrl.contains(QLatin1String("/publicdomain/zero/"))) {
            licenseName = i18nc("Creative Commons License", "Creative Commons 0");
            licenseShortName = i18nc("Creative Commons License (short)", "CC 0");
        } else if (licenseUrl.endsWith(QLatin1String("/publicdomain")) || licenseUrl.contains(QLatin1String("openclipart.org/share"))) {
            licenseName = i18nc("License", "Public Domain");
        } else {
            licenseShortName = i18nc("Short for: Unknown Creative Commons License", "Unknown CC License");
            licenseName = i18n("Unknown Creative Commons License");
        }
423

Julius Künzel's avatar
Julius Künzel committed
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
        if (licenseUrl.contains(QStringLiteral("/1.0"))) {
            licenseName.append(QStringLiteral(" 1.0"));
            licenseShortName.append(QStringLiteral(" 1.0"));
        } else if (licenseUrl.contains(QStringLiteral("/2.0"))) {
            licenseName.append(QStringLiteral(" 2.0"));
            licenseShortName.append(QStringLiteral(" 2.0"));
        } else if (licenseUrl.contains(QStringLiteral("/2.5"))) {
            licenseName.append(QStringLiteral(" 2.5"));
            licenseShortName.append(QStringLiteral(" 2.5"));
        } else if (licenseUrl.contains(QStringLiteral("/3.0"))) {
            licenseName.append(QStringLiteral(" 3.0"));
            licenseShortName.append(QStringLiteral(" 3.0"));
        } else if (licenseUrl.contains(QStringLiteral("/4.0"))) {
            licenseName.append(QStringLiteral(" 4.0"));
            licenseShortName.append(QStringLiteral(" 4.0"));
        }
    } else if (licenseUrl.contains("pexels.com/license/")){
        licenseName = i18n("Pexels License");
    } else if (licenseUrl.contains("pixabay.com/service/license/")) {
        licenseName = i18n("Pixabay License");;
    } else {
        licenseName = i18n("Unknown License");
446
447
    }

Julius Künzel's avatar
Julius Künzel committed
448
    if(shortName && !licenseShortName.isEmpty()) {
449
450
451
452
453
454
        return licenseShortName;
    } else {
        return licenseName;
    }
}

Julius Künzel's avatar
Julius Künzel committed
455
456
457
458
459
/**
 * @brief ResourceWidget::slotSetIconSize
 * @param size
 * Set the icon size for the search_results ListView
 */
460
461
462
463
464
465
466
467
468
469
470
void ResourceWidget::slotSetIconSize(int size)
{
    if (!search_results) {
        return;
    }
    QSize zoom = m_iconSize;
    zoom = zoom * (size / 4.0);
    search_results->setIconSize(zoom);
}

/**
Julius Künzel's avatar
Julius Künzel committed
471
472
 * @brief ResourceWidget::slotPreviewItem
 * Emits the previewClip signal if m_currentItem is valid to display a preview in the Clip Monitor
473
474
475
476
477
478
479
480
 */
void ResourceWidget::slotPreviewItem()
{
    if (!m_currentItem) {
        return;
    }

    blockUI(true);
481
    emit previewClip(m_currentItem->data(previewRole).toString(), i18n("Online Resources Preview"));
482
483
484
    blockUI(false);
}

Julius Künzel's avatar
Julius Künzel committed
485
486
487
488
489
490
491
/**
 * @brief ResourceWidget::slotChooseVersion
 * @param urls list of download urls pointing to the certain file version
 * @param labels list of labels for the certain file version (needs to have the same order than urls)
 * @param accessToken access token to pass through to slotSaveItem
 * Displays a dialog to let the user choose a file version (e.g. filetype, quality) if there a multiple versions avaible
 */
492
493
494
495
496
497
498
499
500
void ResourceWidget::slotChooseVersion(const QStringList &urls, const QStringList &labels, const QString &accessToken) {
    if(urls.isEmpty() || labels.isEmpty()) {
        return;
    }
    if(urls.length() == 1) {
        slotSaveItem(urls.first(), accessToken);
        return;
    }
    bool ok;
Julius Künzel's avatar
Julius Künzel committed
501
    QString name = QInputDialog::getItem(this, i18n("Choose File Version"), i18n("Please choose the version you want to download"), labels, 0, false, &ok);
502
503
504
505
506
507
508
509
    if(!ok || name.isEmpty()) {
        return;
    }
    QString url = urls.at(labels.indexOf(name));
    slotSaveItem(url, accessToken);
}

/**
Julius Künzel's avatar
Julius Künzel committed
510
511
512
513
514
 * @brief ResourceWidget::slotSaveItem
 * @param originalUrl url pointing to the download file
 * @param accessToken the access token (optional)
 * Opens a dialog for user to choose a save location and start the download of the file
 */
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
void ResourceWidget::slotSaveItem(const QString &originalUrl, const QString &accessToken)
{
    QUrl saveUrl;

    if (!m_currentItem) {
        return;
    }

    QUrl srcUrl(originalUrl.isEmpty() ? m_currentItem->data(downloadRole).toString() : originalUrl);
    if (srcUrl.isEmpty()) {
        return;
    }

    QString path = KRecentDirs::dir(QStringLiteral(":KdenliveOnlineResourceFolder"));
    QString ext;
    QString sFileExt;

    if (path.isEmpty()) {
        path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
    }
    if (!path.endsWith(QLatin1Char('/'))) {
        path.append(QLatin1Char('/'));
    }
    if (!srcUrl.fileName().isEmpty()) {
        path.append(srcUrl.fileName());
        ext = "*." + srcUrl.fileName().section(QLatin1Char('.'), -1);
    } else if(!m_currentItem->data(filetypeRole).toString().isEmpty()) {
        ext = "*." + m_currentItem->data(filetypeRole).toString();
    } else {
        if (m_currentProvider->get()->type() == ProviderModel::AUDIO) {
            ext = i18n("Audio") + QStringLiteral(" (*.wav *.mp3 *.ogg *.aif *.aiff *.m4a *.flac)") + QStringLiteral(";;");
        } else if (m_currentProvider->get()->type() == ProviderModel::VIDEO) {
            ext = i18n("Video") + QStringLiteral(" (*.mp4 *.webm *.mpg *.mov *.avi *.mkv)") + QStringLiteral(";;");
        } else if (m_currentProvider->get()->type() == ProviderModel:: ProviderModel::IMAGE) {
            ext = i18n("Images") + QStringLiteral(" (*.png *.jpg *.jpeg *.svg") + QStringLiteral(";;");
        }
        ext.append(i18n("All Files") + QStringLiteral(" (*.*)"));
    }
    if (path.endsWith(QLatin1Char('/'))) {
        if(m_currentItem->data(nameRole).toString().isEmpty()) {
            path.append(i18n("Unnamed"));
        } else {
            path.append(m_currentItem->data(nameRole).toString());
        }
        path.append(srcUrl.fileName().section(QLatin1Char('.'), -1));
    }

    QString attribution;
    if(KMessageBox::questionYesNo(this,
Julius Künzel's avatar
Julius Künzel committed
564
565
566
567
568
569
570
571
572
                                  i18n("Be aware that the usage of the resource is maybe restricted by license terms or law!\n"
                                       "Do you want to add license attribution to your Project Notes?"),
                                  QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), i18n("Remember this decision")) == KMessageBox::Yes) {
        attribution = i18nc("item name, item url, author name, license name, license url", "This video uses \"%1\" (%2) by \"%3\" licensed under %4. To view a copy of this license, visit %5",
            m_currentItem->data(nameRole).toString().isEmpty() ? i18n("Unnamed") : m_currentItem->data(nameRole).toString(),
            m_currentItem->data(urlRole).toString(),
            m_currentItem->data(authorRole).toString(),
            ResourceWidget::licenseNameFromUrl(m_currentItem->data(licenseRole).toString(), true),
            m_currentItem->data(licenseRole).toString());
573
574
575
576
    }

    QString saveUrlstring = QFileDialog::getSaveFileName(this, QString(), path, ext);

Julius Künzel's avatar
Julius Künzel committed
577
578
    //if user cancels save
    if (saveUrlstring.isEmpty()) {
579
580
581
582
583
584
        return;
    }

    saveUrl = QUrl::fromLocalFile(saveUrlstring);

    KIO::FileCopyJob *getJob = KIO::file_copy(srcUrl, saveUrl, -1, KIO::Overwrite);
Julius Künzel's avatar
Julius Künzel committed
585
586
    if(!accessToken.isEmpty()) {
        getJob->addMetaData("customHTTPHeader", QStringLiteral("Authorization: Bearer %1").arg(accessToken));
587
588
589
590
591
592
593
594
595
596
597
    }
    KFileItem info(srcUrl);
    getJob->setSourceSize(info.size());
    getJob->setProperty("attribution", attribution);
    getJob->setProperty("usedOAuth2", !accessToken.isEmpty());

    connect(getJob, &KJob::result, this, &ResourceWidget::slotGotFile);
    getJob->start();
}

/**
Julius Künzel's avatar
Julius Künzel committed
598
 * @brief ResourceWidget::slotGotFile
599
 * @param job
Julius Künzel's avatar
Julius Künzel committed
600
601
 * Finish the download by emiting addClip and if necessary addLicenseInfo
 * Enables the import button
602
603
604
605
606
607
608
 */
void ResourceWidget::slotGotFile(KJob *job)

{
    if (job->error() != 0) {
        const QString errTxt = job->errorString();
        if(job->property("usedOAuth2").toBool()) {
Julius Künzel's avatar
Julius Künzel committed
609
            KMessageBox::sorry(this,  i18n("%1 Try again.", errTxt), i18n("Error Loading Data"));
610
611
612
613
614
615
616
617
618
619
620
621
        } else {
            KMessageBox::sorry(this, errTxt, i18n("Error Loading Data"));
        }
        qCDebug(KDENLIVE_LOG) << "//file import job errored: " << errTxt;
        return;
    }
    auto *copyJob = static_cast<KIO::FileCopyJob *>(job);
    const QUrl filePath = copyJob->destUrl();
    KRecentDirs::add(QStringLiteral(":KdenliveOnlineResourceFolder"), filePath.adjusted(QUrl::RemoveFilename).toLocalFile());

    KMessageBox::information(this, i18n("Resource saved to %1", filePath.toLocalFile()), i18n("Data Imported"));
    emit addClip(filePath, QString());
Julius Künzel's avatar
Julius Künzel committed
622

623
624
625
626
627
628
629
    if(!copyJob->property("attribution").toString().isEmpty()) {
        emit addLicenseInfo(copyJob->property("attribution").toString());
    }
}

/**
 * @brief ResourceWidget::slotAccessTokenReceived
Julius Künzel's avatar
Julius Künzel committed
630
631
 * @param accessToken - the access token obtained from the provider
 * Calls slotSaveItem or slotChooseVersion for auth protected files
632
633
634
635
636
637
638
639
640
641
642
643
 */
void ResourceWidget::slotAccessTokenReceived(const QString &accessToken)
{
    if (!accessToken.isEmpty()) {
        if(m_currentItem->data(singleDownloadRole).toBool()) {
            if(m_currentItem->data(downloadRole).toString().isEmpty()) {
                m_currentProvider->get()->slotFetchFiles(m_currentItem->data(idRole).toString());
                return;
            } else {
                slotSaveItem(QString(), accessToken);
            }
        } else {
Julius Künzel's avatar
Julius Künzel committed
644
            slotChooseVersion(m_currentItem->data(downloadRole).toStringList(), m_currentItem->data(downloadLabelRole).toStringList(), accessToken);
645
646
647
        }

    } else {
Julius Künzel's avatar
Julius Künzel committed
648
        KMessageBox::error(this, i18n("Try importing again to obtain a new connection"), i18n("Error Getting Access Token from %1.", m_currentProvider->get()->name()));
649
650
    }
}