Improve internet classes

This commit is contained in:
Jonas Kvinge 2020-04-13 06:30:40 +02:00
parent aa43d42cdb
commit 2f72c41cda
17 changed files with 880 additions and 1015 deletions

View File

@ -286,7 +286,6 @@ set(SOURCES
internet/internetservices.cpp internet/internetservices.cpp
internet/internetservice.cpp internet/internetservice.cpp
internet/internetplaylistitem.cpp internet/internetplaylistitem.cpp
internet/internetsearch.cpp
internet/internetsearchview.cpp internet/internetsearchview.cpp
internet/internetsearchmodel.cpp internet/internetsearchmodel.cpp
internet/internetsearchsortmodel.cpp internet/internetsearchsortmodel.cpp
@ -472,7 +471,6 @@ set(HEADERS
internet/internetservices.h internet/internetservices.h
internet/internetservice.h internet/internetservice.h
internet/internetsongmimedata.h internet/internetsongmimedata.h
internet/internetsearch.h
internet/internetsearchview.h internet/internetsearchview.h
internet/internetsearchmodel.h internet/internetsearchmodel.h
internet/localredirectserver.h internet/localredirectserver.h

View File

@ -61,7 +61,7 @@
# include "dbus/metatypes.h" # include "dbus/metatypes.h"
#endif #endif
#include "internet/internetsearch.h" #include "internet/internetsearchview.h"
void RegisterMetaTypes() { void RegisterMetaTypes() {
@ -117,7 +117,7 @@ void RegisterMetaTypes() {
#endif #endif
#endif #endif
qRegisterMetaType<InternetSearch::ResultList>("InternetSearch::ResultList"); qRegisterMetaType<InternetSearchView::ResultList>("InternetSearchView::ResultList");
qRegisterMetaType<InternetSearch::Result>("InternetSearch::Result"); qRegisterMetaType<InternetSearchView::Result>("InternetSearchView::Result");
} }

View File

@ -1,318 +0,0 @@
/*
* Strawberry Music Player
* This code was part of Clementine (GlobalSearch)
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry 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.
*
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <QtGlobal>
#include <QObject>
#include <QList>
#include <QMap>
#include <QString>
#include <QStringList>
#include <QStringBuilder>
#include <QUrl>
#include <QRegExp>
#include <QImage>
#include <QPixmap>
#include <QPainter>
#include <QSize>
#include <QTimerEvent>
#include "core/application.h"
#include "core/song.h"
#include "core/mimedata.h"
#include "covermanager/albumcoverloader.h"
#include "internet/internetsongmimedata.h"
#include "internetsearch.h"
#include "internetservice.h"
#include "internetservices.h"
const int InternetSearch::kDelayedSearchTimeoutMs = 200;
const int InternetSearch::kArtHeight = 32;
InternetSearch::InternetSearch(Application *app, Song::Source source, QObject *parent)
: QObject(parent),
app_(app),
source_(source),
service_(app->internet_services()->ServiceBySource(source)),
searches_next_id_(1),
art_searches_next_id_(1) {
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, QUrl, QImage)), SLOT(AlbumCoverLoaded(quint64, QUrl, QImage)));
connect(this, SIGNAL(SearchAsyncSig(const int, const QString&, const SearchType)), this, SLOT(DoSearchAsync(const int, const QString&, const SearchType)));
connect(service_, SIGNAL(SearchUpdateStatus(const int, const QString&)), SLOT(UpdateStatusSlot(const int, const QString&)));
connect(service_, SIGNAL(SearchProgressSetMaximum(const int, const int)), SLOT(ProgressSetMaximumSlot(const int, const int)));
connect(service_, SIGNAL(SearchUpdateProgress(const int, const int)), SLOT(UpdateProgressSlot(const int, const int)));
connect(service_, SIGNAL(SearchResults(const int, const SongList&, const QString&)), SLOT(SearchDone(const int, const SongList&, const QString&)));
}
InternetSearch::~InternetSearch() {}
QStringList InternetSearch::TokenizeQuery(const QString &query) {
QStringList tokens(query.split(QRegExp("\\s+")));
for (QStringList::iterator it = tokens.begin(); it != tokens.end(); ++it) {
(*it).remove('(');
(*it).remove(')');
(*it).remove('"');
const int colon = (*it).indexOf(":");
if (colon != -1) {
(*it).remove(0, colon + 1);
}
}
return tokens;
}
bool InternetSearch::Matches(const QStringList &tokens, const QString &string) {
for (const QString &token : tokens) {
if (!string.contains(token, Qt::CaseInsensitive)) {
return false;
}
}
return true;
}
int InternetSearch::SearchAsync(const QString &query, const SearchType type) {
const int id = searches_next_id_++;
emit SearchAsyncSig(id, query, type);
return id;
}
void InternetSearch::SearchAsync(const int id, const QString &query, const SearchType type) {
const int service_id = service_->Search(query, type);
pending_searches_[service_id] = PendingState(id, TokenizeQuery(query));
}
void InternetSearch::DoSearchAsync(const int id, const QString &query, const SearchType type) {
int timer_id = startTimer(kDelayedSearchTimeoutMs);
delayed_searches_[timer_id].id_ = id;
delayed_searches_[timer_id].query_ = query;
delayed_searches_[timer_id].type_ = type;
}
void InternetSearch::SearchDone(const int service_id, const SongList &songs, const QString &error) {
if (!pending_searches_.contains(service_id)) return;
// Map back to the original id.
const PendingState state = pending_searches_.take(service_id);
const int search_id = state.orig_id_;
if (songs.isEmpty()) {
emit SearchError(search_id, error);
return;
}
ResultList results;
for (const Song &song : songs) {
Result result;
result.metadata_ = song;
results << result;
}
if (results.isEmpty()) return;
// Load cached pixmaps into the results
for (InternetSearch::ResultList::iterator it = results.begin(); it != results.end(); ++it) {
it->pixmap_cache_key_ = PixmapCacheKey(*it);
}
emit AddResults(search_id, results);
MaybeSearchFinished(search_id);
}
void InternetSearch::MaybeSearchFinished(const int id) {
if (pending_searches_.keys(PendingState(id, QStringList())).isEmpty()) {
emit SearchFinished(id);
}
}
void InternetSearch::CancelSearch(const int id) {
QMap<int, DelayedSearch>::iterator it;
for (it = delayed_searches_.begin(); it != delayed_searches_.end(); ++it) {
if (it.value().id_ == id) {
killTimer(it.key());
delayed_searches_.erase(it);
return;
}
}
service_->CancelSearch();
}
void InternetSearch::timerEvent(QTimerEvent *e) {
QMap<int, DelayedSearch>::iterator it = delayed_searches_.find(e->timerId());
if (it != delayed_searches_.end()) {
SearchAsync(it.value().id_, it.value().query_, it.value().type_);
delayed_searches_.erase(it);
return;
}
QObject::timerEvent(e);
}
QString InternetSearch::PixmapCacheKey(const InternetSearch::Result &result) const {
return "internet:" % result.metadata_.url().toString();
}
bool InternetSearch::FindCachedPixmap(const InternetSearch::Result &result, QPixmap *pixmap) const {
return pixmap_cache_.find(result.pixmap_cache_key_, pixmap);
}
int InternetSearch::LoadAlbumCoverAsync(const InternetSearch::Result &result) {
const int id = art_searches_next_id_++;
pending_art_searches_[id] = result.pixmap_cache_key_;
quint64 loader_id = app_->album_cover_loader()->LoadImageAsync(cover_loader_options_, result.metadata_);
cover_loader_tasks_[loader_id] = id;
return id;
}
void InternetSearch::AlbumCoverLoaded(const quint64 id, const QUrl &cover_url, const QImage &image) {
Q_UNUSED(cover_url);
if (!cover_loader_tasks_.contains(id)) return;
int orig_id = cover_loader_tasks_.take(id);
const QString key = pending_art_searches_.take(orig_id);
QPixmap pixmap = QPixmap::fromImage(image);
pixmap_cache_.insert(key, pixmap);
emit AlbumCoverLoaded(orig_id, pixmap);
}
QImage InternetSearch::ScaleAndPad(const QImage &image) {
if (image.isNull()) return QImage();
const QSize target_size = QSize(kArtHeight, kArtHeight);
if (image.size() == target_size) return image;
// Scale the image down
QImage copy;
copy = image.scaled(target_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
// Pad the image to kHeight x kHeight
if (copy.size() == target_size) return copy;
QImage padded_image(kArtHeight, kArtHeight, QImage::Format_ARGB32);
padded_image.fill(0);
QPainter p(&padded_image);
p.drawImage((kArtHeight - copy.width()) / 2, (kArtHeight - copy.height()) / 2, copy);
p.end();
return padded_image;
}
MimeData *InternetSearch::LoadTracks(const ResultList &results) {
if (results.isEmpty()) {
return nullptr;
}
ResultList results_copy;
for (const Result &result : results) {
results_copy << result;
}
SongList songs;
for (const Result &result : results) {
songs << result.metadata_;
}
InternetSongMimeData *internet_song_mime_data = new InternetSongMimeData(service_);
internet_song_mime_data->songs = songs;
MimeData *mime_data = internet_song_mime_data;
QList<QUrl> urls;
for (const Result &result : results) {
urls << result.metadata_.url();
}
mime_data->setUrls(urls);
return mime_data;
}
void InternetSearch::UpdateStatusSlot(const int service_id, const QString &text) {
if (!pending_searches_.contains(service_id)) return;
const PendingState state = pending_searches_[service_id];
const int search_id = state.orig_id_;
emit UpdateStatus(search_id, text);
}
void InternetSearch::ProgressSetMaximumSlot(const int service_id, const int max) {
if (!pending_searches_.contains(service_id)) return;
const PendingState state = pending_searches_[service_id];
const int search_id = state.orig_id_;
emit ProgressSetMaximum(search_id, max);
}
void InternetSearch::UpdateProgressSlot(const int service_id, const int progress) {
if (!pending_searches_.contains(service_id)) return;
const PendingState state = pending_searches_[service_id];
const int search_id = state.orig_id_;
emit UpdateProgress(search_id, progress);
}

View File

@ -1,167 +0,0 @@
/*
* Strawberry Music Player
* This code was part of Clementine (GlobalSearch)
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry 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.
*
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef INTERNETSEARCH_H
#define INTERNETSEARCH_H
#include "config.h"
#include <QtGlobal>
#include <QObject>
#include <QMetaType>
#include <QSet>
#include <QList>
#include <QMap>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QImage>
#include <QPixmap>
#include <QPixmapCache>
#include "core/song.h"
#include "covermanager/albumcoverloaderoptions.h"
class QTimerEvent;
class Application;
class MimeData;
class AlbumCoverLoader;
class InternetService;
class InternetSearch : public QObject {
Q_OBJECT
public:
explicit InternetSearch(Application *app, Song::Source source, QObject *parent = nullptr);
~InternetSearch();
enum SearchType {
SearchType_Artists = 1,
SearchType_Albums = 2,
SearchType_Songs = 3,
};
struct Result {
Song metadata_;
QString pixmap_cache_key_;
};
typedef QList<Result> ResultList;
static const int kDelayedSearchTimeoutMs;
Application *application() const { return app_; }
Song::Source source() const { return source_; }
InternetService *service() const { return service_; }
int SearchAsync(const QString &query, SearchType type);
int LoadAlbumCoverAsync(const InternetSearch::Result &result);
void CancelSearch(const int id);
void CancelArt(const int id);
// Loads tracks for results that were previously emitted by ResultsAvailable.
// The implementation creates a SongMimeData with one Song for each Result.
MimeData *LoadTracks(const ResultList &results);
signals:
void SearchAsyncSig(const int id, const QString &query, const SearchType type);
void ResultsAvailable(const int id, const InternetSearch::ResultList &results);
void AddResults(const int id, const InternetSearch::ResultList &results);
void SearchError(const int id, const QString &error);
void SearchFinished(const int id);
void UpdateStatus(const int id, const QString &text);
void ProgressSetMaximum(const int id, const int progress);
void UpdateProgress(const int id, const int max);
void AlbumCoverLoaded(const int id, const QPixmap &pixmap);
protected:
struct PendingState {
PendingState() : orig_id_(-1) {}
PendingState(int orig_id, QStringList tokens)
: orig_id_(orig_id), tokens_(tokens) {}
int orig_id_;
QStringList tokens_;
bool operator<(const PendingState &b) const {
return orig_id_ < b.orig_id_;
}
bool operator==(const PendingState &b) const {
return orig_id_ == b.orig_id_;
}
};
void timerEvent(QTimerEvent *e);
// These functions treat queries in the same way as CollectionQuery.
// They're useful for figuring out whether you got a result because it matched in the song title or the artist/album name.
static QStringList TokenizeQuery(const QString &query);
static bool Matches(const QStringList &tokens, const QString &string);
private slots:
void DoSearchAsync(const int id, const QString &query, const SearchType type);
void SearchDone(const int service_id, const SongList &songs, const QString &error);
void AlbumCoverLoaded(const quint64 id, const QUrl &cover_url, const QImage &image);
void UpdateStatusSlot(const int id, const QString &text);
void ProgressSetMaximumSlot(const int id, const int progress);
void UpdateProgressSlot(const int id, const int max);
private:
void SearchAsync(const int id, const QString &query, const SearchType type);
bool FindCachedPixmap(const InternetSearch::Result &result, QPixmap *pixmap) const;
QString PixmapCacheKey(const InternetSearch::Result &result) const;
void MaybeSearchFinished(const int id);
void ShowConfig() {}
static QImage ScaleAndPad(const QImage &image);
private:
struct DelayedSearch {
int id_;
QString query_;
SearchType type_;
};
static const int kArtHeight;
Application *app_;
Song::Source source_;
InternetService *service_;
int searches_next_id_;
int art_searches_next_id_;
QMap<int, DelayedSearch> delayed_searches_;
QMap<int, QString> pending_art_searches_;
QPixmapCache pixmap_cache_;
AlbumCoverLoaderOptions cover_loader_options_;
QMap<quint64, int> cover_loader_tasks_;
QMap<int, PendingState> pending_searches_;
};
Q_DECLARE_METATYPE(InternetSearch::Result)
Q_DECLARE_METATYPE(InternetSearch::ResultList)
#endif // INTERNETSEARCH_H

View File

@ -32,12 +32,14 @@
#include "core/mimedata.h" #include "core/mimedata.h"
#include "core/iconloader.h" #include "core/iconloader.h"
#include "internetsearch.h" #include "internetsongmimedata.h"
#include "internetservice.h"
#include "internetsearchmodel.h" #include "internetsearchmodel.h"
#include "internetsearchview.h"
InternetSearchModel::InternetSearchModel(InternetSearch *engine, QObject *parent) InternetSearchModel::InternetSearchModel(InternetService *service, QObject *parent)
: QStandardItemModel(parent), : QStandardItemModel(parent),
engine_(engine), service_(service),
proxy_(nullptr), proxy_(nullptr),
use_pretty_covers_(true), use_pretty_covers_(true),
artist_icon_(IconLoader::Load("folder-sound")), artist_icon_(IconLoader::Load("folder-sound")),
@ -52,11 +54,11 @@ InternetSearchModel::InternetSearchModel(InternetSearch *engine, QObject *parent
} }
void InternetSearchModel::AddResults(const InternetSearch::ResultList &results) { void InternetSearchModel::AddResults(const InternetSearchView::ResultList &results) {
int sort_index = 0; int sort_index = 0;
for (const InternetSearch::Result &result : results) { for (const InternetSearchView::Result &result : results) {
QStandardItem *parent = invisibleRootItem(); QStandardItem *parent = invisibleRootItem();
// Find (or create) the container nodes for this result if we can. // Find (or create) the container nodes for this result if we can.
@ -277,7 +279,7 @@ void InternetSearchModel::Clear() {
clear(); clear();
} }
InternetSearch::ResultList InternetSearchModel::GetChildResults(const QModelIndexList &indexes) const { InternetSearchView::ResultList InternetSearchModel::GetChildResults(const QModelIndexList &indexes) const {
QList<QStandardItem*> items; QList<QStandardItem*> items;
for (const QModelIndex &index : indexes) { for (const QModelIndex &index : indexes) {
@ -287,9 +289,9 @@ InternetSearch::ResultList InternetSearchModel::GetChildResults(const QModelInde
} }
InternetSearch::ResultList InternetSearchModel::GetChildResults(const QList<QStandardItem*> &items) const { InternetSearchView::ResultList InternetSearchModel::GetChildResults(const QList<QStandardItem*> &items) const {
InternetSearch::ResultList results; InternetSearchView::ResultList results;
QSet<const QStandardItem*> visited; QSet<const QStandardItem*> visited;
for (QStandardItem *item : items) { for (QStandardItem *item : items) {
@ -300,7 +302,7 @@ InternetSearch::ResultList InternetSearchModel::GetChildResults(const QList<QSta
} }
void InternetSearchModel::GetChildResults(const QStandardItem *item, InternetSearch::ResultList *results, QSet<const QStandardItem*> *visited) const { void InternetSearchModel::GetChildResults(const QStandardItem *item, InternetSearchView::ResultList *results, QSet<const QStandardItem*> *visited) const {
if (visited->contains(item)) { if (visited->contains(item)) {
return; return;
@ -322,7 +324,7 @@ void InternetSearchModel::GetChildResults(const QStandardItem *item, InternetSea
// No - maybe it's a song, add its result if valid // No - maybe it's a song, add its result if valid
QVariant result = item->data(Role_Result); QVariant result = item->data(Role_Result);
if (result.isValid()) { if (result.isValid()) {
results->append(result.value<InternetSearch::Result>()); results->append(result.value<InternetSearchView::Result>());
} }
else { else {
// Maybe it's a provider then? // Maybe it's a provider then?
@ -344,15 +346,17 @@ void InternetSearchModel::GetChildResults(const QStandardItem *item, InternetSea
} }
QMimeData *InternetSearchModel::mimeData(const QModelIndexList &indexes) const { QMimeData *InternetSearchModel::mimeData(const QModelIndexList &indexes) const {
return engine_->LoadTracks(GetChildResults(indexes));
return LoadTracks(GetChildResults(indexes));
} }
namespace { namespace {
void GatherResults(const QStandardItem *parent, InternetSearch::ResultList *results) { void GatherResults(const QStandardItem *parent, InternetSearchView::ResultList *results) {
QVariant result_variant = parent->data(InternetSearchModel::Role_Result); QVariant result_variant = parent->data(InternetSearchModel::Role_Result);
if (result_variant.isValid()) { if (result_variant.isValid()) {
InternetSearch::Result result = result_variant.value<InternetSearch::Result>(); InternetSearchView::Result result = result_variant.value<InternetSearchView::Result>();
(*results).append(result); (*results).append(result);
} }
@ -369,7 +373,7 @@ void InternetSearchModel::SetGroupBy(const CollectionModel::Grouping &grouping,
if (regroup_now && group_by_ != old_group_by) { if (regroup_now && group_by_ != old_group_by) {
// Walk the tree gathering the results we have already // Walk the tree gathering the results we have already
InternetSearch::ResultList results; InternetSearchView::ResultList results;
GatherResults(invisibleRootItem(), &results); GatherResults(invisibleRootItem(), &results);
// Reset the model and re-add all the results using the new grouping. // Reset the model and re-add all the results using the new grouping.
@ -378,3 +382,34 @@ void InternetSearchModel::SetGroupBy(const CollectionModel::Grouping &grouping,
} }
} }
MimeData *InternetSearchModel::LoadTracks(const InternetSearchView::ResultList &results) const {
if (results.isEmpty()) {
return nullptr;
}
InternetSearchView::ResultList results_copy;
for (const InternetSearchView::Result &result : results) {
results_copy << result;
}
SongList songs;
for (const InternetSearchView::Result &result : results) {
songs << result.metadata_;
}
InternetSongMimeData *internet_song_mime_data = new InternetSongMimeData(service_);
internet_song_mime_data->songs = songs;
MimeData *mime_data = internet_song_mime_data;
QList<QUrl> urls;
for (const InternetSearchView::Result &result : results) {
urls << result.metadata_.url();
}
mime_data->setUrls(urls);
return mime_data;
}

View File

@ -37,16 +37,19 @@
#include "core/song.h" #include "core/song.h"
#include "collection/collectionmodel.h" #include "collection/collectionmodel.h"
#include "internetsearch.h" #include "internetsearchview.h"
class QMimeData; class QMimeData;
class QSortFilterProxyModel; class QSortFilterProxyModel;
class MimeData;
class InternetService;
class InternetSearchModel : public QStandardItemModel { class InternetSearchModel : public QStandardItemModel {
Q_OBJECT Q_OBJECT
public: public:
explicit InternetSearchModel(InternetSearch *engine, QObject *parent = nullptr); explicit InternetSearchModel(InternetService *service, QObject *parent = nullptr);
enum Role { enum Role {
Role_Result = CollectionModel::LastRole, Role_Result = CollectionModel::LastRole,
@ -61,25 +64,29 @@ class InternetSearchModel : public QStandardItemModel {
}; };
void set_proxy(QSortFilterProxyModel *proxy) { proxy_ = proxy; } void set_proxy(QSortFilterProxyModel *proxy) { proxy_ = proxy; }
void set_use_pretty_covers(bool pretty) { use_pretty_covers_ = pretty; } void set_use_pretty_covers(const bool pretty) { use_pretty_covers_ = pretty; }
void SetGroupBy(const CollectionModel::Grouping &grouping, bool regroup_now); void SetGroupBy(const CollectionModel::Grouping &grouping, bool regroup_now);
void Clear(); void Clear();
InternetSearch::ResultList GetChildResults(const QModelIndexList &indexes) const; InternetSearchView::ResultList GetChildResults(const QModelIndexList &indexes) const;
InternetSearch::ResultList GetChildResults(const QList<QStandardItem*> &items) const; InternetSearchView::ResultList GetChildResults(const QList<QStandardItem*> &items) const;
QMimeData *mimeData(const QModelIndexList &indexes) const; QMimeData *mimeData(const QModelIndexList &indexes) const;
// Loads tracks for results that were previously emitted by ResultsAvailable.
// The implementation creates a SongMimeData with one Song for each Result.
MimeData *LoadTracks(const InternetSearchView::ResultList &results) const;
public slots: public slots:
void AddResults(const InternetSearch::ResultList &results); void AddResults(const InternetSearchView::ResultList &results);
private: private:
QStandardItem *BuildContainers(const Song &metadata, QStandardItem *parent, ContainerKey *key, int level = 0); QStandardItem *BuildContainers(const Song &metadata, QStandardItem *parent, ContainerKey *key, int level = 0);
void GetChildResults(const QStandardItem *item, InternetSearch::ResultList *results, QSet<const QStandardItem*> *visited) const; void GetChildResults(const QStandardItem *item, InternetSearchView::ResultList *results, QSet<const QStandardItem*> *visited) const;
private: private:
InternetSearch *engine_; InternetService *service_;
QSortFilterProxyModel *proxy_; QSortFilterProxyModel *proxy_;
bool use_pretty_covers_; bool use_pretty_covers_;
QIcon artist_icon_; QIcon artist_icon_;

View File

@ -28,9 +28,9 @@
#include "core/song.h" #include "core/song.h"
#include "collection/collectionmodel.h" #include "collection/collectionmodel.h"
#include "internetsearch.h"
#include "internetsearchmodel.h" #include "internetsearchmodel.h"
#include "internetsearchsortmodel.h" #include "internetsearchsortmodel.h"
#include "internetsearchview.h"
InternetSearchSortModel::InternetSearchSortModel(QObject *parent) InternetSearchSortModel::InternetSearchSortModel(QObject *parent)
: QSortFilterProxyModel(parent) {} : QSortFilterProxyModel(parent) {}
@ -58,8 +58,8 @@ bool InternetSearchSortModel::lessThan(const QModelIndex &left, const QModelInde
} }
// Otherwise we're comparing songs. Sort by disc, track, then title. // Otherwise we're comparing songs. Sort by disc, track, then title.
const InternetSearch::Result r1 = left.data(InternetSearchModel::Role_Result).value<InternetSearch::Result>(); const InternetSearchView::Result r1 = left.data(InternetSearchModel::Role_Result).value<InternetSearchView::Result>();
const InternetSearch::Result r2 = right.data(InternetSearchModel::Role_Result).value<InternetSearch::Result>(); const InternetSearchView::Result r2 = right.data(InternetSearchModel::Role_Result).value<InternetSearchView::Result>();
#define CompareInt(field) \ #define CompareInt(field) \
if (r1.metadata_.field() < r2.metadata_.field()) return true; \ if (r1.metadata_.field() < r2.metadata_.field()) return true; \

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
* Strawberry Music Player * Strawberry Music Player
* This code was part of Clementine (GlobalSearch) * This code was part of Clementine (GlobalSearch)
* Copyright 2012, David Sansome <me@davidsansome.com> * Copyright 2012, David Sansome <me@davidsansome.com>
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net> * Copyright 2018-2020, Jonas Kvinge <jonas@jkvinge.net>
* *
* Strawberry is free software: you can redistribute it and/or modify * Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -24,18 +24,25 @@
#include "config.h" #include "config.h"
#include <QtGlobal>
#include <QObject> #include <QObject>
#include <QWidget> #include <QWidget>
#include <QMap> #include <QSet>
#include <QList> #include <QList>
#include <QMap>
#include <QString> #include <QString>
#include <QStringList>
#include <QUrl>
#include <QImage>
#include <QPixmap> #include <QPixmap>
#include <QPixmapCache>
#include <QScopedPointer> #include <QScopedPointer>
#include <QMetaType>
#include "core/song.h" #include "core/song.h"
#include "collection/collectionmodel.h" #include "collection/collectionmodel.h"
#include "covermanager/albumcoverloaderoptions.h"
#include "settings/settingsdialog.h" #include "settings/settingsdialog.h"
#include "internetsearch.h"
class QSortFilterProxyModel; class QSortFilterProxyModel;
class QMimeData; class QMimeData;
@ -48,11 +55,13 @@ class QKeyEvent;
class QShowEvent; class QShowEvent;
class QHideEvent; class QHideEvent;
class QContextMenuEvent; class QContextMenuEvent;
class QTimerEvent;
class QModelIndex;
class Application; class Application;
class MimeData; class MimeData;
class GroupByDialog; class GroupByDialog;
class AlbumCoverLoader;
class InternetService;
class InternetSearchModel; class InternetSearchModel;
class Ui_InternetSearchView; class Ui_InternetSearchView;
@ -63,80 +72,127 @@ class InternetSearchView : public QWidget {
explicit InternetSearchView(QWidget *parent = nullptr); explicit InternetSearchView(QWidget *parent = nullptr);
~InternetSearchView(); ~InternetSearchView();
void Init(Application *app, InternetSearch *engine, const QString &settings_group, const SettingsDialog::Page settings_page, const bool artists = false, const bool albums = false, const bool songs = false); enum SearchType {
SearchType_Artists = 1,
SearchType_Albums = 2,
SearchType_Songs = 3,
};
struct Result {
Song metadata_;
QString pixmap_cache_key_;
};
typedef QList<Result> ResultList;
static const int kSwapModelsTimeoutMsec; void Init(Application *app, InternetService *service);
void LazyLoadAlbumCover(const QModelIndex &index); void LazyLoadAlbumCover(const QModelIndex &index);
protected:
struct PendingState {
PendingState() : orig_id_(-1) {}
PendingState(int orig_id, QStringList tokens) : orig_id_(orig_id), tokens_(tokens) {}
int orig_id_;
QStringList tokens_;
bool operator<(const PendingState &b) const {
return orig_id_ < b.orig_id_;
}
bool operator==(const PendingState &b) const {
return orig_id_ == b.orig_id_;
}
};
void showEvent(QShowEvent *e); void showEvent(QShowEvent *e);
void hideEvent(QHideEvent *e); void hideEvent(QHideEvent *e);
bool eventFilter(QObject *object, QEvent *event); bool eventFilter(QObject *object, QEvent *e);
void timerEvent(QTimerEvent *e);
public slots: // These functions treat queries in the same way as CollectionQuery.
void ReloadSettings(); // They're useful for figuring out whether you got a result because it matched in the song title or the artist/album name.
void StartSearch(const QString &query); static QStringList TokenizeQuery(const QString &query);
static bool Matches(const QStringList &tokens, const QString &string);
private:
struct DelayedSearch {
int id_;
QString query_;
SearchType type_;
};
bool SearchKeyEvent(QKeyEvent *e);
bool ResultsContextMenuEvent(QContextMenuEvent *e);
void FocusSearchField(); void FocusSearchField();
void OpenSettingsDialog();
MimeData *SelectedMimeData();
void SetSearchType(const SearchType type);
int SearchAsync(const QString &query, SearchType type);
void SearchAsync(const int id, const QString &query, const SearchType type);
void SearchError(const int id, const QString &error);
void CancelSearch(const int id);
QString PixmapCacheKey(const Result &result) const;
bool FindCachedPixmap(const Result &result, QPixmap *pixmap) const;
static QImage ScaleAndPad(const QImage &image);
int LoadAlbumCoverAsync(const Result &result);
signals: signals:
void AddToPlaylist(QMimeData *data); void AddToPlaylist(QMimeData*);
void AddArtistsSignal(SongList songs); void AddArtistsSignal(SongList);
void AddAlbumsSignal(SongList songs); void AddAlbumsSignal(SongList);
void AddSongsSignal(SongList songs); void AddSongsSignal(SongList);
private slots: private slots:
void SwapModels(); void SwapModels();
void TextEdited(const QString &text); void TextEdited(const QString &text);
void StartSearch(const QString &query);
void SearchDone(const int service_id, const SongList &songs, const QString &error);
void UpdateStatus(const int id, const QString &text); void UpdateStatus(const int id, const QString &text);
void ProgressSetMaximum(const int id, const int progress); void ProgressSetMaximum(const int id, const int progress);
void UpdateProgress(const int id, const int max); void UpdateProgress(const int id, const int max);
void AddResults(const int id, const InternetSearch::ResultList &results); void AddResults(const int id, const ResultList &results);
void SearchError(const int id, const QString &error);
void AlbumCoverLoaded(const int id, const QPixmap &pixmap);
void FocusOnFilter(QKeyEvent *event); void FocusOnFilter(QKeyEvent *e);
void AddSelectedToPlaylist(); void AddSelectedToPlaylist();
void LoadSelected(); void LoadSelected();
void OpenSelectedInNewPlaylist(); void OpenSelectedInNewPlaylist();
void AddSelectedToPlaylistEnqueue(); void AddSelectedToPlaylistEnqueue();
void SearchForThis();
void SearchArtistsClicked(bool);
void SearchAlbumsClicked(bool);
void SearchSongsClicked(bool);
void GroupByClicked(QAction *action);
void SetSearchType(const InternetSearch::SearchType type);
void SetGroupBy(const CollectionModel::Grouping &g);
void AddArtists(); void AddArtists();
void AddAlbums(); void AddAlbums();
void AddSongs(); void AddSongs();
void SearchForThis();
void OpenSettingsDialog();
void SearchArtistsClicked(const bool);
void SearchAlbumsClicked(const bool);
void SearchSongsClicked(const bool);
void GroupByClicked(QAction *action);
void SetGroupBy(const CollectionModel::Grouping &g);
void AlbumCoverLoaded(const quint64 id, const QUrl&, const QImage &image);
public slots:
void ReloadSettings();
private: private:
MimeData *SelectedMimeData(); static const int kSwapModelsTimeoutMsec;
static const int kDelayedSearchTimeoutMs;
bool SearchKeyEvent(QKeyEvent *event); static const int kArtHeight;
bool ResultsContextMenuEvent(QContextMenuEvent *event);
private:
Application *app_; Application *app_;
InternetSearch *engine_; InternetService *service_;
QString settings_group_;
SettingsDialog::Page settings_page_;
Ui_InternetSearchView *ui_; Ui_InternetSearchView *ui_;
QScopedPointer<GroupByDialog> group_by_dialog_; QScopedPointer<GroupByDialog> group_by_dialog_;
bool artists_;
bool albums_;
bool songs_;
QMenu *context_menu_; QMenu *context_menu_;
QList<QAction*> context_actions_; QList<QAction*> context_actions_;
QActionGroup *group_by_actions_; QActionGroup *group_by_actions_;
int last_search_id_;
// Like graphics APIs have a front buffer and a back buffer, there's a front model and a back model // Like graphics APIs have a front buffer and a back buffer, there's a front model and a back model
// The front model is the one that's shown in the UI and the back model is the one that lies in wait. // The front model is the one that's shown in the UI and the back model is the one that lies in wait.
// current_model_ will point to either the front or the back model. // current_model_ will point to either the front or the back model.
@ -148,13 +204,24 @@ class InternetSearchView : public QWidget {
QSortFilterProxyModel *back_proxy_; QSortFilterProxyModel *back_proxy_;
QSortFilterProxyModel *current_proxy_; QSortFilterProxyModel *current_proxy_;
QMap<int, QModelIndex> art_requests_;
QTimer *swap_models_timer_; QTimer *swap_models_timer_;
InternetSearch::SearchType search_type_; SearchType search_type_;
bool error_; bool search_error_;
int last_search_id_;
int searches_next_id_;
int art_searches_next_id_;
QMap<int, DelayedSearch> delayed_searches_;
QMap<int, PendingState> pending_searches_;
QMap<int, QString> pending_art_searches_;
QMap<int, QModelIndex> art_requests_;
AlbumCoverLoaderOptions cover_loader_options_;
QMap<quint64, quint64> cover_loader_tasks_;
QPixmapCache pixmap_cache_;
}; };
Q_DECLARE_METATYPE(InternetSearchView::Result)
Q_DECLARE_METATYPE(InternetSearchView::ResultList)
#endif // INTERNETSEARCHVIEW_H #endif // INTERNETSEARCHVIEW_H

View File

@ -10,6 +10,9 @@
<height>660</height> <height>660</height>
</rect> </rect>
</property> </property>
<property name="windowTitle">
<string>Internet Search View</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
@ -39,7 +42,7 @@
<item> <item>
<widget class="QSearchField" name="search" native="true"> <widget class="QSearchField" name="search" native="true">
<property name="placeholderText" stdset="0"> <property name="placeholderText" stdset="0">
<string>Search for anything</string> <string/>
</property> </property>
</widget> </widget>
</item> </item>
@ -62,58 +65,65 @@
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="layout_searchby"> <widget class="QWidget" name="widget_searchby" native="true">
<property name="sizeConstraint"> <property name="minimumSize">
<enum>QLayout::SetFixedSize</enum> <size>
<width>0</width>
<height>20</height>
</size>
</property> </property>
<item> <layout class="QHBoxLayout" name="horizontalLayout_2">
<widget class="QLabel" name="label_searchby"> <property name="spacing">
<property name="enabled"> <number>2</number>
<bool>true</bool> </property>
</property> <property name="leftMargin">
<property name="text"> <number>0</number>
<string>Search type</string> </property>
</property> <property name="topMargin">
<property name="margin"> <number>0</number>
<number>10</number> </property>
</property> <property name="rightMargin">
</widget> <number>0</number>
</item> </property>
<item> <property name="bottomMargin">
<widget class="QRadioButton" name="radiobutton_search_artists"> <number>0</number>
<property name="text"> </property>
<string>ar&amp;tists</string> <item>
</property> <widget class="QRadioButton" name="radiobutton_search_artists">
</widget> <property name="text">
</item> <string>artists</string>
<item> </property>
<widget class="QRadioButton" name="radiobutton_search_albums"> </widget>
<property name="text"> </item>
<string>a&amp;lbums</string> <item>
</property> <widget class="QRadioButton" name="radiobutton_search_albums">
</widget> <property name="text">
</item> <string>albums</string>
<item> </property>
<widget class="QRadioButton" name="radiobutton_search_songs"> </widget>
<property name="text"> </item>
<string>son&amp;gs</string> <item>
</property> <widget class="QRadioButton" name="radiobutton_search_songs">
</widget> <property name="text">
</item> <string>songs</string>
<item> </property>
<spacer name="horizontalSpacer"> </widget>
<property name="orientation"> </item>
<enum>Qt::Horizontal</enum> <item>
</property> <spacer name="spacer_searchby">
<property name="sizeHint" stdset="0"> <property name="orientation">
<size> <enum>Qt::Horizontal</enum>
<width>40</width> </property>
<height>20</height> <property name="sizeHint" stdset="0">
</size> <size>
</property> <width>40</width>
</spacer> <height>20</height>
</item> </size>
</layout> </property>
</spacer>
</item>
</layout>
</widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="layout_progress"> <layout class="QVBoxLayout" name="layout_progress">
@ -215,7 +225,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>398</width> <width>398</width>
<height>511</height> <height>528</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QVBoxLayout" name="verticalLayout_5">

View File

@ -22,9 +22,10 @@
#include "internetservice.h" #include "internetservice.h"
#include "core/song.h" #include "core/song.h"
#include "settings/settingsdialog.h"
class Application; class Application;
InternetService::InternetService(Song::Source source, const QString &name, const QString &url_scheme, Application *app, QObject *parent) InternetService::InternetService(Song::Source source, const QString &name, const QString &url_scheme, const QString &settings_group, SettingsDialog::Page settings_page, Application *app, QObject *parent)
: QObject(parent), app_(app), source_(source), name_(name), url_scheme_(url_scheme) { : QObject(parent), app_(app), source_(source), name_(name), url_scheme_(url_scheme), settings_group_(settings_group), settings_page_(settings_page) {
} }

View File

@ -28,7 +28,8 @@
#include <QIcon> #include <QIcon>
#include "core/song.h" #include "core/song.h"
#include "internetsearch.h" #include "settings/settingsdialog.h"
#include "internetsearchview.h"
class QSortFilterProxyModel; class QSortFilterProxyModel;
class Application; class Application;
@ -39,7 +40,7 @@ class InternetService : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit InternetService(Song::Source source, const QString &name, const QString &url_scheme, Application *app, QObject *parent = nullptr); explicit InternetService(Song::Source source, const QString &name, const QString &url_scheme, const QString &settings_group, SettingsDialog::Page settings_page, Application *app, QObject *parent = nullptr);
virtual ~InternetService() {} virtual ~InternetService() {}
virtual void Exit() {} virtual void Exit() {}
@ -47,13 +48,15 @@ class InternetService : public QObject {
virtual Song::Source source() const { return source_; } virtual Song::Source source() const { return source_; }
virtual QString name() const { return name_; } virtual QString name() const { return name_; }
virtual QString url_scheme() const { return url_scheme_; } virtual QString url_scheme() const { return url_scheme_; }
virtual QString settings_group() const { return settings_group_; }
virtual SettingsDialog::Page settings_page() const { return settings_page_; }
virtual bool has_initial_load_settings() const { return false; } virtual bool has_initial_load_settings() const { return false; }
virtual void InitialLoadSettings() {} virtual void InitialLoadSettings() {}
virtual void ReloadSettings() {} virtual void ReloadSettings() {}
virtual QIcon Icon() { return Song::IconForSource(source_); } virtual QIcon Icon() { return Song::IconForSource(source_); }
virtual bool oauth() { return false; } virtual bool oauth() { return false; }
virtual bool authenticated() { return false; } virtual bool authenticated() { return false; }
virtual int Search(const QString &query, InternetSearch::SearchType type) { Q_UNUSED(query); Q_UNUSED(type); return 0; } virtual int Search(const QString &query, InternetSearchView::SearchType type) { Q_UNUSED(query); Q_UNUSED(type); return 0; }
virtual void CancelSearch() {} virtual void CancelSearch() {}
virtual CollectionBackend *artists_collection_backend() { return nullptr; } virtual CollectionBackend *artists_collection_backend() { return nullptr; }
@ -129,10 +132,13 @@ class InternetService : public QObject {
protected: protected:
Application *app_; Application *app_;
private: private:
Song::Source source_; Song::Source source_;
QString name_; QString name_;
QString url_scheme_; QString url_scheme_;
QString settings_group_;
SettingsDialog::Page settings_page_;
}; };
Q_DECLARE_METATYPE(InternetService*) Q_DECLARE_METATYPE(InternetService*)

View File

@ -57,15 +57,15 @@ InternetSongsView::InternetSongsView(Application *app, InternetService *service,
ui_->filter->SetCollectionModel(service_->songs_collection_model()); ui_->filter->SetCollectionModel(service_->songs_collection_model());
connect(ui_->view, SIGNAL(GetSongs()), SLOT(GetSongs())); connect(ui_->view, SIGNAL(GetSongs()), SLOT(GetSongs()));
connect(ui_->view, SIGNAL(RemoveSongs(const SongList&)), service_, SIGNAL(RemoveSongs(const SongList&))); connect(ui_->view, SIGNAL(RemoveSongs(SongList)), service_, SIGNAL(RemoveSongs(SongList)));
connect(ui_->refresh, SIGNAL(clicked()), SLOT(GetSongs())); connect(ui_->refresh, SIGNAL(clicked()), SLOT(GetSongs()));
connect(ui_->close, SIGNAL(clicked()), SLOT(AbortGetSongs())); connect(ui_->close, SIGNAL(clicked()), SLOT(AbortGetSongs()));
connect(ui_->abort, SIGNAL(clicked()), SLOT(AbortGetSongs())); connect(ui_->abort, SIGNAL(clicked()), SLOT(AbortGetSongs()));
connect(service_, SIGNAL(SongsResults(const SongList&, const QString&)), SLOT(SongsFinished(const SongList&, const QString&))); connect(service_, SIGNAL(SongsResults(SongList, QString)), SLOT(SongsFinished(SongList, QString)));
connect(service_, SIGNAL(SongsUpdateStatus(const QString&)), ui_->status, SLOT(setText(const QString&))); connect(service_, SIGNAL(SongsUpdateStatus(QString)), ui_->status, SLOT(setText(QString)));
connect(service_, SIGNAL(SongsProgressSetMaximum(const int)), ui_->progressbar, SLOT(setMaximum(const int))); connect(service_, SIGNAL(SongsProgressSetMaximum(int)), ui_->progressbar, SLOT(setMaximum(int)));
connect(service_, SIGNAL(SongsUpdateProgress(const int)), ui_->progressbar, SLOT(setValue(const int))); connect(service_, SIGNAL(SongsUpdateProgress(int)), ui_->progressbar, SLOT(setValue(int)));
connect(service_->songs_collection_model(), SIGNAL(TotalArtistCountUpdated(int)), ui_->view, SLOT(TotalArtistCountUpdated(int))); connect(service_->songs_collection_model(), SIGNAL(TotalArtistCountUpdated(int)), ui_->view, SLOT(TotalArtistCountUpdated(int)));
connect(service_->songs_collection_model(), SIGNAL(TotalAlbumCountUpdated(int)), ui_->view, SLOT(TotalAlbumCountUpdated(int))); connect(service_->songs_collection_model(), SIGNAL(TotalAlbumCountUpdated(int)), ui_->view, SLOT(TotalAlbumCountUpdated(int)));

View File

@ -40,14 +40,12 @@
#include "internettabsview.h" #include "internettabsview.h"
#include "internetcollectionview.h" #include "internetcollectionview.h"
#include "internetcollectionviewcontainer.h" #include "internetcollectionviewcontainer.h"
#include "internetsearchview.h"
#include "ui_internettabsview.h" #include "ui_internettabsview.h"
InternetTabsView::InternetTabsView(Application *app, InternetService *service, InternetSearch *engine, const QString &settings_group, const SettingsDialog::Page settings_page, QWidget *parent) InternetTabsView::InternetTabsView(Application *app, InternetService *service, const QString &settings_group, const SettingsDialog::Page settings_page, QWidget *parent)
: QWidget(parent), : QWidget(parent),
app_(app), app_(app),
service_(service), service_(service),
engine_(engine),
settings_group_(settings_group), settings_group_(settings_group),
settings_page_(settings_page), settings_page_(settings_page),
ui_(new Ui_InternetTabsView) ui_(new Ui_InternetTabsView)
@ -55,10 +53,10 @@ InternetTabsView::InternetTabsView(Application *app, InternetService *service, I
ui_->setupUi(this); ui_->setupUi(this);
ui_->search_view->Init(app, engine, settings_group, settings_page, service_->artists_collection_model(), service_->albums_collection_model(), service_->songs_collection_model()); ui_->search_view->Init(app, service);
connect(ui_->search_view, SIGNAL(AddArtistsSignal(const SongList&)), service_, SIGNAL(AddArtists(const SongList&))); connect(ui_->search_view, SIGNAL(AddArtistsSignal(SongList)), service_, SIGNAL(AddArtists(SongList)));
connect(ui_->search_view, SIGNAL(AddAlbumsSignal(const SongList&)), service_, SIGNAL(AddAlbums(const SongList&))); connect(ui_->search_view, SIGNAL(AddAlbumsSignal(SongList)), service_, SIGNAL(AddAlbums(SongList)));
connect(ui_->search_view, SIGNAL(AddSongsSignal(const SongList&)), service_, SIGNAL(AddSongs(const SongList&))); connect(ui_->search_view, SIGNAL(AddSongsSignal(SongList)), service_, SIGNAL(AddSongs(SongList)));
if (service_->artists_collection_model()) { if (service_->artists_collection_model()) {
ui_->artists_collection->stacked()->setCurrentWidget(ui_->artists_collection->internetcollection_page()); ui_->artists_collection->stacked()->setCurrentWidget(ui_->artists_collection->internetcollection_page());
@ -70,15 +68,15 @@ InternetTabsView::InternetTabsView(Application *app, InternetService *service, I
ui_->artists_collection->filter()->SetCollectionModel(service_->artists_collection_model()); ui_->artists_collection->filter()->SetCollectionModel(service_->artists_collection_model());
connect(ui_->artists_collection->view(), SIGNAL(GetSongs()), SLOT(GetArtists())); connect(ui_->artists_collection->view(), SIGNAL(GetSongs()), SLOT(GetArtists()));
connect(ui_->artists_collection->view(), SIGNAL(RemoveSongs(const SongList&)), service_, SIGNAL(RemoveArtists(const SongList&))); connect(ui_->artists_collection->view(), SIGNAL(RemoveSongs(SongList)), service_, SIGNAL(RemoveArtists(SongList)));
connect(ui_->artists_collection->button_refresh(), SIGNAL(clicked()), SLOT(GetArtists())); connect(ui_->artists_collection->button_refresh(), SIGNAL(clicked()), SLOT(GetArtists()));
connect(ui_->artists_collection->button_close(), SIGNAL(clicked()), SLOT(AbortGetArtists())); connect(ui_->artists_collection->button_close(), SIGNAL(clicked()), SLOT(AbortGetArtists()));
connect(ui_->artists_collection->button_abort(), SIGNAL(clicked()), SLOT(AbortGetArtists())); connect(ui_->artists_collection->button_abort(), SIGNAL(clicked()), SLOT(AbortGetArtists()));
connect(service_, SIGNAL(ArtistsResults(const SongList&, const QString&)), SLOT(ArtistsFinished(const SongList&, const QString&))); connect(service_, SIGNAL(ArtistsResults(SongList, QString)), SLOT(ArtistsFinished(SongList, QString)));
connect(service_, SIGNAL(ArtistsUpdateStatus(const QString&)), ui_->artists_collection->status(), SLOT(setText(const QString&))); connect(service_, SIGNAL(ArtistsUpdateStatus(QString)), ui_->artists_collection->status(), SLOT(setText(QString)));
connect(service_, SIGNAL(ArtistsProgressSetMaximum(const int)), ui_->artists_collection->progressbar(), SLOT(setMaximum(const int))); connect(service_, SIGNAL(ArtistsProgressSetMaximum(int)), ui_->artists_collection->progressbar(), SLOT(setMaximum(int)));
connect(service_, SIGNAL(ArtistsUpdateProgress(const int)), ui_->artists_collection->progressbar(), SLOT(setValue(const int))); connect(service_, SIGNAL(ArtistsUpdateProgress(int)), ui_->artists_collection->progressbar(), SLOT(setValue(int)));
connect(service_->artists_collection_model(), SIGNAL(TotalArtistCountUpdated(int)), ui_->artists_collection->view(), SLOT(TotalArtistCountUpdated(int))); connect(service_->artists_collection_model(), SIGNAL(TotalArtistCountUpdated(int)), ui_->artists_collection->view(), SLOT(TotalArtistCountUpdated(int)));
connect(service_->artists_collection_model(), SIGNAL(TotalAlbumCountUpdated(int)), ui_->artists_collection->view(), SLOT(TotalAlbumCountUpdated(int))); connect(service_->artists_collection_model(), SIGNAL(TotalAlbumCountUpdated(int)), ui_->artists_collection->view(), SLOT(TotalAlbumCountUpdated(int)));
@ -101,15 +99,15 @@ InternetTabsView::InternetTabsView(Application *app, InternetService *service, I
ui_->albums_collection->filter()->SetCollectionModel(service_->albums_collection_model()); ui_->albums_collection->filter()->SetCollectionModel(service_->albums_collection_model());
connect(ui_->albums_collection->view(), SIGNAL(GetSongs()), SLOT(GetAlbums())); connect(ui_->albums_collection->view(), SIGNAL(GetSongs()), SLOT(GetAlbums()));
connect(ui_->albums_collection->view(), SIGNAL(RemoveSongs(const SongList&)), service_, SIGNAL(RemoveAlbums(const SongList&))); connect(ui_->albums_collection->view(), SIGNAL(RemoveSongs(SongList)), service_, SIGNAL(RemoveAlbums(SongList)));
connect(ui_->albums_collection->button_refresh(), SIGNAL(clicked()), SLOT(GetAlbums())); connect(ui_->albums_collection->button_refresh(), SIGNAL(clicked()), SLOT(GetAlbums()));
connect(ui_->albums_collection->button_close(), SIGNAL(clicked()), SLOT(AbortGetAlbums())); connect(ui_->albums_collection->button_close(), SIGNAL(clicked()), SLOT(AbortGetAlbums()));
connect(ui_->albums_collection->button_abort(), SIGNAL(clicked()), SLOT(AbortGetAlbums())); connect(ui_->albums_collection->button_abort(), SIGNAL(clicked()), SLOT(AbortGetAlbums()));
connect(service_, SIGNAL(AlbumsResults(const SongList&, const QString&)), SLOT(AlbumsFinished(const SongList&, const QString&))); connect(service_, SIGNAL(AlbumsResults(SongList, QString)), SLOT(AlbumsFinished(SongList, QString)));
connect(service_, SIGNAL(AlbumsUpdateStatus(const QString&)), ui_->albums_collection->status(), SLOT(setText(const QString&))); connect(service_, SIGNAL(AlbumsUpdateStatus(QString)), ui_->albums_collection->status(), SLOT(setText(QString)));
connect(service_, SIGNAL(AlbumsProgressSetMaximum(const int)), ui_->albums_collection->progressbar(), SLOT(setMaximum(const int))); connect(service_, SIGNAL(AlbumsProgressSetMaximum(int)), ui_->albums_collection->progressbar(), SLOT(setMaximum(int)));
connect(service_, SIGNAL(AlbumsUpdateProgress(const int)), ui_->albums_collection->progressbar(), SLOT(setValue(const int))); connect(service_, SIGNAL(AlbumsUpdateProgress(int)), ui_->albums_collection->progressbar(), SLOT(setValue(int)));
connect(service_->albums_collection_model(), SIGNAL(TotalArtistCountUpdated(int)), ui_->albums_collection->view(), SLOT(TotalArtistCountUpdated(int))); connect(service_->albums_collection_model(), SIGNAL(TotalArtistCountUpdated(int)), ui_->albums_collection->view(), SLOT(TotalArtistCountUpdated(int)));
connect(service_->albums_collection_model(), SIGNAL(TotalAlbumCountUpdated(int)), ui_->albums_collection->view(), SLOT(TotalAlbumCountUpdated(int))); connect(service_->albums_collection_model(), SIGNAL(TotalAlbumCountUpdated(int)), ui_->albums_collection->view(), SLOT(TotalAlbumCountUpdated(int)));
@ -132,15 +130,15 @@ InternetTabsView::InternetTabsView(Application *app, InternetService *service, I
ui_->songs_collection->filter()->SetCollectionModel(service_->songs_collection_model()); ui_->songs_collection->filter()->SetCollectionModel(service_->songs_collection_model());
connect(ui_->songs_collection->view(), SIGNAL(GetSongs()), SLOT(GetSongs())); connect(ui_->songs_collection->view(), SIGNAL(GetSongs()), SLOT(GetSongs()));
connect(ui_->songs_collection->view(), SIGNAL(RemoveSongs(const SongList&)), service_, SIGNAL(RemoveSongs(const SongList&))); connect(ui_->songs_collection->view(), SIGNAL(RemoveSongs(SongList)), service_, SIGNAL(RemoveSongs(SongList)));
connect(ui_->songs_collection->button_refresh(), SIGNAL(clicked()), SLOT(GetSongs())); connect(ui_->songs_collection->button_refresh(), SIGNAL(clicked()), SLOT(GetSongs()));
connect(ui_->songs_collection->button_close(), SIGNAL(clicked()), SLOT(AbortGetSongs())); connect(ui_->songs_collection->button_close(), SIGNAL(clicked()), SLOT(AbortGetSongs()));
connect(ui_->songs_collection->button_abort(), SIGNAL(clicked()), SLOT(AbortGetSongs())); connect(ui_->songs_collection->button_abort(), SIGNAL(clicked()), SLOT(AbortGetSongs()));
connect(service_, SIGNAL(SongsResults(const SongList&, const QString&)), SLOT(SongsFinished(const SongList&, const QString&))); connect(service_, SIGNAL(SongsResults(SongList, QString)), SLOT(SongsFinished(SongList, QString)));
connect(service_, SIGNAL(SongsUpdateStatus(const QString&)), ui_->songs_collection->status(), SLOT(setText(const QString&))); connect(service_, SIGNAL(SongsUpdateStatus(QString)), ui_->songs_collection->status(), SLOT(setText(QString)));
connect(service_, SIGNAL(SongsProgressSetMaximum(const int)), ui_->songs_collection->progressbar(), SLOT(setMaximum(const int))); connect(service_, SIGNAL(SongsProgressSetMaximum(int)), ui_->songs_collection->progressbar(), SLOT(setMaximum(int)));
connect(service_, SIGNAL(SongsUpdateProgress(const int)), ui_->songs_collection->progressbar(), SLOT(setValue(const int))); connect(service_, SIGNAL(SongsUpdateProgress(int)), ui_->songs_collection->progressbar(), SLOT(setValue(int)));
connect(service_->songs_collection_model(), SIGNAL(TotalArtistCountUpdated(int)), ui_->songs_collection->view(), SLOT(TotalArtistCountUpdated(int))); connect(service_->songs_collection_model(), SIGNAL(TotalArtistCountUpdated(int)), ui_->songs_collection->view(), SLOT(TotalArtistCountUpdated(int)));
connect(service_->songs_collection_model(), SIGNAL(TotalAlbumCountUpdated(int)), ui_->songs_collection->view(), SLOT(TotalAlbumCountUpdated(int))); connect(service_->songs_collection_model(), SIGNAL(TotalAlbumCountUpdated(int)), ui_->songs_collection->view(), SLOT(TotalAlbumCountUpdated(int)));

View File

@ -35,7 +35,6 @@ class QContextMenuEvent;
class Application; class Application;
class InternetService; class InternetService;
class InternetSearch;
class InternetCollectionView; class InternetCollectionView;
class InternetSearchView; class InternetSearchView;
@ -43,7 +42,7 @@ class InternetTabsView : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit InternetTabsView(Application *app, InternetService *service, InternetSearch *engine, const QString &settings_group, const SettingsDialog::Page settings_page, QWidget *parent = nullptr); explicit InternetTabsView(Application *app, InternetService *service, const QString &settings_group, const SettingsDialog::Page settings_page, QWidget *parent = nullptr);
~InternetTabsView(); ~InternetTabsView();
void ReloadSettings(); void ReloadSettings();
@ -68,7 +67,6 @@ class InternetTabsView : public QWidget {
private: private:
Application *app_; Application *app_;
InternetService *service_; InternetService *service_;
InternetSearch *engine_;
QString settings_group_; QString settings_group_;
SettingsDialog::Page settings_page_; SettingsDialog::Page settings_page_;
Ui_InternetTabsView *ui_; Ui_InternetTabsView *ui_;

View File

@ -10,9 +10,18 @@
<height>660</height> <height>660</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <property name="windowTitle">
<string>Internet Tabs View</string>
</property>
<layout class="QVBoxLayout" name="layout_internettabsview">
<item> <item>
<widget class="QTabWidget" name="tabs"> <widget class="QTabWidget" name="tabs">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>

View File

@ -67,7 +67,7 @@ const char *SubsonicService::kSongsFtsTable = "subsonic_songs_fts";
const int SubsonicService::kMaxRedirects = 3; const int SubsonicService::kMaxRedirects = 3;
SubsonicService::SubsonicService(Application *app, QObject *parent) SubsonicService::SubsonicService(Application *app, QObject *parent)
: InternetService(Song::Source_Subsonic, "Subsonic", "subsonic", app, parent), : InternetService(Song::Source_Subsonic, "Subsonic", "subsonic", SubsonicSettingsPage::kSettingsGroup, SettingsDialog::Page_Subsonic, app, parent),
app_(app), app_(app),
network_(new QNetworkAccessManager), network_(new QNetworkAccessManager),
url_handler_(new SubsonicUrlHandler(app, this)), url_handler_(new SubsonicUrlHandler(app, this)),