strawberry-audio-player-win.../src/internet/internetsearchmodel.cpp

412 lines
13 KiB
C++
Raw Normal View History

2018-10-14 00:08:33 +02:00
/*
* Strawberry Music Player
* This code was part of Clementine (GlobalSearch)
* Copyright 2012, David Sansome <me@davidsansome.com>
2021-03-20 21:14:47 +01:00
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
2018-10-14 00:08:33 +02:00
*
* 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 <QStandardItemModel>
2020-02-09 02:29:35 +01:00
#include <QSortFilterProxyModel>
#include <QMimeData>
2018-10-14 00:08:33 +02:00
#include <QList>
#include <QSet>
#include <QVariant>
#include <QString>
2020-02-09 02:29:35 +01:00
#include <QSize>
2018-10-14 00:08:33 +02:00
#include "core/mimedata.h"
#include "core/iconloader.h"
2020-04-13 06:30:40 +02:00
#include "internetsongmimedata.h"
#include "internetservice.h"
#include "internetsearchmodel.h"
2020-04-13 06:30:40 +02:00
#include "internetsearchview.h"
2018-10-14 00:08:33 +02:00
InternetSearchModel::InternetSearchModel(InternetServicePtr service, QObject *parent)
2018-10-14 00:08:33 +02:00
: QStandardItemModel(parent),
2020-04-13 06:30:40 +02:00
service_(service),
2018-10-14 00:08:33 +02:00
proxy_(nullptr),
use_pretty_covers_(true),
2024-04-09 23:20:26 +02:00
artist_icon_(IconLoader::Load(QStringLiteral("folder-sound"))),
album_icon_(IconLoader::Load(QStringLiteral("cdcase"))) {
2018-10-14 00:08:33 +02:00
2023-02-18 14:09:27 +01:00
group_by_[0] = CollectionModel::GroupBy::AlbumArtist;
group_by_[1] = CollectionModel::GroupBy::AlbumDisc;
group_by_[2] = CollectionModel::GroupBy::None;
2018-10-14 00:08:33 +02:00
2021-03-21 04:47:11 +01:00
QList<QSize> nocover_sizes = album_icon_.availableSizes();
no_cover_icon_ = album_icon_.pixmap(nocover_sizes.last()).scaled(CollectionModel::kPrettyCoverSize, CollectionModel::kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
2018-10-14 00:08:33 +02:00
}
2020-04-13 06:30:40 +02:00
void InternetSearchModel::AddResults(const InternetSearchView::ResultList &results) {
2018-10-14 00:08:33 +02:00
2020-04-13 06:30:40 +02:00
for (const InternetSearchView::Result &result : results) {
2018-10-14 00:08:33 +02:00
QStandardItem *parent = invisibleRootItem();
// Find (or create) the container nodes for this result if we can.
ContainerKey key;
parent = BuildContainers(result.metadata_, parent, &key);
// Create the item
QStandardItem *item = new QStandardItem;
item->setText(result.metadata_.TitleWithCompilationArtist());
item->setData(QVariant::fromValue(result), Role_Result);
parent->appendRow(item);
}
}
QStandardItem *InternetSearchModel::BuildContainers(const Song &s, QStandardItem *parent, ContainerKey *key, const int level) {
2018-10-14 00:08:33 +02:00
if (level >= 3) {
return parent;
}
bool has_artist_icon = false;
bool has_album_icon = false;
QString display_text;
QString sort_text;
QString unique_tag;
2018-10-14 00:08:33 +02:00
switch (group_by_[level]) {
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::AlbumArtist:
if (s.is_compilation()) {
display_text = tr("Various artists");
2024-04-09 23:20:26 +02:00
sort_text = QStringLiteral("aaaaaa");
}
else {
display_text = CollectionModel::TextOrUnknown(s.effective_albumartist());
sort_text = CollectionModel::SortTextForArtist(s.effective_albumartist(), true);
}
has_artist_icon = true;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Artist:
2018-10-14 00:08:33 +02:00
if (s.is_compilation()) {
display_text = tr("Various artists");
2024-04-09 23:20:26 +02:00
sort_text = QStringLiteral("aaaaaa");
2018-10-14 00:08:33 +02:00
}
else {
display_text = CollectionModel::TextOrUnknown(s.artist());
sort_text = CollectionModel::SortTextForArtist(s.artist(), true);
2018-10-14 00:08:33 +02:00
}
has_artist_icon = true;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Album:
display_text = CollectionModel::TextOrUnknown(s.album());
sort_text = CollectionModel::SortTextForArtist(s.album(), true);
unique_tag = s.album_id();
has_album_icon = true;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::AlbumDisc:{
int disc = qMax(0, s.disc());
display_text = CollectionModel::PrettyAlbumDisc(s.album(), disc);
sort_text = s.album() + CollectionModel::SortTextForNumber(disc);
unique_tag = s.album_id();
has_album_icon = true;
break;
}
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::YearAlbum:{
2019-08-05 19:17:31 +02:00
int year = qMax(0, s.year());
2018-10-14 00:08:33 +02:00
display_text = CollectionModel::PrettyYearAlbum(year, s.album());
sort_text = CollectionModel::SortTextForNumber(year) + s.album();
unique_tag = s.album_id();
has_album_icon = true;
break;
2019-08-05 19:17:31 +02:00
}
2018-10-14 00:08:33 +02:00
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::YearAlbumDisc:{
int year = qMax(0, s.year());
int disc = qMax(0, s.disc());
display_text = CollectionModel::PrettyYearAlbumDisc(year, s.album(), disc);
sort_text = CollectionModel::SortTextForNumber(year) + s.album() + CollectionModel::SortTextForNumber(disc);
unique_tag = s.album_id();
has_album_icon = true;
break;
}
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::OriginalYearAlbum:{
2019-08-05 19:17:31 +02:00
int year = qMax(0, s.effective_originalyear());
2018-10-14 00:08:33 +02:00
display_text = CollectionModel::PrettyYearAlbum(year, s.album());
sort_text = CollectionModel::SortTextForNumber(year) + s.album();
unique_tag = s.album_id();
has_album_icon = true;
break;
2019-08-05 19:17:31 +02:00
}
2018-10-14 00:08:33 +02:00
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::OriginalYearAlbumDisc:{
const int year = qMax(0, s.effective_originalyear());
const int disc = qMax(0, s.disc());
display_text = CollectionModel::PrettyYearAlbumDisc(year, s.album(), disc);
sort_text = CollectionModel::SortTextForNumber(year) + s.album() + CollectionModel::SortTextForNumber(disc);
unique_tag = s.album_id();
has_album_icon = true;
break;
}
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Disc:
display_text = CollectionModel::PrettyDisc(s.disc());
sort_text = CollectionModel::SortTextForArtist(display_text, true);
has_album_icon = true;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Year:{
const int year = qMax(0, s.year());
2018-10-14 00:08:33 +02:00
display_text = QString::number(year);
sort_text = CollectionModel::SortTextForNumber(year) + QLatin1Char(' ');
2018-10-14 00:08:33 +02:00
break;
2019-08-05 19:17:31 +02:00
}
2018-10-14 00:08:33 +02:00
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::OriginalYear:{
const int year = qMax(0, s.effective_originalyear());
2018-10-14 00:08:33 +02:00
display_text = QString::number(year);
sort_text = CollectionModel::SortTextForNumber(year) + QLatin1Char(' ');
2018-10-14 00:08:33 +02:00
break;
2019-08-05 19:17:31 +02:00
}
2018-10-14 00:08:33 +02:00
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Genre:
display_text = CollectionModel::TextOrUnknown(s.genre());
sort_text = CollectionModel::SortTextForArtist(s.genre(), true);
has_album_icon = true;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Composer:
display_text = CollectionModel::TextOrUnknown(s.composer());
sort_text = CollectionModel::SortTextForArtist(s.composer(), true);
has_album_icon = true;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Performer:
display_text = CollectionModel::TextOrUnknown(s.performer());
sort_text = CollectionModel::SortTextForArtist(s.performer(), true);
has_album_icon = true;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Grouping:
display_text = CollectionModel::TextOrUnknown(s.grouping());
sort_text = CollectionModel::SortTextForArtist(s.grouping(), true);
has_album_icon = true;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::FileType:
2018-10-14 00:08:33 +02:00
display_text = s.TextForFiletype();
sort_text = display_text;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Format:
2019-03-30 22:03:33 +01:00
if (s.samplerate() <= 0) {
display_text = s.TextForFiletype();
}
else {
if (s.bitdepth() <= 0) {
2024-04-09 23:20:26 +02:00
display_text = QStringLiteral("%1 (%2)").arg(s.TextForFiletype(), QString::number(s.samplerate() / 1000.0, 'G', 5));
2019-03-30 22:03:33 +01:00
}
else {
2024-04-09 23:20:26 +02:00
display_text = QStringLiteral("%1 (%2/%3)").arg(s.TextForFiletype(), QString::number(s.samplerate() / 1000.0, 'G', 5), QString::number(s.bitdepth()));
2019-03-30 22:03:33 +01:00
}
}
sort_text = display_text;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Samplerate:
2020-11-17 01:21:38 +01:00
display_text = QString::number(s.samplerate());
sort_text = display_text;
2019-08-05 19:17:31 +02:00
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Bitdepth:
2020-11-17 01:21:38 +01:00
display_text = QString::number(s.bitdepth());
sort_text = display_text;
2019-08-05 19:17:31 +02:00
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::Bitrate:
2020-11-17 01:21:38 +01:00
display_text = QString::number(s.bitrate());
sort_text = display_text;
break;
2023-02-18 14:09:27 +01:00
case CollectionModel::GroupBy::None:
case CollectionModel::GroupBy::GroupByCount:
2018-10-14 00:08:33 +02:00
return parent;
}
2024-04-09 23:20:26 +02:00
if (display_text.isEmpty()) display_text = QStringLiteral("Unknown");
if (sort_text.isEmpty()) sort_text = QStringLiteral("Unknown");
2018-10-14 00:08:33 +02:00
// Find a container for this level
key->group_[level] = display_text + unique_tag;
2021-07-11 02:27:26 +02:00
QStandardItem *container = nullptr;
if (containers_.contains(*key)) {
container = containers_[*key];
}
else {
2018-10-14 00:08:33 +02:00
container = new QStandardItem(display_text);
container->setData(sort_text, CollectionModel::Role_SortText);
2023-02-18 14:09:27 +01:00
container->setData(static_cast<int>(group_by_[level]), CollectionModel::Role_ContainerType);
2018-10-14 00:08:33 +02:00
if (has_artist_icon) {
container->setIcon(artist_icon_);
}
else if (has_album_icon) {
if (use_pretty_covers_) {
container->setData(no_cover_icon_, Qt::DecorationRole);
}
else {
container->setIcon(album_icon_);
}
}
parent->appendRow(container);
containers_[*key] = container;
}
// Create the container for the next level.
return BuildContainers(s, container, key, level + 1);
}
void InternetSearchModel::Clear() {
2018-10-14 00:08:33 +02:00
containers_.clear();
clear();
2018-10-14 00:08:33 +02:00
}
2020-04-13 06:30:40 +02:00
InternetSearchView::ResultList InternetSearchModel::GetChildResults(const QModelIndexList &indexes) const {
2018-10-14 00:08:33 +02:00
QList<QStandardItem*> items;
2021-06-20 19:04:08 +02:00
items.reserve(indexes.count());
2021-01-26 16:48:04 +01:00
for (const QModelIndex &idx : indexes) {
items << itemFromIndex(idx);
2018-10-14 00:08:33 +02:00
}
return GetChildResults(items);
}
2020-04-13 06:30:40 +02:00
InternetSearchView::ResultList InternetSearchModel::GetChildResults(const QList<QStandardItem*> &items) const {
2018-10-14 00:08:33 +02:00
2020-04-13 06:30:40 +02:00
InternetSearchView::ResultList results;
2018-10-14 00:08:33 +02:00
QSet<const QStandardItem*> visited;
for (QStandardItem *item : items) {
GetChildResults(item, &results, &visited);
}
return results;
}
2020-04-13 06:30:40 +02:00
void InternetSearchModel::GetChildResults(const QStandardItem *item, InternetSearchView::ResultList *results, QSet<const QStandardItem*> *visited) const {
2018-10-14 00:08:33 +02:00
if (visited->contains(item)) {
return;
}
visited->insert(item);
// Does this item have children?
2021-06-22 13:54:58 +02:00
if (item->rowCount() > 0) {
2018-10-14 00:08:33 +02:00
const QModelIndex parent_proxy_index = proxy_->mapFromSource(item->index());
2022-08-28 02:44:37 +02:00
// Yes - visit all the children, but do so through the proxy, so we get them in the right order.
2021-08-23 21:21:08 +02:00
for (int i = 0; i < item->rowCount(); ++i) {
2020-01-05 23:25:23 +01:00
const QModelIndex proxy_index = parent_proxy_index.model()->index(i, 0, parent_proxy_index);
2021-01-26 16:48:04 +01:00
const QModelIndex idx = proxy_->mapToSource(proxy_index);
GetChildResults(itemFromIndex(idx), results, visited);
2018-10-14 00:08:33 +02:00
}
}
else {
// No - maybe it's a song, add its result if valid
QVariant result = item->data(Role_Result);
if (result.isValid()) {
2020-04-13 06:30:40 +02:00
results->append(result.value<InternetSearchView::Result>());
2018-10-14 00:08:33 +02:00
}
}
}
QMimeData *InternetSearchModel::mimeData(const QModelIndexList &indexes) const {
2020-04-13 06:30:40 +02:00
return LoadTracks(GetChildResults(indexes));
2018-10-14 00:08:33 +02:00
}
namespace {
2021-06-20 23:55:02 +02:00
2020-04-13 06:30:40 +02:00
void GatherResults(const QStandardItem *parent, InternetSearchView::ResultList *results) {
2018-10-14 00:08:33 +02:00
QVariant result_variant = parent->data(InternetSearchModel::Role_Result);
2018-10-14 00:08:33 +02:00
if (result_variant.isValid()) {
2020-04-13 06:30:40 +02:00
InternetSearchView::Result result = result_variant.value<InternetSearchView::Result>();
2018-10-14 00:08:33 +02:00
(*results).append(result);
}
2021-08-23 21:21:08 +02:00
for (int i = 0; i < parent->rowCount(); ++i) {
2018-10-14 00:08:33 +02:00
GatherResults(parent->child(i), results);
}
2021-06-20 23:55:02 +02:00
2018-10-14 00:08:33 +02:00
}
2021-06-20 23:55:02 +02:00
} // namespace
2018-10-14 00:08:33 +02:00
2021-06-20 19:04:08 +02:00
void InternetSearchModel::SetGroupBy(const CollectionModel::Grouping grouping, const bool regroup_now) {
2018-10-14 00:08:33 +02:00
const CollectionModel::Grouping old_group_by = group_by_;
group_by_ = grouping;
if (regroup_now && group_by_ != old_group_by) {
// Walk the tree gathering the results we have already
2020-04-13 06:30:40 +02:00
InternetSearchView::ResultList results;
2018-10-14 00:08:33 +02:00
GatherResults(invisibleRootItem(), &results);
// Reset the model and re-add all the results using the new grouping.
Clear();
AddResults(results);
}
}
2020-04-13 06:30:40 +02:00
MimeData *InternetSearchModel::LoadTracks(const InternetSearchView::ResultList &results) const {
if (results.isEmpty()) {
return nullptr;
}
SongList songs;
QList<QUrl> urls;
2022-06-13 00:23:42 +02:00
songs.reserve(results.count());
2021-06-20 19:04:08 +02:00
urls.reserve(results.count());
2020-04-13 06:30:40 +02:00
for (const InternetSearchView::Result &result : results) {
songs.append(result.metadata_);
urls << result.metadata_.url();
2020-04-13 06:30:40 +02:00
}
InternetSongMimeData *internet_song_mime_data = new InternetSongMimeData(service_);
internet_song_mime_data->songs = songs;
MimeData *mime_data = internet_song_mime_data;
mime_data->setUrls(urls);
return mime_data;
}