diff --git a/src/core/player.cpp b/src/core/player.cpp index 0be276b25..dbb47aea7 100644 --- a/src/core/player.cpp +++ b/src/core/player.cpp @@ -247,59 +247,107 @@ void Player::ReloadSettings() { void Player::HandleLoadResult(const UrlHandler::LoadResult &result) { + if (loading_async_.contains(result.original_url_)) { + loading_async_.removeAll(result.original_url_); + } + // Might've been an async load, so check we're still on the same item - shared_ptr item = app_->playlist_manager()->active()->current_item(); + PlaylistItemPtr item = app_->playlist_manager()->active()->current_item(); if (!item) { - loading_async_ = QUrl(); + return; + } + const bool has_next_row = app_->playlist_manager()->active()->next_row() != -1; + PlaylistItemPtr next_item; + if (has_next_row) { + next_item = app_->playlist_manager()->active()->item_at(app_->playlist_manager()->active()->next_row()); + } + + bool is_current(false); + bool is_next(false); + + if (result.original_url_ == item->Url()) { + is_current = true; + } + else if (has_next_row && next_item->Url() == result.original_url_) { + is_next = true; + } + else { return; } - if (item->Url() != result.original_url_) return; - switch (result.type_) { case UrlHandler::LoadResult::Error: - loading_async_ = QUrl(); - EngineStateChanged(Engine::Error); - FatalError(); + if (is_current) { + EngineStateChanged(Engine::Error); + FatalError(); + } emit Error(result.error_); break; - case UrlHandler::LoadResult::NoMoreTracks: - qLog(Debug) << "URL handler for" << result.original_url_ << "said no more tracks"; - loading_async_ = QUrl(); - NextItem(stream_change_type_); + case UrlHandler::LoadResult::NoMoreTracks: + qLog(Debug) << "URL handler for" << result.original_url_ << "said no more tracks" << is_current; + if (is_current) NextItem(stream_change_type_); break; case UrlHandler::LoadResult::TrackAvailable: { qLog(Debug) << "URL handler for" << result.original_url_ << "returned" << result.media_url_; - Song song = item->Metadata(); + Song song; + if (is_current) song = item->Metadata(); + else if (is_next) song = next_item->Metadata(); + bool update(false); - // If there was no filetype in the song's metadata, use the one provided by URL handler, if there is one + // Set the media url in the temporary metadata. if ( - (item->Metadata().filetype() == Song::FileType_Unknown && result.filetype_ != Song::FileType_Unknown) + (result.media_url_.isValid()) + && + (result.media_url_ != song.url()) + ) + { + song.set_url(result.media_url_); + update = true; + } + + // If there was no filetype in the song's metadata, use the one provided by URL handler, if there is one. + if ( + (song.filetype() == Song::FileType_Unknown && result.filetype_ != Song::FileType_Unknown) || - (item->Metadata().filetype() == Song::FileType_Stream && result.filetype_ != Song::FileType_Stream) + (song.filetype() == Song::FileType_Stream && result.filetype_ != Song::FileType_Stream) ) { song.set_filetype(result.filetype_); update = true; } - // If there was no length info in song's metadata, use the one provided by URL handler, if there is one - if (item->Metadata().length_nanosec() <= 0 && result.length_nanosec_ != -1) { + + // If there was no length info in song's metadata, use the one provided by URL handler, if there is one. + if (song.length_nanosec() <= 0 && result.length_nanosec_ != -1) { song.set_length_nanosec(result.length_nanosec_); update = true; } - if (update) { - item->SetTemporaryMetadata(song); - app_->playlist_manager()->active()->InformOfCurrentSongChange(); - } - engine_->Play(result.media_url_, result.original_url_, stream_change_type_, item->Metadata().has_cue(), item->Metadata().beginning_nanosec(), item->Metadata().end_nanosec()); - current_item_ = item; - loading_async_ = QUrl(); + if (update) { + if (is_current) { + item->SetTemporaryMetadata(song); + app_->playlist_manager()->active()->InformOfCurrentSongChange(); + } + else if (is_next) { + next_item->SetTemporaryMetadata(song); + app_->playlist_manager()->active()->ItemChanged(next_item); + } + } + + if (is_current) { + qLog(Debug) << "Playing song" << item->Metadata().title() << result.media_url_; + engine_->Play(result.media_url_, result.original_url_, stream_change_type_, item->Metadata().has_cue(), item->Metadata().beginning_nanosec(), item->Metadata().end_nanosec()); + current_item_ = item; + } + else if (is_next) { + qLog(Debug) << "Preloading next song" << next_item->Metadata().title() << result.media_url_; + engine_->StartPreloading(result.media_url_, next_item->Url(), next_item->Metadata().has_cue(), next_item->Metadata().beginning_nanosec(), next_item->Metadata().end_nanosec()); + } + break; } @@ -307,7 +355,7 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) { qLog(Debug) << "URL handler for" << result.original_url_ << "is loading asynchronously"; // We'll get called again later with either NoMoreTracks or TrackAvailable - loading_async_ = result.original_url_; + loading_async_ << result.original_url_; break; } @@ -319,19 +367,6 @@ void Player::NextInternal(Engine::TrackChangeFlags change) { if (HandleStopAfter()) return; - if (app_->playlist_manager()->active()->current_item()) { - const QUrl url = app_->playlist_manager()->active()->current_item()->Url(); - - if (url_handlers_.contains(url.scheme())) { - // The next track is already being loaded - if (url == loading_async_) return; - - stream_change_type_ = change; - HandleLoadResult(url_handlers_[url.scheme()]->LoadNext(url)); - return; - } - } - NextItem(change); } @@ -439,16 +474,20 @@ void Player::RestartOrPrevious() { } void Player::Stop(bool stop_after) { + engine_->Stop(stop_after); app_->playlist_manager()->active()->set_current_row(-1); current_item_.reset(); + } void Player::StopAfterCurrent() { + app_->playlist_manager()->active()->StopAfter(app_->playlist_manager()->active()->current_row()); } bool Player::PreviousWouldRestartTrack() const { + // Check if it has been over two seconds since previous button was pressed return menu_previousmode_ == PreviousBehaviour_Restart && last_pressed_previous_.isValid() && last_pressed_previous_.secsTo(QDateTime::currentDateTime()) >= 2; } @@ -529,10 +568,6 @@ void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle) if (current_item_ && change == Engine::Manual && engine_->position_nanosec() != engine_->length_nanosec()) { emit TrackSkipped(current_item_); - const QUrl &url = current_item_->Url(); - if (url_handlers_.contains(url.scheme())) { - url_handlers_[url.scheme()]->TrackSkipped(); - } } if (current_item_ && app_->playlist_manager()->active()->has_item_at(index) && current_item_->Metadata().IsOnSameAlbum(app_->playlist_manager()->active()->item_at(index)->Metadata())) { @@ -547,25 +582,27 @@ void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle) } current_item_ = app_->playlist_manager()->active()->current_item(); - const QUrl url = current_item_->Url(); + const QUrl url = (current_item_->MediaUrl().isValid() ? current_item_->MediaUrl() : current_item_->Url()); if (url_handlers_.contains(url.scheme())) { // It's already loading - if (url == loading_async_) return; + if (loading_async_.contains(url)) return; stream_change_type_ = change; HandleLoadResult(url_handlers_[url.scheme()]->StartLoading(url)); } else { - loading_async_ = QUrl(); - engine_->Play(current_item_->Url(), current_item_->Url(), change, current_item_->Metadata().has_cue(), current_item_->Metadata().beginning_nanosec(), current_item_->Metadata().end_nanosec()); + qLog(Debug) << "Playing song" << current_item_->Metadata().title() << url; + engine_->Play(url, current_item_->Url(), change, current_item_->Metadata().has_cue(), current_item_->Metadata().beginning_nanosec(), current_item_->Metadata().end_nanosec()); + if (current_item_->HasTemporaryMetadata()) + app_->playlist_manager()->active()->InformOfCurrentSongChange(); } } void Player::CurrentMetadataChanged(const Song &metadata) { - // those things might have changed (especially when a previously invalid song was reloaded) so we push the latest version into Engine + // Those things might have changed (especially when a previously invalid song was reloaded) so we push the latest version into Engine engine_->RefreshMarkers(metadata.beginning_nanosec(), metadata.end_nanosec()); // Send now playing to scrobble services @@ -668,6 +705,7 @@ void Player::Mute() { void Player::Pause() { engine_->Pause(); } void Player::Play() { + switch (GetState()) { case Engine::Playing: SeekTo(0); @@ -692,16 +730,6 @@ void Player::TogglePrettyOSD() { void Player::TrackAboutToEnd() { - // If the current track was from a URL handler then it might have special behaviour to queue up a subsequent track. - // We don't want to preload (and scrobble) the next item in the playlist if it's just going to be stopped again immediately after. - if (app_->playlist_manager()->active()->current_item()) { - const QUrl url = app_->playlist_manager()->active()->current_item()->Url(); - if (url_handlers_.contains(url.scheme())) { - url_handlers_[url.scheme()]->TrackAboutToEnd(); - return; - } - } - const bool has_next_row = app_->playlist_manager()->active()->next_row() != -1; PlaylistItemPtr next_item; @@ -726,28 +754,27 @@ void Player::TrackAboutToEnd() { // Crossfade is off, so start preloading the next track so we don't get a gap between songs. if (!has_next_row || !next_item) return; - QUrl url = next_item->Url(); + QUrl url = (next_item->MediaUrl().isValid() ? next_item->MediaUrl() : next_item->Url()); // Get the actual track URL rather than the stream URL. if (url_handlers_.contains(url.scheme())) { - UrlHandler::LoadResult result = url_handlers_[url.scheme()]->LoadNext(url); + if (loading_async_.contains(url)) return; + UrlHandler::LoadResult result = url_handlers_[url.scheme()]->StartLoading(url); switch (result.type_) { case UrlHandler::LoadResult::Error: - loading_async_ = QUrl(); - EngineStateChanged(Engine::Error); - FatalError(); emit Error(result.error_); return; case UrlHandler::LoadResult::NoMoreTracks: return; case UrlHandler::LoadResult::WillLoadAsynchronously: - loading_async_ = url; + loading_async_ << url; return; case UrlHandler::LoadResult::TrackAvailable: url = result.media_url_; break; } } + engine_->StartPreloading(url, next_item->Url(), next_item->Metadata().has_cue(), next_item->Metadata().beginning_nanosec(), next_item->Metadata().end_nanosec()); } diff --git a/src/core/player.h b/src/core/player.h index 833dae3bb..3fc70f044 100644 --- a/src/core/player.h +++ b/src/core/player.h @@ -232,7 +232,7 @@ class Player : public PlayerInterface { QMap url_handlers_; - QUrl loading_async_; + QList loading_async_; int volume_before_mute_; QDateTime last_pressed_previous_; diff --git a/src/core/urlhandler.h b/src/core/urlhandler.h index 335838ef9..65b184917 100644 --- a/src/core/urlhandler.h +++ b/src/core/urlhandler.h @@ -81,13 +81,6 @@ class UrlHandler : public QObject { // Called by the Player when a song starts loading - gives the handler a chance to do something clever to get a playable track. virtual LoadResult StartLoading(const QUrl &url) { return LoadResult(url); } - // Called by the player when a song finishes - gives the handler a chance to get another track to play. - virtual LoadResult LoadNext(const QUrl &url) { return LoadResult(url); } - - // Functions to be warned when something happen to a track handled by UrlHandler. - virtual void TrackAboutToEnd() {}; - virtual void TrackSkipped() {}; - signals: void AsyncLoadComplete(const UrlHandler::LoadResult &result);