From 0b3e1b2f1980fd479459c4fac52cc29effef4cbf Mon Sep 17 00:00:00 2001 From: casey langen Date: Tue, 23 Jan 2018 16:46:57 -0800 Subject: [PATCH] Added the ability for remote clients to request playback of a previously snapshotted queue. Also fixed a bug in TrackList where the wrong type of wrapped ITrack was getting returned, potentially leading to read-after-free bugs. --- src/core/library/track/TrackList.cpp | 4 +- src/plugins/server/Constants.h | 1 + src/plugins/server/WebSocketServer.cpp | 102 ++++++++++++++++--------- src/plugins/server/WebSocketServer.h | 1 + 4 files changed, 67 insertions(+), 41 deletions(-) diff --git a/src/core/library/track/TrackList.cpp b/src/core/library/track/TrackList.cpp index 591229a26..9185bba94 100755 --- a/src/core/library/track/TrackList.cpp +++ b/src/core/library/track/TrackList.cpp @@ -150,9 +150,7 @@ TrackPtr TrackList::Get(size_t index) const { } ITrack* TrackList::GetTrack(size_t index) const { - auto track = this->Get(index).get(); - track->Retain(); - return track; + return this->Get(index)->GetSdkValue(); } int64_t TrackList::GetId(size_t index) const { diff --git a/src/plugins/server/Constants.h b/src/plugins/server/Constants.h index 5a3699fbc..694cb1f39 100644 --- a/src/plugins/server/Constants.h +++ b/src/plugins/server/Constants.h @@ -179,6 +179,7 @@ namespace request { static const std::string query_albums = "query_albums"; static const std::string query_tracks_by_category = "query_tracks_by_category"; static const std::string play_all_tracks = "play_all_tracks"; + static const std::string play_snapshot_tracks = "play_snapshot_tracks"; static const std::string play_tracks = "play_tracks"; static const std::string play_tracks_by_category = "play_tracks_by_category"; static const std::string query_play_queue_tracks = "query_play_queue_tracks"; diff --git a/src/plugins/server/WebSocketServer.cpp b/src/plugins/server/WebSocketServer.cpp index 68d7bdffe..de4a02478 100644 --- a/src/plugins/server/WebSocketServer.cpp +++ b/src/plugins/server/WebSocketServer.cpp @@ -410,6 +410,10 @@ void WebSocketServer::HandleRequest(connection_hdl connection, json& request) { this->RespondWithPlayAllTracks(connection, request); return; } + else if (name == request::play_snapshot_tracks) { + this->RespondWithPlaySnapshotTracks(connection, request); + return; + } else if (name == request::play_tracks) { this->RespondWithPlayTracks(connection, request); return; @@ -702,6 +706,7 @@ void WebSocketServer::RespondWithQueryTracksByExternalIds(connection_hdl connect track = trackList->GetTrack(i); externalId = GetMetadataString(track, track::ExternalId); tracks[externalId] = this->ReadTrackMetadata(track); + track->Release(); } trackList->Release(); @@ -752,53 +757,48 @@ void WebSocketServer::RespondWithPlayQueueTracks(connection_hdl connection, json }); } else { + bool idsOnly = request[message::options].value(key::ids_only, false); + static auto releaseDeleter = [](ITrack* track) { track->Release(); }; static auto nullDeleter = [](ITrack* track) { }; - std::vector> tracks; - - /* edit the playlist so it can be changed while we're getting the tracks - out of it. only applicable for the "live" type. */ - ITrackListEditor* editor = nullptr; - - if (type == value::live) { - editor = context.playback->EditPlaylist(); - } - - int trackCount = (int)context.playback->Count(); - int to = trackCount; - - if (offset >= 0 && limit >= 0) { - to = std::min(trackCount, offset + limit); - } - - ITrack* track; - std::function deleter; - - if (editor) { deleter = releaseDeleter; } - else { deleter = nullDeleter; } - - for (int i = offset; i < to; i++) { - track = editor ? context.playback->GetTrack(i) : playQueueSnapshot->GetTrack(i); - tracks.push_back(std::shared_ptr(track, deleter)); - } - - if (editor) { - editor->Release(); - } - /* now add the tracks to the output. they will be Release()'d automatically as soon as this scope ends. */ json data = json::array(); - bool idsOnly = request[message::options].value(key::ids_only, false); + if (type == value::live) { + /* edit the playlist so it can be changed while we're getting the tracks + out of it. only applicable for the "live" type. */ + ITrackListEditor* editor = context.playback->EditPlaylist(); + int to = (int)context.playback->Count(); - for (auto track : tracks) { - if (idsOnly) { - data.push_back(GetMetadataString(track.get(), key::external_id)); + if (offset >= 0 && limit >= 0) { + to = std::min(to, offset + limit); } - else { - data.push_back(this->ReadTrackMetadata(track.get())); + + for (int i = offset; i < to; i++) { + ITrack* track = context.playback->GetTrack(i); + if (idsOnly) { data.push_back(GetMetadataString(track, key::external_id)); } + else { data.push_back(this->ReadTrackMetadata(track)); } + track->Release(); + } + + editor->Release(); + } + else if (type == value::snapshot) { + if (this->playQueueSnapshot) { + int to = (int)this->playQueueSnapshot->Count(); + + if (offset >= 0 && limit >= 0) { + to = std::min(to, offset + limit); + } + + for (int i = offset; i < to; i++) { + ITrack* track = playQueueSnapshot->GetTrack(i); + if (idsOnly) { data.push_back(GetMetadataString(track, key::external_id)); } + else { data.push_back(this->ReadTrackMetadata(track)); } + track->Release(); + } } } @@ -1064,6 +1064,32 @@ void WebSocketServer::RespondWithPlayAllTracks(connection_hdl connection, json& RespondWithSuccess(connection, request); } +void WebSocketServer::RespondWithPlaySnapshotTracks(connection_hdl connection, json& request) { + if (this->playQueueSnapshot) { + size_t index = 0; + double time = 0.0; + + if (request.find(message::options) != request.end()) { + index = request[message::options].value(key::index, 0); + time = request[message::options].value(key::time, 0.0); + } + + context.playback->Play(this->playQueueSnapshot, index); + + if (time > 0.0) { + context.playback->SetPosition(time); + } + } + else { + context.playback->Stop(); + auto editor = context.playback->EditPlaylist(); + editor->Clear(); + editor->Release(); + } + + RespondWithSuccess(connection, request); +} + void WebSocketServer::RespondWithPlayTracksByCategory(connection_hdl connection, json& request) { int limit, offset; ITrackList* tracks = this->QueryTracksByCategory(request, limit, offset); diff --git a/src/plugins/server/WebSocketServer.h b/src/plugins/server/WebSocketServer.h index ae61f5ca0..97093de0c 100644 --- a/src/plugins/server/WebSocketServer.h +++ b/src/plugins/server/WebSocketServer.h @@ -146,6 +146,7 @@ class WebSocketServer { void RespondWithListCategories(connection_hdl connection, json& request); void RespondWithQueryCategory(connection_hdl connection, json& request); void RespondWithPlayAllTracks(connection_hdl connection, json& request); + void RespondWithPlaySnapshotTracks(connection_hdl connection, json& request); void RespondWithPlayTracksByCategory(connection_hdl connection, json& request); void RespondWithEnvironment(connection_hdl connection, json& request); void RespondWithCurrentTime(connection_hdl connection, json& request);