Also add a configurable option to increment the play count if song has played for a shorter duration.
This commit is contained in:
parent
9eb92ee2b5
commit
f4d84bc05a
|
@ -35,37 +35,38 @@
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "core/closure.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/player.h"
|
||||||
|
#include "core/qhash_qurl.h"
|
||||||
|
#include "core/tagreaderclient.h"
|
||||||
|
#include "core/timeconstants.h"
|
||||||
|
#include "internet/core/internetmimedata.h"
|
||||||
|
#include "internet/core/internetmodel.h"
|
||||||
|
#include "internet/core/internetplaylistitem.h"
|
||||||
|
#include "internet/core/internetsongmimedata.h"
|
||||||
|
#include "internet/internetradio/savedradio.h"
|
||||||
|
#include "internet/jamendo/jamendoplaylistitem.h"
|
||||||
|
#include "internet/jamendo/jamendoservice.h"
|
||||||
|
#include "internet/magnatune/magnatuneplaylistitem.h"
|
||||||
|
#include "internet/magnatune/magnatuneservice.h"
|
||||||
|
#include "library/library.h"
|
||||||
|
#include "library/librarybackend.h"
|
||||||
|
#include "library/librarymodel.h"
|
||||||
|
#include "library/libraryplaylistitem.h"
|
||||||
#include "playlistbackend.h"
|
#include "playlistbackend.h"
|
||||||
#include "playlistfilter.h"
|
#include "playlistfilter.h"
|
||||||
#include "playlistitemmimedata.h"
|
#include "playlistitemmimedata.h"
|
||||||
#include "playlistundocommands.h"
|
#include "playlistundocommands.h"
|
||||||
#include "playlistview.h"
|
#include "playlistview.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "songloaderinserter.h"
|
|
||||||
#include "songmimedata.h"
|
|
||||||
#include "songplaylistitem.h"
|
|
||||||
#include "core/application.h"
|
|
||||||
#include "core/closure.h"
|
|
||||||
#include "core/logging.h"
|
|
||||||
#include "core/qhash_qurl.h"
|
|
||||||
#include "core/tagreaderclient.h"
|
|
||||||
#include "core/timeconstants.h"
|
|
||||||
#include "internet/jamendo/jamendoplaylistitem.h"
|
|
||||||
#include "internet/jamendo/jamendoservice.h"
|
|
||||||
#include "internet/magnatune/magnatuneplaylistitem.h"
|
|
||||||
#include "internet/magnatune/magnatuneservice.h"
|
|
||||||
#include "internet/core/internetmimedata.h"
|
|
||||||
#include "internet/core/internetmodel.h"
|
|
||||||
#include "internet/core/internetplaylistitem.h"
|
|
||||||
#include "internet/core/internetsongmimedata.h"
|
|
||||||
#include "internet/internetradio/savedradio.h"
|
|
||||||
#include "library/library.h"
|
|
||||||
#include "library/librarybackend.h"
|
|
||||||
#include "library/librarymodel.h"
|
|
||||||
#include "library/libraryplaylistitem.h"
|
|
||||||
#include "smartplaylists/generator.h"
|
#include "smartplaylists/generator.h"
|
||||||
#include "smartplaylists/generatorinserter.h"
|
#include "smartplaylists/generatorinserter.h"
|
||||||
#include "smartplaylists/generatormimedata.h"
|
#include "smartplaylists/generatormimedata.h"
|
||||||
|
#include "songloaderinserter.h"
|
||||||
|
#include "songmimedata.h"
|
||||||
|
#include "songplaylistitem.h"
|
||||||
|
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
using std::placeholders::_2;
|
using std::placeholders::_2;
|
||||||
|
@ -156,6 +157,22 @@ Playlist::Playlist(PlaylistBackend* backend, TaskManager* task_manager,
|
||||||
connect(queue_, SIGNAL(layoutChanged()), SLOT(QueueLayoutChanged()));
|
connect(queue_, SIGNAL(layoutChanged()), SLOT(QueueLayoutChanged()));
|
||||||
|
|
||||||
column_alignments_ = PlaylistView::DefaultColumnAlignment();
|
column_alignments_ = PlaylistView::DefaultColumnAlignment();
|
||||||
|
|
||||||
|
min_play_count_point_nsecs_ = (31ll * kNsecPerSec); // 30 seconds
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup(Player::kSettingsGroup);
|
||||||
|
|
||||||
|
if (settings.value("play_count_short_duration").toBool()) {
|
||||||
|
max_play_count_point_nsecs_ = (60ll * kNsecPerSec); // 1 minute
|
||||||
|
} else {
|
||||||
|
max_play_count_point_nsecs_ = (240ll * kNsecPerSec); // 4 minutes
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
qLog(Debug) << "k_max_scrobble_point"
|
||||||
|
<< (max_play_count_point_nsecs_ / kNsecPerSec);
|
||||||
}
|
}
|
||||||
|
|
||||||
Playlist::~Playlist() {
|
Playlist::~Playlist() {
|
||||||
|
@ -1803,6 +1820,15 @@ Song Playlist::current_item_metadata() const {
|
||||||
return current_item()->Metadata();
|
return current_item()->Metadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last.fm defines a track to be scrobbled when
|
||||||
|
* - the track is longer than 30 seconds
|
||||||
|
* - the track has been played for at least half its duration, or for 4 minutes
|
||||||
|
* (whichever occurs earlier.)
|
||||||
|
*
|
||||||
|
* If you seek a track, the scrobble point is recalculated from the point seeked
|
||||||
|
* to (as 50% or 4 minutes).
|
||||||
|
*/
|
||||||
void Playlist::UpdateScrobblePoint(qint64 seek_point_nanosec) {
|
void Playlist::UpdateScrobblePoint(qint64 seek_point_nanosec) {
|
||||||
const qint64 length = current_item_metadata().length_nanosec();
|
const qint64 length = current_item_metadata().length_nanosec();
|
||||||
|
|
||||||
|
@ -1825,6 +1851,41 @@ void Playlist::UpdateScrobblePoint(qint64 seek_point_nanosec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
set_lastfm_status(LastFM_New);
|
set_lastfm_status(LastFM_New);
|
||||||
|
UpdatePlayCountPoint(seek_point_nanosec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initially the play count tracking and scrobbling went hand in hand.
|
||||||
|
* However, it is is possible that someone's preferences for tracking play
|
||||||
|
* counts are more relaxed than that of scrobbling. For those cases, we use
|
||||||
|
* the following algorithm to track whether a song should increment it's play
|
||||||
|
* count or not.
|
||||||
|
*
|
||||||
|
* Note that that this is very similar to the scrobbling algorithm with the only
|
||||||
|
* difference that the requirement of 4 mins worth of listening is now
|
||||||
|
* configurable via the `max_play_count_point_nsecs_` parameter.
|
||||||
|
*/
|
||||||
|
void Playlist::UpdatePlayCountPoint(qint64 seek_point_nanosec) {
|
||||||
|
const qint64 length = current_item_metadata().length_nanosec();
|
||||||
|
|
||||||
|
if (seek_point_nanosec == 0) {
|
||||||
|
if (length == 0) {
|
||||||
|
play_count_point_ = max_play_count_point_nsecs_; // 4 minutes
|
||||||
|
} else {
|
||||||
|
play_count_point_ = qBound(min_play_count_point_nsecs_, length / 2,
|
||||||
|
max_play_count_point_nsecs_);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (length == 0) {
|
||||||
|
play_count_point_ = seek_point_nanosec + max_play_count_point_nsecs_;
|
||||||
|
} else {
|
||||||
|
play_count_point_ =
|
||||||
|
qBound(seek_point_nanosec + min_play_count_point_nsecs_,
|
||||||
|
seek_point_nanosec + (length / 2),
|
||||||
|
seek_point_nanosec + max_play_count_point_nsecs_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
have_incremented_playcount_ = false;
|
have_incremented_playcount_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -236,6 +236,16 @@ class Playlist : public QAbstractListModel {
|
||||||
void set_have_incremented_playcount() { have_incremented_playcount_ = true; }
|
void set_have_incremented_playcount() { have_incremented_playcount_ = true; }
|
||||||
void UpdateScrobblePoint(qint64 seek_point_nanosec = 0);
|
void UpdateScrobblePoint(qint64 seek_point_nanosec = 0);
|
||||||
|
|
||||||
|
// play count tracking
|
||||||
|
qint64 play_count_point_nanosec() const { return play_count_point_; }
|
||||||
|
void set_max_play_count_point_nsecs(qint64 max_play_count_point_nsecs) {
|
||||||
|
max_play_count_point_nsecs_ = max_play_count_point_nsecs;
|
||||||
|
}
|
||||||
|
qint64 get_max_play_count_point_nsecs() const {
|
||||||
|
return max_play_count_point_nsecs_;
|
||||||
|
}
|
||||||
|
void UpdatePlayCountPoint(qint64 seek_point_nanosec = 0);
|
||||||
|
|
||||||
// Changing the playlist
|
// Changing the playlist
|
||||||
void InsertItems(const PlaylistItemList& items, int pos = -1,
|
void InsertItems(const PlaylistItemList& items, int pos = -1,
|
||||||
bool play_now = false, bool enqueue = false,
|
bool play_now = false, bool enqueue = false,
|
||||||
|
@ -440,6 +450,8 @@ signals:
|
||||||
LastFMStatus lastfm_status_;
|
LastFMStatus lastfm_status_;
|
||||||
bool have_incremented_playcount_;
|
bool have_incremented_playcount_;
|
||||||
|
|
||||||
|
qint64 play_count_point_;
|
||||||
|
|
||||||
PlaylistSequence* playlist_sequence_;
|
PlaylistSequence* playlist_sequence_;
|
||||||
|
|
||||||
// Hack to stop QTreeView::setModel sorting the playlist
|
// Hack to stop QTreeView::setModel sorting the playlist
|
||||||
|
@ -454,6 +466,9 @@ signals:
|
||||||
|
|
||||||
QString special_type_;
|
QString special_type_;
|
||||||
|
|
||||||
|
qint64 min_play_count_point_nsecs_;
|
||||||
|
qint64 max_play_count_point_nsecs_;
|
||||||
|
|
||||||
// Cancel async restore if songs are already replaced
|
// Cancel async restore if songs are already replaced
|
||||||
bool cancel_restore_;
|
bool cancel_restore_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,13 +15,15 @@
|
||||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "core/appearance.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/player.h"
|
||||||
|
#include "core/timeconstants.h"
|
||||||
#include "playlistcontainer.h"
|
#include "playlistcontainer.h"
|
||||||
#include "playlistmanager.h"
|
#include "playlistmanager.h"
|
||||||
#include "ui_playlistcontainer.h"
|
|
||||||
#include "core/logging.h"
|
|
||||||
#include "core/appearance.h"
|
|
||||||
#include "playlistparsers/playlistparser.h"
|
#include "playlistparsers/playlistparser.h"
|
||||||
#include "ui/iconloader.h"
|
#include "ui/iconloader.h"
|
||||||
|
#include "ui_playlistcontainer.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
|
@ -464,4 +466,16 @@ void PlaylistContainer::ReloadSettings() {
|
||||||
settings.beginGroup(Appearance::kSettingsGroup);
|
settings.beginGroup(Appearance::kSettingsGroup);
|
||||||
bool hide_toolbar = settings.value("b_hide_filter_toolbar", false).toBool();
|
bool hide_toolbar = settings.value("b_hide_filter_toolbar", false).toBool();
|
||||||
ui_->toolbar->setVisible(!hide_toolbar);
|
ui_->toolbar->setVisible(!hide_toolbar);
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
settings.beginGroup(Player::kSettingsGroup);
|
||||||
|
if (settings.value("play_count_short_duration").toBool()) {
|
||||||
|
playlist_->set_max_play_count_point_nsecs(60ll * kNsecPerSec);
|
||||||
|
} else {
|
||||||
|
playlist_->set_max_play_count_point_nsecs(240ll * kNsecPerSec);
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
qLog(Debug) << "new max scrobble point:"
|
||||||
|
<< (playlist_->get_max_play_count_point_nsecs() / kNsecPerSec);
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,6 +172,14 @@ void BehaviourSettingsPage::Load() {
|
||||||
s.value("menu_previousmode", Player::PreviousBehaviour_DontRestart)
|
s.value("menu_previousmode", Player::PreviousBehaviour_DontRestart)
|
||||||
.toInt()));
|
.toInt()));
|
||||||
ui_->seek_step_sec->setValue(s.value("seek_step_sec", 10).toInt());
|
ui_->seek_step_sec->setValue(s.value("seek_step_sec", 10).toInt());
|
||||||
|
|
||||||
|
if (s.value("play_count_short_duration", false).toBool()) {
|
||||||
|
ui_->b_play_count_short_duration->setChecked(true);
|
||||||
|
ui_->b_play_count_normal_duration->setChecked(false);
|
||||||
|
} else {
|
||||||
|
ui_->b_play_count_short_duration->setChecked(false);
|
||||||
|
ui_->b_play_count_normal_duration->setChecked(true);
|
||||||
|
}
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
s.beginGroup("General");
|
s.beginGroup("General");
|
||||||
|
@ -277,6 +285,13 @@ void BehaviourSettingsPage::Save() {
|
||||||
ui_->stop_play_if_fail_->isChecked());
|
ui_->stop_play_if_fail_->isChecked());
|
||||||
s.setValue("menu_previousmode", menu_previousmode);
|
s.setValue("menu_previousmode", menu_previousmode);
|
||||||
s.setValue("seek_step_sec", ui_->seek_step_sec->value());
|
s.setValue("seek_step_sec", ui_->seek_step_sec->value());
|
||||||
|
|
||||||
|
if (ui_->b_play_count_short_duration->isChecked()) {
|
||||||
|
s.setValue("play_count_short_duration", true);
|
||||||
|
} else {
|
||||||
|
s.setValue("play_count_short_duration", false);
|
||||||
|
}
|
||||||
|
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
s.beginGroup("General");
|
s.beginGroup("General");
|
||||||
|
|
|
@ -428,6 +428,32 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_play_count">
|
||||||
|
<property name="title">
|
||||||
|
<string>When calculating play counts, use</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="b_play_count_normal_duration">
|
||||||
|
<property name="text">
|
||||||
|
<string>Normal duration (at least 4 minutes or half the track length)</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="b_play_count_short_duration">
|
||||||
|
<property name="text">
|
||||||
|
<string>Short duration (at least 1 minute or half the track length)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer_2">
|
<spacer name="verticalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|
|
@ -1468,6 +1468,9 @@ void MainWindow::Seeked(qlonglong microseconds) {
|
||||||
if (ui_->action_toggle_scrobbling->isVisible()) SetToggleScrobblingIcon(true);
|
if (ui_->action_toggle_scrobbling->isVisible()) SetToggleScrobblingIcon(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update track position, tray icon, playcount
|
||||||
|
*/
|
||||||
void MainWindow::UpdateTrackPosition() {
|
void MainWindow::UpdateTrackPosition() {
|
||||||
// Track position in seconds
|
// Track position in seconds
|
||||||
Playlist* playlist = app_->playlist_manager()->active();
|
Playlist* playlist = app_->playlist_manager()->active();
|
||||||
|
@ -1477,29 +1480,32 @@ void MainWindow::UpdateTrackPosition() {
|
||||||
float(app_->player()->engine()->position_nanosec()) / kNsecPerSec + 0.5);
|
float(app_->player()->engine()->position_nanosec()) / kNsecPerSec + 0.5);
|
||||||
const int length = app_->player()->engine()->length_nanosec() / kNsecPerSec;
|
const int length = app_->player()->engine()->length_nanosec() / kNsecPerSec;
|
||||||
const int scrobble_point = playlist->scrobble_point_nanosec() / kNsecPerSec;
|
const int scrobble_point = playlist->scrobble_point_nanosec() / kNsecPerSec;
|
||||||
|
const int play_count_point =
|
||||||
|
playlist->play_count_point_nanosec() / kNsecPerSec;
|
||||||
|
|
||||||
if (length <= 0) {
|
if (length <= 0) {
|
||||||
// Probably a stream that we don't know the length of
|
// Probably a stream that we don't know the length of
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_LIBLASTFM
|
#ifdef HAVE_LIBLASTFM
|
||||||
|
// Time to scrobble?
|
||||||
const bool last_fm_enabled = ui_->action_toggle_scrobbling->isVisible() &&
|
const bool last_fm_enabled = ui_->action_toggle_scrobbling->isVisible() &&
|
||||||
app_->scrobbler()->IsScrobblingEnabled() &&
|
app_->scrobbler()->IsScrobblingEnabled() &&
|
||||||
app_->scrobbler()->IsAuthenticated();
|
app_->scrobbler()->IsAuthenticated();
|
||||||
#endif
|
|
||||||
|
|
||||||
// Time to scrobble?
|
|
||||||
if (position >= scrobble_point) {
|
if (position >= scrobble_point) {
|
||||||
if (playlist->get_lastfm_status() == Playlist::LastFM_New) {
|
if (playlist->get_lastfm_status() == Playlist::LastFM_New) {
|
||||||
#ifdef HAVE_LIBLASTFM
|
|
||||||
if (app_->scrobbler()->IsScrobblingEnabled() &&
|
if (app_->scrobbler()->IsScrobblingEnabled() &&
|
||||||
app_->scrobbler()->IsAuthenticated()) {
|
app_->scrobbler()->IsAuthenticated()) {
|
||||||
qLog(Info) << "Scrobbling at" << scrobble_point;
|
qLog(Info) << "Scrobbling at" << scrobble_point;
|
||||||
app_->scrobbler()->Scrobble();
|
app_->scrobbler()->Scrobble();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (position >= play_count_point) {
|
||||||
// Update the play count for the song if it's from the library
|
// Update the play count for the song if it's from the library
|
||||||
if (!playlist->have_incremented_playcount() && item->IsLocalLibraryItem() &&
|
if (!playlist->have_incremented_playcount() && item->IsLocalLibraryItem() &&
|
||||||
item->Metadata().id() != -1 &&
|
item->Metadata().id() != -1 &&
|
||||||
|
@ -1519,8 +1525,14 @@ void MainWindow::UpdateTrackPosition() {
|
||||||
|
|
||||||
// Update the tray icon every 10 seconds
|
// Update the tray icon every 10 seconds
|
||||||
if (position % 10 == 0) {
|
if (position % 10 == 0) {
|
||||||
qLog(Debug) << "position" << position << "scrobble point" << scrobble_point
|
qLog(Debug) << "position:" << position
|
||||||
<< "status" << playlist->get_lastfm_status();
|
<< ", scrobble point:" << scrobble_point
|
||||||
|
<< ", lastfm status:" << playlist->get_lastfm_status()
|
||||||
|
<< ", play count point:" << play_count_point
|
||||||
|
<< ", is local libary item:" << item->IsLocalLibraryItem()
|
||||||
|
<< ", playlist have incremented playcount: "
|
||||||
|
<< playlist->have_incremented_playcount();
|
||||||
|
|
||||||
if (tray_icon_) tray_icon_->SetProgress(double(position) / length * 100);
|
if (tray_icon_) tray_icon_->SetProgress(double(position) / length * 100);
|
||||||
|
|
||||||
// if we're waiting for the scrobble point, update the icon
|
// if we're waiting for the scrobble point, update the icon
|
||||||
|
|
Loading…
Reference in New Issue