mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-18 12:28:31 +01:00
Merge branch 'soundcloud'
This commit is contained in:
commit
e545b6d71b
@ -290,6 +290,7 @@
|
|||||||
<file>providers/skyfm.png</file>
|
<file>providers/skyfm.png</file>
|
||||||
<file>providers/somafm.png</file>
|
<file>providers/somafm.png</file>
|
||||||
<file>providers/songkick.png</file>
|
<file>providers/songkick.png</file>
|
||||||
|
<file>providers/soundcloud.png</file>
|
||||||
<file>providers/twitter.png</file>
|
<file>providers/twitter.png</file>
|
||||||
<file>providers/wikipedia.png</file>
|
<file>providers/wikipedia.png</file>
|
||||||
<file>sample.mood</file>
|
<file>sample.mood</file>
|
||||||
|
BIN
data/providers/soundcloud.png
Normal file
BIN
data/providers/soundcloud.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
@ -152,6 +152,7 @@ set(SOURCES
|
|||||||
globalsearch/searchproviderstatuswidget.cpp
|
globalsearch/searchproviderstatuswidget.cpp
|
||||||
globalsearch/simplesearchprovider.cpp
|
globalsearch/simplesearchprovider.cpp
|
||||||
globalsearch/somafmsearchprovider.cpp
|
globalsearch/somafmsearchprovider.cpp
|
||||||
|
globalsearch/soundcloudsearchprovider.cpp
|
||||||
globalsearch/suggestionwidget.cpp
|
globalsearch/suggestionwidget.cpp
|
||||||
globalsearch/urlsearchprovider.cpp
|
globalsearch/urlsearchprovider.cpp
|
||||||
|
|
||||||
@ -185,6 +186,7 @@ set(SOURCES
|
|||||||
internet/searchboxwidget.cpp
|
internet/searchboxwidget.cpp
|
||||||
internet/somafmservice.cpp
|
internet/somafmservice.cpp
|
||||||
internet/somafmurlhandler.cpp
|
internet/somafmurlhandler.cpp
|
||||||
|
internet/soundcloudservice.cpp
|
||||||
|
|
||||||
library/groupbydialog.cpp
|
library/groupbydialog.cpp
|
||||||
library/library.cpp
|
library/library.cpp
|
||||||
@ -424,6 +426,7 @@ set(HEADERS
|
|||||||
globalsearch/groovesharksearchprovider.h
|
globalsearch/groovesharksearchprovider.h
|
||||||
globalsearch/searchprovider.h
|
globalsearch/searchprovider.h
|
||||||
globalsearch/simplesearchprovider.h
|
globalsearch/simplesearchprovider.h
|
||||||
|
globalsearch/soundcloudsearchprovider.h
|
||||||
globalsearch/suggestionwidget.h
|
globalsearch/suggestionwidget.h
|
||||||
|
|
||||||
internet/digitallyimportedclient.h
|
internet/digitallyimportedclient.h
|
||||||
@ -452,6 +455,7 @@ set(HEADERS
|
|||||||
internet/searchboxwidget.h
|
internet/searchboxwidget.h
|
||||||
internet/somafmservice.h
|
internet/somafmservice.h
|
||||||
internet/somafmurlhandler.h
|
internet/somafmurlhandler.h
|
||||||
|
internet/soundcloudservice.h
|
||||||
|
|
||||||
library/groupbydialog.h
|
library/groupbydialog.h
|
||||||
library/library.h
|
library/library.h
|
||||||
|
95
src/globalsearch/soundcloudsearchprovider.cpp
Normal file
95
src/globalsearch/soundcloudsearchprovider.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2011, David Sansome <me@davidsansome.com>
|
||||||
|
|
||||||
|
Clementine 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 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "soundcloudsearchprovider.h"
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "covers/albumcoverloader.h"
|
||||||
|
#include "internet/soundcloudservice.h"
|
||||||
|
|
||||||
|
SoundCloudSearchProvider::SoundCloudSearchProvider(Application* app, QObject* parent)
|
||||||
|
: SearchProvider(app, parent),
|
||||||
|
service_(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudSearchProvider::Init(SoundCloudService* service) {
|
||||||
|
service_ = service;
|
||||||
|
SearchProvider::Init("SoundCloud", "soundcloud",
|
||||||
|
QIcon(":providers/soundcloud.png"),
|
||||||
|
WantsDelayedQueries | ArtIsProbablyRemote | CanShowConfig);
|
||||||
|
|
||||||
|
connect(service_, SIGNAL(SimpleSearchResults(int, SongList)),
|
||||||
|
SLOT(SearchDone(int, SongList)));
|
||||||
|
connect(service_, SIGNAL(AlbumSearchResult(int, QList<quint64>)),
|
||||||
|
SLOT(AlbumSearchResult(int, QList<quint64>)));
|
||||||
|
connect(service_, SIGNAL(AlbumSongsLoaded(quint64, SongList)),
|
||||||
|
SLOT(AlbumSongsLoaded(quint64, SongList)));
|
||||||
|
|
||||||
|
cover_loader_options_.desired_height_ = kArtHeight;
|
||||||
|
cover_loader_options_.pad_output_image_ = true;
|
||||||
|
cover_loader_options_.scale_output_image_ = true;
|
||||||
|
|
||||||
|
connect(app_->album_cover_loader(),
|
||||||
|
SIGNAL(ImageLoaded(quint64, QImage)),
|
||||||
|
SLOT(AlbumArtLoaded(quint64, QImage)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudSearchProvider::SearchAsync(int id, const QString& query) {
|
||||||
|
const int service_id = service_->SimpleSearch(query);
|
||||||
|
pending_searches_[service_id] = PendingState(id, TokenizeQuery(query));;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudSearchProvider::SearchDone(int id, const SongList& songs) {
|
||||||
|
// Map back to the original id.
|
||||||
|
const PendingState state = pending_searches_.take(id);
|
||||||
|
const int global_search_id = state.orig_id_;
|
||||||
|
|
||||||
|
ResultList ret;
|
||||||
|
foreach (const Song& song, songs) {
|
||||||
|
Result result(this);
|
||||||
|
result.metadata_ = song;
|
||||||
|
|
||||||
|
ret << result;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit ResultsAvailable(global_search_id, ret);
|
||||||
|
MaybeSearchFinished(global_search_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudSearchProvider::MaybeSearchFinished(int id) {
|
||||||
|
if (pending_searches_.keys(PendingState(id, QStringList())).isEmpty()) {
|
||||||
|
emit SearchFinished(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudSearchProvider::LoadArtAsync(int id, const Result& result) {
|
||||||
|
quint64 loader_id = app_->album_cover_loader()->LoadImageAsync(
|
||||||
|
cover_loader_options_, result.metadata_);
|
||||||
|
cover_loader_tasks_[loader_id] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudSearchProvider::AlbumArtLoaded(quint64 id, const QImage& image) {
|
||||||
|
if (!cover_loader_tasks_.contains(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int original_id = cover_loader_tasks_.take(id);
|
||||||
|
emit ArtLoaded(original_id, image);
|
||||||
|
}
|
53
src/globalsearch/soundcloudsearchprovider.h
Normal file
53
src/globalsearch/soundcloudsearchprovider.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
|
|
||||||
|
Clementine 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 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOUNDCLOUDSEARCHPROVIDER_H
|
||||||
|
#define SOUNDCLOUDSEARCHPROVIDER_H
|
||||||
|
|
||||||
|
#include "searchprovider.h"
|
||||||
|
#include "covers/albumcoverloaderoptions.h"
|
||||||
|
#include "internet/soundcloudservice.h"
|
||||||
|
|
||||||
|
class AlbumCoverLoader;
|
||||||
|
|
||||||
|
class SoundCloudSearchProvider : public SearchProvider {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SoundCloudSearchProvider(Application* app, QObject* parent = 0);
|
||||||
|
void Init(SoundCloudService* service);
|
||||||
|
|
||||||
|
// SearchProvider
|
||||||
|
void SearchAsync(int id, const QString& query);
|
||||||
|
void LoadArtAsync(int id, const Result& result);
|
||||||
|
InternetService* internet_service() { return service_; }
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void AlbumArtLoaded(quint64 id, const QImage& image);
|
||||||
|
void SearchDone(int id, const SongList& songs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void MaybeSearchFinished(int id);
|
||||||
|
|
||||||
|
SoundCloudService* service_;
|
||||||
|
QMap<int, PendingState> pending_searches_;
|
||||||
|
|
||||||
|
AlbumCoverLoaderOptions cover_loader_options_;
|
||||||
|
QMap<quint64, int> cover_loader_tasks_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "digitallyimportedservicebase.h"
|
#include "digitallyimportedservicebase.h"
|
||||||
|
#include "groovesharkservice.h"
|
||||||
#include "icecastservice.h"
|
#include "icecastservice.h"
|
||||||
#include "jamendoservice.h"
|
#include "jamendoservice.h"
|
||||||
#include "magnatuneservice.h"
|
#include "magnatuneservice.h"
|
||||||
@ -24,7 +25,7 @@
|
|||||||
#include "internetservice.h"
|
#include "internetservice.h"
|
||||||
#include "savedradio.h"
|
#include "savedradio.h"
|
||||||
#include "somafmservice.h"
|
#include "somafmservice.h"
|
||||||
#include "groovesharkservice.h"
|
#include "soundcloudservice.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/mergedproxymodel.h"
|
#include "core/mergedproxymodel.h"
|
||||||
#include "podcasts/podcastservice.h"
|
#include "podcasts/podcastservice.h"
|
||||||
@ -71,6 +72,7 @@ InternetModel::InternetModel(Application* app, QObject* parent)
|
|||||||
AddService(new SavedRadio(app, this));
|
AddService(new SavedRadio(app, this));
|
||||||
AddService(new SkyFmService(app, this));
|
AddService(new SkyFmService(app, this));
|
||||||
AddService(new SomaFMService(app, this));
|
AddService(new SomaFMService(app, this));
|
||||||
|
AddService(new SoundCloudService(app, this));
|
||||||
#ifdef HAVE_SPOTIFY
|
#ifdef HAVE_SPOTIFY
|
||||||
AddService(new SpotifyService(app, this));
|
AddService(new SpotifyService(app, this));
|
||||||
#endif
|
#endif
|
||||||
|
293
src/internet/soundcloudservice.cpp
Normal file
293
src/internet/soundcloudservice.cpp
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
|
|
||||||
|
Clementine 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 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "soundcloudservice.h"
|
||||||
|
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <qjson/parser.h>
|
||||||
|
#include <qjson/serializer.h>
|
||||||
|
|
||||||
|
#include "internetmodel.h"
|
||||||
|
#include "searchboxwidget.h"
|
||||||
|
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "core/closure.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/mergedproxymodel.h"
|
||||||
|
#include "core/network.h"
|
||||||
|
#include "core/song.h"
|
||||||
|
#include "core/taskmanager.h"
|
||||||
|
#include "core/timeconstants.h"
|
||||||
|
#include "core/utilities.h"
|
||||||
|
#include "globalsearch/globalsearch.h"
|
||||||
|
#include "globalsearch/soundcloudsearchprovider.h"
|
||||||
|
#include "ui/iconloader.h"
|
||||||
|
|
||||||
|
const char* SoundCloudService::kApiClientId = "2add0f709fcfae1fd7a198ec7573d2d4";
|
||||||
|
|
||||||
|
const char* SoundCloudService::kServiceName = "SoundCloud";
|
||||||
|
const char* SoundCloudService::kSettingsGroup = "SoundCloud";
|
||||||
|
const char* SoundCloudService::kUrl = "https://api.soundcloud.com/";
|
||||||
|
const char* SoundCloudService::kHomepage = "http://soundcloud.com/";
|
||||||
|
|
||||||
|
const int SoundCloudService::kSearchDelayMsec = 400;
|
||||||
|
const int SoundCloudService::kSongSearchLimit = 100;
|
||||||
|
const int SoundCloudService::kSongSimpleSearchLimit = 10;
|
||||||
|
|
||||||
|
typedef QPair<QString, QString> Param;
|
||||||
|
|
||||||
|
SoundCloudService::SoundCloudService(Application* app, InternetModel *parent)
|
||||||
|
: InternetService(kServiceName, app, parent, parent),
|
||||||
|
root_(NULL),
|
||||||
|
search_(NULL),
|
||||||
|
network_(new NetworkAccessManager(this)),
|
||||||
|
context_menu_(NULL),
|
||||||
|
search_box_(new SearchBoxWidget(this)),
|
||||||
|
search_delay_(new QTimer(this)),
|
||||||
|
next_pending_search_id_(0) {
|
||||||
|
|
||||||
|
search_delay_->setInterval(kSearchDelayMsec);
|
||||||
|
search_delay_->setSingleShot(true);
|
||||||
|
connect(search_delay_, SIGNAL(timeout()), SLOT(DoSearch()));
|
||||||
|
|
||||||
|
SoundCloudSearchProvider* search_provider = new SoundCloudSearchProvider(app_, this);
|
||||||
|
search_provider->Init(this);
|
||||||
|
app_->global_search()->AddProvider(search_provider);
|
||||||
|
|
||||||
|
connect(search_box_, SIGNAL(TextChanged(QString)), SLOT(Search(QString)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SoundCloudService::~SoundCloudService() {
|
||||||
|
}
|
||||||
|
|
||||||
|
QStandardItem* SoundCloudService::CreateRootItem() {
|
||||||
|
root_ = new QStandardItem(QIcon(":providers/soundcloud.png"), kServiceName);
|
||||||
|
root_->setData(true, InternetModel::Role_CanLazyLoad);
|
||||||
|
root_->setData(InternetModel::PlayBehaviour_DoubleClickAction,
|
||||||
|
InternetModel::Role_PlayBehaviour);
|
||||||
|
return root_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::LazyPopulate(QStandardItem* item) {
|
||||||
|
switch (item->data(InternetModel::Role_Type).toInt()) {
|
||||||
|
case InternetModel::Type_Service: {
|
||||||
|
EnsureItemsCreated();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::EnsureItemsCreated() {
|
||||||
|
search_ = new QStandardItem(IconLoader::Load("edit-find"),
|
||||||
|
tr("Search results"));
|
||||||
|
search_->setToolTip(tr("Start typing something on the search box above to "
|
||||||
|
"fill this search results list"));
|
||||||
|
search_->setData(InternetModel::PlayBehaviour_MultipleItems,
|
||||||
|
InternetModel::Role_PlayBehaviour);
|
||||||
|
root_->appendRow(search_);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* SoundCloudService::HeaderWidget() const {
|
||||||
|
return search_box_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::Homepage() {
|
||||||
|
QDesktopServices::openUrl(QUrl(kHomepage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::Search(const QString& text, bool now) {
|
||||||
|
pending_search_ = text;
|
||||||
|
|
||||||
|
// If there is no text (e.g. user cleared search box), we don't need to do a
|
||||||
|
// real query that will return nothing: we can clear the playlist now
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
search_delay_->stop();
|
||||||
|
ClearSearchResults();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now) {
|
||||||
|
search_delay_->stop();
|
||||||
|
DoSearch();
|
||||||
|
} else {
|
||||||
|
search_delay_->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::DoSearch() {
|
||||||
|
ClearSearchResults();
|
||||||
|
|
||||||
|
QList<Param> parameters;
|
||||||
|
parameters << Param("q", pending_search_);
|
||||||
|
QNetworkReply* reply = CreateRequest("tracks", parameters);
|
||||||
|
const int id = next_pending_search_id_++;
|
||||||
|
NewClosure(reply, SIGNAL(finished()),
|
||||||
|
this, SLOT(SearchFinished(QNetworkReply*,int)),
|
||||||
|
reply, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::SearchFinished(QNetworkReply* reply, int task_id) {
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
SongList songs = ExtractSongs(ExtractResult(reply));
|
||||||
|
// Fill results list
|
||||||
|
foreach (const Song& song, songs) {
|
||||||
|
QStandardItem* child = CreateSongItem(song);
|
||||||
|
search_->appendRow(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex index = model()->merged_model()->mapFromSource(search_->index());
|
||||||
|
ScrollToIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::ClearSearchResults() {
|
||||||
|
if (search_)
|
||||||
|
search_->removeRows(0, search_->rowCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
int SoundCloudService::SimpleSearch(const QString& text) {
|
||||||
|
QList<Param> parameters;
|
||||||
|
parameters << Param("q", text);
|
||||||
|
QNetworkReply* reply = CreateRequest("tracks", parameters);
|
||||||
|
const int id = next_pending_search_id_++;
|
||||||
|
NewClosure(reply, SIGNAL(finished()),
|
||||||
|
this, SLOT(SimpleSearchFinished(QNetworkReply*,int)),
|
||||||
|
reply, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::SimpleSearchFinished(QNetworkReply* reply, int id) {
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
SongList songs = ExtractSongs(ExtractResult(reply));
|
||||||
|
emit SimpleSearchResults(id, songs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::EnsureMenuCreated() {
|
||||||
|
if(!context_menu_) {
|
||||||
|
context_menu_ = new QMenu;
|
||||||
|
context_menu_->addActions(GetPlaylistActions());
|
||||||
|
context_menu_->addSeparator();
|
||||||
|
context_menu_->addAction(IconLoader::Load("download"),
|
||||||
|
tr("Open %1 in browser").arg("soundcloud.com"),
|
||||||
|
this, SLOT(Homepage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundCloudService::ShowContextMenu(const QPoint& global_pos) {
|
||||||
|
EnsureMenuCreated();
|
||||||
|
|
||||||
|
context_menu_->popup(global_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkReply* SoundCloudService::CreateRequest(
|
||||||
|
const QString& ressource_name,
|
||||||
|
const QList<Param>& params) {
|
||||||
|
|
||||||
|
QUrl url(kUrl);
|
||||||
|
|
||||||
|
url.setPath(ressource_name);
|
||||||
|
|
||||||
|
url.addQueryItem("client_id", kApiClientId);
|
||||||
|
foreach(const Param& param, params) {
|
||||||
|
url.addQueryItem(param.first, param.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
qLog(Debug) << "Request Url: " << url.toEncoded();
|
||||||
|
|
||||||
|
QNetworkRequest req(url);
|
||||||
|
req.setRawHeader("Accept", "application/json");
|
||||||
|
QNetworkReply *reply = network_->get(req);
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant SoundCloudService::ExtractResult(QNetworkReply* reply) {
|
||||||
|
QJson::Parser parser;
|
||||||
|
bool ok;
|
||||||
|
QVariant result = parser.parse(reply, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
qLog(Error) << "Error while parsing SoundCloud result";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SongList SoundCloudService::ExtractSongs(const QVariant& result) {
|
||||||
|
SongList songs;
|
||||||
|
|
||||||
|
QVariantList q_variant_list = result.toList();
|
||||||
|
foreach(const QVariant& q, q_variant_list) {
|
||||||
|
Song song = ExtractSong(q.toMap());
|
||||||
|
if (song.is_valid()) {
|
||||||
|
songs << song;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
|
||||||
|
Song SoundCloudService::ExtractSong(const QVariantMap& result_song) {
|
||||||
|
Song song;
|
||||||
|
if (!result_song.isEmpty() && result_song["streamable"].toBool()) {
|
||||||
|
QUrl stream_url = result_song["stream_url"].toUrl();
|
||||||
|
stream_url.addQueryItem("client_id", kApiClientId);
|
||||||
|
song.set_url(stream_url);
|
||||||
|
|
||||||
|
QString username = result_song["user"].toMap()["username"].toString();
|
||||||
|
// We don't have a real artist name, but username is the most similar thing
|
||||||
|
// we have
|
||||||
|
song.set_artist(username);
|
||||||
|
|
||||||
|
QString title = result_song["title"].toString();
|
||||||
|
song.set_title(title);
|
||||||
|
|
||||||
|
QString genre = result_song["genre"].toString();
|
||||||
|
song.set_genre(genre);
|
||||||
|
|
||||||
|
float bpm = result_song["bpm"].toFloat();
|
||||||
|
song.set_bpm(bpm);
|
||||||
|
|
||||||
|
QVariant cover = result_song["artwork_url"];
|
||||||
|
if (cover.isValid()) {
|
||||||
|
// SoundCloud covers URL are https, but our cover loader doesn't seem to
|
||||||
|
// deal well with https URL. Anyway, we don't need a secure connection to
|
||||||
|
// get a cover image.
|
||||||
|
QUrl cover_url = cover.toUrl();
|
||||||
|
cover_url.setScheme("http");
|
||||||
|
song.set_art_automatic(cover_url.toEncoded());
|
||||||
|
}
|
||||||
|
|
||||||
|
int playcount = result_song["playback_count"].toInt();
|
||||||
|
song.set_playcount(playcount);
|
||||||
|
|
||||||
|
int year = result_song["release_year"].toInt();
|
||||||
|
song.set_year(year);
|
||||||
|
|
||||||
|
QVariant q_duration = result_song["duration"];
|
||||||
|
quint64 duration = q_duration.toULongLong() * kNsecPerMsec;
|
||||||
|
song.set_length_nanosec(duration);
|
||||||
|
|
||||||
|
song.set_valid(true);
|
||||||
|
}
|
||||||
|
return song;
|
||||||
|
}
|
97
src/internet/soundcloudservice.h
Normal file
97
src/internet/soundcloudservice.h
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
|
|
||||||
|
Clementine 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 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOUNDCLOUDSERVICE_H
|
||||||
|
#define SOUNDCLOUDSERVICE_H
|
||||||
|
|
||||||
|
#include "internetmodel.h"
|
||||||
|
#include "internetservice.h"
|
||||||
|
|
||||||
|
class NetworkAccessManager;
|
||||||
|
class SearchBoxWidget;
|
||||||
|
|
||||||
|
class QMenu;
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
class SoundCloudService : public InternetService {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SoundCloudService(Application* app, InternetModel *parent);
|
||||||
|
~SoundCloudService();
|
||||||
|
|
||||||
|
// Internet Service methods
|
||||||
|
QStandardItem* CreateRootItem();
|
||||||
|
void LazyPopulate(QStandardItem *parent);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//QList<QAction*> playlistitem_actions(const Song& song);
|
||||||
|
void ShowContextMenu(const QPoint& global_pos);
|
||||||
|
QWidget* HeaderWidget() const;
|
||||||
|
|
||||||
|
int SimpleSearch(const QString& query);
|
||||||
|
|
||||||
|
static const char* kServiceName;
|
||||||
|
static const char* kSettingsGroup;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void SimpleSearchResults(int id, SongList songs);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void Search(const QString& text, bool now = false);
|
||||||
|
void DoSearch();
|
||||||
|
void SearchFinished(QNetworkReply* reply, int task);
|
||||||
|
void SimpleSearchFinished(QNetworkReply* reply, int id);
|
||||||
|
|
||||||
|
void Homepage();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ClearSearchResults();
|
||||||
|
void EnsureItemsCreated();
|
||||||
|
void EnsureMenuCreated();
|
||||||
|
QNetworkReply* CreateRequest(const QString& ressource_name,
|
||||||
|
const QList<QPair<QString, QString> >& params);
|
||||||
|
// Convenient function for extracting result from reply
|
||||||
|
QVariant ExtractResult(QNetworkReply* reply);
|
||||||
|
SongList ExtractSongs(const QVariant& result);
|
||||||
|
Song ExtractSong(const QVariantMap& result_song);
|
||||||
|
|
||||||
|
QStandardItem* root_;
|
||||||
|
QStandardItem* search_;
|
||||||
|
|
||||||
|
NetworkAccessManager* network_;
|
||||||
|
|
||||||
|
QMenu* context_menu_;
|
||||||
|
SearchBoxWidget* search_box_;
|
||||||
|
QTimer* search_delay_;
|
||||||
|
QString pending_search_;
|
||||||
|
int next_pending_search_id_;
|
||||||
|
|
||||||
|
QByteArray api_key_;
|
||||||
|
|
||||||
|
static const char* kUrl;
|
||||||
|
static const char* kUrlCover;
|
||||||
|
static const char* kHomepage;
|
||||||
|
|
||||||
|
static const int kSongSearchLimit;
|
||||||
|
static const int kSongSimpleSearchLimit;
|
||||||
|
static const int kSearchDelayMsec;
|
||||||
|
|
||||||
|
static const char* kApiClientId;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SOUNDCLOUDSERVICE_H
|
Loading…
Reference in New Issue
Block a user