Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MusicBrainz: Extended metadata support (pre-gsoc2020) #2522

Merged
merged 11 commits into from
Mar 4, 2020
9 changes: 6 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -425,13 +425,16 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/mixer/samplerbank.cpp
src/mixxx.cpp
src/mixxxapplication.cpp
src/musicbrainz/acoustidclient.cpp
src/musicbrainz/chromaprinter.cpp
src/musicbrainz/crc.c
src/musicbrainz/gzip.cpp
src/musicbrainz/musicbrainzclient.cpp
src/musicbrainz/network.cpp
src/musicbrainz/musicbrainz.cpp
src/musicbrainz/musicbrainzxml.cpp
src/musicbrainz/tagfetcher.cpp
src/musicbrainz/web/acoustidlookuptask.cpp
src/musicbrainz/web/musicbrainzrecordingstask.cpp
src/network/jsonwebtask.cpp
src/network/webtask.cpp
src/preferences/broadcastprofile.cpp
src/preferences/broadcastsettings.cpp
src/preferences/broadcastsettings_legacy.cpp
Expand Down
10 changes: 7 additions & 3 deletions build/depends.py
Original file line number Diff line number Diff line change
Expand Up @@ -996,13 +996,14 @@ def sources(self, build):
"src/widget/wsingletoncontainer.cpp",
"src/widget/wmainmenubar.cpp",

"src/musicbrainz/network.cpp",
"src/musicbrainz/tagfetcher.cpp",
"src/musicbrainz/gzip.cpp",
"src/musicbrainz/crc.c",
"src/musicbrainz/acoustidclient.cpp",
"src/musicbrainz/chromaprinter.cpp",
"src/musicbrainz/musicbrainzclient.cpp",
"src/musicbrainz/musicbrainz.cpp",
"src/musicbrainz/musicbrainzxml.cpp",
"src/musicbrainz/web/acoustidlookuptask.cpp",
"src/musicbrainz/web/musicbrainzrecordingstask.cpp",

"src/widget/wtracktableview.cpp",
"src/widget/wtracktableviewheader.cpp",
Expand Down Expand Up @@ -1128,6 +1129,9 @@ def sources(self, build):

"src/library/trackloader.cpp",

"src/network/jsonwebtask.cpp",
"src/network/webtask.cpp",

"src/widget/wwaveformviewer.cpp",

"src/waveform/sharedglcontext.cpp",
Expand Down
142 changes: 109 additions & 33 deletions src/library/dlgtagfetcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@
#include <QtDebug>

#include "library/dlgtagfetcher.h"
#include "track/tracknumbers.h"

namespace {

QStringList track2row(const Track& track) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 2row? Track2MetadataList.

It look like the underlying issue is, that the resultAvailable signal returning tracks.

It would be easier to understand and is probably more performant if it will directly return a custom track object containing the results (a single row) only. Can this be TrackRelease?

The string list solution here draws the column sorting into the management code which is also subobtimal. I think we should at least name the indexes of the list.

Thinking about all of this ... can't we deduplicate the results earlyer just after receiving them.

A custom deduplicate code, working in the temporary track objects would also be a solution.

Ideas?

Copy link
Contributor Author

@uklotzde uklotzde Mar 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the dialog only as needed. I will not implements further improvements other than passing-through of mixxx::musicbrainz::TrackRelease as suggested.

PS: Performance doesn't matter here. I only changed the signal types, because the status quo of using QObjects for this purpose was wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dialogue needs to be extended anyway in the future. Whoever will do this can improve the deduplication logic and replace the QStringList. The deduplication currently depends on the displayed columns that are actually displayed and therefore must be done here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The simplistic deduplication logic before is really displaced and the code is difficult to follow. Now everything is co-located on the display layer were it actually matters. It's not optimal, but without a rationale for deduplication this is the best we could do for now.

const auto trackNumbers = TrackNumbers::joinStrings(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads as if the return value is a QStringList. I think this is not the case so it is not a good use case for auto.

We should also consider to rename the function to makeNumberOfTotalString()
or something more intelligent.

track.getTrackNumber(),
track.getTrackTotal());
QStringList row;
row.reserve(6);
row
<< track.getYear()
<< track.getAlbum()
<< track.getAlbumArtist()
<< trackNumbers
<< track.getTitle()
<< track.getArtist();
return row;
}

void addTrack(
const QStringList& trackRow,
int resultIndex,
QTreeWidget* parent) {
QTreeWidgetItem* item = new QTreeWidgetItem(parent, trackRow);
item->setData(0, Qt::UserRole, resultIndex);
item->setData(0, Qt::TextAlignmentRole, Qt::AlignRight);
}

} // anonymous namespace

DlgTagFetcher::DlgTagFetcher(QWidget *parent)
: QDialog(parent),
Expand Down Expand Up @@ -32,11 +62,12 @@ void DlgTagFetcher::init() {
this, &DlgTagFetcher::slotNetworkResult);

// Resize columns, this can't be set in the ui file
results->setColumnWidth(0, 50); // Track column
results->setColumnWidth(1, 50); // Year column
results->setColumnWidth(2, 160); // Title column
results->setColumnWidth(3, 160); // Artist column
results->setColumnWidth(4, 160); // Album column
results->setColumnWidth(0, 50); // Year column
results->setColumnWidth(1, 160); // Album column
results->setColumnWidth(2, 160); // Album artist column
results->setColumnWidth(3, 50); // Track (numbers) column
results->setColumnWidth(4, 160); // Title column
results->setColumnWidth(5, 160); // Artist column
}

void DlgTagFetcher::loadTrack(const TrackPointer& track) {
Expand Down Expand Up @@ -71,23 +102,71 @@ void DlgTagFetcher::slotTrackChanged(TrackId trackId) {
void DlgTagFetcher::apply() {
int resultIndex = m_data.m_selectedResult;
if (resultIndex > -1) {
if (!m_data.m_results[resultIndex]->getAlbum().isEmpty()) {
m_track->setAlbum(m_data.m_results[resultIndex]->getAlbum());
mixxx::TrackMetadata importedMetadata;
m_data.m_results[resultIndex]->readTrackMetadata(&importedMetadata);
mixxx::TrackMetadata updatedMetadata;
m_track->readTrackMetadata(&updatedMetadata);
if (!importedMetadata.getTrackInfo().getArtist().isEmpty()) {
updatedMetadata.refTrackInfo().setArtist(
importedMetadata.getTrackInfo().getArtist());
}
if (!importedMetadata.getTrackInfo().getTitle().isEmpty()) {
updatedMetadata.refTrackInfo().setTitle(
importedMetadata.getTrackInfo().getTitle());
}
if (!importedMetadata.getTrackInfo().getTrackNumber().isEmpty()) {
updatedMetadata.refTrackInfo().setTrackNumber(
importedMetadata.getTrackInfo().getTrackNumber()
);
}
if (!importedMetadata.getTrackInfo().getTrackTotal().isEmpty()) {
updatedMetadata.refTrackInfo().setTrackTotal(
importedMetadata.getTrackInfo().getTrackTotal()
);
}
if (!importedMetadata.getTrackInfo().getYear().isEmpty()) {
updatedMetadata.refTrackInfo().setYear(
importedMetadata.getTrackInfo().getYear());
}
if (!m_data.m_results[resultIndex]->getArtist().isEmpty()) {
m_track->setArtist(m_data.m_results[resultIndex]->getArtist());
if (!importedMetadata.getAlbumInfo().getArtist().isEmpty()) {
updatedMetadata.refAlbumInfo().setArtist(
importedMetadata.getAlbumInfo().getArtist());
}
if (!m_data.m_results[resultIndex]->getTitle().isEmpty()) {
m_track->setTitle(m_data.m_results[resultIndex]->getTitle());
if (!importedMetadata.getAlbumInfo().getTitle().isEmpty()) {
updatedMetadata.refAlbumInfo().setTitle(
importedMetadata.getAlbumInfo().getTitle());
}
if (!m_data.m_results[resultIndex]->getYear().isEmpty() &&
m_data.m_results[resultIndex]->getYear() != "0") {
m_track->setYear(m_data.m_results[resultIndex]->getYear());
#if defined(__EXTRA_METADATA__)
if (!importedMetadata.getTrackInfo().getMusicBrainzArtistId().isNull()) {
updatedMetadata.refTrackInfo().setMusicBrainzArtistId(
importedMetadata.getTrackInfo().getMusicBrainzArtistId()
);
}
if (!m_data.m_results[resultIndex]->getTrackNumber().isEmpty() &&
m_data.m_results[resultIndex]->getTrackNumber() != "0") {
m_track->setTrackNumber(m_data.m_results[resultIndex]->getTrackNumber());
if (!importedMetadata.getTrackInfo().getMusicBrainzRecordingId().isNull()) {
updatedMetadata.refTrackInfo().setMusicBrainzRecordingId(
importedMetadata.getTrackInfo().getMusicBrainzRecordingId()
);
}
if (!importedMetadata.getTrackInfo().getMusicBrainzReleaseId().isNull()) {
updatedMetadata.refTrackInfo().setMusicBrainzReleaseId(
importedMetadata.getTrackInfo().getMusicBrainzReleaseId()
);
}
if (!importedMetadata.getAlbumInfo().getMusicBrainzArtistId().isNull()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how the artist Id compared to the string representation. I think we need to handle them at once.
For example if the user changes the Artist, it does not match the string stored for this Id at music brains.
This can be an issue when handling Artist with special characters like Tiësto or P!ink or features like "Lena feat. Nico Santos"
For my feeling it is required to use the consolodated string when assigning the Artist ID.
What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the recording id and the title? I will not make up a custom correlation logic without any foundation.

I can only offer to implement one of the following options:

  • Overwrite all fields unconditionally
  • Set single fields one-by-one conditionally (current version)

If someone knows how to correlate the fields in a consistent manner, please do so. I refuse to do it, because I am not able to come up with a solution that is supposed to be "correct".

updatedMetadata.refAlbumInfo().setMusicBrainzArtistId(
importedMetadata.getAlbumInfo().getMusicBrainzArtistId()
);
}
if (!importedMetadata.getAlbumInfo().getMusicBrainzReleaseId().isNull()) {
updatedMetadata.refAlbumInfo().setMusicBrainzReleaseId(
importedMetadata.getAlbumInfo().getMusicBrainzReleaseId());
}
if (!importedMetadata.getAlbumInfo().getMusicBrainzReleaseGroupId().isNull()) {
updatedMetadata.refAlbumInfo().setMusicBrainzReleaseGroupId(
importedMetadata.getAlbumInfo().getMusicBrainzReleaseGroupId());
}
#endif // __EXTRA_METADATA__
m_track->importMetadata(std::move(updatedMetadata));
}
}

Expand Down Expand Up @@ -147,13 +226,21 @@ void DlgTagFetcher::updateStack() {
results->clear();

addDivider(tr("Original tags"), results);
addTrack(m_track, -1, results);
addTrack(track2row(*m_track), -1, results);

addDivider(tr("Suggested tags"), results);

int trackIndex = 0;
foreach (const TrackPointer track, m_data.m_results) {
addTrack(track, trackIndex++, results);
{
int trackIndex = 0;
QSet<QStringList> trackRows;
foreach (const TrackPointer track, m_data.m_results) {
const auto trackRow = track2row(*track);
// Ignore duplicate results
if (!trackRows.contains(trackRow)) {
trackRows.insert(trackRow);
addTrack(trackRow, trackIndex, results);
}
++trackIndex;
}
}

// Find the item that was selected last time
Expand All @@ -167,17 +254,6 @@ void DlgTagFetcher::updateStack() {
}
}

void DlgTagFetcher::addTrack(const TrackPointer track, int resultIndex,
QTreeWidget* parent) const {
QStringList values;
values << track->getTrackNumber() << track->getYear() << track->getTitle()
<< track->getArtist() << track->getAlbum();

QTreeWidgetItem* item = new QTreeWidgetItem(parent, values);
item->setData(0, Qt::UserRole, resultIndex);
item->setData(0, Qt::TextAlignmentRole, Qt::AlignRight);
}

void DlgTagFetcher::addDivider(const QString& text, QTreeWidget* parent) const {
QTreeWidgetItem* item = new QTreeWidgetItem(parent);
item->setFirstColumnSpanned(true);
Expand Down
2 changes: 0 additions & 2 deletions src/library/dlgtagfetcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ class DlgTagFetcher : public QDialog, public Ui::DlgTagFetcher {
private:
void updateStack();
void addDivider(const QString& text, QTreeWidget* parent) const;
void addTrack(const TrackPointer track, int resultIndex,
QTreeWidget* parent) const;

TagFetcher m_tagFetcher;

Expand Down
15 changes: 10 additions & 5 deletions src/library/dlgtagfetcher.ui
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,32 @@
</attribute>
<column>
<property name="text">
<string>Track</string>
<string>Year</string>
</property>
</column>
<column>
<property name="text">
<string>Year</string>
<string>Album</string>
</property>
</column>
<column>
<property name="text">
<string>Title</string>
<string>Album Artist</string>
</property>
</column>
<column>
<property name="text">
<string>Artist</string>
<string>Track</string>
</property>
</column>
<column>
<property name="text">
<string>Album</string>
<string>Title</string>
</property>
</column>
<column>
<property name="text">
<string>Artist</string>
</property>
</column>
</widget>
Expand Down
Loading