Make scrobbler handle streams
This commit is contained in:
parent
5ba00b61be
commit
4abc650edf
|
@ -510,6 +510,8 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
|||
connect(app_->player(), SIGNAL(VolumeChanged(int)), osd_, SLOT(VolumeChanged(int)));
|
||||
connect(app_->player(), SIGNAL(VolumeChanged(int)), ui_->volume, SLOT(setValue(int)));
|
||||
connect(app_->player(), SIGNAL(ForceShowOSD(Song, bool)), SLOT(ForceShowOSD(Song, bool)));
|
||||
connect(app_->player(), SIGNAL(SendNowPlaying()), SLOT(SendNowPlaying()));
|
||||
|
||||
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), SLOT(SongChanged(Song)));
|
||||
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), app_->player(), SLOT(CurrentMetadataChanged(Song)));
|
||||
connect(app_->playlist_manager(), SIGNAL(EditingFinished(QModelIndex)), SLOT(PlaylistEditFinished(QModelIndex)));
|
||||
|
@ -854,15 +856,13 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
|||
if (!options.contains_play_options()) {
|
||||
LoadPlaybackStatus();
|
||||
}
|
||||
if (app_->scrobbler()->IsEnabled() && !app_->scrobbler()->IsOffline()) app_->scrobbler()->Submit();
|
||||
|
||||
RefreshStyleSheet();
|
||||
|
||||
qLog(Debug) << "Started" << QThread::currentThread();
|
||||
initialised_ = true;
|
||||
|
||||
app_->scrobbler()->ConnectError();
|
||||
if (app_->scrobbler()->IsEnabled() && !app_->scrobbler()->IsOffline()) app_->scrobbler()->Submit();
|
||||
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
|
@ -1101,10 +1101,18 @@ void MainWindow::MediaPlaying() {
|
|||
track_position_timer_->start();
|
||||
track_slider_timer_->start();
|
||||
UpdateTrackPosition();
|
||||
SendNowPlaying();
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::SendNowPlaying() {
|
||||
|
||||
PlaylistItemPtr item(app_->player()->GetCurrentItem());
|
||||
if (!item) return;
|
||||
|
||||
// Send now playing to scrobble services
|
||||
Playlist *playlist = app_->playlist_manager()->active();
|
||||
if (app_->scrobbler()->IsEnabled() && playlist && !playlist->nowplaying() && item->Metadata().is_metadata_good() && item->Metadata().length_nanosec() > 0) {
|
||||
if (app_->scrobbler()->IsEnabled() && playlist && !playlist->nowplaying() && item->Metadata().is_metadata_good()) {
|
||||
app_->scrobbler()->UpdateNowPlaying(item->Metadata());
|
||||
playlist->set_nowplaying(true);
|
||||
ui_->action_love->setEnabled(true);
|
||||
|
@ -2039,7 +2047,7 @@ void MainWindow::CommandlineOptionsReceived(const CommandlineOptions &options) {
|
|||
if (!options.urls().empty()) {
|
||||
|
||||
#ifdef HAVE_TIDAL
|
||||
for (const QUrl url : options.urls()) {
|
||||
for (const QUrl &url : options.urls()) {
|
||||
if (url.scheme() == "tidal" && url.host() == "login") {
|
||||
emit AuthorisationUrlReceived(url);
|
||||
return;
|
||||
|
@ -2671,7 +2679,7 @@ void MainWindow::LoveButtonVisibilityChanged(const bool value) {
|
|||
void MainWindow::SetToggleScrobblingIcon(const bool value) {
|
||||
|
||||
if (value) {
|
||||
if (app_->playlist_manager()->active()->scrobbled())
|
||||
if (app_->playlist_manager()->active() && app_->playlist_manager()->active()->scrobbled())
|
||||
ui_->action_toggle_scrobbling->setIcon(IconLoader::Load("scrobble", 22));
|
||||
else
|
||||
ui_->action_toggle_scrobbling->setIcon(IconLoader::Load("scrobble", 22)); // TODO: Create a faint version of the icon
|
||||
|
|
|
@ -258,6 +258,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
|||
void ScrobblingEnabledChanged(const bool value);
|
||||
void ScrobbleButtonVisibilityChanged(const bool value);
|
||||
void LoveButtonVisibilityChanged(const bool value);
|
||||
void SendNowPlaying();
|
||||
void Love();
|
||||
|
||||
void ExitFinished();
|
||||
|
|
|
@ -622,9 +622,8 @@ void Player::CurrentMetadataChanged(const Song &metadata) {
|
|||
if (app_->scrobbler()->IsEnabled() && engine_->state() == Engine::Playing) {
|
||||
Playlist *playlist = app_->playlist_manager()->active();
|
||||
current_item_ = playlist->current_item();
|
||||
if (playlist && current_item_ && !playlist->nowplaying() && current_item_->Metadata() == metadata && current_item_->Metadata().length_nanosec() > 0) {
|
||||
app_->scrobbler()->UpdateNowPlaying(metadata);
|
||||
playlist->set_nowplaying(true);
|
||||
if (playlist && current_item_ && !playlist->nowplaying() && current_item_->Metadata() == metadata && current_item_->Metadata().is_metadata_good()) {
|
||||
emit SendNowPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -121,6 +121,7 @@ class PlayerInterface : public QObject {
|
|||
// The toggle parameter is true when user requests to toggle visibility for Pretty OSD
|
||||
void ForceShowOSD(Song, bool toggle);
|
||||
|
||||
void SendNowPlaying();
|
||||
void Authenticated();
|
||||
|
||||
};
|
||||
|
|
|
@ -348,7 +348,7 @@ const QString &Song::cue_path() const { return d->cue_path_; }
|
|||
bool Song::has_cue() const { return !d->cue_path_.isEmpty(); }
|
||||
|
||||
bool Song::is_collection_song() const { return d->source_ == Source_Collection; }
|
||||
bool Song::is_metadata_good() const { return !d->title_.isEmpty() && !d->artist_.isEmpty() && !d->url_.isEmpty() && d->end_ > 0; }
|
||||
bool Song::is_metadata_good() const { return !d->url_.isEmpty() && !d->artist_.isEmpty() && !d->title_.isEmpty(); }
|
||||
bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal || d->source_ == Source_Subsonic || d->source_ == Source_Qobuz; }
|
||||
bool Song::is_cdda() const { return d->source_ == Source_CDDA; }
|
||||
bool Song::is_compilation() const { return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && !d->compilation_off_; }
|
||||
|
|
|
@ -611,7 +611,6 @@ void Playlist::set_current_row(int i, bool is_stopping) {
|
|||
|
||||
if (current_item_index_ == old_current_item_index) {
|
||||
UpdateScrobblePoint();
|
||||
nowplaying_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -649,7 +648,6 @@ void Playlist::set_current_row(int i, bool is_stopping) {
|
|||
}
|
||||
|
||||
UpdateScrobblePoint();
|
||||
nowplaying_ = false;
|
||||
|
||||
}
|
||||
|
||||
|
@ -1503,7 +1501,7 @@ void Playlist::SetStreamMetadata(const QUrl &url, const Song &song, const bool m
|
|||
|
||||
//qLog(Debug) << "Setting temporary metadata for" << url;
|
||||
|
||||
bool length_changed = song.length_nanosec() != current_item_metadata().length_nanosec();
|
||||
bool update_scrobble_point = song.length_nanosec() != current_item_metadata().length_nanosec();
|
||||
|
||||
current_item()->SetTemporaryMetadata(song);
|
||||
|
||||
|
@ -1518,10 +1516,11 @@ void Playlist::SetStreamMetadata(const QUrl &url, const Song &song, const bool m
|
|||
}
|
||||
}
|
||||
else {
|
||||
update_scrobble_point = true;
|
||||
InformOfCurrentSongChange();
|
||||
}
|
||||
|
||||
if (length_changed) UpdateScrobblePoint();
|
||||
if (update_scrobble_point) UpdateScrobblePoint();
|
||||
|
||||
}
|
||||
|
||||
|
@ -2022,6 +2021,7 @@ void Playlist::UpdateScrobblePoint(const qint64 seek_point_nanosec) {
|
|||
}
|
||||
}
|
||||
|
||||
nowplaying_ = false;
|
||||
scrobbled_ = false;
|
||||
|
||||
}
|
||||
|
|
|
@ -55,6 +55,10 @@ AudioScrobbler::AudioScrobbler(Application *app, QObject *parent) :
|
|||
|
||||
ReloadSettings();
|
||||
|
||||
for (ScrobblerService *service : scrobbler_services_->List()) {
|
||||
connect(service, SIGNAL(ErrorMessage(QString)), SLOT(ErrorReceived(QString)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AudioScrobbler::~AudioScrobbler() {}
|
||||
|
@ -116,53 +120,61 @@ void AudioScrobbler::ShowConfig() {
|
|||
}
|
||||
|
||||
void AudioScrobbler::UpdateNowPlaying(const Song &song) {
|
||||
|
||||
qLog(Debug) << "Sending now playing for song" << song.artist() << song.album() << song.title();
|
||||
|
||||
for (ScrobblerService *service : scrobbler_services_->List()) {
|
||||
if (!service->IsEnabled()) continue;
|
||||
service->UpdateNowPlaying(song);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AudioScrobbler::ClearPlaying() {
|
||||
|
||||
for (ScrobblerService *service : scrobbler_services_->List()) {
|
||||
if (!service->IsEnabled()) continue;
|
||||
service->ClearPlaying();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AudioScrobbler::Scrobble(const Song &song, const int scrobble_point) {
|
||||
|
||||
qLog(Debug) << "Scrobbling song" << song.artist() << song.album() << song.title() << "at" << scrobble_point;
|
||||
|
||||
for (ScrobblerService *service : scrobbler_services_->List()) {
|
||||
if (!service->IsEnabled()) continue;
|
||||
service->Scrobble(song);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AudioScrobbler::Love() {
|
||||
|
||||
for (ScrobblerService *service : scrobbler_services_->List()) {
|
||||
if (!service->IsEnabled() || !service->IsAuthenticated()) continue;
|
||||
service->Love();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AudioScrobbler::Submit() {
|
||||
|
||||
for (ScrobblerService *service : scrobbler_services_->List()) {
|
||||
if (!service->IsEnabled() || !service->IsAuthenticated() || service->IsSubmitted()) continue;
|
||||
service->DoSubmit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AudioScrobbler::WriteCache() {
|
||||
|
||||
for (ScrobblerService *service : scrobbler_services_->List()) {
|
||||
if (!service->IsEnabled()) continue;
|
||||
service->WriteCache();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioScrobbler::ConnectError() {
|
||||
for (ScrobblerService *service : scrobbler_services_->List()) {
|
||||
connect(service, SIGNAL(ErrorMessage(QString)), SLOT(ErrorReceived(QString)));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioScrobbler::ErrorReceived(QString error) {
|
||||
|
|
|
@ -51,7 +51,6 @@ class AudioScrobbler : public QObject {
|
|||
void ClearPlaying();
|
||||
void Scrobble(const Song &song, const int scrobble_point);
|
||||
void ShowConfig();
|
||||
void ConnectError();
|
||||
|
||||
ScrobblerService *ServiceByName(const QString &name) const { return scrobbler_services_->ServiceByName(name); }
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ ListenBrainzScrobbler::ListenBrainzScrobbler(Application *app, QObject *parent)
|
|||
enabled_(false),
|
||||
expires_in_(-1),
|
||||
submitted_(false),
|
||||
scrobbled_(false),
|
||||
timestamp_(0) {
|
||||
|
||||
ReloadSettings();
|
||||
|
@ -345,7 +346,10 @@ QByteArray ListenBrainzScrobbler::GetReplyData(QNetworkReply *reply) {
|
|||
|
||||
void ListenBrainzScrobbler::UpdateNowPlaying(const Song &song) {
|
||||
|
||||
CheckScrobblePrevSong();
|
||||
|
||||
song_playing_ = song;
|
||||
scrobbled_ = false;
|
||||
timestamp_ = QDateTime::currentDateTime().toTime_t();
|
||||
|
||||
if (!song.is_metadata_good() || !IsAuthenticated() || app_->scrobbler()->IsOffline()) return;
|
||||
|
@ -418,13 +422,20 @@ void ListenBrainzScrobbler::UpdateNowPlayingRequestFinished(QNetworkReply *reply
|
|||
}
|
||||
|
||||
void ListenBrainzScrobbler::ClearPlaying() {
|
||||
|
||||
CheckScrobblePrevSong();
|
||||
song_playing_ = Song();
|
||||
scrobbled_ = false;
|
||||
timestamp_ = 0;
|
||||
|
||||
}
|
||||
|
||||
void ListenBrainzScrobbler::Scrobble(const Song &song) {
|
||||
|
||||
if (song.id() != song_playing_.id() || song.url() != song_playing_.url() || !song.is_metadata_good()) return;
|
||||
|
||||
scrobbled_ = true;
|
||||
|
||||
cache_->Add(song, timestamp_);
|
||||
|
||||
if (app_->scrobbler()->IsOffline()) return;
|
||||
|
@ -552,3 +563,12 @@ void ListenBrainzScrobbler::Error(const QString &error, const QVariant &debug) {
|
|||
|
||||
}
|
||||
|
||||
void ListenBrainzScrobbler::CheckScrobblePrevSong() {
|
||||
|
||||
quint64 duration = QDateTime::currentDateTime().toTime_t() - timestamp_;
|
||||
|
||||
if (!scrobbled_ && song_playing_.is_metadata_good() && song_playing_.source() == Song::Source_Stream && duration > 30) {
|
||||
Scrobble(song_playing_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -88,6 +88,7 @@ class ListenBrainzScrobbler : public ScrobblerService {
|
|||
void AuthError(const QString &error);
|
||||
void Error(const QString &error, const QVariant &debug = QVariant());
|
||||
void DoSubmit();
|
||||
void CheckScrobblePrevSong();
|
||||
|
||||
static const char *kAuthUrl;
|
||||
static const char *kAuthTokenUrl;
|
||||
|
@ -110,6 +111,7 @@ class ListenBrainzScrobbler : public ScrobblerService {
|
|||
QString refresh_token_;
|
||||
bool submitted_;
|
||||
Song song_playing_;
|
||||
bool scrobbled_;
|
||||
quint64 timestamp_;
|
||||
|
||||
};
|
||||
|
|
|
@ -77,6 +77,7 @@ ScrobblingAPI20::ScrobblingAPI20(const QString &name, const QString &settings_gr
|
|||
enabled_(false),
|
||||
subscriber_(false),
|
||||
submitted_(false),
|
||||
scrobbled_(false),
|
||||
timestamp_(0) {}
|
||||
|
||||
ScrobblingAPI20::~ScrobblingAPI20() {}
|
||||
|
@ -422,8 +423,11 @@ QByteArray ScrobblingAPI20::GetReplyData(QNetworkReply *reply) {
|
|||
|
||||
void ScrobblingAPI20::UpdateNowPlaying(const Song &song) {
|
||||
|
||||
CheckScrobblePrevSong();
|
||||
|
||||
song_playing_ = song;
|
||||
timestamp_ = QDateTime::currentDateTime().toTime_t();
|
||||
scrobbled_ = false;
|
||||
|
||||
if (!IsAuthenticated() || !song.is_metadata_good() || app_->scrobbler()->IsOffline()) return;
|
||||
|
||||
|
@ -477,13 +481,21 @@ void ScrobblingAPI20::UpdateNowPlayingRequestFinished(QNetworkReply *reply) {
|
|||
}
|
||||
|
||||
void ScrobblingAPI20::ClearPlaying() {
|
||||
|
||||
CheckScrobblePrevSong();
|
||||
|
||||
song_playing_ = Song();
|
||||
scrobbled_ = false;
|
||||
timestamp_ = 0;
|
||||
|
||||
}
|
||||
|
||||
void ScrobblingAPI20::Scrobble(const Song &song) {
|
||||
|
||||
if (song.id() != song_playing_.id() || song.url() != song_playing_.url() || !song.is_metadata_good()) return;
|
||||
|
||||
scrobbled_ = true;
|
||||
|
||||
cache()->Add(song, timestamp_);
|
||||
|
||||
if (app_->scrobbler()->IsOffline()) return;
|
||||
|
@ -595,15 +607,15 @@ void ScrobblingAPI20::ScrobbleRequestFinished(QNetworkReply *reply, QList<quint6
|
|||
|
||||
cache()->Flush(list);
|
||||
|
||||
QJsonValue json_scrobbles = json_obj["scrobbles"];
|
||||
if (!json_scrobbles.isObject()) {
|
||||
QJsonValue value_scrobbles = json_obj["scrobbles"];
|
||||
if (!value_scrobbles.isObject()) {
|
||||
Error("Json scrobbles is not an object.", json_obj);
|
||||
DoSubmit();
|
||||
return;
|
||||
}
|
||||
json_obj = json_scrobbles.toObject();
|
||||
json_obj = value_scrobbles.toObject();
|
||||
if (json_obj.isEmpty()) {
|
||||
Error("Json scrobbles object is empty.", json_scrobbles);
|
||||
Error("Json scrobbles object is empty.", value_scrobbles);
|
||||
DoSubmit();
|
||||
return;
|
||||
}
|
||||
|
@ -775,14 +787,14 @@ void ScrobblingAPI20::SingleScrobbleRequestFinished(QNetworkReply *reply, quint6
|
|||
cache()->Remove(timestamp);
|
||||
item = nullptr;
|
||||
|
||||
QJsonValue json_scrobbles = json_obj["scrobbles"];
|
||||
if (!json_scrobbles.isObject()) {
|
||||
QJsonValue value_scrobbles = json_obj["scrobbles"];
|
||||
if (!value_scrobbles.isObject()) {
|
||||
Error("Json scrobbles is not an object.", json_obj);
|
||||
return;
|
||||
}
|
||||
json_obj = json_scrobbles.toObject();
|
||||
json_obj = value_scrobbles.toObject();
|
||||
if (json_obj.isEmpty()) {
|
||||
Error("Json scrobbles object is empty.", json_scrobbles);
|
||||
Error("Json scrobbles object is empty.", value_scrobbles);
|
||||
return;
|
||||
}
|
||||
if (!json_obj.contains("@attr") || !json_obj.contains("scrobble")) {
|
||||
|
@ -980,3 +992,13 @@ QString ScrobblingAPI20::ErrorString(const ScrobbleErrorCode error) const {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
void ScrobblingAPI20::CheckScrobblePrevSong() {
|
||||
|
||||
quint64 time = QDateTime::currentDateTime().toTime_t() - timestamp_;
|
||||
|
||||
if (!scrobbled_ && song_playing_.is_metadata_good() && song_playing_.source() == Song::Source_Stream && time > 30) {
|
||||
Scrobble(song_playing_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ class ScrobblingAPI20 : public ScrobblerService {
|
|||
void Error(const QString &error, const QVariant &debug = QVariant());
|
||||
QString ErrorString(const ScrobbleErrorCode error) const;
|
||||
void DoSubmit();
|
||||
void CheckScrobblePrevSong();
|
||||
|
||||
QString name_;
|
||||
QString settings_group_;
|
||||
|
@ -153,6 +154,7 @@ class ScrobblingAPI20 : public ScrobblerService {
|
|||
|
||||
bool submitted_;
|
||||
Song song_playing_;
|
||||
bool scrobbled_;
|
||||
quint64 timestamp_;
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue