]> git.sesse.net Git - kdenlive/blob - src/utils/resourcewidget.cpp
Various fixes and improvments to online resources
[kdenlive] / src / utils / resourcewidget.cpp
1 /***************************************************************************
2  *   Copyright (C) 2011 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
19  ***************************************************************************/
20
21
22 #include "resourcewidget.h"
23 #include "freesound.h"
24 #include "openclipart.h"
25 #include "archiveorg.h"
26
27 #include <QPushButton>
28 #include <QSpinBox>
29 #include <QListWidget>
30 #include <QAction>
31 #include <QMenu>
32
33 #include <KDebug>
34 #include <kdeversion.h>
35 #include "kdenlivesettings.h"
36 #include <KGlobalSettings>
37 #include <KMessageBox>
38 #include <KFileDialog>
39 #include <kio/job.h>
40 #include <KIO/NetAccess>
41 #include <Solid/Networking>
42 #include <KRun>
43 #include <KPixmapSequence>
44 #include <KPixmapSequenceOverlayPainter>
45
46
47 #ifdef USE_NEPOMUK
48 #if KDE_IS_VERSION(4,6,0)
49 #include <Nepomuk/Variant>
50 #include <Nepomuk/Resource>
51 #include <Nepomuk/ResourceManager>
52 #include <Nepomuk/Vocabulary/NIE>
53 #include <Nepomuk/Vocabulary/NCO>
54 #include <Nepomuk/Vocabulary/NDO>
55 #include <kfileitem.h>
56 #endif
57 #endif
58
59 ResourceWidget::ResourceWidget(const QString & folder, QWidget * parent) :
60         QDialog(parent),
61         m_folder(folder),
62         m_currentService(NULL)
63 {
64     setFont(KGlobalSettings::toolBarFont());
65     setupUi(this);
66     setAttribute(Qt::WA_DeleteOnClose);
67 #ifdef USE_QJSON
68     service_list->addItem(i18n("Freesound Audio Library"), FREESOUND);
69     service_list->addItem(i18n("Archive.org Video Library"), ARCHIVEORG);
70 #endif
71     service_list->addItem(i18n("Open Clip Art Graphic Library"), OPENCLIPART);
72     setWindowTitle(i18n("Search Online Resources"));
73     info_browser->setStyleSheet(QString("QTextBrowser { background-color: transparent;}"));
74     connect(button_search, SIGNAL(clicked()), this, SLOT(slotStartSearch()));
75     connect(search_results, SIGNAL(currentRowChanged(int)), this, SLOT(slotUpdateCurrentSound()));
76     connect(button_preview, SIGNAL(clicked()), this, SLOT(slotPlaySound()));
77     connect(button_import, SIGNAL(clicked()), this, SLOT(slotSaveItem()));
78     connect(item_license, SIGNAL(leftClickedUrl(const QString &)), this, SLOT(slotOpenUrl(const QString &)));
79     connect(service_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChangeService()));
80     if (Solid::Networking::status() == Solid::Networking::Unconnected) {
81         slotOffline();
82     }
83     connect(Solid::Networking::notifier(), SIGNAL(shouldConnect()), this, SLOT(slotOnline()));
84     connect(Solid::Networking::notifier(), SIGNAL(shouldDisconnect()), this, SLOT(slotOffline()));
85     connect(page_next, SIGNAL(clicked()), this, SLOT(slotNextPage()));
86     connect(page_prev, SIGNAL(clicked()), this, SLOT(slotPreviousPage()));
87     connect(page_number, SIGNAL(valueChanged(int)), this, SLOT(slotStartSearch(int)));
88     connect(info_browser, SIGNAL(anchorClicked(const QUrl &)), this, SLOT(slotOpenLink(const QUrl &)));
89     
90     m_autoPlay = new QAction(i18n("Auto Play"), this);
91     m_autoPlay->setCheckable(true);
92     QMenu *resourceMenu = new QMenu;
93     resourceMenu->addAction(m_autoPlay);
94     config_button->setMenu(resourceMenu);
95     config_button->setIcon(KIcon("configure"));
96
97     m_busyWidget = new KPixmapSequenceOverlayPainter(this);
98     m_busyWidget->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
99     m_busyWidget->setWidget(search_results->viewport());
100     
101     sound_box->setEnabled(false);
102     search_text->setFocus();
103 #ifdef USE_NEPOMUK
104 #if KDE_IS_VERSION(4,6,0)    
105     Nepomuk::ResourceManager::instance()->init();
106 #endif
107 #endif
108     slotChangeService();
109 }
110
111 ResourceWidget::~ResourceWidget()
112 {
113     if (m_currentService) delete m_currentService;
114     KIO::NetAccess::removeTempFile(m_tmpThumbFile);
115 }
116
117 void ResourceWidget::slotStartSearch(int page)
118 {
119     page_number->blockSignals(true);
120     page_number->setValue(page);
121     page_number->blockSignals(false);
122     m_busyWidget->start();
123     m_currentService->slotStartSearch(search_text->text(), page);
124 }
125
126 void ResourceWidget::slotUpdateCurrentSound()
127 {
128     KIO::NetAccess::removeTempFile(m_tmpThumbFile);
129     if (!m_autoPlay->isChecked()) m_currentService->stopItemPreview(NULL);
130     item_license->clear();
131     m_title.clear();
132     m_image.clear();
133     m_desc.clear();
134     m_meta.clear();
135     QListWidgetItem *item = search_results->currentItem();
136     if (!item) {
137         sound_box->setEnabled(false);
138         return;
139     }
140     m_currentInfo = m_currentService->displayItemDetails(item);
141     
142     if (m_autoPlay->isChecked() && m_currentService->hasPreview) m_currentService->startItemPreview(item);
143     sound_box->setEnabled(true);
144     QString title = "<h3>" + m_currentInfo.itemName;
145     if (!m_currentInfo.infoUrl.isEmpty()) title += QString(" (<a href=\"%1\">%2</a>)").arg(m_currentInfo.infoUrl).arg(i18nc("the url link pointing to a web page", "link"));
146     title.append("</h3>");
147     
148     if (!m_currentInfo.authorUrl.isEmpty()) {
149         title += QString("<a href=\"%1\">").arg(m_currentInfo.authorUrl);
150         if (!m_currentInfo.author.isEmpty())
151             title.append(m_currentInfo.author);
152         else title.append(i18n("Author"));
153         title.append("</a><br />");
154     }
155     else if (!m_currentInfo.author.isEmpty())
156         title.append(m_currentInfo.author + "<br />");
157     else title.append("<br />");
158     
159     slotSetTitle(title);
160     if (!m_currentInfo.description.isEmpty()) slotSetDescription(m_currentInfo.description);
161     if (!m_currentInfo.license.isEmpty()) parseLicense(m_currentInfo.license);
162 }
163
164
165 void ResourceWidget::slotLoadThumb(const QString url)
166 {
167     KUrl img(url);
168     if (img.isEmpty()) return;
169     if (KIO::NetAccess::exists(img, KIO::NetAccess::SourceSide, this)) {
170         if (KIO::NetAccess::download(img, m_tmpThumbFile, this)) {
171             slotSetImage(m_tmpThumbFile);
172             /*QPixmap pix(tmpFile);
173             
174             int newHeight = pix.height() * item_image->width() / pix.width();
175             if (newHeight > 200) {
176                 item_image->setScaledContents(false);
177                 //item_image->setFixedHeight(item_image->width());
178             }
179             else {
180                 item_image->setScaledContents(true);
181                 item_image->setFixedHeight(newHeight);
182             }
183             item_image->setPixmap(pix);*/
184         }
185     }
186 }
187
188
189 void ResourceWidget::slotDisplayMetaInfo(QMap <QString, QString> metaInfo)
190 {
191     if (metaInfo.contains("license")) {
192         parseLicense(metaInfo.value("license"));
193     }
194     if (metaInfo.contains("description")) {
195         slotSetDescription(metaInfo.value("description"));
196     }
197 }
198
199
200 void ResourceWidget::slotPlaySound()
201 {
202     if (!m_currentService) return;
203     bool started = m_currentService->startItemPreview(search_results->currentItem());
204     if (started) button_preview->setText(i18n("Preview"));
205     else button_preview->setText(i18n("Stop"));
206 }
207
208
209 void ResourceWidget::slotForcePlaySound(bool play)
210 {
211     /*
212     if (m_service != FREESOUND) return;
213     m_previewProcess->close();
214     if (m_currentPreview.isEmpty()) return;
215     if (play)
216         m_previewProcess->start("ffplay", QStringList() << m_currentPreview << "-nodisp");
217     */
218 }
219
220 void ResourceWidget::slotPreviewStatusChanged(QProcess::ProcessState state)
221 {
222     /*if (state == QProcess::NotRunning)
223         button_preview->setText(i18n("Preview"));
224     else 
225         button_preview->setText(i18n("Stop"));*/
226 }
227
228 void ResourceWidget::slotSaveItem(const QString originalUrl)
229 {
230     //if (m_currentUrl.isEmpty()) return;
231     QListWidgetItem *item = search_results->currentItem();
232     if (!item) return;
233     QString path = m_folder;
234     QString ext;
235     if (!path.endsWith('/')) path.append('/');
236     if (!originalUrl.isEmpty()) {
237         path.append(KUrl(originalUrl).fileName());
238         ext = "*." + KUrl(originalUrl).fileName().section(".", -1);
239         m_currentInfo.itemDownload = originalUrl;
240     }
241     else {
242         path.append(m_currentService->getDefaultDownloadName(item));
243         ext = m_currentService->getExtension(search_results->currentItem());
244     }
245     QString saveUrl = KFileDialog::getSaveFileName(KUrl(path), ext);
246     KIO::UDSEntry entry;
247     KUrl srcUrl(m_currentInfo.itemDownload);
248     if (saveUrl.isEmpty() || !KIO::NetAccess::stat(srcUrl, entry, this)) return;
249     KIO::FileCopyJob * getJob = KIO::file_copy(srcUrl, KUrl(saveUrl), -1, KIO::Overwrite);
250     
251     KFileItem info(entry, srcUrl); 
252     getJob->setSourceSize(info.size());
253     getJob->setProperty("license", item_license->text());
254     getJob->setProperty("licenseurl", item_license->url());
255     getJob->setProperty("originurl", m_currentInfo.itemDownload);
256     if (!m_currentInfo.authorUrl.isEmpty()) getJob->setProperty("author", m_currentInfo.authorUrl);
257     else if (!m_currentInfo.author.isEmpty()) getJob->setProperty("author", m_currentInfo.author);
258     connect(getJob, SIGNAL(result(KJob*)), this, SLOT(slotGotFile(KJob*)));
259     getJob->start();
260 }
261
262 void ResourceWidget::slotGotFile(KJob *job)
263 {
264     if (job->error() != 0 ) return;
265     KIO::FileCopyJob* copyJob = static_cast<KIO::FileCopyJob*>( job );
266     const KUrl filePath = copyJob->destUrl();
267 #ifdef USE_NEPOMUK
268 #if KDE_IS_VERSION(4,6,0)
269         Nepomuk::Resource res( filePath );
270         res.setProperty( Nepomuk::Vocabulary::NIE::license(), (Nepomuk::Variant) job->property("license") );
271         res.setProperty( Nepomuk::Vocabulary::NIE::licenseType(), (Nepomuk::Variant) job->property("licenseurl") );
272         res.setProperty( Nepomuk::Vocabulary::NDO::copiedFrom(), (Nepomuk::Variant) job->property("originurl") );
273         res.setProperty( Nepomuk::Vocabulary::NDO::copiedFrom(), (Nepomuk::Variant) job->property("originurl") );
274         res.setProperty( Nepomuk::Vocabulary::NCO::creator(), (Nepomuk::Variant) job->property("author") );
275         //res.setDescription(item_description->toPlainText());
276         //res.setProperty( Soprano::Vocabulary::NAO::description(), 
277 #endif
278 #endif
279         emit addClip(filePath, QString());
280 }
281
282 void ResourceWidget::slotOpenUrl(const QString &url)
283 {
284     new KRun(KUrl(url), this);
285 }
286
287 void ResourceWidget::slotChangeService()
288 {
289     if (m_currentService) {
290         delete m_currentService;
291         m_currentService = NULL;
292     }
293     SERVICETYPE service = (SERVICETYPE) service_list->itemData(service_list->currentIndex()).toInt();
294     if (service == FREESOUND) {
295         m_currentService = new FreeSound(search_results);
296     }
297     else if (service == OPENCLIPART) {
298         m_currentService = new OpenClipArt(search_results);
299     }
300     else if (service == ARCHIVEORG) {
301         m_currentService = new ArchiveOrg(search_results);
302     }
303
304     connect(m_currentService, SIGNAL(gotMetaInfo(const QString)), this, SLOT(slotSetMetadata(const QString)));
305     connect(m_currentService, SIGNAL(gotMetaInfo(QMap <QString, QString>)), this, SLOT(slotDisplayMetaInfo(QMap <QString, QString>)));
306     connect(m_currentService, SIGNAL(maxPages(int)), page_number, SLOT(setMaximum(int)));
307     connect(m_currentService, SIGNAL(searchInfo(QString)), search_info, SLOT(setText(QString)));
308     connect(m_currentService, SIGNAL(gotThumb(const QString)), this, SLOT(slotLoadThumb(const QString)));
309     connect(m_currentService, SIGNAL(searchDone()), m_busyWidget, SLOT(stop()));
310     
311     button_preview->setVisible(m_currentService->hasPreview);
312     button_import->setVisible(!m_currentService->inlineDownload);
313     search_info->setText(QString());
314     if (!search_text->text().isEmpty()) slotStartSearch();
315 }
316
317 void ResourceWidget::slotOnline()
318 {
319     button_search->setEnabled(true);
320     search_info->setText(QString());
321 }
322
323 void ResourceWidget::slotOffline()
324 {
325     button_search->setEnabled(false);
326     search_info->setText(i18n("You need to be online\n for searching"));
327 }
328
329 void ResourceWidget::slotNextPage()
330 {
331     int ix = page_number->value();
332     if (search_results->count() > 0) page_number->setValue(ix + 1);
333 }
334
335 void ResourceWidget::slotPreviousPage()
336 {
337     int ix = page_number->value();
338     if (ix > 1) page_number->setValue(ix - 1);
339 }
340
341 void ResourceWidget::parseLicense(const QString &licenseUrl)
342 {
343     QString licenseName;
344     if (licenseUrl.contains("/sampling+/"))
345         licenseName = "Sampling+";
346     else if (licenseUrl.contains("/by/"))
347         licenseName = "Attribution";
348     else if (licenseUrl.contains("/by-nd/"))
349         licenseName = "Attribution-NoDerivs";
350     else if (licenseUrl.contains("/by-nc-sa/"))
351         licenseName = "Attribution-NonCommercial-ShareAlike";
352     else if (licenseUrl.contains("/by-sa/"))
353         licenseName = "Attribution-ShareAlike";
354     else if (licenseUrl.contains("/by-nc/"))
355         licenseName = "Attribution-NonCommercial";
356     else if (licenseUrl.contains("/by-nc-nd/"))
357         licenseName = "Attribution-NonCommercial-NoDerivs";
358     else if (licenseUrl.contains("/publicdomain/zero/"))
359         licenseName = "Creative Commons 0";
360     else if (licenseUrl.endsWith("/publicdomain"))
361         licenseName = "Public Domain";
362     else licenseName = i18n("Unknown");
363     item_license->setText(licenseName);
364     item_license->setUrl(licenseUrl);
365 }
366
367
368 void ResourceWidget::slotOpenLink(const QUrl &url)
369 {
370     QString path = url.toEncoded();
371     if (path.endsWith("_import")) {
372         path.chop(7);
373         // import file in Kdenlive
374         slotSaveItem(path);
375     }
376     else {
377         slotOpenUrl(path);
378     }
379 }
380
381 void ResourceWidget::slotSetDescription(const QString desc)
382 {
383     m_desc = desc;
384     updateLayout();
385 }
386
387 void ResourceWidget::slotSetMetadata(const QString desc)
388 {
389     m_meta = desc;
390     updateLayout();
391 }
392
393 void ResourceWidget::slotSetImage(const QString desc)
394 {
395     m_image = QString("<img src=\"%1\" style=\"max-height:150px\" max-width=\"%2px\" />").arg(desc).arg((int) (info_browser->width() * 0.9));
396     updateLayout();
397 }
398
399 void ResourceWidget::slotSetTitle(const QString desc)
400 {
401     m_title = desc;
402     updateLayout();
403 }
404
405 void ResourceWidget::updateLayout()
406 {
407     QString content = m_title;
408     if (!m_image.isEmpty()) content.append(m_image + "<br clear=\"all\" />");
409     if (!m_desc.isEmpty()) content.append(m_desc);
410     if (!m_meta.isEmpty()) content.append(m_meta);
411     info_browser->setHtml(content);
412 }