]> git.sesse.net Git - kdenlive/blob - src/utils/resourcewidget.cpp
Asynchronous download for online resource widget (still some bugs)
[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 <QDomDocument>
31
32 #include <KDebug>
33 #include <kdeversion.h>
34 #include "kdenlivesettings.h"
35 #include <KGlobalSettings>
36 #include <KMessageBox>
37 #include <KFileDialog>
38 #include <kio/job.h>
39 #include <KIO/NetAccess>
40 #include <Solid/Networking>
41 #include <KRun>
42
43 #ifdef USE_NEPOMUK
44 #if KDE_IS_VERSION(4,6,0)
45 #include <Nepomuk/Variant>
46 #include <Nepomuk/Resource>
47 #include <Nepomuk/ResourceManager>
48 #include <Soprano/Vocabulary/NAO>
49 #include <Nepomuk/Vocabulary/NIE>
50 #include <Nepomuk/Vocabulary/NDO>
51 #include <kfileitem.h>
52 #endif
53 #endif
54
55 ResourceWidget::ResourceWidget(const QString & folder, QWidget * parent) :
56         QDialog(parent),
57         m_folder(folder),
58         m_currentService(NULL)
59 {
60     setFont(KGlobalSettings::toolBarFont());
61     setupUi(this);
62     setAttribute(Qt::WA_DeleteOnClose);
63 #ifdef USE_QJSON
64     service_list->addItem(i18n("Freesound Audio Library"), FREESOUND);
65     service_list->addItem(i18n("Archive.org Video Library"), ARCHIVEORG);
66 #endif
67     service_list->addItem(i18n("Open Clip Art Graphic Library"), OPENCLIPART);
68     setWindowTitle(i18n("Search Online Resources"));
69     info_browser->setStyleSheet(QString("QTextBrowser { background-color: transparent;}"));
70     connect(button_search, SIGNAL(clicked()), this, SLOT(slotStartSearch()));
71     connect(search_results, SIGNAL(currentRowChanged(int)), this, SLOT(slotUpdateCurrentSound()));
72     connect(button_preview, SIGNAL(clicked()), this, SLOT(slotPlaySound()));
73     connect(button_import, SIGNAL(clicked()), this, SLOT(slotSaveItem()));
74     connect(sound_author, SIGNAL(leftClickedUrl(const QString &)), this, SLOT(slotOpenUrl(const QString &)));
75     connect(item_license, SIGNAL(leftClickedUrl(const QString &)), this, SLOT(slotOpenUrl(const QString &)));
76     connect(sound_name, SIGNAL(leftClickedUrl(const QString &)), this, SLOT(slotOpenUrl(const QString &)));
77     connect(service_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChangeService()));
78     item_image->setFixedWidth(180);
79     if (Solid::Networking::status() == Solid::Networking::Unconnected) {
80         slotOffline();
81     }
82     connect(Solid::Networking::notifier(), SIGNAL(shouldConnect()), this, SLOT(slotOnline()));
83     connect(Solid::Networking::notifier(), SIGNAL(shouldDisconnect()), this, SLOT(slotOffline()));
84     connect(page_next, SIGNAL(clicked()), this, SLOT(slotNextPage()));
85     connect(page_prev, SIGNAL(clicked()), this, SLOT(slotPreviousPage()));
86     connect(page_number, SIGNAL(valueChanged(int)), this, SLOT(slotStartSearch(int)));
87     connect(info_browser, SIGNAL(anchorClicked(const QUrl &)), this, SLOT(slotOpenLink(const QUrl &)));
88     
89     sound_box->setEnabled(false);
90     search_text->setFocus();
91 #ifdef USE_NEPOMUK
92 #if KDE_IS_VERSION(4,6,0)    
93     Nepomuk::ResourceManager::instance()->init();
94 #endif
95 #endif
96     slotChangeService();
97 }
98
99 ResourceWidget::~ResourceWidget()
100 {
101     if (m_currentService) delete m_currentService;
102 }
103
104 void ResourceWidget::slotStartSearch(int page)
105 {
106     page_number->blockSignals(true);
107     page_number->setValue(page);
108     page_number->blockSignals(false);
109     m_currentService->slotStartSearch(search_text->text(), page);
110 }
111
112 void ResourceWidget::slotUpdateCurrentSound()
113 {
114     item_image->setEnabled(false);
115     if (!sound_autoplay->isChecked()) m_currentService->stopItemPreview(NULL);
116     info_browser->clear();
117     item_license->clear();
118     QListWidgetItem *item = search_results->currentItem();
119     if (!item) {
120         sound_box->setEnabled(false);
121         return;
122     }
123     m_currentInfo = m_currentService->displayItemDetails(item);
124     
125     if (sound_autoplay->isChecked()) m_currentService->startItemPreview(item);
126     sound_box->setEnabled(true);
127     sound_name->setText(item->text());
128     sound_name->setUrl(m_currentInfo.infoUrl);
129     sound_author->setText(m_currentInfo.author);
130     sound_author->setUrl(m_currentInfo.authorUrl);
131     if (!m_currentInfo.description.isEmpty()) info_browser->setHtml("<em>" + m_currentInfo.description + "</em>");
132     if (!m_currentInfo.license.isEmpty()) parseLicense(m_currentInfo.license);
133 }
134
135
136 void ResourceWidget::slotLoadThumb(const QString url)
137 {
138     KUrl img(url);
139     if (img.isEmpty()) return;
140     if (KIO::NetAccess::exists(img, KIO::NetAccess::SourceSide, this)) {
141         QString tmpFile;
142         if (KIO::NetAccess::download(img, tmpFile, this)) {
143             QPixmap pix(tmpFile);
144             int newHeight = pix.height() * item_image->width() / pix.width();
145             if (newHeight > 200) {
146                 item_image->setScaledContents(false);
147                 //item_image->setFixedHeight(item_image->width());
148             }
149             else {
150                 item_image->setScaledContents(true);
151                 item_image->setFixedHeight(newHeight);
152             }
153             item_image->setPixmap(pix);
154             KIO::NetAccess::removeTempFile(tmpFile);
155         }
156     }
157     item_image->setEnabled(true);
158 }
159
160
161 void ResourceWidget::slotDisplayMetaInfo(QMap <QString, QString> metaInfo)
162 {
163     if (metaInfo.contains("license")) {
164         parseLicense(metaInfo.value("license"));
165     }
166 }
167
168
169 void ResourceWidget::slotPlaySound()
170 {
171     if (!m_currentService) return;
172     bool started = m_currentService->startItemPreview(search_results->currentItem());
173     if (started) button_preview->setText(i18n("Preview"));
174     else button_preview->setText(i18n("Stop"));
175 }
176
177
178 void ResourceWidget::slotForcePlaySound(bool play)
179 {
180     /*
181     if (m_service != FREESOUND) return;
182     m_previewProcess->close();
183     if (m_currentPreview.isEmpty()) return;
184     if (play)
185         m_previewProcess->start("ffplay", QStringList() << m_currentPreview << "-nodisp");
186     */
187 }
188
189 void ResourceWidget::slotPreviewStatusChanged(QProcess::ProcessState state)
190 {
191     /*if (state == QProcess::NotRunning)
192         button_preview->setText(i18n("Preview"));
193     else 
194         button_preview->setText(i18n("Stop"));*/
195 }
196
197 void ResourceWidget::slotSaveItem(const QString originalUrl)
198 {
199     //if (m_currentUrl.isEmpty()) return;
200     QListWidgetItem *item = search_results->currentItem();
201     if (!item) return;
202     QString path = m_folder;
203     QString ext;
204     if (!path.endsWith('/')) path.append('/');
205     if (!originalUrl.isEmpty()) {
206         path.append(KUrl(originalUrl).fileName());
207         ext = "*." + KUrl(originalUrl).fileName().section(".", -1);
208         m_currentInfo.itemDownload = originalUrl;
209     }
210     else {
211         path.append(m_currentService->getDefaultDownloadName(item));
212         ext = m_currentService->getExtension(search_results->currentItem());
213     }
214     QString saveUrl = KFileDialog::getSaveFileName(KUrl(path), ext);
215     KIO::UDSEntry entry;
216     KUrl srcUrl(m_currentInfo.itemDownload);
217     if (saveUrl.isEmpty() || !KIO::NetAccess::stat(srcUrl, entry, this)) return;
218     KIO::FileCopyJob * getJob = KIO::file_copy(srcUrl, KUrl(saveUrl), -1, KIO::Overwrite);
219     
220     KFileItem info(entry, srcUrl); 
221     getJob->setSourceSize(info.size());
222     connect(getJob, SIGNAL(result(KJob*)), this, SLOT(slotGotFile(KJob*)));
223     getJob->start();
224 }
225
226 void ResourceWidget::slotGotFile(KJob *job)
227 {
228     if (job->error() != 0 ) return;
229     KIO::FileCopyJob* copyJob = static_cast<KIO::FileCopyJob*>( job );
230     const KUrl filePath = copyJob->destUrl();
231 #ifdef USE_NEPOMUK
232 #if KDE_IS_VERSION(4,6,0)
233         Nepomuk::Resource res( filePath );
234         res.setProperty( Nepomuk::Vocabulary::NIE::license(), (Nepomuk::Variant) item_license->text() );
235         res.setProperty( Nepomuk::Vocabulary::NIE::licenseType(), (Nepomuk::Variant) item_license->url() );
236         res.setProperty( Nepomuk::Vocabulary::NDO::copiedFrom(), sound_name->url() );
237         //res.setDescription(item_description->toPlainText());
238         //res.setProperty( Soprano::Vocabulary::NAO::description(), 
239 #endif
240 #endif
241         emit addClip(filePath, QString());//, sound_name->url());
242 }
243
244 void ResourceWidget::slotOpenUrl(const QString &url)
245 {
246     new KRun(KUrl(url), this);
247 }
248
249 void ResourceWidget::slotChangeService()
250 {
251     if (m_currentService) {
252         delete m_currentService;
253         m_currentService = NULL;
254     }
255     SERVICETYPE service = (SERVICETYPE) service_list->itemData(service_list->currentIndex()).toInt();
256     if (service == FREESOUND) {
257         m_currentService = new FreeSound(search_results);
258     }
259     else if (service == OPENCLIPART) {
260         m_currentService = new OpenClipArt(search_results);
261     }
262     else if (service == ARCHIVEORG) {
263         m_currentService = new ArchiveOrg(search_results);
264     }
265
266     connect(m_currentService, SIGNAL(gotMetaInfo(const QString)), this, SLOT(slotGotMetaInfo(const QString)));
267     connect(m_currentService, SIGNAL(gotMetaInfo(QMap <QString, QString>)), this, SLOT(slotDisplayMetaInfo(QMap <QString, QString>)));
268     connect(m_currentService, SIGNAL(maxPages(int)), page_number, SLOT(setMaximum(int)));
269     connect(m_currentService, SIGNAL(searchInfo(QString)), search_info, SLOT(setText(QString)));
270     connect(m_currentService, SIGNAL(gotThumb(const QString)), this, SLOT(slotLoadThumb(const QString)));
271     
272     button_preview->setVisible(m_currentService->hasPreview);
273     button_import->setVisible(!m_currentService->inlineDownload);
274     sound_autoplay->setVisible(m_currentService->hasPreview);
275     search_info->setText(QString());
276     info_browser->setVisible(m_currentService->hasMetadata);
277     if (!search_text->text().isEmpty()) slotStartSearch();
278 }
279
280 void ResourceWidget::slotOnline()
281 {
282     button_search->setEnabled(true);
283     search_info->setText(QString());
284 }
285
286 void ResourceWidget::slotOffline()
287 {
288     button_search->setEnabled(false);
289     search_info->setText(i18n("You need to be online\n for searching"));
290 }
291
292 void ResourceWidget::slotNextPage()
293 {
294     int ix = page_number->value();
295     if (search_results->count() > 0) page_number->setValue(ix + 1);
296 }
297
298 void ResourceWidget::slotPreviousPage()
299 {
300     int ix = page_number->value();
301     if (ix > 1) page_number->setValue(ix - 1);
302 }
303
304 void ResourceWidget::parseLicense(const QString &licenseUrl)
305 {
306     QString licenseName;
307     if (licenseUrl.contains("/sampling+/"))
308         licenseName = "Sampling+";
309     else if (licenseUrl.contains("/by/"))
310         licenseName = "Attribution";
311     else if (licenseUrl.contains("/by-nd/"))
312         licenseName = "Attribution-NoDerivs";
313     else if (licenseUrl.contains("/by-nc-sa/"))
314         licenseName = "Attribution-NonCommercial-ShareAlike";
315     else if (licenseUrl.contains("/by-sa/"))
316         licenseName = "Attribution-ShareAlike";
317     else if (licenseUrl.contains("/by-nc/"))
318         licenseName = "Attribution-NonCommercial";
319     else if (licenseUrl.contains("/by-nc-nd/"))
320         licenseName = "Attribution-NonCommercial-NoDerivs";
321     else if (licenseUrl.contains("/publicdomain/zero/"))
322         licenseName = "Creative Commons 0";
323     else if (licenseUrl.endsWith("/publicdomain"))
324         licenseName = "Public Domain";
325     else licenseName = i18n("Unknown");
326     item_license->setText(licenseName);
327     item_license->setUrl(licenseUrl);
328 }
329
330 void ResourceWidget::slotGotMetaInfo(const QString info)
331 {
332     info_browser->setHtml(info_browser->toHtml() + info);
333 }
334
335
336 void ResourceWidget::slotOpenLink(const QUrl &url)
337 {
338     QString path = url.toEncoded();
339     if (path.endsWith("_preview")) {
340         path.chop(8);
341         slotOpenUrl(path);
342     }
343     else {
344         // import file in Kdenlive
345         slotSaveItem(path);
346     }
347 }
348