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

[WIP] Livemetadata PR #1675

Closed
wants to merge 79 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
32cece7
Added .vscode on gitignore
davidhm May 5, 2018
ba829a2
First few files
davidhm May 14, 2018
5169748
So far manual tests are working
davidhm May 16, 2018
3d8c501
Pre automatic tests
davidhm May 18, 2018
523d09b
Using github as a backup
davidhm May 18, 2018
23ef862
Error when compiling moc generated cpp
davidhm May 20, 2018
0a4a4d6
WIP: Automatic tests
davidhm May 23, 2018
d203e85
Moved timers away from Track object
davidhm May 24, 2018
1a6bc0d
Pre volume scrobbling
davidhm May 26, 2018
ff27795
Pre tests, scrobbling manager
davidhm May 27, 2018
db2db23
Deadlock
davidhm May 30, 2018
7f55150
Solved the deadlock, simple tests pass
davidhm Jun 1, 2018
121443e
WIP: Adding file listener
davidhm Jun 2, 2018
2cd5bbc
Fixed file buffer, it's automatically updated now
davidhm Jun 3, 2018
b9c4a6c
[Untested] Fixed file info, modified metadatabroadcast
davidhm Jun 5, 2018
e10df9e
[WIP] Adding tests for scrobbling manager
davidhm Jun 7, 2018
c1de7db
Added first test for scrobbling manager
davidhm Jun 9, 2018
50019b0
File listener template + factory
davidhm Jun 10, 2018
d805306
Scrobbling tests done
davidhm Jun 12, 2018
4e714d5
[WIP] Adding file preferences
davidhm Jun 12, 2018
b3f8ea8
Preferences mock-up
davidhm Jun 13, 2018
8cd9c16
Changed everything to a weak pointer
davidhm Jun 13, 2018
d4b9aa5
Added file listener path in options
davidhm Jun 14, 2018
ec98ef6
[WIP] Persistent config now playing file
davidhm Jun 14, 2018
0209b4a
[WIP] Requested changes
davidhm Jun 15, 2018
2d8f4a2
Adding new tab
davidhm Jun 17, 2018
88c4819
Added mock-up in preferences
davidhm Jun 17, 2018
ab8b524
Table view mockup
davidhm Jun 18, 2018
97daf77
Pre changing prefmetadata class
davidhm Jun 19, 2018
4bc2de3
Added file settings
davidhm Jun 20, 2018
49b8b93
[Untested] Added options to file listener
davidhm Jun 20, 2018
567e9b5
Modified settings, added concurrency
davidhm Jun 23, 2018
5748fa9
Added dedicated thread
davidhm Jun 26, 2018
3a677b6
Modified author and title string
davidhm Jun 26, 2018
9c43d09
Merge branch 'master' into Livemetadata
davidhm Jun 26, 2018
2c21183
Added connections to lambda expressions in player manager, file liste…
davidhm Jun 28, 2018
a6d67d7
Fixed tests failing
davidhm Jun 29, 2018
ba8a6fc
Added mock network manager, gotta test with mock server
davidhm Jul 1, 2018
6b7f207
Now listening scrobbles work with ListenBrainz
davidhm Jul 5, 2018
f7c6a83
Deleted log file
davidhm Jul 5, 2018
1057b06
Merge branch 'master' into Livemetadata
davidhm Jul 6, 2018
152925c
ListenBrainz full scrobbles work now too
davidhm Jul 6, 2018
ad43628
Fixed double delete in developer mode
davidhm Jul 7, 2018
751f567
Forgot to compile
davidhm Jul 7, 2018
64d35c5
Modified metadata file options
davidhm Jul 7, 2018
ddfb859
Editable combobox
davidhm Jul 9, 2018
e4dc8df
Added mpris stub, not working
davidhm Jul 11, 2018
a1ad4c6
MPRIS now reflects the playback state
davidhm Jul 12, 2018
ee4c5a5
Disabled failing test until a solution is found
davidhm Jul 13, 2018
4571d43
Fixed broken tests, continuing MPRIS implementation
davidhm Jul 18, 2018
185d2f2
Fixed non compiling build
davidhm Jul 18, 2018
0bc22be
Segfault on weak_ptr
davidhm Jul 19, 2018
0af2279
Fixed eject bug
davidhm Jul 19, 2018
6be0042
Mpris reflects AutoDJ enabled
davidhm Jul 19, 2018
62eae49
Pause, play and go next MPRIS functions work
davidhm Jul 21, 2018
c2317c7
Fixed correct fade-in in MPRIS
davidhm Jul 22, 2018
8b5e54a
Added volume, playback status and loop status as well as a LINUX define
davidhm Jul 23, 2018
cac8c1f
Moved MPRIS into a feature
davidhm Jul 24, 2018
6807838
Added MPRIS macro in includes too
davidhm Jul 24, 2018
d6880a1
MPRIS is seekable now
davidhm Jul 24, 2018
32e565a
MPRIS broadcasts current track and rate works now too
davidhm Jul 25, 2018
1b77c8b
Modified linked lists into hash maps
davidhm Aug 1, 2018
7ba10a7
Added space after commas
davidhm Aug 1, 2018
94ab525
Changed ref ampersand and disabled non working test
davidhm Aug 1, 2018
5e8fb60
Added more button in encoding combo box
davidhm Aug 3, 2018
41bdfb9
Resized combobox
davidhm Aug 6, 2018
0def3e3
Merge remote-tracking branch 'upstream/master' into Livemetadata
davidhm Aug 6, 2018
9fe5277
Added few UI suggestions
davidhm Aug 6, 2018
97271d8
Missing include
davidhm Aug 7, 2018
f4b9649
Added cover art to mpris
davidhm Aug 11, 2018
dde6c47
Added cover art to MPRIS player
davidhm Aug 12, 2018
e1cfd7a
Fixed few things
davidhm Aug 14, 2018
69a3020
Deleting all cover art files
davidhm Aug 17, 2018
7af4dd7
Single file image
davidhm Aug 17, 2018
170e6c9
Cover art file is now QTemporaryFile
davidhm Aug 18, 2018
8c6fa58
Revamped encoding combobox
davidhm Aug 18, 2018
899f207
Fixed some cover URL generation issues
daschuer Aug 19, 2018
4a13665
Small fixes
davidhm Aug 20, 2018
39dc901
Merge pull request #1 from daschuer/Livemetadata
davidhm Aug 20, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ available_features = [features.Mad,
features.LocaleCompare,
features.Lilv,
features.Battery,
features.MPRIS,

# "Features" of dubious quality
features.PerfTools,
Expand Down
27 changes: 26 additions & 1 deletion build/depends.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ def configure(self, build, conf):
class TestHeaders(Dependence):
def configure(self, build, conf):
build.env.Append(CPPPATH="#lib/gtest-1.7.0/include")
build.env.Append(CPPPATH="#lib/gmock-1.7.0/include")

class FidLib(Dependence):
def sources(self, build):
Expand Down Expand Up @@ -699,6 +700,13 @@ def configure(self, build, conf):
raise Exception(
"Could not find qtkeychain.")

class QtDBus(Dependence):
def configure(self, build, conf):
libs = ['Qt5DBus']
if not conf.CheckLib(libs):
raise Exception('Couldn\'t find Qt5Dbus5 library.')


class MixxxCore(Feature):

def description(self):
Expand All @@ -724,6 +732,16 @@ def sources(self, build):
"control/controlttrotary.cpp",
"control/controlencoder.cpp",

"broadcast/metadatabroadcast.cpp",
"broadcast/scrobblingmanager.cpp",
"broadcast/filelistener/filelistener.cpp",
"broadcast/filelistener/metadatafileworker.cpp",
"broadcast/listenbrainzlistener/networkrequest.cpp",
"broadcast/listenbrainzlistener/networkmanager.cpp",
"broadcast/listenbrainzlistener/networkreply.cpp",
"broadcast/listenbrainzlistener/listenbrainzservice.cpp",
"broadcast/listenbrainzlistener/listenbrainzjsonfactory.cpp",

"controllers/dlgcontrollerlearning.cpp",
"controllers/dlgprefcontroller.cpp",
"controllers/dlgprefcontrollers.cpp",
Expand All @@ -737,6 +755,7 @@ def sources(self, build):
"preferences/dialog/dlgprefeffects.cpp",
"preferences/dialog/dlgprefeq.cpp",
"preferences/dialog/dlgpreferences.cpp",
"preferences/dialog/dlgprefmetadata.cpp",
"preferences/dialog/dlgprefinterface.cpp",
"preferences/dialog/dlgpreflibrary.cpp",
"preferences/dialog/dlgprefnovinyl.cpp",
Expand All @@ -752,6 +771,8 @@ def sources(self, build):
"preferences/broadcastsettingsmodel.cpp",
"preferences/effectsettingsmodel.cpp",
"preferences/broadcastprofile.cpp",
"preferences/metadatafilesettings.cpp",
"preferences/listenbrainzsettings.cpp",
"preferences/upgrade.cpp",
"preferences/dlgpreferencepage.cpp",

Expand Down Expand Up @@ -1155,6 +1176,8 @@ def sources(self, build):
"track/trackinfo.cpp",
"track/trackrecord.cpp",
"track/trackref.cpp",
"track/trackplaytimers.cpp",
"track/tracktiminginfo.cpp",

"mixer/auxiliary.cpp",
"mixer/baseplayer.cpp",
Expand Down Expand Up @@ -1266,6 +1289,8 @@ def sources(self, build):
'preferences/dialog/dlgprefautodjdlg.ui',
'preferences/dialog/dlgprefbeatsdlg.ui',
'preferences/dialog/dlgprefdeckdlg.ui',
'preferences/dialog/dlgprefmetadatadlg.ui',
'preferences/dialog/dlgfilelistenerbox.ui',
'preferences/dialog/dlgprefcrossfaderdlg.ui',
'preferences/dialog/dlgpreflv2dlg.ui',
'preferences/dialog/dlgprefeffectsdlg.ui',
Expand Down Expand Up @@ -1356,7 +1381,7 @@ def configure(self, build, conf):
# http://clang.llvm.org/docs/ThreadSafetyAnalysis.html
build.env.Append(CCFLAGS='-Wthread-safety')
build.env.Append(CCFLAGS='-Wextra')

build.env.Append(CCFLAGS='-Werror=return-type')
# Always generate debugging info.
build.env.Append(CCFLAGS='-g')
elif build.toolchain_is_msvs:
Expand Down
34 changes: 34 additions & 0 deletions build/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -1363,3 +1363,37 @@ def sources(self, build):

def depends(self, build):
return [depends.QtKeychain]

class MPRIS(Feature):
def description(self):
return "MPRIS implementation using QtDbus"

def enabled(self,build):
if build.platform_is_linux:
build.flags['mpris'] = util.get_flags(build.env, 'mpris', 1)
else:
build.flags['mpris'] = util.get_flags(build.env, 'mpris', 0)
if int(build.flags['mpris']):
return True
return False

def add_options(self, build, vars):
vars.Add('mpris', 'Set to 1 to enable MPRIS implementation to broadcast current track and interact with Mixxx', 0)

def configure(self,build,conf):
if build.platform_is_linux and self.enabled(build):
build.env.Append(CPPDEFINES='__MPRIS__')

def sources(self,build):
if build.platform_is_linux:
return ["broadcast/mpris/mprisservice.cpp",
"broadcast/mpris/mpris.cpp",
"broadcast/mpris/mediaplayer2.cpp",
"broadcast/mpris/mediaplayer2player.cpp",
"broadcast/mpris/mprisplayer.cpp",
"broadcast/mpris/mediaplayer2playlists.cpp",
"broadcast/mpris/mediaplayer2tracklist.cpp"]
return []
def depends(self,build):
return [depends.QtDBus]

2 changes: 1 addition & 1 deletion src/broadcast/broadcastmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ BroadcastManager::BroadcastManager(SettingsManager* pSettingsManager,
m_pNetworkStream(pSoundManager->getNetworkStream()) {
const bool persist = true;
m_pBroadcastEnabled = new ControlPushButton(
ConfigKey(BROADCAST_PREF_KEY,"enabled"), persist);
ConfigKey(BROADCAST_PREF_KEY, "enabled"), persist);
m_pBroadcastEnabled->setButtonMode(ControlPushButton::TOGGLE);
connect(m_pBroadcastEnabled, SIGNAL(valueChanged(double)),
this, SLOT(slotControlEnabled(double)));
Expand Down
117 changes: 117 additions & 0 deletions src/broadcast/filelistener/filelistener.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@

#include <QTextCodec>
#include <QtConcurrentRun>

#include "broadcast/filelistener/filelistener.h"
#include "broadcast/filelistener/metadatafileworker.h"
#include "preferences/metadatafilesettings.h"


FileListener::FileListener(UserSettingsPointer pConfig)
: m_COsettingsChanged(kFileSettingsChanged),
m_pConfig(pConfig),
m_latestSettings(MetadataFileSettings::getPersistedSettings(pConfig)),
m_filePathChanged(false),
m_tracksPaused(false) {

MetadataFileWorker *newWorker = new MetadataFileWorker(m_latestSettings.filePath);
Copy link
Member

Choose a reason for hiding this comment

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

Where is this one deleted?
You can make it a patented_ptr to let Qt take care of this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When the signal finished from the thread is emitted the object is deleted.

Copy link
Member

Choose a reason for hiding this comment

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

That is hard to spot. Where exactly is it?
We should follow the rule to pass the pointer with the ownership. Since this is a QObject, the qt object tee may works best as an owner.

newWorker->moveToThread(&m_workerThread);

connect(&m_workerThread, SIGNAL(finished()),
newWorker, SLOT(deleteLater()));

connect(this, SIGNAL(deleteFile()),
newWorker, SLOT(slotDeleteFile()));

connect(this, SIGNAL(moveFile(QString)),
newWorker, SLOT(slotMoveFile(QString)));

connect(this, SIGNAL(writeMetadataToFile(QByteArray)),
newWorker, SLOT(slotWriteMetadataToFile(QByteArray)));

connect(this, SIGNAL(clearFile()),
newWorker, SLOT(slotClearFile()));

connect(&m_COsettingsChanged, SIGNAL(valueChanged(double)),
this, SLOT(slotFileSettingsChanged(double)));

m_workerThread.start();
}

FileListener::~FileListener() {
emit clearFile();
m_workerThread.quit();
m_workerThread.wait();
}


void FileListener::slotBroadcastCurrentTrack(TrackPointer pTrack) {
if (!m_latestSettings.enabled) {
return;
}
if (!pTrack) {
emit clearFile();
}
else {
m_fileContents.title = pTrack->getTitle();
m_fileContents.artist = pTrack->getArtist();
QString writtenString(m_latestSettings.fileFormatString);
writtenString.replace("$author", pTrack->getArtist()).
replace("$title", pTrack->getTitle()) += '\n';
QTextCodec *codec = QTextCodec::codecForName(m_latestSettings.fileEncoding);
DEBUG_ASSERT(codec);
QByteArray fileContents = codec->fromUnicode(writtenString);
m_tracksPaused = false;
emit writeMetadataToFile(fileContents);
}
}

void FileListener::slotScrobbleTrack(TrackPointer pTrack) {
Q_UNUSED(pTrack);
}

void FileListener::slotAllTracksPaused() {
if (!m_latestSettings.enabled) {
return;
}
m_tracksPaused = true;
emit clearFile();
}

void FileListener::slotFileSettingsChanged(double value) {
if (value) {
FileSettings latestSettings = MetadataFileSettings::getLatestSettings();
m_filePathChanged = latestSettings.filePath != m_latestSettings.filePath;
m_latestSettings = latestSettings;
updateStateFromSettings();
}
}

void FileListener::updateStateFromSettings() {
if (m_latestSettings.enabled) {
updateFile();
}
else {
emit deleteFile();
}
}

void FileListener::updateFile() {
if (m_filePathChanged) {
emit moveFile(m_latestSettings.filePath);
m_filePathChanged = false;
}
Copy link
Member

Choose a reason for hiding this comment

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

I think m_filePathChanged = false; is missing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, thanks.

if (!m_tracksPaused && !m_fileContents.isEmpty()) {
QTextCodec *codec = QTextCodec::codecForName(m_latestSettings.fileEncoding);
if (!codec) {
qWarning() << "Text codec selected from metadata broadcast settings doesn't exist";
codec = QTextCodec::codecForName("UTF-8");
}
QString newContents(m_latestSettings.fileFormatString);
newContents.replace("$author", m_fileContents.artist)
.replace("$title", m_fileContents.title) += '\n';
QByteArray contentsBinary = codec->fromUnicode(newContents);
emit writeMetadataToFile(contentsBinary);
}
}

43 changes: 43 additions & 0 deletions src/broadcast/filelistener/filelistener.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

#include <QFile>
#include <QThread>
#include "preferences/dialog/dlgprefmetadata.h"
#include "control/controlpushbutton.h"
#include "broadcast/scrobblingservice.h"

class FileListener: public ScrobblingService {
Q_OBJECT
public:
explicit FileListener(UserSettingsPointer pSettings);
~FileListener() override;
void slotBroadcastCurrentTrack(TrackPointer pTrack) override;
void slotScrobbleTrack(TrackPointer pTrack) override;
void slotAllTracksPaused() override;
signals:
void deleteFile();
void moveFile(QString destination);
void writeMetadataToFile(QByteArray contents);
void clearFile();
private slots:
void slotFileSettingsChanged(double value);
private:

struct WrittenMetadata {
QString title, artist;
bool isEmpty() {
return title.isEmpty() && artist.isEmpty();
}
};

void updateStateFromSettings();
void updateFile();

ControlPushButton m_COsettingsChanged;
UserSettingsPointer m_pConfig;
FileSettings m_latestSettings;
QThread m_workerThread;
WrittenMetadata m_fileContents;
bool m_filePathChanged;
bool m_tracksPaused;
};
29 changes: 29 additions & 0 deletions src/broadcast/filelistener/metadatafileworker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

#include "broadcast/filelistener/metadatafileworker.h"

MetadataFileWorker::MetadataFileWorker(const QString& filePath)
: m_file(filePath) {
}

void MetadataFileWorker::slotDeleteFile() {
m_file.remove();
}

void MetadataFileWorker::slotMoveFile(QString destination) {
m_file.remove();
m_file.setFileName(destination);
}

void MetadataFileWorker::slotWriteMetadataToFile(QByteArray fileContents) {
m_file.open(QIODevice::WriteOnly |
QIODevice::Text |
QIODevice::Unbuffered);
m_file.write(fileContents);
m_file.close();
}

void MetadataFileWorker::slotClearFile() {
m_file.resize(0);
}


18 changes: 18 additions & 0 deletions src/broadcast/filelistener/metadatafileworker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <QObject>
#include <QFile>

class MetadataFileWorker : public QObject {
Q_OBJECT
public:
explicit MetadataFileWorker(const QString& filePath);
public slots:
void slotDeleteFile();
void slotMoveFile(QString destination);
void slotWriteMetadataToFile(QByteArray fileContents);
void slotClearFile();
private:
QFile m_file;
};

38 changes: 38 additions & 0 deletions src/broadcast/listenbrainzlistener/listenbrainzjsonfactory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

#include <QDateTime>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>

#include "broadcast/listenbrainzlistener/listenbrainzjsonfactory.h"


QByteArray ListenBrainzJSONFactory::getJSONFromTrack(TrackPointer pTrack, JsonType type) {
QJsonObject jsonObject;
QString stringType;
if (type == NowListening) {
stringType = "playing_now";
}
else {
stringType = "single";
}

QJsonArray payloadArray;
QJsonObject payloadObject;
QJsonObject metadataObject;
QString title = pTrack->getTitle();
QString artist = pTrack->getArtist();
metadataObject.insert("artist_name", artist);
metadataObject.insert("track_name", title);
payloadObject.insert("track_metadata", metadataObject);
qint64 timeStamp = QDateTime::currentMSecsSinceEpoch() / 1000;

if (type == Single) {
payloadObject.insert("listened_at", timeStamp);
}
payloadArray.append(payloadObject);
jsonObject.insert("listen_type", stringType);
jsonObject.insert("payload", payloadArray);
QJsonDocument doc(jsonObject);
return doc.toJson(QJsonDocument::Compact);
}
Loading