Improve resume playback on startup, re-request stream URL when unpausing
Fixes #270
This commit is contained in:
parent
9db59d5deb
commit
8fd32aba4f
|
@ -766,7 +766,7 @@ MainWindow::MainWindow(Application *app, std::shared_ptr<SystemTrayIcon> tray_ic
|
||||||
|
|
||||||
#ifdef HAVE_GLOBALSHORTCUTS
|
#ifdef HAVE_GLOBALSHORTCUTS
|
||||||
// Global shortcuts
|
// Global shortcuts
|
||||||
QObject::connect(globalshortcuts_manager_, &GlobalShortcutsManager::Play, app_->player(), &Player::Play);
|
QObject::connect(globalshortcuts_manager_, &GlobalShortcutsManager::Play, app_->player(), &Player::PlayHelper);
|
||||||
QObject::connect(globalshortcuts_manager_, &GlobalShortcutsManager::Pause, app_->player(), &Player::Pause);
|
QObject::connect(globalshortcuts_manager_, &GlobalShortcutsManager::Pause, app_->player(), &Player::Pause);
|
||||||
QObject::connect(globalshortcuts_manager_, &GlobalShortcutsManager::PlayPause, ui_->action_play_pause, &QAction::trigger);
|
QObject::connect(globalshortcuts_manager_, &GlobalShortcutsManager::PlayPause, ui_->action_play_pause, &QAction::trigger);
|
||||||
QObject::connect(globalshortcuts_manager_, &GlobalShortcutsManager::Stop, ui_->action_stop, &QAction::trigger);
|
QObject::connect(globalshortcuts_manager_, &GlobalShortcutsManager::Stop, ui_->action_stop, &QAction::trigger);
|
||||||
|
@ -1446,13 +1446,7 @@ void MainWindow::ResumePlayback() {
|
||||||
app_->player()->PlayPause();
|
app_->player()->PlayPause();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Seek after we got song length.
|
app_->player()->Play(playback_position * kNsecPerSec);
|
||||||
std::shared_ptr<QMetaObject::Connection> connection = std::make_shared<QMetaObject::Connection>();
|
|
||||||
*connection = QObject::connect(track_position_timer_, &QTimer::timeout, this, [this, connection, playback_position]() {
|
|
||||||
QObject::disconnect(*connection);
|
|
||||||
ResumePlaybackSeek(playback_position);
|
|
||||||
});
|
|
||||||
app_->player()->Play();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset saved playback status so we don't resume again from the same position.
|
// Reset saved playback status so we don't resume again from the same position.
|
||||||
|
@ -1464,14 +1458,6 @@ void MainWindow::ResumePlayback() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::ResumePlaybackSeek(const int playback_position) {
|
|
||||||
|
|
||||||
if (app_->player()->engine()->length_nanosec() > 0) {
|
|
||||||
app_->player()->SeekTo(playback_position);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::PlayIndex(const QModelIndex &idx, Playlist::AutoScroll autoscroll) {
|
void MainWindow::PlayIndex(const QModelIndex &idx, Playlist::AutoScroll autoscroll) {
|
||||||
|
|
||||||
if (!idx.isValid()) return;
|
if (!idx.isValid()) return;
|
||||||
|
|
|
@ -242,7 +242,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||||
void SavePlaybackStatus();
|
void SavePlaybackStatus();
|
||||||
void LoadPlaybackStatus();
|
void LoadPlaybackStatus();
|
||||||
void ResumePlayback();
|
void ResumePlayback();
|
||||||
void ResumePlaybackSeek(const int playback_position);
|
|
||||||
|
|
||||||
void Exit();
|
void Exit();
|
||||||
void DoExit();
|
void DoExit();
|
||||||
|
|
|
@ -90,7 +90,8 @@ Player::Player(Application *app, QObject *parent)
|
||||||
greyout_(true),
|
greyout_(true),
|
||||||
menu_previousmode_(BehaviourSettingsPage::PreviousBehaviour_DontRestart),
|
menu_previousmode_(BehaviourSettingsPage::PreviousBehaviour_DontRestart),
|
||||||
seek_step_sec_(10),
|
seek_step_sec_(10),
|
||||||
volume_control_(true)
|
volume_control_(true),
|
||||||
|
play_offset_nanosec_(0)
|
||||||
{
|
{
|
||||||
|
|
||||||
settings_.beginGroup(kSettingsGroup);
|
settings_.beginGroup(kSettingsGroup);
|
||||||
|
@ -333,9 +334,10 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_current) {
|
if (is_current) {
|
||||||
qLog(Debug) << "Playing song" << item->Metadata().title() << result.stream_url_;
|
qLog(Debug) << "Playing song" << item->Metadata().title() << result.stream_url_ << "position" << play_offset_nanosec_;
|
||||||
engine_->Play(result.stream_url_, result.original_url_, stream_change_type_, song.has_cue(), song.beginning_nanosec(), song.end_nanosec());
|
engine_->Play(result.stream_url_, result.original_url_, stream_change_type_, song.has_cue(), song.beginning_nanosec(), song.end_nanosec(), play_offset_nanosec_);
|
||||||
current_item_ = item;
|
current_item_ = item;
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
}
|
}
|
||||||
else if (is_next) {
|
else if (is_next) {
|
||||||
qLog(Debug) << "Preloading next song" << next_item->Metadata().title() << result.stream_url_;
|
qLog(Debug) << "Preloading next song" << next_item->Metadata().title() << result.stream_url_;
|
||||||
|
@ -359,6 +361,9 @@ void Player::Next() { NextInternal(Engine::Manual, Playlist::AutoScroll_Always);
|
||||||
|
|
||||||
void Player::NextInternal(const Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll) {
|
void Player::NextInternal(const Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll) {
|
||||||
|
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
|
|
||||||
if (HandleStopAfter(autoscroll)) return;
|
if (HandleStopAfter(autoscroll)) return;
|
||||||
|
|
||||||
NextItem(change, autoscroll);
|
NextItem(change, autoscroll);
|
||||||
|
@ -367,6 +372,9 @@ void Player::NextInternal(const Engine::TrackChangeFlags change, const Playlist:
|
||||||
|
|
||||||
void Player::NextItem(const Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll) {
|
void Player::NextItem(const Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll) {
|
||||||
|
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
|
|
||||||
Playlist *active_playlist = app_->playlist_manager()->active();
|
Playlist *active_playlist = app_->playlist_manager()->active();
|
||||||
|
|
||||||
// If we received too many errors in auto change, with repeat enabled, we stop
|
// If we received too many errors in auto change, with repeat enabled, we stop
|
||||||
|
@ -402,7 +410,11 @@ void Player::PlayPlaylist(const QString &playlist_name) {
|
||||||
PlayPlaylistInternal(Engine::Manual, Playlist::AutoScroll_Always, playlist_name);
|
PlayPlaylistInternal(Engine::Manual, Playlist::AutoScroll_Always, playlist_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::PlayPlaylistInternal(Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const QString &playlist_name) {
|
void Player::PlayPlaylistInternal(const Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const QString &playlist_name) {
|
||||||
|
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
|
|
||||||
Playlist *playlist = nullptr;
|
Playlist *playlist = nullptr;
|
||||||
for (Playlist *p : app_->playlist_manager()->GetAllPlaylists()) {
|
for (Playlist *p : app_->playlist_manager()->GetAllPlaylists()) {
|
||||||
if (playlist_name == app_->playlist_manager()->GetPlaylistName(p->id())) {
|
if (playlist_name == app_->playlist_manager()->GetPlaylistName(p->id())) {
|
||||||
|
@ -425,6 +437,7 @@ void Player::PlayPlaylistInternal(Engine::TrackChangeFlags change, const Playlis
|
||||||
if (i == -1) i = 0;
|
if (i == -1) i = 0;
|
||||||
|
|
||||||
PlayAt(i, change, autoscroll, true);
|
PlayAt(i, change, autoscroll, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -442,6 +455,7 @@ bool Player::HandleStopAfter(const Playlist::AutoScroll autoscroll) {
|
||||||
Stop(true);
|
Stop(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -458,11 +472,13 @@ void Player::TrackEnded() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::PlayPause(Playlist::AutoScroll autoscroll) {
|
void Player::PlayPause(const quint64 offset_nanosec, const Playlist::AutoScroll autoscroll) {
|
||||||
|
|
||||||
|
play_offset_nanosec_ = offset_nanosec;
|
||||||
|
|
||||||
switch (engine_->state()) {
|
switch (engine_->state()) {
|
||||||
case Engine::Paused:
|
case Engine::Paused:
|
||||||
engine_->Unpause();
|
UnPause();
|
||||||
emit Resumed();
|
emit Resumed();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -471,6 +487,8 @@ void Player::PlayPause(Playlist::AutoScroll autoscroll) {
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
pause_time_ = QDateTime::currentDateTime();
|
||||||
|
play_offset_nanosec_ = engine_->position_nanosec();
|
||||||
engine_->Pause();
|
engine_->Pause();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -479,6 +497,7 @@ void Player::PlayPause(Playlist::AutoScroll autoscroll) {
|
||||||
case Engine::Empty:
|
case Engine::Empty:
|
||||||
case Engine::Error:
|
case Engine::Error:
|
||||||
case Engine::Idle: {
|
case Engine::Idle: {
|
||||||
|
pause_time_ = QDateTime();
|
||||||
app_->playlist_manager()->SetActivePlaylist(app_->playlist_manager()->current_id());
|
app_->playlist_manager()->SetActivePlaylist(app_->playlist_manager()->current_id());
|
||||||
if (app_->playlist_manager()->active()->rowCount() == 0) break;
|
if (app_->playlist_manager()->active()->rowCount() == 0) break;
|
||||||
int i = app_->playlist_manager()->active()->current_row();
|
int i = app_->playlist_manager()->active()->current_row();
|
||||||
|
@ -491,19 +510,45 @@ void Player::PlayPause(Playlist::AutoScroll autoscroll) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::UnPause() {
|
||||||
|
|
||||||
|
if (current_item_ && pause_time_.isValid()) {
|
||||||
|
const Song &song = current_item_->Metadata();
|
||||||
|
if (url_handlers_.contains(song.url().scheme()) && song.stream_url_can_expire()) {
|
||||||
|
const quint64 time = QDateTime::currentDateTime().toSecsSinceEpoch() - pause_time_.toSecsSinceEpoch();
|
||||||
|
if (time >= 30) { // Stream URL might be expired.
|
||||||
|
qLog(Debug) << "Re-requesting stream URL for" << song.url();
|
||||||
|
HandleLoadResult(url_handlers_[song.url().scheme()]->StartLoading(song.url()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
|
|
||||||
|
engine_->Unpause();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void Player::RestartOrPrevious() {
|
void Player::RestartOrPrevious() {
|
||||||
|
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
|
|
||||||
if (engine_->position_nanosec() < 8 * kNsecPerSec) return Previous();
|
if (engine_->position_nanosec() < 8 * kNsecPerSec) return Previous();
|
||||||
|
|
||||||
SeekTo(0);
|
SeekTo(0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::Stop(bool stop_after) {
|
void Player::Stop(const bool stop_after) {
|
||||||
|
|
||||||
engine_->Stop(stop_after);
|
engine_->Stop(stop_after);
|
||||||
app_->playlist_manager()->active()->set_current_row(-1);
|
app_->playlist_manager()->active()->set_current_row(-1);
|
||||||
current_item_.reset();
|
current_item_.reset();
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,12 +560,16 @@ bool Player::PreviousWouldRestartTrack() const {
|
||||||
|
|
||||||
// Check if it has been over two seconds since previous button was pressed
|
// Check if it has been over two seconds since previous button was pressed
|
||||||
return menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour_Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2;
|
return menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour_Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::Previous() { PreviousItem(Engine::Manual); }
|
void Player::Previous() { PreviousItem(Engine::Manual); }
|
||||||
|
|
||||||
void Player::PreviousItem(const Engine::TrackChangeFlags change) {
|
void Player::PreviousItem(const Engine::TrackChangeFlags change) {
|
||||||
|
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
|
|
||||||
const bool ignore_repeat_track = change & Engine::Manual;
|
const bool ignore_repeat_track = change & Engine::Manual;
|
||||||
|
|
||||||
if (menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour_Restart) {
|
if (menu_previousmode_ == BehaviourSettingsPage::PreviousBehaviour_Restart) {
|
||||||
|
@ -557,9 +606,13 @@ void Player::EngineStateChanged(const Engine::State state) {
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Engine::Paused:
|
case Engine::Paused:
|
||||||
|
pause_time_ = QDateTime::currentDateTime();
|
||||||
|
play_offset_nanosec_ = engine_->position_nanosec();
|
||||||
emit Paused();
|
emit Paused();
|
||||||
break;
|
break;
|
||||||
case Engine::Playing:
|
case Engine::Playing:
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
emit Playing();
|
emit Playing();
|
||||||
break;
|
break;
|
||||||
case Engine::Error:
|
case Engine::Error:
|
||||||
|
@ -567,9 +620,12 @@ void Player::EngineStateChanged(const Engine::State state) {
|
||||||
// fallthrough
|
// fallthrough
|
||||||
case Engine::Empty:
|
case Engine::Empty:
|
||||||
case Engine::Idle:
|
case Engine::Idle:
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
emit Stopped();
|
emit Stopped();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_state_ = state;
|
last_state_ = state;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -605,6 +661,8 @@ void Player::PlayAt(const int index, Engine::TrackChangeFlags change, const Play
|
||||||
app_->playlist_manager()->active()->set_current_row(index, autoscroll, false, force_inform);
|
app_->playlist_manager()->active()->set_current_row(index, autoscroll, false, force_inform);
|
||||||
if (app_->playlist_manager()->active()->current_row() == -1) {
|
if (app_->playlist_manager()->active()->current_row() == -1) {
|
||||||
// Maybe index didn't exist in the playlist.
|
// Maybe index didn't exist in the playlist.
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,15 +671,19 @@ void Player::PlayAt(const int index, Engine::TrackChangeFlags change, const Play
|
||||||
|
|
||||||
if (url_handlers_.contains(url.scheme())) {
|
if (url_handlers_.contains(url.scheme())) {
|
||||||
// It's already loading
|
// It's already loading
|
||||||
if (loading_async_.contains(url)) return;
|
if (loading_async_.contains(url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
stream_change_type_ = change;
|
stream_change_type_ = change;
|
||||||
autoscroll_ = autoscroll;
|
autoscroll_ = autoscroll;
|
||||||
HandleLoadResult(url_handlers_[url.scheme()]->StartLoading(url));
|
HandleLoadResult(url_handlers_[url.scheme()]->StartLoading(url));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qLog(Debug) << "Playing song" << current_item_->Metadata().title() << url;
|
qLog(Debug) << "Playing song" << current_item_->Metadata().title() << url << "position" << play_offset_nanosec_;
|
||||||
engine_->Play(url, current_item_->Url(), change, current_item_->Metadata().has_cue(), current_item_->effective_beginning_nanosec(), current_item_->effective_end_nanosec());
|
engine_->Play(url, current_item_->Url(), change, current_item_->Metadata().has_cue(), current_item_->effective_beginning_nanosec(), current_item_->effective_end_nanosec(), play_offset_nanosec_);
|
||||||
|
pause_time_ = QDateTime();
|
||||||
|
play_offset_nanosec_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -717,17 +779,17 @@ void Player::Mute() {
|
||||||
|
|
||||||
void Player::Pause() { engine_->Pause(); }
|
void Player::Pause() { engine_->Pause(); }
|
||||||
|
|
||||||
void Player::Play() {
|
void Player::Play(const quint64 offset_nanosec) {
|
||||||
|
|
||||||
switch (GetState()) {
|
switch (GetState()) {
|
||||||
case Engine::Playing:
|
case Engine::Playing:
|
||||||
SeekTo(0);
|
SeekTo(offset_nanosec);
|
||||||
break;
|
break;
|
||||||
case Engine::Paused:
|
case Engine::Paused:
|
||||||
engine_->Unpause();
|
UnPause();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PlayPause();
|
PlayPause(offset_nanosec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ class PlayerInterface : public QObject {
|
||||||
virtual int GetVolume() const = 0;
|
virtual int GetVolume() const = 0;
|
||||||
|
|
||||||
virtual PlaylistItemPtr GetCurrentItem() const = 0;
|
virtual PlaylistItemPtr GetCurrentItem() const = 0;
|
||||||
virtual PlaylistItemPtr GetItemAt(int pos) const = 0;
|
virtual PlaylistItemPtr GetItemAt(const int pos) const = 0;
|
||||||
|
|
||||||
virtual void RegisterUrlHandler(UrlHandler *handler) = 0;
|
virtual void RegisterUrlHandler(UrlHandler *handler) = 0;
|
||||||
virtual void UnregisterUrlHandler(UrlHandler *handler) = 0;
|
virtual void UnregisterUrlHandler(UrlHandler *handler) = 0;
|
||||||
|
@ -76,7 +76,7 @@ class PlayerInterface : public QObject {
|
||||||
virtual void PlayAt(const int index, Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const bool reshuffle, const bool force_inform = false) = 0;
|
virtual void PlayAt(const int index, Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const bool reshuffle, const bool force_inform = false) = 0;
|
||||||
|
|
||||||
// If there's currently a song playing, pause it, otherwise play the track that was playing last, or the first one on the playlist
|
// If there's currently a song playing, pause it, otherwise play the track that was playing last, or the first one on the playlist
|
||||||
virtual void PlayPause(Playlist::AutoScroll autoscroll = Playlist::AutoScroll_Always) = 0;
|
virtual void PlayPause(const quint64 offset_nanosec = 0, const Playlist::AutoScroll autoscroll = Playlist::AutoScroll_Always) = 0;
|
||||||
virtual void PlayPauseHelper() = 0;
|
virtual void PlayPauseHelper() = 0;
|
||||||
virtual void RestartOrPrevious() = 0;
|
virtual void RestartOrPrevious() = 0;
|
||||||
|
|
||||||
|
@ -97,8 +97,9 @@ class PlayerInterface : public QObject {
|
||||||
|
|
||||||
virtual void Mute() = 0;
|
virtual void Mute() = 0;
|
||||||
virtual void Pause() = 0;
|
virtual void Pause() = 0;
|
||||||
virtual void Stop(bool stop_after = false) = 0;
|
virtual void Stop(const bool stop_after = false) = 0;
|
||||||
virtual void Play() = 0;
|
virtual void Play(const quint64 offset_nanosec = 0) = 0;
|
||||||
|
virtual void PlayHelper() = 0;
|
||||||
virtual void ShowOSD() = 0;
|
virtual void ShowOSD() = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -143,7 +144,7 @@ class Player : public PlayerInterface {
|
||||||
int GetVolume() const override;
|
int GetVolume() const override;
|
||||||
|
|
||||||
PlaylistItemPtr GetCurrentItem() const override { return current_item_; }
|
PlaylistItemPtr GetCurrentItem() const override { return current_item_; }
|
||||||
PlaylistItemPtr GetItemAt(int pos) const override;
|
PlaylistItemPtr GetItemAt(const int pos) const override;
|
||||||
|
|
||||||
void RegisterUrlHandler(UrlHandler *handler) override;
|
void RegisterUrlHandler(UrlHandler *handler) override;
|
||||||
void UnregisterUrlHandler(UrlHandler *handler) override;
|
void UnregisterUrlHandler(UrlHandler *handler) override;
|
||||||
|
@ -159,8 +160,8 @@ class Player : public PlayerInterface {
|
||||||
void ReloadSettings() override;
|
void ReloadSettings() override;
|
||||||
|
|
||||||
void PlayAt(const int index, Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const bool reshuffle, const bool force_inform = false) override;
|
void PlayAt(const int index, Engine::TrackChangeFlags change, const Playlist::AutoScroll autoscroll, const bool reshuffle, const bool force_inform = false) override;
|
||||||
void PlayPause(Playlist::AutoScroll autoscroll = Playlist::AutoScroll_Always) override;
|
void PlayPause(const quint64 offset_nanosec = 0, const Playlist::AutoScroll autoscroll = Playlist::AutoScroll_Always) override;
|
||||||
void PlayPauseHelper() override { PlayPause(); }
|
void PlayPauseHelper() override { PlayPause(play_offset_nanosec_); }
|
||||||
void RestartOrPrevious() override;
|
void RestartOrPrevious() override;
|
||||||
void Next() override;
|
void Next() override;
|
||||||
void Previous() override;
|
void Previous() override;
|
||||||
|
@ -176,9 +177,10 @@ class Player : public PlayerInterface {
|
||||||
|
|
||||||
void Mute() override;
|
void Mute() override;
|
||||||
void Pause() override;
|
void Pause() override;
|
||||||
void Stop(bool stop_after = false) override;
|
void Stop(const bool stop_after = false) override;
|
||||||
void StopAfterCurrent();
|
void StopAfterCurrent();
|
||||||
void Play() override;
|
void Play(const quint64 offset_nanosec = 0) override;
|
||||||
|
void PlayHelper() override { Play(); }
|
||||||
void ShowOSD() override;
|
void ShowOSD() override;
|
||||||
void TogglePrettyOSD();
|
void TogglePrettyOSD();
|
||||||
|
|
||||||
|
@ -197,7 +199,7 @@ class Player : public PlayerInterface {
|
||||||
void PreviousItem(const Engine::TrackChangeFlags change);
|
void PreviousItem(const Engine::TrackChangeFlags change);
|
||||||
|
|
||||||
void NextInternal(const Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll);
|
void NextInternal(const Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll);
|
||||||
void PlayPlaylistInternal(Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll, const QString &playlist_name);
|
void PlayPlaylistInternal(const Engine::TrackChangeFlags, const Playlist::AutoScroll autoscroll, const QString &playlist_name);
|
||||||
|
|
||||||
void FatalError();
|
void FatalError();
|
||||||
void ValidSongRequested(const QUrl&);
|
void ValidSongRequested(const QUrl&);
|
||||||
|
@ -210,6 +212,8 @@ class Player : public PlayerInterface {
|
||||||
// Returns true if we were supposed to stop after this track.
|
// Returns true if we were supposed to stop after this track.
|
||||||
bool HandleStopAfter(const Playlist::AutoScroll autoscroll);
|
bool HandleStopAfter(const Playlist::AutoScroll autoscroll);
|
||||||
|
|
||||||
|
void UnPause();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application *app_;
|
Application *app_;
|
||||||
std::unique_ptr<EngineBase> engine_;
|
std::unique_ptr<EngineBase> engine_;
|
||||||
|
@ -241,6 +245,9 @@ class Player : public PlayerInterface {
|
||||||
|
|
||||||
bool volume_control_;
|
bool volume_control_;
|
||||||
|
|
||||||
|
QDateTime pause_time_;
|
||||||
|
quint64 play_offset_nanosec_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PLAYER_H
|
#endif // PLAYER_H
|
||||||
|
|
|
@ -379,6 +379,7 @@ bool Song::is_metadata_good() const { return !d->url_.isEmpty() && !d->artist_.i
|
||||||
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_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_cdda() const { return d->source_ == Source_CDDA; }
|
||||||
bool Song::is_compilation() const { return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && !d->compilation_off_; }
|
bool Song::is_compilation() const { return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && !d->compilation_off_; }
|
||||||
|
bool Song::stream_url_can_expire() const { return d->source_ == Song::Source_Tidal || d->source_ == Song::Source_Qobuz; }
|
||||||
|
|
||||||
bool Song::art_automatic_is_valid() const {
|
bool Song::art_automatic_is_valid() const {
|
||||||
return !d->art_automatic_.isEmpty() &&
|
return !d->art_automatic_.isEmpty() &&
|
||||||
|
|
|
@ -263,6 +263,7 @@ class Song {
|
||||||
bool art_manual_is_valid() const;
|
bool art_manual_is_valid() const;
|
||||||
bool has_valid_art() const;
|
bool has_valid_art() const;
|
||||||
bool is_compilation() const;
|
bool is_compilation() const;
|
||||||
|
bool stream_url_can_expire() const;
|
||||||
|
|
||||||
// Playlist views are special because you don't want to fill in album artists automatically for compilations, but you do for normal albums:
|
// Playlist views are special because you don't want to fill in album artists automatically for compilations, but you do for normal albums:
|
||||||
const QString &playlist_albumartist() const;
|
const QString &playlist_albumartist() const;
|
||||||
|
|
|
@ -42,8 +42,6 @@ Engine::Base::Base()
|
||||||
beginning_nanosec_(0),
|
beginning_nanosec_(0),
|
||||||
end_nanosec_(0),
|
end_nanosec_(0),
|
||||||
scope_(kScopeSize),
|
scope_(kScopeSize),
|
||||||
output_(""),
|
|
||||||
device_(QVariant()),
|
|
||||||
rg_enabled_(false),
|
rg_enabled_(false),
|
||||||
rg_mode_(0),
|
rg_mode_(0),
|
||||||
rg_preamp_(0.0),
|
rg_preamp_(0.0),
|
||||||
|
@ -66,7 +64,7 @@ Engine::Base::Base()
|
||||||
|
|
||||||
Engine::Base::~Base() {}
|
Engine::Base::~Base() {}
|
||||||
|
|
||||||
bool Engine::Base::Load(const QUrl &stream_url, const QUrl &original_url, TrackChangeFlags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
bool Engine::Base::Load(const QUrl &stream_url, const QUrl &original_url, const TrackChangeFlags, const bool force_stop_at_end, const quint64 beginning_nanosec, const qint64 end_nanosec) {
|
||||||
|
|
||||||
Q_UNUSED(force_stop_at_end);
|
Q_UNUSED(force_stop_at_end);
|
||||||
|
|
||||||
|
@ -76,16 +74,18 @@ bool Engine::Base::Load(const QUrl &stream_url, const QUrl &original_url, TrackC
|
||||||
end_nanosec_ = end_nanosec;
|
end_nanosec_ = end_nanosec;
|
||||||
|
|
||||||
about_to_end_emitted_ = false;
|
about_to_end_emitted_ = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Engine::Base::Play(const QUrl &stream_url, const QUrl &original_url, TrackChangeFlags flags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
bool Engine::Base::Play(const QUrl &stream_url, const QUrl &original_url, const TrackChangeFlags flags, const bool force_stop_at_end, const quint64 beginning_nanosec, const qint64 end_nanosec, const quint64 offset_nanosec) {
|
||||||
|
|
||||||
if (!Load(stream_url, original_url, flags, force_stop_at_end, beginning_nanosec, end_nanosec))
|
if (!Load(stream_url, original_url, flags, force_stop_at_end, beginning_nanosec, end_nanosec)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return Play(0);
|
return Play(offset_nanosec);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ void Engine::Base::SetVolume(const uint value) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint Engine::Base::MakeVolumeLogarithmic(uint volume) {
|
uint Engine::Base::MakeVolumeLogarithmic(const uint volume) {
|
||||||
// We're using a logarithmic function to make the volume ramp more natural.
|
// We're using a logarithmic function to make the volume ramp more natural.
|
||||||
return static_cast<uint>( 100 - 100.0 * std::log10( ( 100 - volume ) * 0.09 + 1.0 ) );
|
return static_cast<uint>( 100 - 100.0 * std::log10( ( 100 - volume ) * 0.09 + 1.0 ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ class Base : public QObject {
|
||||||
|
|
||||||
// Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length).
|
// Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length).
|
||||||
// Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown.
|
// Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown.
|
||||||
bool Play(const QUrl &stream_url, const QUrl &original_url, const TrackChangeFlags flags, const bool force_stop_at_end, const quint64 beginning_nanosec, const qint64 end_nanosec);
|
bool Play(const QUrl &stream_url, const QUrl &original_url, const TrackChangeFlags flags, const bool force_stop_at_end, const quint64 beginning_nanosec, const qint64 end_nanosec, const quint64 offset_nanosec);
|
||||||
void SetVolume(const uint value);
|
void SetVolume(const uint value);
|
||||||
static uint MakeVolumeLogarithmic(const uint volume);
|
static uint MakeVolumeLogarithmic(const uint volume);
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ class PlaylistView : public QTreeView {
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void PlayItem(QModelIndex idx, Playlist::AutoScroll autoscroll);
|
void PlayItem(QModelIndex idx, Playlist::AutoScroll autoscroll);
|
||||||
void PlayPause(Playlist::AutoScroll autoscroll = Playlist::AutoScroll_Never);
|
void PlayPause(const quint64 offset_nanosec = 0, Playlist::AutoScroll autoscroll = Playlist::AutoScroll_Never);
|
||||||
void RightClicked(QPoint global_pos, QModelIndex idx);
|
void RightClicked(QPoint global_pos, QModelIndex idx);
|
||||||
void SeekForward();
|
void SeekForward();
|
||||||
void SeekBackward();
|
void SeekBackward();
|
||||||
|
|
Loading…
Reference in New Issue