1 /***************************************************************************
2 * Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
3 * Copyright (C) 2011 by Marco Gittler (marco@gitma.de) *
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. *
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. *
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 ***************************************************************************/
22 #include "freesound.h"
24 #include <QPushButton>
26 #include <QListWidget>
27 #include <QDomDocument>
30 #include "kdenlivesettings.h"
31 #include <KGlobalSettings>
32 #include <KMessageBox>
33 #include <KFileDialog>
35 #include <KIO/NetAccess>
36 #include <Solid/Networking>
40 #include <qjson/parser.h>
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;
53 FreeSound::FreeSound(const QString & folder, QWidget * parent) :
58 setFont(KGlobalSettings::toolBarFont());
60 setAttribute(Qt::WA_DeleteOnClose);
62 service_list->addItem(i18n("Freesound Audio Library"), FREESOUND);
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) {
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)));
86 FreeSound::~FreeSound()
88 if (m_previewProcess) delete m_previewProcess;
91 void FreeSound::slotStartSearch(int page)
94 m_currentPreview.clear();
96 page_number->blockSignals(true);
97 page_number->setValue(page);
98 page_number->blockSignals(false);
100 if (m_service == FREESOUND) {
101 uri = "http://www.freesound.org/api/sounds/search/?q=";
102 uri.append(search_text->text());
103 if (page > 1) uri.append("&p=" + QString::number(page));
104 uri.append("&api_key=a1772c8236e945a4bee30a64058dabf8");
106 else if (m_service == OPENCLIPART) {
107 uri = "http://openclipart.org/api/search/?query=";
108 uri.append(search_text->text());
109 if (page > 1) uri.append("&page=" + QString::number(page));
111 KIO::TransferJob *job = KIO::get(KUrl(uri));
112 connect (job, SIGNAL( data(KIO::Job *, const QByteArray & )), this, SLOT(slotDataIsHere(KIO::Job *,const QByteArray &)));
113 connect(job, SIGNAL(result(KJob*)), this, SLOT(slotShowResults()));
116 void FreeSound::slotDataIsHere(KIO::Job *,const QByteArray & data )
118 m_result.append(data);
121 void FreeSound::slotShowResults()
123 search_results->blockSignals(true);
124 search_results->clear();
125 if (m_service == FREESOUND) {
127 QJson::Parser parser;
129 //kDebug()<<"// GOT RESULT: "<<m_result;
130 m_data = parser.parse(m_result, &ok);
132 if (m_data.canConvert(QVariant::Map)) {
133 QMap <QString, QVariant> map = m_data.toMap();
134 QMap<QString, QVariant>::const_iterator i = map.constBegin();
135 while (i != map.constEnd()) {
136 if (i.key() == "num_results") search_info->setText(i18np("Found %1 result", "Found %1 results", i.value().toInt()));
137 else if (i.key() == "num_pages") {
138 page_number->setMaximum(i.value().toInt());
140 else if (i.key() == "sounds") {
142 if (sounds.canConvert(QVariant::List)) {
143 QList <QVariant> soundsList = sounds.toList();
144 for (int j = 0; j < soundsList.count(); j++) {
145 if (soundsList.at(j).canConvert(QVariant::Map)) {
146 QMap <QString, QVariant> soundmap = soundsList.at(j).toMap();
147 if (soundmap.contains("original_filename")) {
148 QListWidgetItem *item = new QListWidgetItem(soundmap.value("original_filename").toString(), search_results);
149 item->setData(imageRole, soundmap.value("waveform_m").toString());
150 item->setData(infoUrl, soundmap.value("url").toString());
151 item->setData(durationRole, soundmap.value("duration").toDouble());
152 item->setData(previewRole, soundmap.value("preview-hq-mp3").toString());
153 item->setData(downloadRole, soundmap.value("serve").toString() + "?api_key=a1772c8236e945a4bee30a64058dabf8");
154 QVariant authorInfo = soundmap.value("user");
155 if (authorInfo.canConvert(QVariant::Map)) {
156 QMap <QString, QVariant> authorMap = authorInfo.toMap();
157 if (authorMap.contains("username")) {
158 item->setData(authorRole, authorMap.value("username").toString());
159 item->setData(authorUrl, authorMap.value("url").toString());
172 else if (m_service == OPENCLIPART) {
174 doc.setContent(m_result);
175 QDomNodeList items = doc.documentElement().elementsByTagName("item");
176 for (int i = 0; i < items.count(); i++) {
177 QDomElement currentClip = items.at(i).toElement();
178 QDomElement title = currentClip.firstChildElement("title");
179 QListWidgetItem *item = new QListWidgetItem(title.firstChild().nodeValue(), search_results);
180 QDomElement thumb = currentClip.firstChildElement("media:thumbnail");
181 item->setData(imageRole, thumb.attribute("url"));
182 QDomElement enclosure = currentClip.firstChildElement("enclosure");
183 item->setData(downloadRole, enclosure.attribute("url"));
184 QDomElement link = currentClip.firstChildElement("link");
185 item->setData(infoUrl, link.firstChild().nodeValue());
186 QDomElement author = currentClip.firstChildElement("dc:creator");
187 item->setData(authorRole, author.firstChild().nodeValue());
188 item->setData(authorUrl, QString("http://openclipart.org/user-detail/") + author.firstChild().nodeValue());
191 search_results->blockSignals(false);
192 search_results->setCurrentRow(0);
195 void FreeSound::slotUpdateCurrentSound()
197 if (!sound_autoplay->isChecked()) slotPlaySound(false);
198 m_currentPreview.clear();
199 m_currentUrl.clear();
200 QListWidgetItem *item = search_results->currentItem();
202 sound_box->setEnabled(false);
205 m_currentPreview = item->data(previewRole).toString();
206 m_currentUrl = item->data(downloadRole).toString();
207 if (sound_autoplay->isChecked()) slotPlaySound(true);
208 button_preview->setEnabled(!m_currentPreview.isEmpty());
209 sound_box->setEnabled(true);
210 sound_name->setText(item->text());
211 sound_name->setUrl(item->data(infoUrl).toString());
212 sound_author->setText(item->data(authorRole).toString());
213 sound_author->setUrl(item->data(authorUrl).toString());
214 if (!item->data(durationRole).isNull()) sound_duration->setText(QString::number(item->data(durationRole).toDouble()));
215 KUrl img(item->data(imageRole).toString());
216 if (img.isEmpty()) return;
217 if (KIO::NetAccess::exists(img, KIO::NetAccess::SourceSide, this)) {
219 if (KIO::NetAccess::download(img, tmpFile, this)) {
220 QPixmap pix(tmpFile);
221 int newHeight = pix.height() * sound_image->width() / pix.width();
222 if (newHeight > 2 * sound_image->width()) {
223 sound_image->setScaledContents(false);
224 //sound_image->setFixedHeight(sound_image->width());
227 sound_image->setScaledContents(true);
228 sound_image->setFixedHeight(newHeight);
230 sound_image->setPixmap(pix);
231 KIO::NetAccess::removeTempFile(tmpFile);
237 void FreeSound::slotPlaySound(bool play)
239 if (m_currentPreview.isEmpty()) return;
240 if (!play || (m_previewProcess && m_previewProcess->state() != QProcess::NotRunning)) {
241 m_previewProcess->close();
244 m_previewProcess->start("ffplay", QStringList() << m_currentPreview << "-nodisp");
247 void FreeSound::slotPreviewStatusChanged(QProcess::ProcessState state)
249 if (state == QProcess::NotRunning)
250 button_preview->setText(i18n("Preview"));
252 button_preview->setText(i18n("Stop"));
255 void FreeSound::slotSaveSound()
257 if (m_currentUrl.isEmpty()) return;
258 QString path = m_folder;
259 if (!path.endsWith('/')) path.append('/');
260 path.append(sound_name->text());
262 if (m_service == FREESOUND) {
263 ext = "*." + sound_name->text().section('.', -1);
265 else if (m_service == OPENCLIPART) {
266 path.append("." + m_currentUrl.section('.', -1));
267 ext = "*." + m_currentUrl.section('.', -1);
269 QString saveUrl = KFileDialog::getSaveFileName(KUrl(path), ext);
270 if (saveUrl.isEmpty()) return;
271 if (KIO::NetAccess::download(KUrl(m_currentUrl), saveUrl, this)) {
272 emit addClip(KUrl(saveUrl), sound_name->url());
276 void FreeSound::slotOpenUrl(const QString &url)
278 new KRun(KUrl(url), this);
281 void FreeSound::slotChangeService()
283 m_service = (SERVICETYPE) service_list->itemData(service_list->currentIndex()).toInt();
284 if (m_service == FREESOUND) {
285 button_preview->setVisible(true);
286 duration_label->setVisible(true);
287 search_info->setVisible(true);
288 sound_autoplay->setVisible(true);
290 else if (m_service == OPENCLIPART) {
291 button_preview->setVisible(false);
292 duration_label->setVisible(false);
293 search_info->setVisible(false);
294 sound_autoplay->setVisible(false);
295 sound_duration->setText(QString());
297 if (!search_text->text().isEmpty()) slotStartSearch();
300 void FreeSound::slotOnline()
302 button_search->setEnabled(true);
303 search_info->setText(QString());
306 void FreeSound::slotOffline()
308 button_search->setEnabled(false);
309 search_info->setText(i18n("You need to be online\n for searching"));
312 void FreeSound::slotNextPage()
314 int ix = page_number->value();
315 if (search_results->count() > 0) page_number->setValue(ix + 1);
318 void FreeSound::slotPreviousPage()
320 int ix = page_number->value();
321 if (ix > 1) page_number->setValue(ix - 1);