Refactor playlist items
Fix a bug where playlist items cover is not updated
This commit is contained in:
@@ -308,13 +308,13 @@ QVariant Playlist::data(const QModelIndex &idx, const int role) const {
|
||||
return queue_->PositionOf(idx);
|
||||
|
||||
case Role_CanSetRating:
|
||||
return static_cast<Column>(idx.column()) == Column::Rating && items_[idx.row()]->IsLocalCollectionItem() && items_[idx.row()]->Metadata().id() != -1;
|
||||
return static_cast<Column>(idx.column()) == Column::Rating && items_[idx.row()]->IsLocalCollectionItem() && items_[idx.row()]->EffectiveMetadata().id() != -1;
|
||||
|
||||
case Qt::EditRole:
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::DisplayRole:{
|
||||
const PlaylistItemPtr item = items_[idx.row()];
|
||||
const Song song = item->Metadata();
|
||||
const Song song = item->EffectiveMetadata();
|
||||
|
||||
// Don't forget to change Playlist::CompareItems when adding new columns
|
||||
switch (static_cast<Column>(idx.column())) {
|
||||
@@ -340,7 +340,7 @@ QVariant Playlist::data(const QModelIndex &idx, const int role) const {
|
||||
case Column::Bitdepth: return song.bitdepth();
|
||||
case Column::Bitrate: return song.bitrate();
|
||||
|
||||
case Column::Filename: return song.effective_stream_url();
|
||||
case Column::URL: return song.effective_url();
|
||||
case Column::BaseFilename: return song.basefilename();
|
||||
case Column::Filesize: return song.filesize();
|
||||
case Column::Filetype: return QVariant::fromValue(song.filetype());
|
||||
@@ -441,7 +441,7 @@ bool Playlist::setData(const QModelIndex &idx, const QVariant &value, const int
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
else if (song.is_radio()) {
|
||||
item->SetMetadata(song);
|
||||
item->SetOriginalMetadata(song);
|
||||
ScheduleSave();
|
||||
}
|
||||
|
||||
@@ -489,13 +489,13 @@ void Playlist::ItemReloadComplete(const QPersistentModelIndex &idx, const Song &
|
||||
if (idx.isValid()) {
|
||||
const PlaylistItemPtr item = item_at(idx.row());
|
||||
if (item) {
|
||||
ItemChanged(idx.row(), ChangedColumns(old_metadata, item->Metadata()));
|
||||
RowDataChanged(idx.row(), ChangedColumns(old_metadata, item->EffectiveMetadata()));
|
||||
if (idx.row() == current_row()) {
|
||||
if (MinorMetadataChange(old_metadata, item->Metadata())) {
|
||||
Q_EMIT CurrentSongMetadataChanged(item->Metadata());
|
||||
if (MinorMetadataChange(old_metadata, item->EffectiveMetadata())) {
|
||||
Q_EMIT CurrentSongMetadataChanged(item->EffectiveMetadata());
|
||||
}
|
||||
else {
|
||||
Q_EMIT CurrentSongChanged(item->Metadata());
|
||||
Q_EMIT CurrentSongChanged(item->EffectiveMetadata());
|
||||
}
|
||||
}
|
||||
if (metadata_edit) {
|
||||
@@ -560,7 +560,7 @@ int Playlist::NextVirtualIndex(int i, const bool ignore_repeat_track) const {
|
||||
if (item_at(virtual_items_[j])->GetShouldSkip()) {
|
||||
continue;
|
||||
}
|
||||
const Song this_song = item_at(virtual_items_[j])->Metadata();
|
||||
const Song this_song = item_at(virtual_items_[j])->EffectiveMetadata();
|
||||
if (((last_song.is_compilation() && this_song.is_compilation()) ||
|
||||
last_song.effective_albumartist() == this_song.effective_albumartist()) &&
|
||||
last_song.album() == this_song.album() &&
|
||||
@@ -600,7 +600,7 @@ int Playlist::PreviousVirtualIndex(int i, const bool ignore_repeat_track) const
|
||||
if (item_at(virtual_items_[j])->GetShouldSkip()) {
|
||||
continue;
|
||||
}
|
||||
Song this_song = item_at(virtual_items_[j])->Metadata();
|
||||
Song this_song = item_at(virtual_items_[j])->EffectiveMetadata();
|
||||
if (((last_song.is_compilation() && this_song.is_compilation()) || last_song.artist() == this_song.artist()) && last_song.album() == this_song.album() && FilterContainsVirtualIndex(j)) {
|
||||
return j; // Found one
|
||||
}
|
||||
@@ -687,7 +687,7 @@ void Playlist::set_current_row(const int i, const AutoScroll autoscroll, const b
|
||||
if (nextrow != -1 && nextrow != i) {
|
||||
PlaylistItemPtr next_item = item_at(nextrow);
|
||||
if (next_item) {
|
||||
next_item->ClearTemporaryMetadata();
|
||||
next_item->ClearStreamMetadata();
|
||||
Q_EMIT dataChanged(index(nextrow, 0), index(nextrow, ColumnCount - 1));
|
||||
}
|
||||
}
|
||||
@@ -788,7 +788,7 @@ Qt::ItemFlags Playlist::flags(const QModelIndex &idx) const {
|
||||
|
||||
if (idx.isValid()) {
|
||||
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
|
||||
if (item_at(idx.row())->Metadata().IsEditable() && column_is_editable(static_cast<Column>(idx.column()))) flags |= Qt::ItemIsEditable;
|
||||
if (item_at(idx.row())->EffectiveMetadata().IsEditable() && column_is_editable(static_cast<Column>(idx.column()))) flags |= Qt::ItemIsEditable;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@@ -1131,9 +1131,9 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemPtrList &items, const in
|
||||
virtual_items_ << static_cast<int>(virtual_items_.count());
|
||||
|
||||
if (Song::IsLinkedCollectionSource(item->source())) {
|
||||
const int id = item->Metadata().id();
|
||||
const int id = item->EffectiveMetadata().id();
|
||||
if (id != -1) {
|
||||
collection_items_[item->Metadata().source_id()].insert(id, item);
|
||||
collection_items_[item->EffectiveMetadata().source_id()].insert(id, item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1235,7 +1235,7 @@ void Playlist::UpdateItems(SongList songs) {
|
||||
while (it.hasNext()) {
|
||||
const Song &song = it.next();
|
||||
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->EffectiveMetadata().url() == song.url() && (item->EffectiveMetadata().filetype() == Song::FileType::Unknown || item->EffectiveMetadata().filetype() == Song::FileType::Stream || item->EffectiveMetadata().filetype() == Song::FileType::CDDA || !item->EffectiveMetadata().init_from_file())) {
|
||||
PlaylistItemPtr new_item;
|
||||
if (song.is_linked_collection_song()) {
|
||||
new_item = make_shared<CollectionPlaylistItem>(song);
|
||||
@@ -1290,7 +1290,7 @@ QMimeData *Playlist::mimeData(const QModelIndexList &indexes) const {
|
||||
for (const QModelIndex &idx : indexes) {
|
||||
if (idx.column() != first_column) continue;
|
||||
|
||||
urls << items_[idx.row()]->Url();
|
||||
urls << items_[idx.row()]->OriginalUrl();
|
||||
rows << idx.row();
|
||||
}
|
||||
|
||||
@@ -1321,8 +1321,8 @@ bool Playlist::CompareItems(const Column column, const Qt::SortOrder order, Play
|
||||
PlaylistItemPtr a = order == Qt::AscendingOrder ? _a : _b;
|
||||
PlaylistItemPtr b = order == Qt::AscendingOrder ? _b : _a;
|
||||
|
||||
#define cmp(field) return a->Metadata().field() < b->Metadata().field()
|
||||
#define strcmp(field) return QString::localeAwareCompare(a->Metadata().field().toLower(), b->Metadata().field().toLower()) < 0;
|
||||
#define cmp(field) return a->EffectiveMetadata().field() < b->EffectiveMetadata().field()
|
||||
#define strcmp(field) return QString::localeAwareCompare(a->EffectiveMetadata().field().toLower(), b->EffectiveMetadata().field().toLower()) < 0;
|
||||
|
||||
switch (column) {
|
||||
case Column::Title: strcmp(title_sortable);
|
||||
@@ -1346,8 +1346,8 @@ bool Playlist::CompareItems(const Column column, const Qt::SortOrder order, Play
|
||||
case Column::Bitrate: cmp(bitrate);
|
||||
case Column::Samplerate: cmp(samplerate);
|
||||
case Column::Bitdepth: cmp(bitdepth);
|
||||
case Column::Filename:
|
||||
return QString::localeAwareCompare(a->Url().path(), b->Url().path()) < 0;
|
||||
case Column::URL:
|
||||
return QString::localeAwareCompare(a->OriginalUrl().path(), b->OriginalUrl().path()) < 0;
|
||||
case Column::BaseFilename: cmp(basefilename);
|
||||
case Column::Filesize: cmp(filesize);
|
||||
case Column::Filetype: cmp(filetype);
|
||||
@@ -1401,7 +1401,7 @@ QString Playlist::column_name(const Column column) {
|
||||
case Column::Bitdepth: return tr("Bit Depth");
|
||||
case Column::Bitrate: return tr("Bitrate");
|
||||
|
||||
case Column::Filename: return tr("File Name");
|
||||
case Column::URL: return tr("URL");
|
||||
case Column::BaseFilename: return tr("File Name (without path)");
|
||||
case Column::Filesize: return tr("File Size");
|
||||
case Column::Filetype: return tr("File Type");
|
||||
@@ -1595,7 +1595,7 @@ void Playlist::ItemsLoaded() {
|
||||
while (it.hasNext()) {
|
||||
PlaylistItemPtr item = it.next();
|
||||
|
||||
if (item->IsLocalCollectionItem() && item->Metadata().url().isEmpty()) {
|
||||
if (item->IsLocalCollectionItem() && item->EffectiveMetadata().url().isEmpty()) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
@@ -1727,8 +1727,8 @@ PlaylistItemPtrList Playlist::RemoveItemsWithoutUndo(const int row, const int co
|
||||
for (int i = 0; i < count; ++i) {
|
||||
PlaylistItemPtr item(items_.takeAt(row));
|
||||
items << item;
|
||||
const int id = item->Metadata().id();
|
||||
const int source_id = item->Metadata().source_id();
|
||||
const int id = item->EffectiveMetadata().id();
|
||||
const int source_id = item->EffectiveMetadata().source_id();
|
||||
if (id != -1 && collection_items_[source_id].contains(id, item)) {
|
||||
collection_items_[source_id].remove(id, item);
|
||||
}
|
||||
@@ -1792,11 +1792,11 @@ void Playlist::ClearStreamMetadata() {
|
||||
|
||||
if (!current_item() || !current_item_index_.isValid()) return;
|
||||
|
||||
const Song old_metadata = current_item()->Metadata();
|
||||
current_item()->ClearTemporaryMetadata();
|
||||
const Song &new_metadata = current_item()->Metadata();
|
||||
const Song old_metadata = current_item()->EffectiveMetadata();
|
||||
current_item()->ClearStreamMetadata();
|
||||
const Song &new_metadata = current_item()->EffectiveMetadata();
|
||||
|
||||
ItemChanged(current_row(), ChangedColumns(old_metadata, new_metadata));
|
||||
RowDataChanged(current_row(), ChangedColumns(old_metadata, new_metadata));
|
||||
|
||||
if (old_metadata.length_nanosec() != new_metadata.length_nanosec()) {
|
||||
UpdateScrobblePoint();
|
||||
@@ -1832,7 +1832,7 @@ PlaylistItem::Options Playlist::current_item_options() const {
|
||||
|
||||
Song Playlist::current_item_metadata() const {
|
||||
if (!current_item()) return Song();
|
||||
return current_item()->Metadata();
|
||||
return current_item()->EffectiveMetadata();
|
||||
}
|
||||
|
||||
void Playlist::Clear() {
|
||||
@@ -1910,7 +1910,7 @@ void Playlist::ReloadItems(const QList<int> &rows) {
|
||||
const PlaylistItemPtr item = item_at(row);
|
||||
const QPersistentModelIndex idx = index(row, 0);
|
||||
if (idx.isValid()) {
|
||||
ItemReload(idx, item->Metadata(), false);
|
||||
ItemReload(idx, item->EffectiveMetadata(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1981,7 +1981,7 @@ void Playlist::ReshuffleIndices() {
|
||||
// Find all the unique albums in the playlist
|
||||
for (QList<int>::const_iterator it = virtual_items_.constBegin(); it != virtual_items_.constEnd(); ++it) {
|
||||
const int index = *it;
|
||||
const QString key = items_[index]->Metadata().AlbumKey();
|
||||
const QString key = items_[index]->EffectiveMetadata().AlbumKey();
|
||||
album_keys[index] = key;
|
||||
album_key_set << key;
|
||||
}
|
||||
@@ -1993,7 +1993,7 @@ void Playlist::ReshuffleIndices() {
|
||||
|
||||
// If the user is currently playing a song, force its album to be first
|
||||
if (current_row() != -1) {
|
||||
const QString key = items_[current_row()]->Metadata().AlbumKey();
|
||||
const QString key = items_[current_row()]->EffectiveMetadata().AlbumKey();
|
||||
const qint64 pos = shuffled_album_keys.indexOf(key);
|
||||
if (pos >= 1) {
|
||||
std::swap(shuffled_album_keys[0], shuffled_album_keys[pos]);
|
||||
@@ -2039,7 +2039,7 @@ SongList Playlist::GetAllSongs() const {
|
||||
SongList songs;
|
||||
songs.reserve(items_.count());
|
||||
for (PlaylistItemPtr item : items_) { // clazy:exclude=range-loop-reference
|
||||
songs << item->Metadata();
|
||||
songs << item->EffectiveMetadata();
|
||||
}
|
||||
return songs;
|
||||
|
||||
@@ -2051,7 +2051,7 @@ quint64 Playlist::GetTotalLength() const {
|
||||
|
||||
quint64 total_length = 0;
|
||||
for (PlaylistItemPtr item : items_) { // clazy:exclude=range-loop-reference
|
||||
qint64 length = item->Metadata().length_nanosec();
|
||||
qint64 length = item->EffectiveMetadata().length_nanosec();
|
||||
if (length > 0) total_length += length;
|
||||
}
|
||||
|
||||
@@ -2151,8 +2151,9 @@ Playlist::Columns Playlist::ChangedColumns(const Song &metadata1, const Song &me
|
||||
if (metadata1.bitrate() != metadata2.bitrate()) {
|
||||
columns << Column::Bitrate;
|
||||
}
|
||||
if (metadata1.url() != metadata2.url()) {
|
||||
columns << Column::Filename;
|
||||
if (metadata1.effective_url() != metadata2.effective_url()) {
|
||||
qLog(Debug) << "URL is changed for" << metadata1.PrettyTitleWithArtist();
|
||||
columns << Column::URL;
|
||||
columns << Column::BaseFilename;
|
||||
}
|
||||
if (metadata1.filesize() != metadata2.filesize()) {
|
||||
@@ -2211,36 +2212,38 @@ bool Playlist::MinorMetadataChange(const Song &old_metadata, const Song &new_met
|
||||
|
||||
}
|
||||
|
||||
void Playlist::UpdateItemMetadata(PlaylistItemPtr item, const Song &new_metadata, const bool temporary) {
|
||||
void Playlist::UpdateItemMetadata(PlaylistItemPtr item, const Song &new_metadata, const bool stream_metadata_update) {
|
||||
|
||||
if (!items_.contains(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int row = static_cast<int>(items_.indexOf(item, 0)); row != -1; row = static_cast<int>(items_.indexOf(item, row + 1))) {
|
||||
UpdateItemMetadata(row, item, new_metadata, temporary);
|
||||
UpdateItemMetadata(row, item, new_metadata, stream_metadata_update);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Playlist::UpdateItemMetadata(const int row, PlaylistItemPtr item, const Song &new_metadata, const bool temporary) {
|
||||
void Playlist::UpdateItemMetadata(const int row, PlaylistItemPtr item, const Song &new_metadata, const bool stream_metadata_update) {
|
||||
|
||||
const Song old_metadata = item->Metadata();
|
||||
if (new_metadata.IsEqual(stream_metadata_update ? item->EffectiveMetadata() : item->OriginalMetadata())) return;
|
||||
|
||||
const Columns columns = ChangedColumns(old_metadata, new_metadata);
|
||||
if (columns.isEmpty()) return;
|
||||
const Song old_metadata = item->EffectiveMetadata();
|
||||
const Columns changed_columns = ChangedColumns(old_metadata, new_metadata);
|
||||
|
||||
if (temporary) {
|
||||
item->SetTemporaryMetadata(new_metadata);
|
||||
if (stream_metadata_update) {
|
||||
item->SetStreamMetadata(new_metadata);
|
||||
}
|
||||
else {
|
||||
item->SetMetadata(new_metadata);
|
||||
if (item->HasTemporaryMetadata()) {
|
||||
item->UpdateTemporaryMetadata(new_metadata);
|
||||
item->SetOriginalMetadata(new_metadata);
|
||||
if (item->HasStreamMetadata()) {
|
||||
item->UpdateStreamMetadata(new_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
ItemChanged(row, columns);
|
||||
if (!changed_columns.isEmpty()) {
|
||||
RowDataChanged(row, changed_columns);
|
||||
}
|
||||
|
||||
if (row == current_row()) {
|
||||
InformOfCurrentSongChange(MinorMetadataChange(old_metadata, new_metadata));
|
||||
@@ -2251,7 +2254,7 @@ void Playlist::UpdateItemMetadata(const int row, PlaylistItemPtr item, const Son
|
||||
|
||||
}
|
||||
|
||||
void Playlist::ItemChanged(const int row, const Columns &columns) {
|
||||
void Playlist::RowDataChanged(const int row, const Columns &columns) {
|
||||
|
||||
if (columns.count() > 5) {
|
||||
const QModelIndex idx_column_first = index(row, 0);
|
||||
@@ -2293,7 +2296,7 @@ void Playlist::InvalidateDeletedSongs() {
|
||||
|
||||
for (int row = 0; row < items_.count(); ++row) {
|
||||
PlaylistItemPtr item = items_.value(row);
|
||||
const Song song = item->Metadata();
|
||||
const Song song = item->EffectiveMetadata();
|
||||
|
||||
if (song.url().isValid() && song.url().isLocalFile()) {
|
||||
const bool exists = QFile::exists(song.url().toLocalFile());
|
||||
@@ -2322,7 +2325,7 @@ void Playlist::RemoveDeletedSongs() {
|
||||
|
||||
for (int row = 0; row < items_.count(); ++row) {
|
||||
const PlaylistItemPtr item = items_.value(row);
|
||||
const Song song = item->Metadata();
|
||||
const Song song = item->EffectiveMetadata();
|
||||
|
||||
if (song.url().isLocalFile() && !QFile::exists(song.url().toLocalFile())) {
|
||||
rows_to_remove.append(row); // clazy:exclude=reserve-candidates
|
||||
@@ -2356,7 +2359,7 @@ void Playlist::RemoveDuplicateSongs() {
|
||||
|
||||
for (int row = 0; row < items_.count(); ++row) {
|
||||
const PlaylistItemPtr item = items_.value(row);
|
||||
const Song &song = item->Metadata();
|
||||
const Song &song = item->EffectiveMetadata();
|
||||
|
||||
bool found_duplicate = false;
|
||||
|
||||
@@ -2389,7 +2392,7 @@ void Playlist::RemoveUnavailableSongs() {
|
||||
QList<int> rows_to_remove;
|
||||
for (int row = 0; row < items_.count(); ++row) {
|
||||
const PlaylistItemPtr item = items_.value(row);
|
||||
const Song &song = item->Metadata();
|
||||
const Song &song = item->EffectiveMetadata();
|
||||
|
||||
// Check only local files
|
||||
if (song.url().isLocalFile() && !QFile::exists(song.url().toLocalFile())) {
|
||||
@@ -2406,7 +2409,7 @@ bool Playlist::ApplyValidityOnCurrentSong(const QUrl &url, const bool valid) {
|
||||
const PlaylistItemPtr current = current_item();
|
||||
|
||||
if (current) {
|
||||
const Song current_song = current->Metadata();
|
||||
const Song current_song = current->EffectiveMetadata();
|
||||
|
||||
// If validity has changed, reload the item
|
||||
if (current_song.source() == Song::Source::LocalFile || current_song.source() == Song::Source::Collection) {
|
||||
@@ -2472,7 +2475,7 @@ void Playlist::AlbumCoverLoaded(const Song &song, const AlbumCoverLoaderResult &
|
||||
// Update art_manual for local songs that are not in the collection.
|
||||
if (((result.type == AlbumCoverLoaderResult::Type::Manual && result.album_cover.cover_url.isLocalFile()) || result.type == AlbumCoverLoaderResult::Type::Unset) && (song.source() == Song::Source::LocalFile || song.source() == Song::Source::CDDA || song.source() == Song::Source::Device)) {
|
||||
PlaylistItemPtr item = current_item();
|
||||
if (item && item->Metadata() == song && (!item->Metadata().art_manual_is_valid() || (result.type == AlbumCoverLoaderResult::Type::Unset && !item->Metadata().art_unset()))) {
|
||||
if (item && item->EffectiveMetadata() == song && (!item->EffectiveMetadata().art_manual_is_valid() || (result.type == AlbumCoverLoaderResult::Type::Unset && !item->EffectiveMetadata().art_unset()))) {
|
||||
qLog(Debug) << "Updating art manual for local song" << song.title() << song.album() << song.title() << "to" << result.album_cover.cover_url << "in playlist.";
|
||||
item->SetArtManual(result.album_cover.cover_url);
|
||||
ScheduleSaveAsync();
|
||||
@@ -2503,8 +2506,8 @@ void Playlist::RateSong(const QModelIndex &idx, const float rating) {
|
||||
|
||||
if (has_item_at(idx.row())) {
|
||||
const PlaylistItemPtr item = item_at(idx.row());
|
||||
if (item && item->IsLocalCollectionItem() && item->Metadata().id() != -1) {
|
||||
collection_backend_->UpdateSongRatingAsync(item->Metadata().id(), rating);
|
||||
if (item && item->IsLocalCollectionItem() && item->EffectiveMetadata().id() != -1) {
|
||||
collection_backend_->UpdateSongRatingAsync(item->EffectiveMetadata().id(), rating);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2517,8 +2520,8 @@ void Playlist::RateSongs(const QModelIndexList &index_list, const float rating)
|
||||
const int row = idx.row();
|
||||
if (has_item_at(row)) {
|
||||
const PlaylistItemPtr item = item_at(row);
|
||||
if (item && item->IsLocalCollectionItem() && item->Metadata().id() != -1) {
|
||||
id_list << item->Metadata().id(); // clazy:exclude=reserve-candidates
|
||||
if (item && item->IsLocalCollectionItem() && item->EffectiveMetadata().id() != -1) {
|
||||
id_list << item->EffectiveMetadata().id(); // clazy:exclude=reserve-candidates
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class Playlist : public QAbstractListModel {
|
||||
Samplerate,
|
||||
Bitdepth,
|
||||
Bitrate,
|
||||
Filename,
|
||||
URL,
|
||||
BaseFilename,
|
||||
Filesize,
|
||||
Filetype,
|
||||
@@ -268,9 +268,9 @@ class Playlist : public QAbstractListModel {
|
||||
|
||||
static Columns ChangedColumns(const Song &metadata1, const Song &metadata2);
|
||||
static bool MinorMetadataChange(const Song &old_metadata, const Song &new_metadata);
|
||||
void UpdateItemMetadata(PlaylistItemPtr item, const Song &new_metadata, const bool temporary);
|
||||
void UpdateItemMetadata(const int row, PlaylistItemPtr item, const Song &new_metadata, const bool temporary);
|
||||
void ItemChanged(const int row, const Columns &columns);
|
||||
void UpdateItemMetadata(PlaylistItemPtr item, const Song &new_metadata, const bool stream_metadata_update);
|
||||
void UpdateItemMetadata(const int row, PlaylistItemPtr item, const Song &new_metadata, const bool stream_metadata_update);
|
||||
void RowDataChanged(const int row, const Columns &columns);
|
||||
|
||||
// Changes rating of a song to the given value asynchronously
|
||||
void RateSong(const QModelIndex &idx, const float rating);
|
||||
|
||||
@@ -273,7 +273,7 @@ PlaylistItemPtr PlaylistBackend::NewPlaylistItemFromQuery(const SqlRow &row, Sha
|
||||
|
||||
Song PlaylistBackend::NewSongFromQuery(const SqlRow &row, SharedPtr<NewSongFromQueryState> state) {
|
||||
|
||||
return NewPlaylistItemFromQuery(row, state)->Metadata();
|
||||
return NewPlaylistItemFromQuery(row, state)->EffectiveMetadata();
|
||||
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ PlaylistItemPtr PlaylistBackend::RestoreCueData(PlaylistItemPtr item, SharedPtr<
|
||||
|
||||
CueParser cue_parser(tagreader_client_, collection_backend_);
|
||||
|
||||
Song song = item->Metadata();
|
||||
Song song = item->EffectiveMetadata();
|
||||
// We're only interested in .cue songs here
|
||||
if (!song.has_cue()) return item;
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ bool PlaylistFilter::filterAcceptsRow(const int source_row, const QModelIndex &s
|
||||
query_hash_ = hash;
|
||||
}
|
||||
|
||||
return filter_tree_->accept(item->Metadata());
|
||||
return filter_tree_->accept(item->EffectiveMetadata());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,28 @@ PlaylistItemPtr PlaylistItem::NewFromSong(const Song &song) {
|
||||
|
||||
}
|
||||
|
||||
void PlaylistItem::SetStreamMetadata(const Song &song) {
|
||||
stream_song_ = song;
|
||||
}
|
||||
|
||||
void PlaylistItem::UpdateStreamMetadata(const Song &song) {
|
||||
|
||||
if (!stream_song_.is_valid()) return;
|
||||
|
||||
const Song old_stream_song = stream_song_;
|
||||
stream_song_ = song;
|
||||
|
||||
// Keep samplerate, bitdepth and bitrate from the old metadata if it's not present in the new.
|
||||
if (stream_song_.samplerate() <= 0 && old_stream_song.samplerate() > 0) stream_song_.set_samplerate(old_stream_song.samplerate());
|
||||
if (stream_song_.bitdepth() <= 0 && old_stream_song.bitdepth() > 0) stream_song_.set_bitdepth(old_stream_song.bitdepth());
|
||||
if (stream_song_.bitrate() <= 0 && old_stream_song.bitrate() > 0) stream_song_.set_bitrate(old_stream_song.bitrate());
|
||||
|
||||
}
|
||||
|
||||
void PlaylistItem::ClearStreamMetadata() {
|
||||
stream_song_ = Song();
|
||||
}
|
||||
|
||||
void PlaylistItem::BindToQuery(SqlQuery *query) const {
|
||||
|
||||
query->BindValue(u":type"_s, static_cast<int>(source_));
|
||||
@@ -102,28 +124,6 @@ void PlaylistItem::BindToQuery(SqlQuery *query) const {
|
||||
|
||||
}
|
||||
|
||||
void PlaylistItem::SetTemporaryMetadata(const Song &metadata) {
|
||||
temp_metadata_ = metadata;
|
||||
}
|
||||
|
||||
void PlaylistItem::UpdateTemporaryMetadata(const Song &metadata) {
|
||||
|
||||
if (!temp_metadata_.is_valid()) return;
|
||||
|
||||
const Song old_metadata = temp_metadata_;
|
||||
temp_metadata_ = metadata;
|
||||
|
||||
// Keep samplerate, bitdepth and bitrate from the old metadata if it's not present in the new.
|
||||
if (temp_metadata_.samplerate() <= 0 && old_metadata.samplerate() > 0) temp_metadata_.set_samplerate(old_metadata.samplerate());
|
||||
if (temp_metadata_.bitdepth() <= 0 && old_metadata.bitdepth() > 0) temp_metadata_.set_bitdepth(old_metadata.bitdepth());
|
||||
if (temp_metadata_.bitrate() <= 0 && old_metadata.bitrate() > 0) temp_metadata_.set_bitrate(old_metadata.bitrate());
|
||||
|
||||
}
|
||||
|
||||
void PlaylistItem::ClearTemporaryMetadata() {
|
||||
temp_metadata_ = Song();
|
||||
}
|
||||
|
||||
static void ReloadPlaylistItem(PlaylistItemPtr item) {
|
||||
item->Reload();
|
||||
}
|
||||
@@ -135,12 +135,15 @@ QFuture<void> PlaylistItem::BackgroundReload() {
|
||||
void PlaylistItem::SetBackgroundColor(short priority, const QColor &color) {
|
||||
background_colors_[priority] = color;
|
||||
}
|
||||
|
||||
bool PlaylistItem::HasBackgroundColor(short priority) const {
|
||||
return background_colors_.contains(priority);
|
||||
}
|
||||
|
||||
void PlaylistItem::RemoveBackgroundColor(short priority) {
|
||||
background_colors_.remove(priority);
|
||||
}
|
||||
|
||||
QColor PlaylistItem::GetCurrentBackgroundColor() const {
|
||||
|
||||
if (background_colors_.isEmpty()) {
|
||||
@@ -151,6 +154,7 @@ QColor PlaylistItem::GetCurrentBackgroundColor() const {
|
||||
return background_colors_[background_colors_keys.last()];
|
||||
|
||||
}
|
||||
|
||||
bool PlaylistItem::HasCurrentBackgroundColor() const {
|
||||
return !background_colors_.isEmpty();
|
||||
}
|
||||
@@ -158,12 +162,15 @@ bool PlaylistItem::HasCurrentBackgroundColor() const {
|
||||
void PlaylistItem::SetForegroundColor(const short priority, const QColor &color) {
|
||||
foreground_colors_[priority] = color;
|
||||
}
|
||||
|
||||
bool PlaylistItem::HasForegroundColor(const short priority) const {
|
||||
return foreground_colors_.contains(priority);
|
||||
}
|
||||
|
||||
void PlaylistItem::RemoveForegroundColor(const short priority) {
|
||||
foreground_colors_.remove(priority);
|
||||
}
|
||||
|
||||
QColor PlaylistItem::GetCurrentForegroundColor() const {
|
||||
|
||||
if (foreground_colors_.isEmpty()) return QColor();
|
||||
@@ -172,8 +179,11 @@ QColor PlaylistItem::GetCurrentForegroundColor() const {
|
||||
return foreground_colors_[foreground_colors_keys.last()];
|
||||
|
||||
}
|
||||
|
||||
bool PlaylistItem::HasCurrentForegroundColor() const {
|
||||
return !foreground_colors_.isEmpty();
|
||||
}
|
||||
|
||||
void PlaylistItem::SetShouldSkip(const bool should_skip) { should_skip_ = should_skip; }
|
||||
|
||||
bool PlaylistItem::GetShouldSkip() const { return should_skip_; }
|
||||
|
||||
@@ -66,37 +66,31 @@ class PlaylistItem : public enable_shared_from_this<PlaylistItem> {
|
||||
Q_DECLARE_FLAGS(Options, Option)
|
||||
|
||||
virtual Song::Source source() const { return source_; }
|
||||
|
||||
virtual Options options() const { return Option::Default; }
|
||||
|
||||
virtual QList<QAction*> actions() { return QList<QAction*>(); }
|
||||
|
||||
virtual Song OriginalMetadata() const = 0;
|
||||
virtual QUrl OriginalUrl() const = 0;
|
||||
virtual void SetOriginalMetadata(const Song &song) { Q_UNUSED(song); }
|
||||
|
||||
Song EffectiveMetadata() const { return HasStreamMetadata() ? stream_song_ : OriginalMetadata(); }
|
||||
QUrl EffectiveUrl() const { return stream_song_.effective_url().isValid() ? stream_song_.effective_url() : OriginalUrl(); }
|
||||
|
||||
void SetStreamMetadata(const Song &song);
|
||||
void UpdateStreamMetadata(const Song &song);
|
||||
void ClearStreamMetadata();
|
||||
bool HasStreamMetadata() const { return stream_song_.is_valid(); }
|
||||
|
||||
qint64 effective_beginning_nanosec() const { return stream_song_.is_valid() && stream_song_.beginning_nanosec() != -1 ? stream_song_.beginning_nanosec() : OriginalMetadata().beginning_nanosec(); }
|
||||
qint64 effective_end_nanosec() const { return stream_song_.is_valid() && stream_song_.end_nanosec() != -1 ? stream_song_.end_nanosec() : OriginalMetadata().end_nanosec(); }
|
||||
|
||||
virtual void SetArtManual(const QUrl &cover_url) = 0;
|
||||
|
||||
virtual bool InitFromQuery(const SqlRow &query) = 0;
|
||||
void BindToQuery(SqlQuery *query) const;
|
||||
virtual void Reload() {}
|
||||
QFuture<void> BackgroundReload();
|
||||
|
||||
virtual Song Metadata() const = 0;
|
||||
virtual Song OriginalMetadata() const = 0;
|
||||
virtual QUrl Url() const = 0;
|
||||
|
||||
virtual void SetMetadata(const Song &song) { Q_UNUSED(song); }
|
||||
|
||||
void SetTemporaryMetadata(const Song &metadata);
|
||||
void UpdateTemporaryMetadata(const Song &metadata);
|
||||
void ClearTemporaryMetadata();
|
||||
bool HasTemporaryMetadata() const { return temp_metadata_.is_valid(); }
|
||||
|
||||
Song StreamMetadata() { return HasTemporaryMetadata() ? temp_metadata_ : Metadata(); }
|
||||
QUrl StreamUrl() const { return HasTemporaryMetadata() && temp_metadata_.effective_stream_url().isValid() ? temp_metadata_.effective_stream_url() : Url(); }
|
||||
|
||||
std::optional<double> effective_ebur128_integrated_loudness_lufs() const { return HasTemporaryMetadata() && temp_metadata_.is_valid() ? temp_metadata_.ebur128_integrated_loudness_lufs() : Metadata().ebur128_integrated_loudness_lufs(); }
|
||||
|
||||
qint64 effective_beginning_nanosec() const { return HasTemporaryMetadata() && temp_metadata_.is_valid() && temp_metadata_.beginning_nanosec() != -1 ? temp_metadata_.beginning_nanosec() : Metadata().beginning_nanosec(); }
|
||||
qint64 effective_end_nanosec() const { return HasTemporaryMetadata() && temp_metadata_.is_valid() && temp_metadata_.end_nanosec() != -1 ? temp_metadata_.end_nanosec() : Metadata().end_nanosec(); }
|
||||
|
||||
virtual void SetArtManual(const QUrl &cover_url) = 0;
|
||||
|
||||
// Background colors.
|
||||
void SetBackgroundColor(const short priority, const QColor &color);
|
||||
bool HasBackgroundColor(const short priority) const;
|
||||
@@ -128,7 +122,7 @@ class PlaylistItem : public enable_shared_from_this<PlaylistItem> {
|
||||
virtual Song DatabaseSongMetadata() const { return Song(); }
|
||||
|
||||
Song::Source source_;
|
||||
Song temp_metadata_;
|
||||
Song stream_song_;
|
||||
|
||||
QMap<short, QColor> background_colors_;
|
||||
QMap<short, QColor> foreground_colors_;
|
||||
|
||||
@@ -478,7 +478,7 @@ void PlaylistManager::UpdateCollectionSongs(const SongList &songs) {
|
||||
for (const Data &data : std::as_const(playlists_)) {
|
||||
const PlaylistItemPtrList items = data.p->collection_items(song.source(), song.id());
|
||||
for (PlaylistItemPtr item : items) {
|
||||
if (item->Metadata().directory_id() != song.directory_id()) continue;
|
||||
if (item->EffectiveMetadata().directory_id() != song.directory_id()) continue;
|
||||
data.p->UpdateItemMetadata(item, song, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ bool PlaylistUndoCommandInsertItems::UpdateItem(const PlaylistItemPtr &updated_i
|
||||
|
||||
for (int i = 0; i < items_.size(); i++) {
|
||||
PlaylistItemPtr item = items_.value(i);
|
||||
if (item->Metadata().url() == updated_item->Metadata().url()) {
|
||||
if (item->EffectiveMetadata().url() == updated_item->EffectiveMetadata().url()) {
|
||||
items_[i] = updated_item;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ void PlaylistView::SetItemDelegates() {
|
||||
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Bitdepth), new PlaylistDelegateBase(this, tr("Bit")));
|
||||
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Bitrate), new PlaylistDelegateBase(this, tr("kbps")));
|
||||
|
||||
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Filename), new NativeSeparatorsDelegate(this));
|
||||
setItemDelegateForColumn(static_cast<int>(Playlist::Column::URL), new NativeSeparatorsDelegate(this));
|
||||
setItemDelegateForColumn(static_cast<int>(Playlist::Column::LastPlayed), new LastPlayedItemDelegate(this));
|
||||
|
||||
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Source), new SongSourceDelegate(this));
|
||||
@@ -354,7 +354,7 @@ void PlaylistView::RestoreHeaderState() {
|
||||
header_->HideSection(static_cast<int>(Playlist::Column::OriginalYear));
|
||||
header_->HideSection(static_cast<int>(Playlist::Column::Disc));
|
||||
header_->HideSection(static_cast<int>(Playlist::Column::Genre));
|
||||
header_->HideSection(static_cast<int>(Playlist::Column::Filename));
|
||||
header_->HideSection(static_cast<int>(Playlist::Column::URL));
|
||||
header_->HideSection(static_cast<int>(Playlist::Column::BaseFilename));
|
||||
header_->HideSection(static_cast<int>(Playlist::Column::Filesize));
|
||||
header_->HideSection(static_cast<int>(Playlist::Column::DateCreated));
|
||||
@@ -1406,7 +1406,7 @@ void PlaylistView::CopyCurrentSongToClipboard() const {
|
||||
}
|
||||
|
||||
// Get the song's URL
|
||||
const QUrl url = model()->data(currentIndex().sibling(currentIndex().row(), static_cast<int>(Playlist::Column::Filename))).toUrl();
|
||||
const QUrl url = model()->data(currentIndex().sibling(currentIndex().row(), static_cast<int>(Playlist::Column::URL))).toUrl();
|
||||
|
||||
QMimeData *mime_data = new QMimeData;
|
||||
mime_data->setUrls(QList<QUrl>() << url);
|
||||
|
||||
@@ -38,8 +38,6 @@ bool SongPlaylistItem::InitFromQuery(const SqlRow &query) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QUrl SongPlaylistItem::Url() const { return song_.url(); }
|
||||
|
||||
void SongPlaylistItem::Reload() {
|
||||
|
||||
if (!song_.url().isLocalFile()) return;
|
||||
@@ -49,18 +47,13 @@ void SongPlaylistItem::Reload() {
|
||||
qLog(Error) << "Could not reload file" << song_.url() << result.error_string();
|
||||
}
|
||||
|
||||
UpdateTemporaryMetadata(song_);
|
||||
UpdateStreamMetadata(song_);
|
||||
|
||||
}
|
||||
|
||||
Song SongPlaylistItem::Metadata() const {
|
||||
if (HasTemporaryMetadata()) return temp_metadata_;
|
||||
return song_;
|
||||
}
|
||||
|
||||
void SongPlaylistItem::SetArtManual(const QUrl &cover_url) {
|
||||
|
||||
song_.set_art_manual(cover_url);
|
||||
if (HasTemporaryMetadata()) temp_metadata_.set_art_manual(cover_url);
|
||||
if (HasStreamMetadata()) stream_song_.set_art_manual(cover_url);
|
||||
|
||||
}
|
||||
|
||||
@@ -40,10 +40,8 @@ class SongPlaylistItem : public PlaylistItem {
|
||||
bool InitFromQuery(const SqlRow &query) override;
|
||||
void Reload() override;
|
||||
|
||||
Song Metadata() const override;
|
||||
Song OriginalMetadata() const override { return song_; }
|
||||
|
||||
QUrl Url() const override;
|
||||
QUrl OriginalUrl() const override { return song_.url(); }
|
||||
|
||||
void SetArtManual(const QUrl &cover_url) override;
|
||||
|
||||
|
||||
@@ -36,6 +36,15 @@ StreamPlaylistItem::StreamPlaylistItem(const Song &song)
|
||||
InitMetadata();
|
||||
}
|
||||
|
||||
void StreamPlaylistItem::InitMetadata() {
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
bool StreamPlaylistItem::InitFromQuery(const SqlRow &query) {
|
||||
|
||||
song_.InitFromQuery(query, false, static_cast<int>(Song::kRowIdColumns.count()));
|
||||
@@ -48,27 +57,9 @@ QVariant StreamPlaylistItem::DatabaseValue(const DatabaseColumn column) const {
|
||||
return PlaylistItem::DatabaseValue(column);
|
||||
}
|
||||
|
||||
void StreamPlaylistItem::InitMetadata() {
|
||||
|
||||
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 song_;
|
||||
|
||||
}
|
||||
|
||||
QUrl StreamPlaylistItem::Url() const { return song_.url(); }
|
||||
|
||||
void StreamPlaylistItem::SetArtManual(const QUrl &cover_url) {
|
||||
|
||||
song_.set_art_manual(cover_url);
|
||||
temp_metadata_.set_art_manual(cover_url);
|
||||
stream_song_.set_art_manual(cover_url);
|
||||
|
||||
}
|
||||
|
||||
@@ -33,12 +33,10 @@ class StreamPlaylistItem : public PlaylistItem {
|
||||
explicit StreamPlaylistItem(const Song::Source source);
|
||||
explicit StreamPlaylistItem(const Song &song);
|
||||
|
||||
bool InitFromQuery(const SqlRow &query) override;
|
||||
Song Metadata() const override;
|
||||
Song OriginalMetadata() const override { return song_; }
|
||||
QUrl Url() const override;
|
||||
|
||||
void SetMetadata(const Song &song) override { song_ = song; }
|
||||
QUrl OriginalUrl() const override { return song_.url(); }
|
||||
void SetOriginalMetadata(const Song &song) override { song_ = song; }
|
||||
bool InitFromQuery(const SqlRow &query) override;
|
||||
void SetArtManual(const QUrl &cover_url) override;
|
||||
|
||||
protected:
|
||||
|
||||
Reference in New Issue
Block a user