Support more collections
This commit is contained in:
@@ -592,6 +592,8 @@ set(SOURCES
|
|||||||
src/playlist/playlistfilter.cpp
|
src/playlist/playlistfilter.cpp
|
||||||
src/playlist/playlistheader.cpp
|
src/playlist/playlistheader.cpp
|
||||||
src/playlist/playlistitem.cpp
|
src/playlist/playlistitem.cpp
|
||||||
|
src/playlist/songplaylistitem.cpp
|
||||||
|
src/playlist/streamplaylistitem.cpp
|
||||||
src/playlist/playlistitemmimedata.cpp
|
src/playlist/playlistitemmimedata.cpp
|
||||||
src/playlist/playlistlistcontainer.cpp
|
src/playlist/playlistlistcontainer.cpp
|
||||||
src/playlist/playlistlistmodel.cpp
|
src/playlist/playlistlistmodel.cpp
|
||||||
@@ -605,7 +607,6 @@ set(SOURCES
|
|||||||
src/playlist/playlistview.cpp
|
src/playlist/playlistview.cpp
|
||||||
src/playlist/playlistproxystyle.cpp
|
src/playlist/playlistproxystyle.cpp
|
||||||
src/playlist/songloaderinserter.cpp
|
src/playlist/songloaderinserter.cpp
|
||||||
src/playlist/songplaylistitem.cpp
|
|
||||||
src/playlist/dynamicplaylistcontrols.cpp
|
src/playlist/dynamicplaylistcontrols.cpp
|
||||||
src/playlist/playlistundocommandbase.cpp
|
src/playlist/playlistundocommandbase.cpp
|
||||||
src/playlist/playlistundocommandinsertitems.cpp
|
src/playlist/playlistundocommandinsertitems.cpp
|
||||||
@@ -757,7 +758,7 @@ set(SOURCES
|
|||||||
|
|
||||||
src/streaming/streamingservices.cpp
|
src/streaming/streamingservices.cpp
|
||||||
src/streaming/streamingservice.cpp
|
src/streaming/streamingservice.cpp
|
||||||
src/streaming/streamplaylistitem.cpp
|
src/streaming/streamserviceplaylistitem.cpp
|
||||||
src/streaming/streamingsearchview.cpp
|
src/streaming/streamingsearchview.cpp
|
||||||
src/streaming/streamingsearchmodel.cpp
|
src/streaming/streamingsearchmodel.cpp
|
||||||
src/streaming/streamingsearchsortmodel.cpp
|
src/streaming/streamingsearchsortmodel.cpp
|
||||||
@@ -775,7 +776,7 @@ set(SOURCES
|
|||||||
src/radios/radioview.cpp
|
src/radios/radioview.cpp
|
||||||
src/radios/radioviewcontainer.cpp
|
src/radios/radioviewcontainer.cpp
|
||||||
src/radios/radioservice.cpp
|
src/radios/radioservice.cpp
|
||||||
src/radios/radioplaylistitem.cpp
|
src/radios/radiostreamplaylistitem.cpp
|
||||||
src/radios/radiochannel.cpp
|
src/radios/radiochannel.cpp
|
||||||
src/radios/somafmservice.cpp
|
src/radios/somafmservice.cpp
|
||||||
src/radios/radioparadiseservice.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_url(QUrl::fromLocalFile(QDir::cleanPath(new_file.filePath())));
|
||||||
updated_song.set_basefilename(new_file.fileName());
|
updated_song.set_basefilename(new_file.fileName());
|
||||||
updated_song.InitArtManual();
|
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());
|
updated_song.set_directory_id(new_collection_directory_id.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
|
||||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -30,41 +28,52 @@
|
|||||||
|
|
||||||
class SqlRow;
|
class SqlRow;
|
||||||
|
|
||||||
CollectionPlaylistItem::CollectionPlaylistItem() : PlaylistItem(Song::Source::Collection) {
|
CollectionPlaylistItem::CollectionPlaylistItem(const Song::Source source) : PlaylistItem(source) {
|
||||||
song_.set_source(Song::Source::Collection);
|
song_.set_source(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectionPlaylistItem::CollectionPlaylistItem(const Song &song) : PlaylistItem(Song::Source::Collection), song_(song) {
|
CollectionPlaylistItem::CollectionPlaylistItem(const Song &song) : PlaylistItem(song.source()), song_(song) {}
|
||||||
song_.set_source(Song::Source::Collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl CollectionPlaylistItem::Url() const { return song_.url(); }
|
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) {
|
bool CollectionPlaylistItem::InitFromQuery(const SqlRow &query) {
|
||||||
|
|
||||||
// Rows from the songs tables come first
|
int col = 0;
|
||||||
song_.InitFromQuery(query, true);
|
switch (source_) {
|
||||||
song_.set_source(Song::Source::Collection);
|
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();
|
return song_.is_valid();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant CollectionPlaylistItem::DatabaseValue(DatabaseColumn column) const {
|
void CollectionPlaylistItem::Reload() {
|
||||||
|
|
||||||
switch (column) {
|
if (song_.url().isLocalFile()) {
|
||||||
case Column_CollectionId: return song_.id();
|
const TagReaderResult result = TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_);
|
||||||
default: return PlaylistItem::DatabaseValue(column);
|
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
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
|
||||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -34,9 +32,11 @@ class SqlRow;
|
|||||||
|
|
||||||
class CollectionPlaylistItem : public PlaylistItem {
|
class CollectionPlaylistItem : public PlaylistItem {
|
||||||
public:
|
public:
|
||||||
explicit CollectionPlaylistItem();
|
explicit CollectionPlaylistItem(const Song::Source source);
|
||||||
explicit CollectionPlaylistItem(const Song &song);
|
explicit CollectionPlaylistItem(const Song &song);
|
||||||
|
|
||||||
|
QUrl Url() const override;
|
||||||
|
|
||||||
bool InitFromQuery(const SqlRow &query) override;
|
bool InitFromQuery(const SqlRow &query) override;
|
||||||
void Reload() override;
|
void Reload() override;
|
||||||
|
|
||||||
@@ -44,15 +44,13 @@ class CollectionPlaylistItem : public PlaylistItem {
|
|||||||
Song OriginalMetadata() const override { return song_; }
|
Song OriginalMetadata() const override { return song_; }
|
||||||
void SetMetadata(const Song &song) override { song_ = 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;
|
void SetArtManual(const QUrl &cover_url) override;
|
||||||
|
|
||||||
|
bool IsLocalCollectionItem() const override { return song_.source() == Song::Source::Collection; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QVariant DatabaseValue(DatabaseColumn column) const override;
|
QVariant DatabaseValue(const DatabaseColumn database_column) const override;
|
||||||
Song DatabaseSongMetadata() const override { return Song(Song::Source::Collection); }
|
Song DatabaseSongMetadata() const override { return Song(source_); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Song song_;
|
Song song_;
|
||||||
|
|||||||
@@ -1516,7 +1516,7 @@ void MainWindow::SongChanged(const Song &song) {
|
|||||||
|
|
||||||
SendNowPlaying();
|
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_->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_to_file_action()->setEnabled(song.has_valid_art() && !song.art_unset());
|
||||||
album_cover_choice_controller_->cover_from_file_action()->setEnabled(enable_change_art);
|
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);
|
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_->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_to_file_action()->setEnabled(result.success && result.type != AlbumCoverLoaderResult::Type::Unset);
|
||||||
album_cover_choice_controller_->cover_from_file_action()->setEnabled(enable_change_art);
|
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_; }
|
int Song::bitdepth() const { return d->bitdepth_; }
|
||||||
|
|
||||||
Song::Source Song::source() const { return d->source_; }
|
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_; }
|
int Song::directory_id() const { return d->directory_id_; }
|
||||||
const QUrl &Song::url() const { return d->url_; }
|
const QUrl &Song::url() const { return d->url_; }
|
||||||
const QString &Song::basefilename() const { return d->basefilename_; }
|
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(); }
|
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_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_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_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; }
|
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) {
|
QString Song::ImageCacheDir(const Source source) {
|
||||||
|
|
||||||
switch (source) {
|
switch (source) {
|
||||||
@@ -1831,8 +1839,8 @@ bool Song::MergeFromEngineMetadata(const EngineMetadata &engine_metadata) {
|
|||||||
|
|
||||||
bool minor = true;
|
bool minor = true;
|
||||||
|
|
||||||
if (d->init_from_file_ || is_collection_song() || d->url_.isLocalFile()) {
|
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.
|
// 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()) {
|
if (title() != engine_metadata.title && title().isEmpty() && !engine_metadata.title.isEmpty()) {
|
||||||
set_title(engine_metadata.title);
|
set_title(engine_metadata.title);
|
||||||
minor = false;
|
minor = false;
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ class Song {
|
|||||||
RadioParadise = 10,
|
RadioParadise = 10,
|
||||||
Spotify = 11
|
Spotify = 11
|
||||||
};
|
};
|
||||||
|
static const int kSourceCount = 16;
|
||||||
|
|
||||||
enum class FileType {
|
enum class FileType {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
@@ -176,6 +177,7 @@ class Song {
|
|||||||
int bitdepth() const;
|
int bitdepth() const;
|
||||||
|
|
||||||
Source source() const;
|
Source source() const;
|
||||||
|
int source_id() const;
|
||||||
int directory_id() const;
|
int directory_id() const;
|
||||||
const QUrl &url() const;
|
const QUrl &url() const;
|
||||||
const QString &basefilename() const;
|
const QString &basefilename() const;
|
||||||
@@ -372,7 +374,8 @@ class Song {
|
|||||||
const QString &playlist_albumartist_sortable() const;
|
const QString &playlist_albumartist_sortable() const;
|
||||||
|
|
||||||
bool is_metadata_good() 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_stream() const;
|
||||||
bool is_radio() const;
|
bool is_radio() const;
|
||||||
bool is_cdda() const;
|
bool is_cdda() const;
|
||||||
@@ -459,6 +462,7 @@ class Song {
|
|||||||
static FileType FiletypeByMimetype(const QString &mimetype);
|
static FileType FiletypeByMimetype(const QString &mimetype);
|
||||||
static FileType FiletypeByDescription(const QString &text);
|
static FileType FiletypeByDescription(const QString &text);
|
||||||
static FileType FiletypeByExtension(const QString &ext);
|
static FileType FiletypeByExtension(const QString &ext);
|
||||||
|
static bool IsLinkedCollectionSource(const Source source);
|
||||||
static QString ImageCacheDir(const Source source);
|
static QString ImageCacheDir(const Source source);
|
||||||
|
|
||||||
// Sort songs alphabetically using their pretty title
|
// Sort songs alphabetically using their pretty title
|
||||||
|
|||||||
@@ -716,7 +716,7 @@ void EditTagDialog::SelectionChanged() {
|
|||||||
}
|
}
|
||||||
ui_->tags_summary->setText(summary);
|
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);
|
ui_->tags_art_button->setEnabled(enable_change_art);
|
||||||
if ((art_different && first_cover_action != UpdateCoverAction::New) || action_different) {
|
if ((art_different && first_cover_action != UpdateCoverAction::New) || action_different) {
|
||||||
tags_cover_art_id_ = -1; // Cancels any pending art load.
|
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();
|
summary = tr("Cover art not set").toHtmlEscaped();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!song.is_collection_song()) {
|
if (!song.is_local_collection_song()) {
|
||||||
if (!summary.isEmpty()) summary += "<br />"_L1;
|
if (!summary.isEmpty()) summary += "<br />"_L1;
|
||||||
summary = tr("Album cover editing is only available for collection songs.");
|
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);
|
summary += GetArtSummary(cover_action);
|
||||||
}
|
}
|
||||||
ui_->tags_summary->setText(summary);
|
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;
|
tags_cover_art_id_ = -1;
|
||||||
album_cover_choice_controller_->show_cover_action()->setEnabled(result.success && result.type != AlbumCoverLoaderResult::Type::Unset);
|
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.
|
// 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()) {
|
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_);
|
collection_songs_.insert(ref.current_.id(), ref.current_);
|
||||||
}
|
}
|
||||||
if (ref.current_ == current_albumcover_loader_->last_song()) {
|
if (ref.current_ == current_albumcover_loader_->last_song()) {
|
||||||
@@ -1476,7 +1476,7 @@ void EditTagDialog::SongSaveTagsComplete(TagReaderReplyPtr reply, const QString
|
|||||||
const QString error = reply->error();
|
const QString error = reply->error();
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
if (song.is_collection_song()) {
|
if (song.is_local_collection_song()) {
|
||||||
if (collection_songs_.contains(song.id())) {
|
if (collection_songs_.contains(song.id())) {
|
||||||
Song old_song = collection_songs_.take(song.id());
|
Song old_song = collection_songs_.take(song.id());
|
||||||
song.set_art_automatic(old_song.art_automatic());
|
song.set_art_automatic(old_song.art_automatic());
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ void Organize::ProcessSomeFiles() {
|
|||||||
|
|
||||||
QString error_text;
|
QString error_text;
|
||||||
if (destination_->CopyToStorage(job, 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
|
// Notify other aspects of system that song has been invalidated
|
||||||
QString root = destination_->LocalPath();
|
QString root = destination_->LocalPath();
|
||||||
QFileInfo new_file = QFileInfo(root + QLatin1Char('/') + task.song_info_.new_filename_);
|
QFileInfo new_file = QFileInfo(root + QLatin1Char('/') + task.song_info_.new_filename_);
|
||||||
|
|||||||
@@ -92,12 +92,12 @@
|
|||||||
#include "smartplaylists/playlistgeneratorinserter.h"
|
#include "smartplaylists/playlistgeneratorinserter.h"
|
||||||
#include "smartplaylists/playlistgeneratormimedata.h"
|
#include "smartplaylists/playlistgeneratormimedata.h"
|
||||||
|
|
||||||
#include "streaming/streamplaylistitem.h"
|
#include "streaming/streamserviceplaylistitem.h"
|
||||||
#include "streaming/streamsongmimedata.h"
|
#include "streaming/streamsongmimedata.h"
|
||||||
#include "streaming/streamingservice.h"
|
#include "streaming/streamingservice.h"
|
||||||
|
|
||||||
#include "radios/radiomimedata.h"
|
#include "radios/radiomimedata.h"
|
||||||
#include "radios/radioplaylistitem.h"
|
#include "radios/radiostreamplaylistitem.h"
|
||||||
|
|
||||||
using std::make_shared;
|
using std::make_shared;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
@@ -186,7 +186,7 @@ Playlist::Playlist(const SharedPtr<TaskManager> task_manager,
|
|||||||
|
|
||||||
Playlist::~Playlist() {
|
Playlist::~Playlist() {
|
||||||
items_.clear();
|
items_.clear();
|
||||||
collection_items_by_id_.clear();
|
ClearCollectionItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
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)) {
|
if (const SongMimeData *song_data = qobject_cast<const SongMimeData*>(data)) {
|
||||||
// Dragged from a collection
|
// 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::IsLinkedCollectionSource(song_data->backend->source())) {
|
||||||
if (song_data->backend && song_data->backend->songs_table() == QLatin1String(CollectionLibrary::kSongsTable)) {
|
|
||||||
InsertSongItems<CollectionPlaylistItem>(song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
|
InsertSongItems<CollectionPlaylistItem>(song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
InsertSongItems<SongPlaylistItem>(song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
|
InsertSongItems<SongPlaylistItem>(song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (const PlaylistItemMimeData *item_data = qobject_cast<const PlaylistItemMimeData*>(data)) {
|
else if (const PlaylistItemMimeData *item_mimedata = qobject_cast<const PlaylistItemMimeData*>(data)) {
|
||||||
InsertItems(item_data->items_, row, play_now, enqueue_now, enqueue_next_now);
|
InsertItems(item_mimedata->items_, row, play_now, enqueue_now, enqueue_next_now);
|
||||||
}
|
}
|
||||||
else if (const PlaylistGeneratorMimeData *generator_data = qobject_cast<const PlaylistGeneratorMimeData*>(data)) {
|
else if (const PlaylistGeneratorMimeData *generator_mimedata = qobject_cast<const PlaylistGeneratorMimeData*>(data)) {
|
||||||
InsertSmartPlaylist(generator_data->generator_, row, play_now, enqueue_now, enqueue_next_now);
|
InsertSmartPlaylist(generator_mimedata->generator_, row, play_now, enqueue_now, enqueue_next_now);
|
||||||
}
|
}
|
||||||
else if (const StreamSongMimeData *stream_song_data = qobject_cast<const StreamSongMimeData*>(data)) {
|
else if (const StreamSongMimeData *stream_song_mimedata = qobject_cast<const StreamSongMimeData*>(data)) {
|
||||||
InsertStreamingItems(stream_song_data->service, stream_song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
|
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)) {
|
else if (const RadioMimeData *radio_mimedata = qobject_cast<const RadioMimeData*>(data)) {
|
||||||
InsertRadioItems(radio_data->songs, row, play_now, enqueue_now, enqueue_next_now);
|
InsertRadioItems(radio_mimedata->songs, row, play_now, enqueue_now, enqueue_next_now);
|
||||||
}
|
}
|
||||||
else if (data->hasFormat(QLatin1String(kRowsMimetype))) {
|
else if (data->hasFormat(QLatin1String(kRowsMimetype))) {
|
||||||
// Dragged from the playlist
|
// Dragged from the playlist
|
||||||
@@ -1131,10 +1130,10 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemPtrList &items, const in
|
|||||||
items_.insert(i, item);
|
items_.insert(i, item);
|
||||||
virtual_items_ << static_cast<int>(virtual_items_.count());
|
virtual_items_ << static_cast<int>(virtual_items_.count());
|
||||||
|
|
||||||
if (item->source() == Song::Source::Collection) {
|
if (Song::IsLinkedCollectionSource(item->source())) {
|
||||||
int id = item->Metadata().id();
|
const int id = item->Metadata().id();
|
||||||
if (id != -1) {
|
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;
|
PlaylistItemPtrList items;
|
||||||
|
items.reserve(songs.count());
|
||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
if (song.url().isLocalFile()) {
|
items << PlaylistItem::NewFromSong(song);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InsertItems(items, pos, play_now, enqueue, enqueue_next);
|
InsertItems(items, pos, play_now, enqueue, enqueue_next);
|
||||||
@@ -1215,7 +1200,7 @@ void Playlist::InsertStreamingItems(StreamingServicePtr service, const SongList
|
|||||||
PlaylistItemPtrList playlist_items;
|
PlaylistItemPtrList playlist_items;
|
||||||
playlist_items.reserve(songs.count());
|
playlist_items.reserve(songs.count());
|
||||||
for (const Song &song : songs) {
|
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);
|
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;
|
PlaylistItemPtrList playlist_items;
|
||||||
playlist_items.reserve(songs.count());
|
playlist_items.reserve(songs.count());
|
||||||
for (const Song &song : songs) {
|
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);
|
InsertItems(playlist_items, pos, play_now, enqueue, enqueue_next);
|
||||||
@@ -1252,22 +1237,22 @@ void Playlist::UpdateItems(SongList songs) {
|
|||||||
const PlaylistItemPtr item = items_.value(i);
|
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())) {
|
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;
|
PlaylistItemPtr new_item;
|
||||||
if (song.url().isLocalFile()) {
|
if (song.is_linked_collection_song()) {
|
||||||
if (song.is_collection_song()) {
|
new_item = make_shared<CollectionPlaylistItem>(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);
|
||||||
if (collection_items_by_id_.contains(song.id(), item)) collection_items_by_id_.remove(song.id(), item);
|
collection_items_[song.source_id()].insert(song.id(), new_item);
|
||||||
collection_items_by_id_.insert(song.id(), new_item);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
new_item = make_shared<SongPlaylistItem>(song);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (song.is_radio()) {
|
if (song.url().isLocalFile()) {
|
||||||
new_item = make_shared<RadioPlaylistItem>(song);
|
new_item = make_shared<SongPlaylistItem>(song);
|
||||||
}
|
}
|
||||||
else {
|
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;
|
items_[i] = new_item;
|
||||||
@@ -1580,7 +1565,7 @@ void Playlist::Restore() {
|
|||||||
|
|
||||||
items_.clear();
|
items_.clear();
|
||||||
virtual_items_.clear();
|
virtual_items_.clear();
|
||||||
collection_items_by_id_.clear();
|
ClearCollectionItems();
|
||||||
|
|
||||||
cancel_restore_ = false;
|
cancel_restore_ = false;
|
||||||
QFuture<PlaylistItemPtrList> future = QtConcurrent::run(&PlaylistBackend::GetPlaylistItems, playlist_backend_, id_);
|
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() {
|
void Playlist::ItemsLoaded() {
|
||||||
|
|
||||||
QFutureWatcher<PlaylistItemPtrList> *watcher = static_cast<QFutureWatcher<PlaylistItemPtrList>*>(sender());
|
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) {
|
for (int i = 0; i < count; ++i) {
|
||||||
PlaylistItemPtr item(items_.takeAt(row));
|
PlaylistItemPtr item(items_.takeAt(row));
|
||||||
items << item;
|
items << item;
|
||||||
|
const int id = item->Metadata().id();
|
||||||
if (item->source() == Song::Source::Collection) {
|
const int source_id = item->Metadata().source_id();
|
||||||
int id = item->Metadata().id();
|
if (id != -1 && collection_items_[source_id].contains(id, item)) {
|
||||||
if (id != -1 && collection_items_by_id_.contains(id, item)) {
|
collection_items_[source_id].remove(id, item);
|
||||||
collection_items_by_id_.remove(id, item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2069,8 +2061,8 @@ quint64 Playlist::GetTotalLength() const {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaylistItemPtrList Playlist::collection_items_by_id(const int id) const {
|
PlaylistItemPtrList Playlist::collection_items(const Song::Source source, const int song_id) const {
|
||||||
return collection_items_by_id_.values(id);
|
return collection_items_[static_cast<int>(source)].values(song_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playlist::TracksAboutToBeDequeued(const QModelIndex &idx, const int begin, const int end) {
|
void Playlist::TracksAboutToBeDequeued(const QModelIndex &idx, const int begin, const int end) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* 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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -203,7 +203,7 @@ class Playlist : public QAbstractListModel {
|
|||||||
PlaylistItem::Options current_item_options() const;
|
PlaylistItem::Options current_item_options() const;
|
||||||
Song current_item_metadata() 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;
|
SongList GetAllSongs() const;
|
||||||
PlaylistItemPtrList GetAllItems() 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.
|
// 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 InvalidateDeletedSongs();
|
||||||
|
|
||||||
|
void ClearCollectionItems();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void TracksAboutToBeDequeued(const QModelIndex&, const int begin, const int end);
|
void TracksAboutToBeDequeued(const QModelIndex&, const int begin, const int end);
|
||||||
void TracksDequeued();
|
void TracksDequeued();
|
||||||
@@ -393,8 +395,7 @@ class Playlist : public QAbstractListModel {
|
|||||||
|
|
||||||
QList<QPersistentModelIndex> played_indexes_;
|
QList<QPersistentModelIndex> played_indexes_;
|
||||||
|
|
||||||
// A map of collection ID to playlist item - for fast lookups when collection items change.
|
QMultiMap<int, PlaylistItemPtr> collection_items_[Song::kSourceCount];
|
||||||
QMultiMap<int, PlaylistItemPtr> collection_items_by_id_;
|
|
||||||
|
|
||||||
QPersistentModelIndex current_item_index_;
|
QPersistentModelIndex current_item_index_;
|
||||||
QPersistentModelIndex last_played_item_index_;
|
QPersistentModelIndex last_played_item_index_;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* 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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -30,30 +30,31 @@
|
|||||||
#include "core/sqlquery.h"
|
#include "core/sqlquery.h"
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
|
|
||||||
#include "collection/collectionplaylistitem.h"
|
|
||||||
#include "playlistitem.h"
|
#include "playlistitem.h"
|
||||||
#include "songplaylistitem.h"
|
#include "songplaylistitem.h"
|
||||||
|
#include "collection/collectionplaylistitem.h"
|
||||||
#include "streaming/streamplaylistitem.h"
|
#include "streaming/streamserviceplaylistitem.h"
|
||||||
#include "radios/radioplaylistitem.h"
|
#include "radios/radiostreamplaylistitem.h"
|
||||||
|
|
||||||
using std::make_shared;
|
using std::make_shared;
|
||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
|
|
||||||
|
PlaylistItem::PlaylistItem(const Song::Source source) : should_skip_(false), source_(source) {}
|
||||||
|
|
||||||
PlaylistItemPtr PlaylistItem::NewFromSource(const Song::Source source) {
|
PlaylistItemPtr PlaylistItem::NewFromSource(const Song::Source source) {
|
||||||
|
|
||||||
switch (source) {
|
switch (source) {
|
||||||
case Song::Source::Collection:
|
case Song::Source::Collection:
|
||||||
return make_shared<CollectionPlaylistItem>();
|
return make_shared<CollectionPlaylistItem>(source);
|
||||||
case Song::Source::Subsonic:
|
case Song::Source::Subsonic:
|
||||||
case Song::Source::Tidal:
|
case Song::Source::Tidal:
|
||||||
case Song::Source::Spotify:
|
case Song::Source::Spotify:
|
||||||
case Song::Source::Qobuz:
|
case Song::Source::Qobuz:
|
||||||
return make_shared<StreamPlaylistItem>(source);
|
return make_shared<StreamServicePlaylistItem>(source);
|
||||||
case Song::Source::Stream:
|
case Song::Source::Stream:
|
||||||
case Song::Source::RadioParadise:
|
case Song::Source::RadioParadise:
|
||||||
case Song::Source::SomaFM:
|
case Song::Source::SomaFM:
|
||||||
return make_shared<RadioPlaylistItem>(source);
|
return make_shared<RadioStreamPlaylistItem>(source);
|
||||||
case Song::Source::LocalFile:
|
case Song::Source::LocalFile:
|
||||||
case Song::Source::CDDA:
|
case Song::Source::CDDA:
|
||||||
case Song::Source::Device:
|
case Song::Source::Device:
|
||||||
@@ -74,11 +75,11 @@ PlaylistItemPtr PlaylistItem::NewFromSong(const Song &song) {
|
|||||||
case Song::Source::Tidal:
|
case Song::Source::Tidal:
|
||||||
case Song::Source::Spotify:
|
case Song::Source::Spotify:
|
||||||
case Song::Source::Qobuz:
|
case Song::Source::Qobuz:
|
||||||
return make_shared<StreamPlaylistItem>(song);
|
return make_shared<StreamServicePlaylistItem>(song);
|
||||||
case Song::Source::Stream:
|
case Song::Source::Stream:
|
||||||
case Song::Source::RadioParadise:
|
case Song::Source::RadioParadise:
|
||||||
case Song::Source::SomaFM:
|
case Song::Source::SomaFM:
|
||||||
return make_shared<RadioPlaylistItem>(song);
|
return make_shared<RadioStreamPlaylistItem>(song);
|
||||||
case Song::Source::LocalFile:
|
case Song::Source::LocalFile:
|
||||||
case Song::Source::CDDA:
|
case Song::Source::CDDA:
|
||||||
case Song::Source::Device:
|
case Song::Source::Device:
|
||||||
@@ -90,12 +91,10 @@ PlaylistItemPtr PlaylistItem::NewFromSong(const Song &song) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaylistItem::~PlaylistItem() = default;
|
|
||||||
|
|
||||||
void PlaylistItem::BindToQuery(SqlQuery *query) const {
|
void PlaylistItem::BindToQuery(SqlQuery *query) const {
|
||||||
|
|
||||||
query->BindValue(u":type"_s, static_cast<int>(source_));
|
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);
|
DatabaseSongMetadata().BindToQuery(query);
|
||||||
|
|
||||||
@@ -174,5 +173,5 @@ QColor PlaylistItem::GetCurrentForegroundColor() const {
|
|||||||
bool PlaylistItem::HasCurrentForegroundColor() const {
|
bool PlaylistItem::HasCurrentForegroundColor() const {
|
||||||
return !foreground_colors_.isEmpty();
|
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_; }
|
bool PlaylistItem::GetShouldSkip() const { return should_skip_; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* 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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -48,8 +48,7 @@ using std::enable_shared_from_this;
|
|||||||
|
|
||||||
class PlaylistItem : public enable_shared_from_this<PlaylistItem> {
|
class PlaylistItem : public enable_shared_from_this<PlaylistItem> {
|
||||||
public:
|
public:
|
||||||
explicit PlaylistItem(const Song::Source source) : should_skip_(false), source_(source) {}
|
explicit PlaylistItem(const Song::Source source);
|
||||||
virtual ~PlaylistItem();
|
|
||||||
|
|
||||||
static SharedPtr<PlaylistItem> NewFromSource(const Song::Source source);
|
static SharedPtr<PlaylistItem> NewFromSource(const Song::Source source);
|
||||||
static SharedPtr<PlaylistItem> NewFromSong(const Song &song);
|
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 Song OriginalMetadata() const = 0;
|
||||||
virtual QUrl Url() 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 SetTemporaryMetadata(const Song &metadata);
|
||||||
void UpdateTemporaryMetadata(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.
|
// 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.
|
// 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; }
|
virtual bool IsLocalCollectionItem() const { return false; }
|
||||||
void SetShouldSkip(const bool val);
|
void SetShouldSkip(const bool should_skip);
|
||||||
bool GetShouldSkip() const;
|
bool GetShouldSkip() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool should_skip_;
|
bool should_skip_;
|
||||||
|
|
||||||
enum DatabaseColumn { Column_CollectionId };
|
enum class DatabaseColumn {
|
||||||
|
CollectionId
|
||||||
|
};
|
||||||
|
|
||||||
virtual QVariant DatabaseValue(DatabaseColumn) const {
|
virtual QVariant DatabaseValue(const DatabaseColumn database_column) const { Q_UNUSED(database_column); return QVariant(QString()); }
|
||||||
return QVariant(QString());
|
|
||||||
}
|
|
||||||
virtual Song DatabaseSongMetadata() const { return Song(); }
|
virtual Song DatabaseSongMetadata() const { return Song(); }
|
||||||
|
|
||||||
Song::Source source_;
|
Song::Source source_;
|
||||||
|
|
||||||
Song temp_metadata_;
|
Song temp_metadata_;
|
||||||
|
|
||||||
QMap<short, QColor> background_colors_;
|
QMap<short, QColor> background_colors_;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* 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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -476,7 +476,7 @@ void PlaylistManager::UpdateCollectionSongs(const SongList &songs) {
|
|||||||
|
|
||||||
for (const Song &song : songs) {
|
for (const Song &song : songs) {
|
||||||
for (const Data &data : std::as_const(playlists_)) {
|
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) {
|
for (PlaylistItemPtr item : items) {
|
||||||
if (item->Metadata().directory_id() != song.directory_id()) continue;
|
if (item->Metadata().directory_id() != song.directory_id()) continue;
|
||||||
data.p->UpdateItemMetadata(item, song, false);
|
data.p->UpdateItemMetadata(item, song, false);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* 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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* 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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
* 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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
|
||||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -25,60 +23,52 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include "streamplaylistitem.h"
|
#include "streamplaylistitem.h"
|
||||||
#include "streamingservice.h"
|
|
||||||
#include "core/sqlrow.h"
|
#include "core/sqlrow.h"
|
||||||
|
|
||||||
StreamPlaylistItem::StreamPlaylistItem(const Song::Source source)
|
StreamPlaylistItem::StreamPlaylistItem(const Song::Source source)
|
||||||
: PlaylistItem(source),
|
: PlaylistItem(source),
|
||||||
source_(source) {}
|
source_(source) {}
|
||||||
|
|
||||||
StreamPlaylistItem::StreamPlaylistItem(const Song &metadata)
|
StreamPlaylistItem::StreamPlaylistItem(const Song &song)
|
||||||
: PlaylistItem(metadata.source()),
|
: PlaylistItem(song.source()),
|
||||||
source_(metadata.source()),
|
source_(song.source()),
|
||||||
metadata_(metadata) {
|
song_(song) {
|
||||||
InitMetadata();
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamPlaylistItem::StreamPlaylistItem(StreamingServicePtr service, const Song &metadata)
|
|
||||||
: PlaylistItem(metadata.source()),
|
|
||||||
source_(service->source()),
|
|
||||||
metadata_(metadata) {
|
|
||||||
InitMetadata();
|
InitMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamPlaylistItem::InitFromQuery(const SqlRow &query) {
|
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();
|
InitMetadata();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant StreamPlaylistItem::DatabaseValue(DatabaseColumn column) const {
|
QVariant StreamPlaylistItem::DatabaseValue(const DatabaseColumn column) const {
|
||||||
return PlaylistItem::DatabaseValue(column);
|
return PlaylistItem::DatabaseValue(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamPlaylistItem::InitMetadata() {
|
void StreamPlaylistItem::InitMetadata() {
|
||||||
|
|
||||||
if (metadata_.title().isEmpty()) metadata_.set_title(metadata_.url().toString());
|
if (song_.title().isEmpty()) song_.set_title(song_.url().toString());
|
||||||
if (metadata_.source() == Song::Source::Unknown) metadata_.set_source(Song::Source::Stream);
|
if (song_.source() == Song::Source::Unknown) song_.set_source(Song::Source::Stream);
|
||||||
if (metadata_.filetype() == Song::FileType::Unknown) metadata_.set_filetype(Song::FileType::Stream);
|
if (song_.filetype() == Song::FileType::Unknown) song_.set_filetype(Song::FileType::Stream);
|
||||||
metadata_.set_valid(true);
|
song_.set_valid(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Song StreamPlaylistItem::Metadata() const {
|
Song StreamPlaylistItem::Metadata() const {
|
||||||
|
|
||||||
if (HasTemporaryMetadata()) return temp_metadata_;
|
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) {
|
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);
|
temp_metadata_.set_art_manual(cover_url);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
|
||||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,43 +20,37 @@
|
|||||||
#ifndef STREAMPLAYLISTITEM_H
|
#ifndef STREAMPLAYLISTITEM_H
|
||||||
#define STREAMPLAYLISTITEM_H
|
#define STREAMPLAYLISTITEM_H
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "includes/shared_ptr.h"
|
|
||||||
#include "core/song.h"
|
#include "core/song.h"
|
||||||
#include "core/sqlrow.h"
|
#include "core/sqlrow.h"
|
||||||
#include "playlist/playlistitem.h"
|
#include "playlist/playlistitem.h"
|
||||||
|
|
||||||
class StreamingService;
|
|
||||||
|
|
||||||
class StreamPlaylistItem : public PlaylistItem {
|
class StreamPlaylistItem : public PlaylistItem {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit StreamPlaylistItem(const Song::Source source);
|
explicit StreamPlaylistItem(const Song::Source source);
|
||||||
explicit StreamPlaylistItem(const Song &metadata);
|
explicit StreamPlaylistItem(const Song &song);
|
||||||
explicit StreamPlaylistItem(SharedPtr<StreamingService> service, const Song &metadata);
|
|
||||||
|
|
||||||
bool InitFromQuery(const SqlRow &query) override;
|
bool InitFromQuery(const SqlRow &query) override;
|
||||||
Song Metadata() const override;
|
Song Metadata() const override;
|
||||||
Song OriginalMetadata() const override { return metadata_; }
|
Song OriginalMetadata() const override { return song_; }
|
||||||
QUrl Url() const override;
|
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;
|
void SetArtManual(const QUrl &cover_url) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QVariant DatabaseValue(DatabaseColumn) const override;
|
QVariant DatabaseValue(const DatabaseColumn column) const override;
|
||||||
Song DatabaseSongMetadata() const override { return metadata_; }
|
Song DatabaseSongMetadata() const override { return song_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitMetadata();
|
void InitMetadata();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Song::Source source_;
|
Song::Source source_;
|
||||||
Song metadata_;
|
Song song_;
|
||||||
|
|
||||||
Q_DISABLE_COPY(StreamPlaylistItem)
|
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
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2011, David Sansome <me@davidsansome.com>
|
* 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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,6 +22,6 @@
|
|||||||
#include "includes/shared_ptr.h"
|
#include "includes/shared_ptr.h"
|
||||||
#include "streamsongmimedata.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);
|
Q_UNUSED(parent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2011, David Sansome <me@davidsansome.com>
|
* 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
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -32,9 +32,9 @@ class StreamSongMimeData : public MimeData {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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;
|
SongList songs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -476,22 +476,22 @@ TEST_F(PlaylistTest, ShuffleThenNext) {
|
|||||||
|
|
||||||
TEST_F(PlaylistTest, CollectionIdMapSingle) {
|
TEST_F(PlaylistTest, CollectionIdMapSingle) {
|
||||||
|
|
||||||
Song song;
|
Song song(Song::Source::Collection);
|
||||||
song.Init(u"title"_s, u"artist"_s, u"album"_s, 123);
|
song.Init(u"title"_s, u"artist"_s, u"album"_s, 123);
|
||||||
song.set_id(1);
|
song.set_id(1);
|
||||||
|
|
||||||
PlaylistItemPtr item(std::make_shared<CollectionPlaylistItem>(song));
|
PlaylistItemPtr item(std::make_shared<CollectionPlaylistItem>(song));
|
||||||
playlist_.InsertItems(PlaylistItemPtrList() << item);
|
playlist_.InsertItems(PlaylistItemPtrList() << item);
|
||||||
|
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(-1).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, -1).count());
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(0).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 0).count());
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 2).count());
|
||||||
ASSERT_EQ(1, playlist_.collection_items_by_id(1).count());
|
ASSERT_EQ(1, playlist_.collection_items(Song::Source::Collection, 1).count());
|
||||||
EXPECT_EQ(song.title(), playlist_.collection_items_by_id(1)[0]->Metadata().title()); // clazy:exclude=detaching-temporary
|
EXPECT_EQ(song.title(), playlist_.collection_items(Song::Source::Collection, 1)[0]->Metadata().title()); // clazy:exclude=detaching-temporary
|
||||||
|
|
||||||
playlist_.Clear();
|
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));
|
PlaylistItemPtr item(std::make_shared<CollectionPlaylistItem>(invalid));
|
||||||
playlist_.InsertItems(PlaylistItemPtrList() << item);
|
playlist_.InsertItems(PlaylistItemPtrList() << item);
|
||||||
|
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(-1).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, -1).count());
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(0).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 0).count());
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(1).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 1).count());
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 2).count());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PlaylistTest, CollectionIdMapMulti) {
|
TEST_F(PlaylistTest, CollectionIdMapMulti) {
|
||||||
|
|
||||||
Song one;
|
Song one(Song::Source::Collection);
|
||||||
one.Init(u"title"_s, u"artist"_s, u"album"_s, 123);
|
one.Init(u"title"_s, u"artist"_s, u"album"_s, 123);
|
||||||
one.set_id(1);
|
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.Init(u"title 2"_s, u"artist 2"_s, u"album 2"_s, 123);
|
||||||
two.set_id(2);
|
two.set_id(2);
|
||||||
|
|
||||||
@@ -526,20 +526,20 @@ TEST_F(PlaylistTest, CollectionIdMapMulti) {
|
|||||||
PlaylistItemPtr item_three(std::make_shared<CollectionPlaylistItem>(one));
|
PlaylistItemPtr item_three(std::make_shared<CollectionPlaylistItem>(one));
|
||||||
playlist_.InsertItems(PlaylistItemPtrList() << item_one << item_two << item_three);
|
playlist_.InsertItems(PlaylistItemPtrList() << item_one << item_two << item_three);
|
||||||
|
|
||||||
EXPECT_EQ(2, playlist_.collection_items_by_id(1).count());
|
EXPECT_EQ(2, playlist_.collection_items(Song::Source::Collection, 1).count());
|
||||||
EXPECT_EQ(1, playlist_.collection_items_by_id(2).count());
|
EXPECT_EQ(1, playlist_.collection_items(Song::Source::Collection, 2).count());
|
||||||
|
|
||||||
playlist_.removeRow(1); // item_two
|
playlist_.removeRow(1); // item_two
|
||||||
EXPECT_EQ(2, playlist_.collection_items_by_id(1).count());
|
EXPECT_EQ(2, playlist_.collection_items(Song::Source::Collection, 1).count());
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 2).count());
|
||||||
|
|
||||||
playlist_.removeRow(1); // item_three
|
playlist_.removeRow(1); // item_three
|
||||||
EXPECT_EQ(1, playlist_.collection_items_by_id(1).count());
|
EXPECT_EQ(1, playlist_.collection_items(Song::Source::Collection, 1).count());
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 2).count());
|
||||||
|
|
||||||
playlist_.removeRow(0); // item_one
|
playlist_.removeRow(0); // item_one
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(1).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 1).count());
|
||||||
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
|
EXPECT_EQ(0, playlist_.collection_items(Song::Source::Collection, 2).count());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user