Support more collections

This commit is contained in:
Jonas Kvinge
2025-03-08 22:24:28 +01:00
parent 5ae0320911
commit bdbe66b116
28 changed files with 327 additions and 341 deletions

View File

@@ -592,6 +592,8 @@ set(SOURCES
src/playlist/playlistfilter.cpp
src/playlist/playlistheader.cpp
src/playlist/playlistitem.cpp
src/playlist/songplaylistitem.cpp
src/playlist/streamplaylistitem.cpp
src/playlist/playlistitemmimedata.cpp
src/playlist/playlistlistcontainer.cpp
src/playlist/playlistlistmodel.cpp
@@ -605,7 +607,6 @@ set(SOURCES
src/playlist/playlistview.cpp
src/playlist/playlistproxystyle.cpp
src/playlist/songloaderinserter.cpp
src/playlist/songplaylistitem.cpp
src/playlist/dynamicplaylistcontrols.cpp
src/playlist/playlistundocommandbase.cpp
src/playlist/playlistundocommandinsertitems.cpp
@@ -757,7 +758,7 @@ set(SOURCES
src/streaming/streamingservices.cpp
src/streaming/streamingservice.cpp
src/streaming/streamplaylistitem.cpp
src/streaming/streamserviceplaylistitem.cpp
src/streaming/streamingsearchview.cpp
src/streaming/streamingsearchmodel.cpp
src/streaming/streamingsearchsortmodel.cpp
@@ -775,7 +776,7 @@ set(SOURCES
src/radios/radioview.cpp
src/radios/radioviewcontainer.cpp
src/radios/radioservice.cpp
src/radios/radioplaylistitem.cpp
src/radios/radiostreamplaylistitem.cpp
src/radios/radiochannel.cpp
src/radios/somafmservice.cpp
src/radios/radioparadiseservice.cpp

View File

@@ -522,7 +522,7 @@ void CollectionBackend::SongPathChanged(const Song &song, const QFileInfo &new_f
updated_song.set_url(QUrl::fromLocalFile(QDir::cleanPath(new_file.filePath())));
updated_song.set_basefilename(new_file.fileName());
updated_song.InitArtManual();
if (updated_song.is_collection_song() && new_collection_directory_id) {
if (updated_song.is_linked_collection_song() && new_collection_directory_id) {
updated_song.set_directory_id(new_collection_directory_id.value());
}

View File

@@ -1,8 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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
@@ -30,41 +28,52 @@
class SqlRow;
CollectionPlaylistItem::CollectionPlaylistItem() : PlaylistItem(Song::Source::Collection) {
song_.set_source(Song::Source::Collection);
CollectionPlaylistItem::CollectionPlaylistItem(const Song::Source source) : PlaylistItem(source) {
song_.set_source(source);
}
CollectionPlaylistItem::CollectionPlaylistItem(const Song &song) : PlaylistItem(Song::Source::Collection), song_(song) {
song_.set_source(Song::Source::Collection);
}
CollectionPlaylistItem::CollectionPlaylistItem(const Song &song) : PlaylistItem(song.source()), song_(song) {}
QUrl CollectionPlaylistItem::Url() const { return song_.url(); }
void CollectionPlaylistItem::Reload() {
const TagReaderResult result = TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_);
if (!result.success()) {
qLog(Error) << "Could not reload file" << song_.url() << result.error_string();
return;
}
UpdateTemporaryMetadata(song_);
}
bool CollectionPlaylistItem::InitFromQuery(const SqlRow &query) {
// Rows from the songs tables come first
song_.InitFromQuery(query, true);
song_.set_source(Song::Source::Collection);
int col = 0;
switch (source_) {
case Song::Source::Collection:
col = 0;
break;
default:
col = static_cast<int>(Song::kRowIdColumns.count());
break;
}
song_.InitFromQuery(query, true, col);
return song_.is_valid();
}
QVariant CollectionPlaylistItem::DatabaseValue(DatabaseColumn column) const {
void CollectionPlaylistItem::Reload() {
switch (column) {
case Column_CollectionId: return song_.id();
default: return PlaylistItem::DatabaseValue(column);
if (song_.url().isLocalFile()) {
const TagReaderResult result = TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_);
if (!result.success()) {
qLog(Error) << "Could not reload file" << song_.url() << result.error_string();
return;
}
UpdateTemporaryMetadata(song_);
}
}
QVariant CollectionPlaylistItem::DatabaseValue(const DatabaseColumn database_column) const {
switch (database_column) {
case DatabaseColumn::CollectionId:
return song_.id();
default:
return PlaylistItem::DatabaseValue(database_column);
}
}

View File

@@ -1,8 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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
@@ -34,9 +32,11 @@ class SqlRow;
class CollectionPlaylistItem : public PlaylistItem {
public:
explicit CollectionPlaylistItem();
explicit CollectionPlaylistItem(const Song::Source source);
explicit CollectionPlaylistItem(const Song &song);
QUrl Url() const override;
bool InitFromQuery(const SqlRow &query) override;
void Reload() override;
@@ -44,15 +44,13 @@ class CollectionPlaylistItem : public PlaylistItem {
Song OriginalMetadata() const override { return song_; }
void SetMetadata(const Song &song) override { song_ = song; }
QUrl Url() const override;
bool IsLocalCollectionItem() const override { return true; }
void SetArtManual(const QUrl &cover_url) override;
bool IsLocalCollectionItem() const override { return song_.source() == Song::Source::Collection; }
protected:
QVariant DatabaseValue(DatabaseColumn column) const override;
Song DatabaseSongMetadata() const override { return Song(Song::Source::Collection); }
QVariant DatabaseValue(const DatabaseColumn database_column) const override;
Song DatabaseSongMetadata() const override { return Song(source_); }
protected:
Song song_;

View File

@@ -1516,7 +1516,7 @@ void MainWindow::SongChanged(const Song &song) {
SendNowPlaying();
const bool enable_change_art = song.is_collection_song() && !song.effective_albumartist().isEmpty() && !song.album().isEmpty();
const bool enable_change_art = song.is_local_collection_song() && !song.effective_albumartist().isEmpty() && !song.album().isEmpty();
album_cover_choice_controller_->show_cover_action()->setEnabled(song.has_valid_art() && !song.art_unset());
album_cover_choice_controller_->cover_to_file_action()->setEnabled(song.has_valid_art() && !song.art_unset());
album_cover_choice_controller_->cover_from_file_action()->setEnabled(enable_change_art);
@@ -3197,7 +3197,7 @@ void MainWindow::AlbumCoverLoaded(const Song &song, const AlbumCoverLoaderResult
Q_EMIT AlbumCoverReady(song, result.album_cover.image);
const bool enable_change_art = song.is_collection_song() && !song.effective_albumartist().isEmpty() && !song.album().isEmpty();
const bool enable_change_art = song.is_local_collection_song() && !song.effective_albumartist().isEmpty() && !song.album().isEmpty();
album_cover_choice_controller_->show_cover_action()->setEnabled(result.success && result.type != AlbumCoverLoaderResult::Type::Unset);
album_cover_choice_controller_->cover_to_file_action()->setEnabled(result.success && result.type != AlbumCoverLoaderResult::Type::Unset);
album_cover_choice_controller_->cover_from_file_action()->setEnabled(enable_change_art);

View File

@@ -439,6 +439,7 @@ int Song::samplerate() const { return d->samplerate_; }
int Song::bitdepth() const { return d->bitdepth_; }
Song::Source Song::source() const { return d->source_; }
int Song::source_id() const { return static_cast<int>(d->source_); }
int Song::directory_id() const { return d->directory_id_; }
const QUrl &Song::url() const { return d->url_; }
const QString &Song::basefilename() const { return d->basefilename_; }
@@ -661,7 +662,8 @@ const QString &Song::playlist_albumartist() const { return is_compilation() ? d-
const QString &Song::playlist_albumartist_sortable() const { return is_compilation() ? d->albumartist_sortable_ : effective_albumartist_sortable(); }
bool Song::is_metadata_good() const { return !d->url_.isEmpty() && !d->artist_.isEmpty() && !d->title_.isEmpty(); }
bool Song::is_collection_song() const { return d->source_ == Source::Collection; }
bool Song::is_local_collection_song() const { return d->source_ == Source::Collection; }
bool Song::is_linked_collection_song() const { return IsLinkedCollectionSource(d->source_); }
bool Song::is_stream() const { return is_radio() || d->source_ == Source::Tidal || d->source_ == Source::Subsonic || d->source_ == Source::Qobuz || d->source_ == Source::Spotify; }
bool Song::is_radio() const { return d->source_ == Source::Stream || d->source_ == Source::SomaFM || d->source_ == Source::RadioParadise; }
bool Song::is_cdda() const { return d->source_ == Source::CDDA; }
@@ -1331,6 +1333,12 @@ Song::FileType Song::FiletypeByExtension(const QString &ext) {
}
bool Song::IsLinkedCollectionSource(const Source source) {
return source == Source::Collection;
}
QString Song::ImageCacheDir(const Source source) {
switch (source) {
@@ -1831,8 +1839,8 @@ bool Song::MergeFromEngineMetadata(const EngineMetadata &engine_metadata) {
bool minor = true;
if (d->init_from_file_ || is_collection_song() || d->url_.isLocalFile()) {
// This Song was already loaded using taglib. Our tags are probably better than the engine's.
if (d->init_from_file_ || is_local_collection_song() || d->url_.isLocalFile()) {
// This Song was already loaded using TagLib. Our tags are probably better than the engine's.
if (title() != engine_metadata.title && title().isEmpty() && !engine_metadata.title.isEmpty()) {
set_title(engine_metadata.title);
minor = false;

View File

@@ -78,6 +78,7 @@ class Song {
RadioParadise = 10,
Spotify = 11
};
static const int kSourceCount = 16;
enum class FileType {
Unknown = 0,
@@ -176,6 +177,7 @@ class Song {
int bitdepth() const;
Source source() const;
int source_id() const;
int directory_id() const;
const QUrl &url() const;
const QString &basefilename() const;
@@ -372,7 +374,8 @@ class Song {
const QString &playlist_albumartist_sortable() const;
bool is_metadata_good() const;
bool is_collection_song() const;
bool is_local_collection_song() const;
bool is_linked_collection_song() const;
bool is_stream() const;
bool is_radio() const;
bool is_cdda() const;
@@ -459,6 +462,7 @@ class Song {
static FileType FiletypeByMimetype(const QString &mimetype);
static FileType FiletypeByDescription(const QString &text);
static FileType FiletypeByExtension(const QString &ext);
static bool IsLinkedCollectionSource(const Source source);
static QString ImageCacheDir(const Source source);
// Sort songs alphabetically using their pretty title

View File

@@ -716,7 +716,7 @@ void EditTagDialog::SelectionChanged() {
}
ui_->tags_summary->setText(summary);
const bool enable_change_art = first_song.is_collection_song();
const bool enable_change_art = first_song.is_local_collection_song();
ui_->tags_art_button->setEnabled(enable_change_art);
if ((art_different && first_cover_action != UpdateCoverAction::New) || action_different) {
tags_cover_art_id_ = -1; // Cancels any pending art load.
@@ -878,7 +878,7 @@ QString EditTagDialog::GetArtSummary(const Song &song, const AlbumCoverLoaderRes
summary = tr("Cover art not set").toHtmlEscaped();
}
if (!song.is_collection_song()) {
if (!song.is_local_collection_song()) {
if (!summary.isEmpty()) summary += "<br />"_L1;
summary = tr("Album cover editing is only available for collection songs.");
}
@@ -962,7 +962,7 @@ void EditTagDialog::AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderRes
summary += GetArtSummary(cover_action);
}
ui_->tags_summary->setText(summary);
enable_change_art = first_song.is_collection_song() && !first_song.effective_albumartist().isEmpty() && !first_song.album().isEmpty();
enable_change_art = first_song.is_local_collection_song() && !first_song.effective_albumartist().isEmpty() && !first_song.album().isEmpty();
}
tags_cover_art_id_ = -1;
album_cover_choice_controller_->show_cover_action()->setEnabled(result.success && result.type != AlbumCoverLoaderResult::Type::Unset);
@@ -1321,7 +1321,7 @@ void EditTagDialog::SaveData() {
}
// If the cover was changed, but no tags written, make sure to update the collection.
else if (ref.cover_action_ != UpdateCoverAction::None && !ref.current_.effective_albumartist().isEmpty() && !ref.current_.album().isEmpty()) {
if (ref.current_.is_collection_song()) {
if (ref.current_.is_local_collection_song()) {
collection_songs_.insert(ref.current_.id(), ref.current_);
}
if (ref.current_ == current_albumcover_loader_->last_song()) {
@@ -1476,7 +1476,7 @@ void EditTagDialog::SongSaveTagsComplete(TagReaderReplyPtr reply, const QString
const QString error = reply->error();
if (success) {
if (song.is_collection_song()) {
if (song.is_local_collection_song()) {
if (collection_songs_.contains(song.id())) {
Song old_song = collection_songs_.take(song.id());
song.set_art_automatic(old_song.art_automatic());

View File

@@ -262,7 +262,7 @@ void Organize::ProcessSomeFiles() {
QString error_text;
if (destination_->CopyToStorage(job, error_text)) {
if (job.remove_original_ && song.is_collection_song() && destination_->source() == Song::Source::Collection) {
if (job.remove_original_ && song.is_local_collection_song() && destination_->source() == Song::Source::Collection) {
// Notify other aspects of system that song has been invalidated
QString root = destination_->LocalPath();
QFileInfo new_file = QFileInfo(root + QLatin1Char('/') + task.song_info_.new_filename_);

View File

@@ -92,12 +92,12 @@
#include "smartplaylists/playlistgeneratorinserter.h"
#include "smartplaylists/playlistgeneratormimedata.h"
#include "streaming/streamplaylistitem.h"
#include "streaming/streamserviceplaylistitem.h"
#include "streaming/streamsongmimedata.h"
#include "streaming/streamingservice.h"
#include "radios/radiomimedata.h"
#include "radios/radioplaylistitem.h"
#include "radios/radiostreamplaylistitem.h"
using std::make_shared;
using namespace std::chrono_literals;
@@ -186,7 +186,7 @@ Playlist::Playlist(const SharedPtr<TaskManager> task_manager,
Playlist::~Playlist() {
items_.clear();
collection_items_by_id_.clear();
ClearCollectionItems();
}
template<typename T>
@@ -828,25 +828,24 @@ bool Playlist::dropMimeData(const QMimeData *data, Qt::DropAction action, const
if (const SongMimeData *song_data = qobject_cast<const SongMimeData*>(data)) {
// Dragged from a collection
// We want to check if these songs are from the actual local file backend, if they are we treat them differently.
if (song_data->backend && song_data->backend->songs_table() == QLatin1String(CollectionLibrary::kSongsTable)) {
if (song_data->backend && Song::IsLinkedCollectionSource(song_data->backend->source())) {
InsertSongItems<CollectionPlaylistItem>(song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
}
else {
InsertSongItems<SongPlaylistItem>(song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
}
}
else if (const PlaylistItemMimeData *item_data = qobject_cast<const PlaylistItemMimeData*>(data)) {
InsertItems(item_data->items_, row, play_now, enqueue_now, enqueue_next_now);
else if (const PlaylistItemMimeData *item_mimedata = qobject_cast<const PlaylistItemMimeData*>(data)) {
InsertItems(item_mimedata->items_, row, play_now, enqueue_now, enqueue_next_now);
}
else if (const PlaylistGeneratorMimeData *generator_data = qobject_cast<const PlaylistGeneratorMimeData*>(data)) {
InsertSmartPlaylist(generator_data->generator_, row, play_now, enqueue_now, enqueue_next_now);
else if (const PlaylistGeneratorMimeData *generator_mimedata = qobject_cast<const PlaylistGeneratorMimeData*>(data)) {
InsertSmartPlaylist(generator_mimedata->generator_, row, play_now, enqueue_now, enqueue_next_now);
}
else if (const StreamSongMimeData *stream_song_data = qobject_cast<const StreamSongMimeData*>(data)) {
InsertStreamingItems(stream_song_data->service, stream_song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
else if (const StreamSongMimeData *stream_song_mimedata = qobject_cast<const StreamSongMimeData*>(data)) {
InsertStreamingItems(stream_song_mimedata->service, stream_song_mimedata->songs, row, play_now, enqueue_now, enqueue_next_now);
}
else if (const RadioMimeData *radio_data = qobject_cast<const RadioMimeData*>(data)) {
InsertRadioItems(radio_data->songs, row, play_now, enqueue_now, enqueue_next_now);
else if (const RadioMimeData *radio_mimedata = qobject_cast<const RadioMimeData*>(data)) {
InsertRadioItems(radio_mimedata->songs, row, play_now, enqueue_now, enqueue_next_now);
}
else if (data->hasFormat(QLatin1String(kRowsMimetype))) {
// Dragged from the playlist
@@ -1131,10 +1130,10 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemPtrList &items, const in
items_.insert(i, item);
virtual_items_ << static_cast<int>(virtual_items_.count());
if (item->source() == Song::Source::Collection) {
int id = item->Metadata().id();
if (Song::IsLinkedCollectionSource(item->source())) {
const int id = item->Metadata().id();
if (id != -1) {
collection_items_by_id_.insert(id, item);
collection_items_[item->Metadata().source_id()].insert(id, item);
}
}
@@ -1187,23 +1186,9 @@ void Playlist::InsertSongsOrCollectionItems(const SongList &songs, const QString
}
PlaylistItemPtrList items;
items.reserve(songs.count());
for (const Song &song : songs) {
if (song.url().isLocalFile()) {
if (song.is_collection_song()) {
items << make_shared<CollectionPlaylistItem>(song);
}
else {
items << make_shared<SongPlaylistItem>(song);
}
}
else {
if (song.is_radio()) {
items << make_shared<RadioPlaylistItem>(song);
}
else {
items << make_shared<StreamPlaylistItem>(song);
}
}
items << PlaylistItem::NewFromSong(song);
}
InsertItems(items, pos, play_now, enqueue, enqueue_next);
@@ -1215,7 +1200,7 @@ void Playlist::InsertStreamingItems(StreamingServicePtr service, const SongList
PlaylistItemPtrList playlist_items;
playlist_items.reserve(songs.count());
for (const Song &song : songs) {
playlist_items << make_shared<StreamPlaylistItem>(service, song);
playlist_items << make_shared<StreamServicePlaylistItem>(service, song);
}
InsertItems(playlist_items, pos, play_now, enqueue, enqueue_next);
@@ -1227,7 +1212,7 @@ void Playlist::InsertRadioItems(const SongList &songs, const int pos, const bool
PlaylistItemPtrList playlist_items;
playlist_items.reserve(songs.count());
for (const Song &song : songs) {
playlist_items << make_shared<RadioPlaylistItem>(song);
playlist_items << make_shared<RadioStreamPlaylistItem>(song);
}
InsertItems(playlist_items, pos, play_now, enqueue, enqueue_next);
@@ -1252,22 +1237,22 @@ void Playlist::UpdateItems(SongList songs) {
const PlaylistItemPtr item = items_.value(i);
if (item->Metadata().url() == song.url() && (item->Metadata().filetype() == Song::FileType::Unknown || item->Metadata().filetype() == Song::FileType::Stream || item->Metadata().filetype() == Song::FileType::CDDA || !item->Metadata().init_from_file())) {
PlaylistItemPtr new_item;
if (song.url().isLocalFile()) {
if (song.is_collection_song()) {
new_item = make_shared<CollectionPlaylistItem>(song);
if (collection_items_by_id_.contains(song.id(), item)) collection_items_by_id_.remove(song.id(), item);
collection_items_by_id_.insert(song.id(), new_item);
}
else {
new_item = make_shared<SongPlaylistItem>(song);
}
if (song.is_linked_collection_song()) {
new_item = make_shared<CollectionPlaylistItem>(song);
if (collection_items_[song.source_id()].contains(song.id(), item)) collection_items_[song.source_id()].remove(song.id(), item);
collection_items_[song.source_id()].insert(song.id(), new_item);
}
else {
if (song.is_radio()) {
new_item = make_shared<RadioPlaylistItem>(song);
if (song.url().isLocalFile()) {
new_item = make_shared<SongPlaylistItem>(song);
}
else {
new_item = make_shared<StreamPlaylistItem>(song);
if (song.is_radio()) {
new_item = make_shared<RadioStreamPlaylistItem>(song);
}
else {
new_item = make_shared<StreamServicePlaylistItem>(song);
}
}
}
items_[i] = new_item;
@@ -1580,7 +1565,7 @@ void Playlist::Restore() {
items_.clear();
virtual_items_.clear();
collection_items_by_id_.clear();
ClearCollectionItems();
cancel_restore_ = false;
QFuture<PlaylistItemPtrList> future = QtConcurrent::run(&PlaylistBackend::GetPlaylistItems, playlist_backend_, id_);
@@ -1590,6 +1575,15 @@ void Playlist::Restore() {
}
void Playlist::ClearCollectionItems() {
constexpr int collection_items_size = static_cast<int>(sizeof(collection_items_)) / sizeof(collection_items_[0]);
for (int i = 0; i < collection_items_size; ++i) {
collection_items_[i].clear();
}
}
void Playlist::ItemsLoaded() {
QFutureWatcher<PlaylistItemPtrList> *watcher = static_cast<QFutureWatcher<PlaylistItemPtrList>*>(sender());
@@ -1735,12 +1729,10 @@ PlaylistItemPtrList Playlist::RemoveItemsWithoutUndo(const int row, const int co
for (int i = 0; i < count; ++i) {
PlaylistItemPtr item(items_.takeAt(row));
items << item;
if (item->source() == Song::Source::Collection) {
int id = item->Metadata().id();
if (id != -1 && collection_items_by_id_.contains(id, item)) {
collection_items_by_id_.remove(id, item);
}
const int id = item->Metadata().id();
const int source_id = item->Metadata().source_id();
if (id != -1 && collection_items_[source_id].contains(id, item)) {
collection_items_[source_id].remove(id, item);
}
}
@@ -2069,8 +2061,8 @@ quint64 Playlist::GetTotalLength() const {
}
PlaylistItemPtrList Playlist::collection_items_by_id(const int id) const {
return collection_items_by_id_.values(id);
PlaylistItemPtrList Playlist::collection_items(const Song::Source source, const int song_id) const {
return collection_items_[static_cast<int>(source)].values(song_id);
}
void Playlist::TracksAboutToBeDequeued(const QModelIndex &idx, const int begin, const int end) {

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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
@@ -203,7 +203,7 @@ class Playlist : public QAbstractListModel {
PlaylistItem::Options current_item_options() const;
Song current_item_metadata() const;
PlaylistItemPtrList collection_items_by_id(const int id) const;
PlaylistItemPtrList collection_items(const Song::Source source, const int song_id) const;
SongList GetAllSongs() const;
PlaylistItemPtrList GetAllItems() const;
@@ -357,6 +357,8 @@ class Playlist : public QAbstractListModel {
// Grays out and reloads all deleted songs in all playlists. Also, "ungreys" those songs which were once deleted but now got restored somehow.
void InvalidateDeletedSongs();
void ClearCollectionItems();
private Q_SLOTS:
void TracksAboutToBeDequeued(const QModelIndex&, const int begin, const int end);
void TracksDequeued();
@@ -393,8 +395,7 @@ class Playlist : public QAbstractListModel {
QList<QPersistentModelIndex> played_indexes_;
// A map of collection ID to playlist item - for fast lookups when collection items change.
QMultiMap<int, PlaylistItemPtr> collection_items_by_id_;
QMultiMap<int, PlaylistItemPtr> collection_items_[Song::kSourceCount];
QPersistentModelIndex current_item_index_;
QPersistentModelIndex last_played_item_index_;

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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
@@ -30,30 +30,31 @@
#include "core/sqlquery.h"
#include "core/song.h"
#include "collection/collectionplaylistitem.h"
#include "playlistitem.h"
#include "songplaylistitem.h"
#include "streaming/streamplaylistitem.h"
#include "radios/radioplaylistitem.h"
#include "collection/collectionplaylistitem.h"
#include "streaming/streamserviceplaylistitem.h"
#include "radios/radiostreamplaylistitem.h"
using std::make_shared;
using namespace Qt::Literals::StringLiterals;
PlaylistItem::PlaylistItem(const Song::Source source) : should_skip_(false), source_(source) {}
PlaylistItemPtr PlaylistItem::NewFromSource(const Song::Source source) {
switch (source) {
case Song::Source::Collection:
return make_shared<CollectionPlaylistItem>();
return make_shared<CollectionPlaylistItem>(source);
case Song::Source::Subsonic:
case Song::Source::Tidal:
case Song::Source::Spotify:
case Song::Source::Qobuz:
return make_shared<StreamPlaylistItem>(source);
return make_shared<StreamServicePlaylistItem>(source);
case Song::Source::Stream:
case Song::Source::RadioParadise:
case Song::Source::SomaFM:
return make_shared<RadioPlaylistItem>(source);
return make_shared<RadioStreamPlaylistItem>(source);
case Song::Source::LocalFile:
case Song::Source::CDDA:
case Song::Source::Device:
@@ -74,11 +75,11 @@ PlaylistItemPtr PlaylistItem::NewFromSong(const Song &song) {
case Song::Source::Tidal:
case Song::Source::Spotify:
case Song::Source::Qobuz:
return make_shared<StreamPlaylistItem>(song);
return make_shared<StreamServicePlaylistItem>(song);
case Song::Source::Stream:
case Song::Source::RadioParadise:
case Song::Source::SomaFM:
return make_shared<RadioPlaylistItem>(song);
return make_shared<RadioStreamPlaylistItem>(song);
case Song::Source::LocalFile:
case Song::Source::CDDA:
case Song::Source::Device:
@@ -90,12 +91,10 @@ PlaylistItemPtr PlaylistItem::NewFromSong(const Song &song) {
}
PlaylistItem::~PlaylistItem() = default;
void PlaylistItem::BindToQuery(SqlQuery *query) const {
query->BindValue(u":type"_s, static_cast<int>(source_));
query->BindValue(u":collection_id"_s, DatabaseValue(Column_CollectionId));
query->BindValue(u":collection_id"_s, DatabaseValue(DatabaseColumn::CollectionId));
DatabaseSongMetadata().BindToQuery(query);
@@ -174,5 +173,5 @@ QColor PlaylistItem::GetCurrentForegroundColor() const {
bool PlaylistItem::HasCurrentForegroundColor() const {
return !foreground_colors_.isEmpty();
}
void PlaylistItem::SetShouldSkip(const bool val) { should_skip_ = val; }
void PlaylistItem::SetShouldSkip(const bool should_skip) { should_skip_ = should_skip; }
bool PlaylistItem::GetShouldSkip() const { return should_skip_; }

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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
@@ -48,8 +48,7 @@ using std::enable_shared_from_this;
class PlaylistItem : public enable_shared_from_this<PlaylistItem> {
public:
explicit PlaylistItem(const Song::Source source) : should_skip_(false), source_(source) {}
virtual ~PlaylistItem();
explicit PlaylistItem(const Song::Source source);
static SharedPtr<PlaylistItem> NewFromSource(const Song::Source source);
static SharedPtr<PlaylistItem> NewFromSong(const Song &song);
@@ -80,7 +79,7 @@ class PlaylistItem : public enable_shared_from_this<PlaylistItem> {
virtual Song OriginalMetadata() const = 0;
virtual QUrl Url() const = 0;
virtual void SetMetadata(const Song&) {}
virtual void SetMetadata(const Song &song) { Q_UNUSED(song); }
void SetTemporaryMetadata(const Song &metadata);
void UpdateTemporaryMetadata(const Song &metadata);
@@ -114,21 +113,20 @@ class PlaylistItem : public enable_shared_from_this<PlaylistItem> {
// Convenience function to find out whether this item is from the local collection, as opposed to a device, a file on disk, or a stream.
// Remember that even if this returns true, the collection item might be invalid, so you might want to check that its id is not equal to -1 before actually using it.
virtual bool IsLocalCollectionItem() const { return false; }
void SetShouldSkip(const bool val);
void SetShouldSkip(const bool should_skip);
bool GetShouldSkip() const;
protected:
bool should_skip_;
enum DatabaseColumn { Column_CollectionId };
enum class DatabaseColumn {
CollectionId
};
virtual QVariant DatabaseValue(DatabaseColumn) const {
return QVariant(QString());
}
virtual QVariant DatabaseValue(const DatabaseColumn database_column) const { Q_UNUSED(database_column); return QVariant(QString()); }
virtual Song DatabaseSongMetadata() const { return Song(); }
Song::Source source_;
Song temp_metadata_;
QMap<short, QColor> background_colors_;

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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
@@ -476,7 +476,7 @@ void PlaylistManager::UpdateCollectionSongs(const SongList &songs) {
for (const Song &song : songs) {
for (const Data &data : std::as_const(playlists_)) {
const PlaylistItemPtrList items = data.p->collection_items_by_id(song.id());
const PlaylistItemPtrList items = data.p->collection_items(song.source(), song.id());
for (PlaylistItemPtr item : items) {
if (item->Metadata().directory_id() != song.directory_id()) continue;
data.p->UpdateItemMetadata(item, song, false);

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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

View File

@@ -1,8 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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
@@ -25,60 +23,52 @@
#include <QVariant>
#include "streamplaylistitem.h"
#include "streamingservice.h"
#include "core/sqlrow.h"
StreamPlaylistItem::StreamPlaylistItem(const Song::Source source)
: PlaylistItem(source),
source_(source) {}
StreamPlaylistItem::StreamPlaylistItem(const Song &metadata)
: PlaylistItem(metadata.source()),
source_(metadata.source()),
metadata_(metadata) {
InitMetadata();
}
StreamPlaylistItem::StreamPlaylistItem(StreamingServicePtr service, const Song &metadata)
: PlaylistItem(metadata.source()),
source_(service->source()),
metadata_(metadata) {
StreamPlaylistItem::StreamPlaylistItem(const Song &song)
: PlaylistItem(song.source()),
source_(song.source()),
song_(song) {
InitMetadata();
}
bool StreamPlaylistItem::InitFromQuery(const SqlRow &query) {
metadata_.InitFromQuery(query, false, static_cast<int>(Song::kRowIdColumns.count()));
song_.InitFromQuery(query, false, static_cast<int>(Song::kRowIdColumns.count()));
InitMetadata();
return true;
}
QVariant StreamPlaylistItem::DatabaseValue(DatabaseColumn column) const {
QVariant StreamPlaylistItem::DatabaseValue(const DatabaseColumn column) const {
return PlaylistItem::DatabaseValue(column);
}
void StreamPlaylistItem::InitMetadata() {
if (metadata_.title().isEmpty()) metadata_.set_title(metadata_.url().toString());
if (metadata_.source() == Song::Source::Unknown) metadata_.set_source(Song::Source::Stream);
if (metadata_.filetype() == Song::FileType::Unknown) metadata_.set_filetype(Song::FileType::Stream);
metadata_.set_valid(true);
if (song_.title().isEmpty()) song_.set_title(song_.url().toString());
if (song_.source() == Song::Source::Unknown) song_.set_source(Song::Source::Stream);
if (song_.filetype() == Song::FileType::Unknown) song_.set_filetype(Song::FileType::Stream);
song_.set_valid(true);
}
Song StreamPlaylistItem::Metadata() const {
if (HasTemporaryMetadata()) return temp_metadata_;
return metadata_;
return song_;
}
QUrl StreamPlaylistItem::Url() const { return metadata_.url(); }
QUrl StreamPlaylistItem::Url() const { return song_.url(); }
void StreamPlaylistItem::SetArtManual(const QUrl &cover_url) {
metadata_.set_art_manual(cover_url);
song_.set_art_manual(cover_url);
temp_metadata_.set_art_manual(cover_url);
}

View File

@@ -1,8 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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
@@ -22,43 +20,37 @@
#ifndef STREAMPLAYLISTITEM_H
#define STREAMPLAYLISTITEM_H
#include "config.h"
#include <QVariant>
#include <QUrl>
#include "includes/shared_ptr.h"
#include "core/song.h"
#include "core/sqlrow.h"
#include "playlist/playlistitem.h"
class StreamingService;
class StreamPlaylistItem : public PlaylistItem {
public:
explicit StreamPlaylistItem(const Song::Source source);
explicit StreamPlaylistItem(const Song &metadata);
explicit StreamPlaylistItem(SharedPtr<StreamingService> service, const Song &metadata);
explicit StreamPlaylistItem(const Song &song);
bool InitFromQuery(const SqlRow &query) override;
Song Metadata() const override;
Song OriginalMetadata() const override { return metadata_; }
Song OriginalMetadata() const override { return song_; }
QUrl Url() const override;
void SetMetadata(const Song &metadata) override { metadata_ = metadata; }
void SetMetadata(const Song &song) override { song_ = song; }
void SetArtManual(const QUrl &cover_url) override;
protected:
QVariant DatabaseValue(DatabaseColumn) const override;
Song DatabaseSongMetadata() const override { return metadata_; }
QVariant DatabaseValue(const DatabaseColumn column) const override;
Song DatabaseSongMetadata() const override { return song_; }
private:
void InitMetadata();
private:
Song::Source source_;
Song metadata_;
Song song_;
Q_DISABLE_COPY(StreamPlaylistItem)
};

View File

@@ -1,74 +0,0 @@
/*
* Strawberry Music Player
* Copyright 2021, 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 <QApplication>
#include <QVariant>
#include <QUrl>
#include "radioplaylistitem.h"
#include "core/sqlrow.h"
RadioPlaylistItem::RadioPlaylistItem(const Song::Source source)
: PlaylistItem(source), source_(source) {}
RadioPlaylistItem::RadioPlaylistItem(const Song &metadata)
: PlaylistItem(metadata.source()),
source_(metadata.source()),
metadata_(metadata) {
InitMetadata();
}
bool RadioPlaylistItem::InitFromQuery(const SqlRow &query) {
metadata_.InitFromQuery(query, false, static_cast<int>(Song::kRowIdColumns.count()));
InitMetadata();
return true;
}
QVariant RadioPlaylistItem::DatabaseValue(DatabaseColumn column) const {
return PlaylistItem::DatabaseValue(column);
}
void RadioPlaylistItem::InitMetadata() {
if (metadata_.title().isEmpty()) metadata_.set_title(metadata_.url().toString());
if (metadata_.source() == Song::Source::Unknown) metadata_.set_source(Song::Source::Stream);
if (metadata_.filetype() == Song::FileType::Unknown) metadata_.set_filetype(Song::FileType::Stream);
metadata_.set_valid(true);
}
Song RadioPlaylistItem::Metadata() const {
if (HasTemporaryMetadata()) return temp_metadata_;
return metadata_;
}
QUrl RadioPlaylistItem::Url() const { return metadata_.url(); }
void RadioPlaylistItem::SetArtManual(const QUrl &cover_url) {
metadata_.set_art_manual(cover_url);
temp_metadata_.set_art_manual(cover_url);
}

View File

@@ -1,62 +0,0 @@
/*
* Strawberry Music Player
* Copyright 2021, 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 RADIOPLAYLISTITEM_H
#define RADIOPLAYLISTITEM_H
#include "config.h"
#include <QVariant>
#include <QUrl>
#include "core/song.h"
#include "core/sqlrow.h"
#include "playlist/playlistitem.h"
class RadioService;
class RadioPlaylistItem : public PlaylistItem {
public:
explicit RadioPlaylistItem(const Song::Source source);
explicit RadioPlaylistItem(const Song &metadata);
bool InitFromQuery(const SqlRow &query) override;
Song Metadata() const override;
Song OriginalMetadata() const override { return metadata_; }
QUrl Url() const override;
void SetMetadata(const Song &metadata) override { metadata_ = metadata; }
void SetArtManual(const QUrl &cover_url) override;
protected:
QVariant DatabaseValue(DatabaseColumn) const override;
Song DatabaseSongMetadata() const override { return metadata_; }
private:
void InitMetadata();
private:
Song::Source source_;
Song metadata_;
Q_DISABLE_COPY(RadioPlaylistItem)
};
#endif // RADIOPLAYLISTITEM_H

View File

@@ -0,0 +1,26 @@
/*
* Strawberry Music Player
* Copyright 2021-2025, 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 "radiostreamplaylistitem.h"
RadioStreamPlaylistItem::RadioStreamPlaylistItem(const Song &song)
: StreamPlaylistItem(song) {}
RadioStreamPlaylistItem::RadioStreamPlaylistItem(const SharedPtr<RadioService> service, const Song &song)
: StreamPlaylistItem(song), service_(service) {}

View File

@@ -0,0 +1,39 @@
/*
* Strawberry Music Player
* Copyright 2021-2025, 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 RADIOSTREAMPLAYLISTITEM_H
#define RADIOSTREAMPLAYLISTITEM_H
#include "includes/shared_ptr.h"
#include "core/song.h"
#include "playlist/streamplaylistitem.h"
class RadioService;
class RadioStreamPlaylistItem : public StreamPlaylistItem {
public:
explicit RadioStreamPlaylistItem(const Song &song);
explicit RadioStreamPlaylistItem(const SharedPtr<RadioService> service, const Song &song);
Q_DISABLE_COPY(RadioStreamPlaylistItem)
private:
const SharedPtr<RadioService> service_;
};
#endif // RADIOSTREAMPLAYLISTITEM_H

View File

@@ -0,0 +1,27 @@
/*
* Strawberry Music Player
* Copyright 2018-2025, 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 "streamserviceplaylistitem.h"
#include "streamingservice.h"
StreamServicePlaylistItem::StreamServicePlaylistItem(const Song &song)
: StreamPlaylistItem(song) {}
StreamServicePlaylistItem::StreamServicePlaylistItem(const StreamingServicePtr service, const Song &song)
: StreamPlaylistItem(song), service_(service) {}

View File

@@ -0,0 +1,38 @@
/*
* Strawberry Music Player
* Copyright 2018-2025, 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 STREAMSERVICEPLAYLISTITEM_H
#define STREAMSERVICEPLAYLISTITEM_H
#include "includes/shared_ptr.h"
#include "core/song.h"
#include "playlist/streamplaylistitem.h"
class StreamingService;
class StreamServicePlaylistItem : public StreamPlaylistItem {
public:
explicit StreamServicePlaylistItem(const Song &song);
explicit StreamServicePlaylistItem(const SharedPtr<StreamingService> service, const Song &song);
Q_DISABLE_COPY(StreamServicePlaylistItem)
private:
SharedPtr<StreamingService> service_;
};
#endif // STREAMPLAYLISTITEM_H

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2011, David Sansome <me@davidsansome.com>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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
@@ -22,6 +22,6 @@
#include "includes/shared_ptr.h"
#include "streamsongmimedata.h"
StreamSongMimeData::StreamSongMimeData(SharedPtr<StreamingService> _service, QObject *parent) : service(_service) {
StreamSongMimeData::StreamSongMimeData(const SharedPtr<StreamingService> _service, QObject *parent) : service(_service) {
Q_UNUSED(parent);
}

View File

@@ -2,7 +2,7 @@
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2011, David Sansome <me@davidsansome.com>
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2018-2025, 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
@@ -32,9 +32,9 @@ class StreamSongMimeData : public MimeData {
Q_OBJECT
public:
explicit StreamSongMimeData(SharedPtr<StreamingService> _service, QObject *parent = nullptr);
explicit StreamSongMimeData(const SharedPtr<StreamingService> _service, QObject *parent = nullptr);
SharedPtr<StreamingService> service;
const SharedPtr<StreamingService> service;
SongList songs;
};

View File

@@ -476,22 +476,22 @@ TEST_F(PlaylistTest, ShuffleThenNext) {
TEST_F(PlaylistTest, CollectionIdMapSingle) {
Song song;
Song song(Song::Source::Collection);
song.Init(u"title"_s, u"artist"_s, u"album"_s, 123);
song.set_id(1);
PlaylistItemPtr item(std::make_shared<CollectionPlaylistItem>(song));
playlist_.InsertItems(PlaylistItemPtrList() << item);
EXPECT_EQ(0, playlist_.collection_items_by_id(-1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(0).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
ASSERT_EQ(1, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(song.title(), playlist_.collection_items_by_id(1)[0]->Metadata().title()); // clazy:exclude=detaching-temporary
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, -1).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 0).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 2).count());
ASSERT_EQ(1, playlist_.collection_items(Song::Source::Collection, 1).count());
EXPECT_EQ(song.title(), playlist_.collection_items(Song::Source::Collection, 1)[0]->Metadata().title()); // clazy:exclude=detaching-temporary
playlist_.Clear();
EXPECT_EQ(0, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 1).count());
}
@@ -504,20 +504,20 @@ TEST_F(PlaylistTest, CollectionIdMapInvalid) {
PlaylistItemPtr item(std::make_shared<CollectionPlaylistItem>(invalid));
playlist_.InsertItems(PlaylistItemPtrList() << item);
EXPECT_EQ(0, playlist_.collection_items_by_id(-1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(0).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, -1).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 0).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 1).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 2).count());
}
TEST_F(PlaylistTest, CollectionIdMapMulti) {
Song one;
Song one(Song::Source::Collection);
one.Init(u"title"_s, u"artist"_s, u"album"_s, 123);
one.set_id(1);
Song two;
Song two(Song::Source::Collection);
two.Init(u"title 2"_s, u"artist 2"_s, u"album 2"_s, 123);
two.set_id(2);
@@ -526,20 +526,20 @@ TEST_F(PlaylistTest, CollectionIdMapMulti) {
PlaylistItemPtr item_three(std::make_shared<CollectionPlaylistItem>(one));
playlist_.InsertItems(PlaylistItemPtrList() << item_one << item_two << item_three);
EXPECT_EQ(2, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(1, playlist_.collection_items_by_id(2).count());
EXPECT_EQ(2, playlist_.collection_items(Song::Source::Collection, 1).count());
EXPECT_EQ(1, playlist_.collection_items(Song::Source::Collection, 2).count());
playlist_.removeRow(1); // item_two
EXPECT_EQ(2, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
EXPECT_EQ(2, playlist_.collection_items(Song::Source::Collection, 1).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 2).count());
playlist_.removeRow(1); // item_three
EXPECT_EQ(1, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
EXPECT_EQ(1, playlist_.collection_items(Song::Source::Collection, 1).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 2).count());
playlist_.removeRow(0); // item_one
EXPECT_EQ(0, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 1).count());
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 2).count());
}