-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
expose current track metadata to external APIs #3483
base: main
Are you sure you want to change the base?
Conversation
c5f2ee0
to
982be0f
Compare
All green now. @poelzi Please give this a try. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Smaller nitpicks and the DEBUG ASSERT needs to proper handle fallback.
Have not tested listenbrainz, but mrpis works nice. Also tested the dbus api.
m_fileContents.artist = pTrack->getArtist(); | ||
QString writtenString(m_latestSettings.fileFormatString); | ||
writtenString.replace("$author", pTrack->getArtist()) | ||
.replace("$title", pTrack->getTitle()) += '\n'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use '{{ track.title }}' and '{{ track.artist }}' here ?
I'm working on better track export functionality and have added the grantlee library for that, so you can define patterns that will become the path name for the exported files. With this syntax we will later be able to switch to a grantlee template and have the full template power later.
} | ||
|
||
void MediaPlayer2::Quit() { | ||
QApplication::quit(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to call the MixxxApplication to quit here otherwise the controlobjects are not destroyed in order, cause lots of Debug Asserts
src/broadcast/mpris/mediaplayer2.cpp
Outdated
} | ||
|
||
void MediaPlayer2::Raise() { | ||
m_pMixxx->raise(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not work when called over dbus.
QStringList ret; | ||
ret << "audio/mpeg" | ||
<< "audio/ogg"; | ||
return ret; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we build something from the supported codecs ? At least wav, flac, mp3, aiff should be in the list
src/broadcast/mpris/mprisplayer.cpp
Outdated
|
||
namespace { | ||
|
||
const QString kPlaybackStatusPlaying = "Playing"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const QString kPlaybackStatusPlaying = "Playing"; | |
const QLatin1String kPlaybackStatusPlaying = "Playing"; |
metadata.insert("mpris:length", m_currentMetadata.trackDuration); | ||
metadata.insert("xesam:artist", m_currentMetadata.artists); | ||
metadata.insert("xesam:title", m_currentMetadata.title); | ||
metadata.insert("mpris:artUrl", m_currentMetadata.coverartUrl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata/#index4h4
I think those would be interesting as well:
album -> xesam:album
rating -> xesam:userRating
play_counter -> xesam:useCount
src/broadcast/mpris/mprisplayer.cpp
Outdated
} | ||
|
||
qlonglong MprisPlayer::seek(qlonglong offset, bool& success) { | ||
if (autoDjIdle()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should use canSeek() here
} | ||
|
||
bool MprisPlayer::canSeek() const { | ||
return autoDjIdle(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this check is wrong when the last track is playing and autodj is already off. you can still seek in the track running.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is also wrong if auto DJ is off and user plays a track manually.
I think a fix will be part of general overhaul.
src/broadcast/mpris/mprisplayer.cpp
Outdated
if (playingDeck == nullptr) { | ||
ControlProxy playing(ConfigKey(m_pausedDeck, "play")); | ||
BaseTrackPlayer* player = m_pPlayerManager->getPlayer(m_pausedDeck); | ||
DEBUG_ASSERT(player); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had one crash here that I could not reproduce later:
3:41:31.777 [Main] debug TrackDAO::updateTrack TrackDAO: Updating track in database 7726 QFileInfo(/home/poelzi/Music/psyblast/[Omveda Records]/Omveda Records - XIII Monkeys - Psychedelic Renaissance (FREE)/XIII MONKEYS - XIII Monkeys - Psychedelic Renaissance (FREE) - 01 Alien Agenda.flac)
3:41:31.777 [Main] debug mixxx::Logger::debug SqlTransaction - Started new SQL database transaction on "MIXXX-1"
3:41:32.131 [Main] debug mixxx::Logger::debug SqlTransaction - Committed SQL database transaction on "MIXXX-1"
3:41:32.131 [Main] debug BaseTrackCache::updateIndexWithQuery BaseTrackCache(0xb16c2e0) updateIndexWithQuery took 0 ms
3:41:32.131 [Main] debug mixxx::Logger::debug GlobalTrackCache - Deleting Track(0x7f7ba83fb480)
3:41:32.134 [Main] debug AutoDJProcessor::calculateTransition player "[Channel2]" calculateTransition()
3:41:32.134 [Controller] debug unknown CDBG XONE:K2 MIDI 1: outgoing: status 0x9E (ch 15, opcode 0x9), ctrl 0x2D, val 0x7F
3:41:32.134 [Controller] debug unknown CDBG XONE:K2 MIDI 1: outgoing: status 0x9D (ch 14, opcode 0x9), ctrl 0x2C, val 0x7F
3:41:32.134 [Controller] debug unknown CDBG XONE:K2 MIDI 1: outgoing: status 0x9C (ch 13, opcode 0x9), ctrl 0x2C, val 0x7F
3:41:32.134 [Controller] debug unknown CDBG XONE:K2 MIDI 1: outgoing: status 0x9B (ch 12, opcode 0x9), ctrl 0x2E, val 0x7F
3:41:32.134 [Controller] debug unknown CDBG XONE:K2 MIDI 1: outgoing: status 0x9A (ch 11, opcode 0x9), ctrl 0x2D, val 0x7F
3:41:32.134 [Controller] debug unknown CDBG XONE:K2 MIDI 1: outgoing: status 0x99 (ch 10, opcode 0x9), ctrl 0x2C, val 0x7F
3:41:32.141 [Main] debug mixxx::Logger::debug AnalyzerThread - Enqueueing next track 7728
3:41:32.141 [AnalyzerThread 0 #1] debug mixxx::Logger::debug AnalyzerThread - Dequeued next track 7728
3:41:32.141 [AnalyzerThread 0 #1] debug mixxx::Logger::debug AnalyzerThread - Analyzing QFileInfo(/home/poelzi/Music/psyblast/[Omveda Records]/Omveda Records - XIII Monkeys - Psychedelic Renaissance (FREE)/XIII MONKEYS - XIII Monkeys - Psychedelic Renaissance (FREE) - 02 Psychedelic Renaissance.flac)
3:41:32.142 [AnalyzerThread 0 #1] debug mixxx::Logger::debug SoundSourceProxy - SoundSourceProvider "Xiph.org libFLAC" created a SoundSource for file "file:///home/poelzi/Music/psyblast/[Omveda Records]/Omveda Records - XIII Monkeys - Psychedelic Renaissance (FREE)/XIII MONKEYS - XIII Monkeys - Psychedelic Renaissance (FREE) - 02 Psychedelic Renaissance.flac" of type "flac"
3:41:32.142 [AnalyzerThread 0 #1] debug AnalysisDao::loadAnalysesFromQuery AnalysisDAO fetched 0 analyses, 0 bytes for track 7728 in 0 ms
3:41:32.143 [AnalyzerThread 0 #1] debug AnalyzerEbur128::initialize Skipping AnalyzerEbur128
3:41:32.143 [AnalyzerThread 0 #1] debug AnalyzerBeats::initialize AnalyzerBeats preference settings:
Plugin: "qm-tempotracker:0"
Min/Max BPM: 70 250
Fixed tempo assumption: true
Offset correction: true
Re-analyze when settings change: false
Fast analysis: false
3:41:32.143 [AnalyzerThread 0 #1] debug AnalyzerKey::initialize AnalyzerKey preference settings:
Plugin: "qm-keydetector:2"
Re-analyze when settings change: false
Fast analysis: false
3:41:32.143 [AnalyzerThread 0 #1] debug AnalyzerKey::shouldAnalyze Track has previous key detection result that is not up to date with latest settings but user preferences indicate we should not re-analyze it.
3:41:36.193 [AnalyzerThread 0 #1] debug Waveform::toByteArray Writing waveform from byte array: dataSize 371166 allSignalSize 371166 visualSampleRate 441 audioVisualRatio 100
3:41:36.729 [AnalyzerThread 0 #1] debug AnalysisDao::saveAnalysis AnalysisDAO saved analysis 7007 "3396322 (1601831 compressed)" bytes for track 7728 in 523 ms
3:41:36.729 [AnalyzerThread 0 #1] debug AnalysisDao::saveTrackAnalyses Saved waveform analysis for trackId 7728 analysisId 7007
3:41:36.729 [AnalyzerThread 0 #1] debug Waveform::toByteArray Writing waveform from byte array: dataSize 3840 allSignalSize 3840 visualSampleRate 4.5625 audioVisualRatio 9665.76
3:41:36.741 [AnalyzerThread 0 #1] debug AnalysisDao::saveAnalysis AnalysisDAO saved analysis 7008 "34941 (15475 compressed)" bytes for track 7728 in 11 ms
3:41:36.741 [AnalyzerThread 0 #1] debug AnalysisDao::saveTrackAnalyses Saved waveform summary analysis for trackId 7728 analysisId 7008
3:41:36.741 [AnalyzerThread 0 #1] debug mixxx::Logger::debug AnalyzerWaveform - Waveform generation for track 7728 done 4 s
3:41:37.269 [Main] critical DEBUG ASSERT: "key.isValid() || flags.testFlag(ControlFlag::AllowInvalidKey)" in function ControlProxy::ControlProxy(const ConfigKey &, QObject *, ControlFlags) at /home/poelzi/Projects/mixxx-test/src/control/controlproxy.cpp:19
3:41:37.270 [Main] critical DEBUG ASSERT: "player" in function void MprisPlayer::play() at /home/poelzi/Projects/mixxx-test/src/broadcast/mpris/mprisplayer.cpp:203
Segmentation fault (core dumped)
@daschuer thanks for picking this one up, it would have taken quite some time for me. apart from smaller suggestions and one crash, this branch works nice. |
Now the crash is fixed. |
Please add comments in the header of every new class explaining its purpose. |
src/broadcast/mpris/mpris.cpp
Outdated
// they are connected to is also deleted). | ||
// http://doc.qt.io/qt-5/qdbusabstractadaptor.html | ||
new MediaPlayer2(pMixxx, this); | ||
m_busConnection.registerObject("/org/mpris/MediaPlayer2", this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QLatin1String?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC QLatin1String can be used when comparing a QString to a literal value. When constructing an actual QString, I think QStringLiteral should be used. Here we want the latter.
src/broadcast/mpris/mprisplayer.cpp
Outdated
// the playback will stop when there are no more tracks to play | ||
const QString kLoopStatusNone = QStringLiteral("None"); | ||
// The current track will start again from the begining once it has finished playing | ||
const QString kLoopStatusTrack = QStringLiteral("Track"); | ||
// The playback loops through a list of tracks | ||
const QString kLoopStatusPlaylist = QStringLiteral("Playlist"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't these be translated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, those are status flags for the dbus api
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. It would help to add a comment to clarify.
|
||
ScrobblingManager::ScrobblingManager(UserSettingsPointer pConfig, | ||
std::shared_ptr<PlayerManager> pPlayerManager, | ||
MixxxMainWindow* pWindow) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused, please remove. Let's decouple application logic from QWidgets as much as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is used for the window control in mpris.
src/coreservices.cpp
Outdated
@@ -114,7 +115,7 @@ void CoreServices::initializeSettings() { | |||
m_pSettingsManager = std::make_unique<SettingsManager>(settingsPath); | |||
} | |||
|
|||
void CoreServices::initialize(QApplication* pApp) { | |||
void CoreServices::initialize(QApplication* pApp, MixxxMainWindow* pMixxx) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revert this! This defeats the purpose of the CoreServices class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is used for the window control in mpris.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coupling CoreServices to QWidgets is a big step backwards. Please find another way to implement this. Using signals and slots instead could work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is not an issue, because it is only the forward declarated class. CoreServices passes only the pointer, without caring what it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is an issue to knowingly create problems for other branches in progress.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please explain the issue that it creates.
We only pass a pointer. This can be even a void pointer in CoreService and we can cast it MixxxMainWindow in MPRIS. But making use of the compilers type safety is better.
@@ -22,6 +22,8 @@ class VinylControlManager; | |||
class TrackCollectionManager; | |||
class Library; | |||
class LV2Backend; | |||
class MixxxMainWindow; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is used for the window control in mpris.
src/broadcast/mpris/mediaplayer2.cpp
Outdated
} | ||
|
||
void MediaPlayer2::setFullscreen(bool fullscreen) { | ||
m_pMixxx->slotViewFullScreen(fullscreen); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The QWidget dependency can be removed from this class by emitting a signal here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we can emit a signal here, the issue is that someone needs to connect the MediaPlayer2 signal to the window.
This way we get mpris internals into the window class which is also a bad idea.
In addition we have also:
m_pMixxx->isFullScreen();
m_pMixxx->raise();
As info in the reverse direction.
Form this point of view the solution is not bad.
If we have one day another window technology we "just" need to implement a common interface for both technologies.
For now the current solution is just fine, because we don't know the requirements of the other window system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can do some proper common interface when the QMLApplication is in some proper state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree. We know this conflicts with #3638. That IMO must be resolved before merging this.
src/broadcast/mpris/mediaplayer2.cpp
Outdated
} | ||
|
||
bool MediaPlayer2::fullscreen() const { | ||
return m_pMixxx->isFullScreen(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can we get this information without m_pMixxx? Or maybe remove it? Is it really needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could track the signals from the mainwindow whne switching from/to fullscreen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to the connect call we get the coupling anyway. So there is not really a benefit using the complexity of signal and slots.
</rect> | ||
</property> | ||
<property name="windowTitle"> | ||
<string>Metadata Broadcast Preferences</string> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<string>Metadata Broadcast Preferences</string> | |
<string>Metadata Output Preferences</string> |
I think the conflation of these new features with broadcasting would confuse a lot of users.
<item> | ||
<widget class="QCheckBox" name="enableFileListener"> | ||
<property name="text"> | ||
<string>Enable metadata file</string> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<string>Enable metadata file</string> | |
<string>Write metadata of current track to a file</string> |
<item> | ||
<layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
<item> | ||
<widget class="QPushButton" name="filePathButton"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels odd on the left of the QLineEdit. It is conventional to put it on the right.
<item> | ||
<widget class="QLabel" name="label_4"> | ||
<property name="text"> | ||
<string>User token. If you don't have one please register <a href="https://listenbrainz.org/login/">here</a></string> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<string>User token. If you don't have one please register <a href="https://listenbrainz.org/login/">here</a></string> | |
<string>User token. If you do not have one, <a href="https://listenbrainz.org/login/">register a ListenBrainz account</a>.</string> |
I'm trying to read this, but I'm not really understanding it;) Is this the only way to remotely control Mixxx? I want to load a file into one of its decks using an API. |
This PR needs some love to split it in review-able chunks to become part of the main branch. I would love to do it one day, but dragged away from it once again. Do you have some spare time and programming skills? First I like to understand your use case? How do you plan to select the track? Do you use a different process that manages your tracks ? On which OS are you? |
I feel you; too many balls in the air here, as well;) I was just planning to escape from it all mixing some music;). I intend to find tracks using Emacs or CLI, then issue a command of some sort to load it into Mixxx. I'm on NixOS. |
@madorian Have you read some of the more advanced techniques for finding tracks within the mixxx library? https://manual.mixxx.org/2.4/en/chapters/library#finding-tracks-search |
That could work and is kind of straight forward. So we need a mixxx_cli process that contacts the main mixxx process via a IPC interface. The project can be split into two parts:
For 1. Go ahead. Pandora's Box is open. What do you need? You may describe it as https://github.com/mixxxdj/proposals 2.: I would prefer to use a standard interface that can be used elsewhere. Like OSC or MPRiS. Will this work for you? |
@daschuer Yes, OSC is perfect and portable. I'm not familiar with MPRiS, but it seems very cool and I will read up on that. |
I just gave that a go and it work great! I haven't had a chance to look into the code much yet, but I have some early feedback on the feature
Also, as an early feedback on the code
This would be quite useful as this is adding a significant amount of file in the repo. Appreciate you may not be interested in finishing this PR, let me know if so and I'd be interested to help! Edit: Just noticed that |
I am very much interested to get this PR merged. My plan was to move the features into single PRs to make them review-able. But I got dragged away. Any help is welcome. My current priority is to release 2.4.2 and 2.5.0. |
Sounds good - glad to hear this is still alive! I'm removing |
This is a rebase of @davidhm Livemetadata #1675 PR on current main.
It fetaures: