]> git.sesse.net Git - kdenlive/blob - src/utils/freesound.cpp
bf313d49d35c5ca5e87eb14237a0fc719f4fc676
[kdenlive] / src / utils / freesound.cpp
1 /***************************************************************************
2  *   Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *   Copyright (C) 2011 by Marco Gittler (marco@gitma.de)                  *
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 "freesound.h"
23
24 #include <QPushButton>
25 #include <QSpinBox>
26 #include <QListWidget>
27 #include <QDomDocument>
28
29 #include <KDebug>
30 #include "kdenlivesettings.h"
31 #include <KGlobalSettings>
32 #include <KMessageBox>
33 #include <KFileDialog>
34 #include <kio/job.h>
35 #include <KIO/NetAccess>
36 #include <Solid/Networking>
37 #include <KRun>
38
39 #ifdef USE_QJSON
40 #include <qjson/parser.h>
41 #endif
42
43 const int imageRole = Qt::UserRole;
44 const int urlRole = Qt::UserRole + 1;
45 const int downloadRole = Qt::UserRole + 2;
46 const int durationRole = Qt::UserRole + 3;
47 const int previewRole = Qt::UserRole + 4;
48 const int authorRole = Qt::UserRole + 5;
49 const int authorUrl = Qt::UserRole + 6;
50 const int infoUrl = Qt::UserRole + 7;
51
52
53 FreeSound::FreeSound(const QString & folder, QWidget * parent) :
54         QDialog(parent),
55         m_folder(folder),
56         m_service(FREESOUND)
57 {
58     setFont(KGlobalSettings::toolBarFont());
59     setupUi(this);
60     setAttribute(Qt::WA_DeleteOnClose);
61 #ifdef USE_QJSON
62     service_list->addItem(i18n("Freesound Audio Library"), FREESOUND);
63 #endif
64     service_list->addItem(i18n("Open Clip Art Graphic Library"), OPENCLIPART);
65     setWindowTitle(i18n("Search Online Resources"));
66     connect(button_search, SIGNAL(clicked()), this, SLOT(slotStartSearch()));
67     connect(search_results, SIGNAL(currentRowChanged(int)), this, SLOT(slotUpdateCurrentSound()));
68     connect(button_preview, SIGNAL(clicked()), this, SLOT(slotPlaySound()));
69     connect(button_import, SIGNAL(clicked()), this, SLOT(slotSaveSound()));
70     connect(sound_author, SIGNAL(leftClickedUrl(const QString &)), this, SLOT(slotOpenUrl(const QString &)));
71     connect(sound_name, SIGNAL(leftClickedUrl(const QString &)), this, SLOT(slotOpenUrl(const QString &)));
72     m_previewProcess = new QProcess;
73     connect(m_previewProcess, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(slotPreviewStatusChanged(QProcess::ProcessState)));
74     connect(service_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChangeService()));
75     sound_image->setFixedWidth(180);
76     if (Solid::Networking::status() == Solid::Networking::Unconnected) {
77         slotOffline();
78     }
79     connect(Solid::Networking::notifier(), SIGNAL(shouldConnect()), this, SLOT(slotOnline()));
80     connect(Solid::Networking::notifier(), SIGNAL(shouldDisconnect()), this, SLOT(slotOffline()));
81     connect(page_next, SIGNAL(clicked()), this, SLOT(slotNextPage()));
82     connect(page_prev, SIGNAL(clicked()), this, SLOT(slotPreviousPage()));
83     connect(page_number, SIGNAL(valueChanged(int)), this, SLOT(slotStartSearch(int)));
84     sound_box->setEnabled(false);
85 }
86
87 FreeSound::~FreeSound()
88 {
89     if (m_previewProcess) delete m_previewProcess;
90 }
91
92 void FreeSound::slotStartSearch(int page)
93 {
94     m_result.clear();
95     m_currentPreview.clear();
96     m_currentUrl.clear();
97     page_number->blockSignals(true);
98     page_number->setValue(page);
99     page_number->blockSignals(false);
100     QString uri;
101     if (m_service == FREESOUND) {
102         uri = "http://www.freesound.org/api/sounds/search/?q=";
103         uri.append(search_text->text());
104         if (page > 1) uri.append("&p=" + QString::number(page));
105         uri.append("&api_key=a1772c8236e945a4bee30a64058dabf8");
106     }
107     else if (m_service == OPENCLIPART) {
108         uri = "http://openclipart.org/api/search/?query=";
109         uri.append(search_text->text());
110         if (page > 1) uri.append("&page=" + QString::number(page));
111     }
112     KIO::TransferJob *job = KIO::get(KUrl(uri));
113     connect (job, SIGNAL(  data(KIO::Job *, const QByteArray & )), this, SLOT(slotDataIsHere(KIO::Job *,const QByteArray &)));
114     connect(job, SIGNAL(result(KJob*)), this, SLOT(slotShowResults()));
115 }
116
117 void FreeSound::slotDataIsHere(KIO::Job *,const QByteArray & data )
118 {
119   m_result.append(data);
120 }
121
122 void FreeSound::slotShowResults()
123 {
124     search_results->blockSignals(true);
125     search_results->clear();
126     if (m_service == FREESOUND) {
127 #ifdef USE_QJSON
128         QJson::Parser parser;
129         bool ok;
130         //kDebug()<<"// GOT RESULT: "<<m_result;
131         m_data = parser.parse(m_result, &ok);
132         QVariant sounds;
133         if (m_data.canConvert(QVariant::Map)) {
134             QMap <QString, QVariant> map = m_data.toMap();
135             QMap<QString, QVariant>::const_iterator i = map.constBegin();
136             while (i != map.constEnd()) {
137                 if (i.key() == "num_results") search_info->setText(i18np("Found %1 result", "Found %1 results", i.value().toInt()));
138                 else if (i.key() == "num_pages") {
139                     page_number->setMaximum(i.value().toInt());
140                 }
141                 else if (i.key() == "sounds") {
142                     sounds = i.value();
143                     if (sounds.canConvert(QVariant::List)) {
144                         QList <QVariant> soundsList = sounds.toList();
145                         for (int j = 0; j < soundsList.count(); j++) {
146                             if (soundsList.at(j).canConvert(QVariant::Map)) {
147                                 QMap <QString, QVariant> soundmap = soundsList.at(j).toMap();
148                                 if (soundmap.contains("original_filename")) {
149                                     QListWidgetItem *item = new   QListWidgetItem(soundmap.value("original_filename").toString(), search_results);
150                                     item->setData(imageRole, soundmap.value("waveform_m").toString());
151                                     item->setData(infoUrl, soundmap.value("url").toString());
152                                     item->setData(durationRole, soundmap.value("duration").toDouble());
153                                     item->setData(previewRole, soundmap.value("preview-hq-mp3").toString());
154                                     item->setData(downloadRole, soundmap.value("serve").toString() + "?api_key=a1772c8236e945a4bee30a64058dabf8");
155                                     QVariant authorInfo = soundmap.value("user");
156                                     if (authorInfo.canConvert(QVariant::Map)) {
157                                         QMap <QString, QVariant> authorMap = authorInfo.toMap();
158                                         if (authorMap.contains("username")) {
159                                             item->setData(authorRole, authorMap.value("username").toString());
160                                             item->setData(authorUrl, authorMap.value("url").toString());
161                                         }
162                                     }
163                                 }
164                             }
165                         }
166                     }
167                 }
168                 ++i;
169             }
170         }
171 #endif  
172     }
173     else if (m_service == OPENCLIPART) {
174         QDomDocument doc;
175         doc.setContent(m_result);
176         QDomNodeList items = doc.documentElement().elementsByTagName("item");
177         for (int i = 0; i < items.count(); i++) {
178             QDomElement currentClip = items.at(i).toElement();
179             QDomElement title = currentClip.firstChildElement("title");
180             QListWidgetItem *item = new QListWidgetItem(title.firstChild().nodeValue(), search_results);
181             QDomElement thumb = currentClip.firstChildElement("media:thumbnail");
182             item->setData(imageRole, thumb.attribute("url"));
183             QDomElement enclosure = currentClip.firstChildElement("enclosure");
184             item->setData(downloadRole, enclosure.attribute("url"));
185             QDomElement link = currentClip.firstChildElement("link");
186             item->setData(infoUrl, link.firstChild().nodeValue());
187             QDomElement author = currentClip.firstChildElement("dc:creator");
188             item->setData(authorRole, author.firstChild().nodeValue());
189             item->setData(authorUrl, QString("http://openclipart.org/user-detail/") + author.firstChild().nodeValue());
190         }
191     }
192     search_results->blockSignals(false);
193     search_results->setCurrentRow(0);
194 }
195
196 void FreeSound::slotUpdateCurrentSound()
197 {
198     if (!sound_autoplay->isChecked()) slotForcePlaySound(false);
199     m_currentPreview.clear();
200     m_currentUrl.clear();
201     QListWidgetItem *item = search_results->currentItem();
202     if (!item) {
203         sound_box->setEnabled(false);
204         return;
205     }
206     m_currentPreview = item->data(previewRole).toString();
207     m_currentUrl = item->data(downloadRole).toString();
208     if (sound_autoplay->isChecked()) slotForcePlaySound(true);
209     button_preview->setEnabled(!m_currentPreview.isEmpty());
210     sound_box->setEnabled(true);
211     sound_name->setText(item->text());
212     sound_name->setUrl(item->data(infoUrl).toString());
213     sound_author->setText(item->data(authorRole).toString());
214     sound_author->setUrl(item->data(authorUrl).toString());
215     if (!item->data(durationRole).isNull()) sound_duration->setText(QString::number(item->data(durationRole).toDouble()));
216     KUrl img(item->data(imageRole).toString());
217     if (img.isEmpty()) return;
218     if (KIO::NetAccess::exists(img, KIO::NetAccess::SourceSide, this)) {
219         QString tmpFile;
220         if (KIO::NetAccess::download(img, tmpFile, this)) {
221             QPixmap pix(tmpFile);
222             int newHeight = pix.height() * sound_image->width() / pix.width();
223             if (newHeight > 2 * sound_image->width()) {
224                 sound_image->setScaledContents(false);
225                 //sound_image->setFixedHeight(sound_image->width());
226             }
227             else {
228                 sound_image->setScaledContents(true);
229                 sound_image->setFixedHeight(newHeight);
230             }
231             sound_image->setPixmap(pix);
232             KIO::NetAccess::removeTempFile(tmpFile);
233         }
234     }
235 }
236
237 void FreeSound::slotPlaySound()
238 {
239     if (m_currentPreview.isEmpty()) return;
240     if (m_previewProcess && m_previewProcess->state() != QProcess::NotRunning) {
241         m_previewProcess->close();
242         return;
243     }
244     m_previewProcess->start("ffplay", QStringList() << m_currentPreview << "-nodisp");
245 }
246
247
248 void FreeSound::slotForcePlaySound(bool play)
249 {
250     if (m_service != FREESOUND) return;
251     m_previewProcess->close();
252     if (m_currentPreview.isEmpty()) return;
253     if (play)
254         m_previewProcess->start("ffplay", QStringList() << m_currentPreview << "-nodisp");
255 }
256
257 void FreeSound::slotPreviewStatusChanged(QProcess::ProcessState state)
258 {
259     if (state == QProcess::NotRunning)
260         button_preview->setText(i18n("Preview"));
261     else 
262         button_preview->setText(i18n("Stop"));
263 }
264
265 void FreeSound::slotSaveSound()
266 {
267     if (m_currentUrl.isEmpty()) return;
268     QString path = m_folder;
269     if (!path.endsWith('/')) path.append('/');
270     path.append(sound_name->text());
271     QString ext;
272     if (m_service == FREESOUND) {
273         ext = "*." + sound_name->text().section('.', -1);
274     }
275     else if (m_service == OPENCLIPART) {
276         path.append("." + m_currentUrl.section('.', -1));
277         ext = "*." + m_currentUrl.section('.', -1);
278     }
279     QString saveUrl = KFileDialog::getSaveFileName(KUrl(path), ext);
280     if (saveUrl.isEmpty()) return;
281     if (KIO::NetAccess::download(KUrl(m_currentUrl), saveUrl, this)) {
282         emit addClip(KUrl(saveUrl), sound_name->url());
283     }
284 }
285
286 void FreeSound::slotOpenUrl(const QString &url)
287 {
288     new KRun(KUrl(url), this);
289 }
290
291 void FreeSound::slotChangeService()
292 {
293     m_service = (SERVICETYPE) service_list->itemData(service_list->currentIndex()).toInt();
294     if (m_service == FREESOUND) {
295         button_preview->setVisible(true);
296         duration_label->setVisible(true);
297         search_info->setVisible(true);
298         sound_autoplay->setVisible(true);
299     }
300     else if (m_service == OPENCLIPART) {
301         button_preview->setVisible(false);
302         duration_label->setVisible(false);
303         search_info->setVisible(false);
304         sound_autoplay->setVisible(false);
305         sound_duration->setText(QString());
306     }
307     if (!search_text->text().isEmpty()) slotStartSearch();
308 }
309
310 void FreeSound::slotOnline()
311 {
312     button_search->setEnabled(true);
313     search_info->setText(QString());
314 }
315
316 void FreeSound::slotOffline()
317 {
318     button_search->setEnabled(false);
319     search_info->setText(i18n("You need to be online\n for searching"));
320 }
321
322 void FreeSound::slotNextPage()
323 {
324     int ix = page_number->value();
325     if (search_results->count() > 0) page_number->setValue(ix + 1);
326 }
327
328 void FreeSound::slotPreviousPage()
329 {
330     int ix = page_number->value();
331     if (ix > 1) page_number->setValue(ix - 1);
332 }
333