Support more collections
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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_; }
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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
|
||||
26
src/radios/radiostreamplaylistitem.cpp
Normal file
26
src/radios/radiostreamplaylistitem.cpp
Normal 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) {}
|
||||
39
src/radios/radiostreamplaylistitem.h
Normal file
39
src/radios/radiostreamplaylistitem.h
Normal 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
|
||||
27
src/streaming/streamserviceplaylistitem.cpp
Normal file
27
src/streaming/streamserviceplaylistitem.cpp
Normal 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) {}
|
||||
38
src/streaming/streamserviceplaylistitem.h
Normal file
38
src/streaming/streamserviceplaylistitem.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user