diff --git a/src/core/library/LocalSimpleDataProvider.cpp b/src/core/library/LocalSimpleDataProvider.cpp index dbbcb7600..aa5a07a9f 100644 --- a/src/core/library/LocalSimpleDataProvider.cpp +++ b/src/core/library/LocalSimpleDataProvider.cpp @@ -49,7 +49,6 @@ #include #include - #define TAG "LocalSimpleDataProvider" using namespace musik::core; @@ -58,6 +57,59 @@ using namespace musik::core::db::local; using namespace musik::core::library; using namespace musik::core::sdk; +class ExternalIdListToTrackListQuery : public LocalQueryBase { + public: + ExternalIdListToTrackListQuery( + ILibraryPtr library, + const char** externalIds, + size_t externalIdCount) + { + this->library = library; + this->externalIds = externalIds; + this->externalIdCount = externalIdCount; + } + + virtual ~ExternalIdListToTrackListQuery() { + } + + std::shared_ptr Result() { + return this->result; + } + + protected: + virtual bool OnRun(musik::core::db::Connection& db) { + std::string sql = "SELECT id FROM tracks WHERE external_id IN("; + for (size_t i = 0; i < externalIdCount; i++) { + sql += (i == 0) ? "?" : ",?"; + } + sql += ");"; + + Statement query(sql.c_str(), db); + + for (size_t i = 0; i < externalIdCount; i++) { + query.BindText(i, externalIds[i]); + } + + this->result = std::make_shared(this->library); + + while (query.Step() == Row) { + result->Add(query.ColumnInt64(0)); + } + + return true; + } + + virtual std::string Name() { + return "ExternalIdListToTrackListQuery"; + } + + private: + ILibraryPtr library; + const char** externalIds; + size_t externalIdCount; + std::shared_ptr result; +}; + LocalSimpleDataProvider::LocalSimpleDataProvider(musik::core::ILibraryPtr library) : library(library) { @@ -213,32 +265,25 @@ IMetadataMapList* LocalSimpleDataProvider::QueryAlbums(const char* filter) { return this->QueryAlbums(nullptr, -1, filter); } -uint64_t LocalSimpleDataProvider::SavePlaylist( - int64_t trackIds[], - size_t trackIdCount, - const char* name, +static uint64_t savePlaylist( + ILibraryPtr library, + std::shared_ptr trackList, + const char* playlistName, const uint64_t playlistId) { - if (playlistId == 0 && (!name || !strlen(name))) { - return 0; - } - try { - std::shared_ptr sharedTrackList = - std::make_shared(this->library, trackIds, trackIdCount); - /* replacing (and optionally renaming) an existing playlist */ if (playlistId != 0) { std::shared_ptr query = - SavePlaylistQuery::Replace(playlistId, sharedTrackList); + SavePlaylistQuery::Replace(playlistId, trackList); - this->library->Enqueue(query, ILibrary::QuerySynchronous); + library->Enqueue(query, ILibrary::QuerySynchronous); if (query->GetStatus() == IQuery::Finished) { - if (strlen(name)) { - query = SavePlaylistQuery::Rename(playlistId, name); + if (strlen(playlistName)) { + query = SavePlaylistQuery::Rename(playlistId, playlistName); - this->library->Enqueue(query, ILibrary::QuerySynchronous); + library->Enqueue(query, ILibrary::QuerySynchronous); if (query->GetStatus() == IQuery::Finished) { return playlistId; @@ -251,9 +296,9 @@ uint64_t LocalSimpleDataProvider::SavePlaylist( } else { std::shared_ptr query = - SavePlaylistQuery::Save(name, sharedTrackList); + SavePlaylistQuery::Save(playlistName, trackList); - this->library->Enqueue(query, ILibrary::QuerySynchronous); + library->Enqueue(query, ILibrary::QuerySynchronous); if (query->GetStatus() == IQuery::Finished) { return query->GetPlaylistId(); @@ -267,6 +312,46 @@ uint64_t LocalSimpleDataProvider::SavePlaylist( return 0; } +uint64_t LocalSimpleDataProvider::SavePlaylistWithIds( + int64_t* trackIds, + size_t trackIdCount, + const char* playlistName, + const uint64_t playlistId) +{ + if (playlistId == 0 && (!playlistName || !strlen(playlistName))) { + return 0; + } + + std::shared_ptr trackList = + std::make_shared(this->library, trackIds, trackIdCount); + + return savePlaylist(this->library, trackList, playlistName, playlistId); +} + +uint64_t LocalSimpleDataProvider::SavePlaylistWithExternalIds( + const char** externalIds, + size_t externalIdCount, + const char* playlistName, + const uint64_t playlistId) +{ + if (playlistId == 0 && (!playlistName || !strlen(playlistName))) { + return 0; + } + + using Query = ExternalIdListToTrackListQuery; + + std::shared_ptr query = + std::make_shared (this->library, externalIds, externalIdCount); + + library->Enqueue(query, ILibrary::QuerySynchronous); + + if (query->GetStatus() == IQuery::Finished) { + return savePlaylist(this->library, query->Result(), playlistName, playlistId); + } + + return 0; +} + bool LocalSimpleDataProvider::RenamePlaylist(const uint64_t playlistId, const char* name) { try { diff --git a/src/core/library/LocalSimpleDataProvider.h b/src/core/library/LocalSimpleDataProvider.h index cfefcdead..95d96ff49 100644 --- a/src/core/library/LocalSimpleDataProvider.h +++ b/src/core/library/LocalSimpleDataProvider.h @@ -78,12 +78,18 @@ namespace musik { namespace core { namespace db { namespace local { int64_t categoryIdValue, const char* filter = "") override; - virtual uint64_t SavePlaylist( - int64_t trackIds[], + virtual uint64_t SavePlaylistWithIds( + int64_t* trackIds, size_t trackIdCount, const char* name, const uint64_t playlistId = 0) override; + virtual uint64_t SavePlaylistWithExternalIds( + const char** externalIds, + size_t externalIdCount, + const char* playlistName, + const uint64_t playlistId = 0) override; + virtual bool RenamePlaylist( const uint64_t playlistId, const char* name) override; diff --git a/src/core/sdk/ISimpleDataProvider.h b/src/core/sdk/ISimpleDataProvider.h index 26df4f43f..7e06f71a2 100644 --- a/src/core/sdk/ISimpleDataProvider.h +++ b/src/core/sdk/ISimpleDataProvider.h @@ -70,12 +70,18 @@ namespace musik { namespace core { namespace sdk { int64_t categoryIdValue, const char* filter = "") = 0; - virtual uint64_t SavePlaylist( - int64_t trackIds[], + virtual uint64_t SavePlaylistWithIds( + int64_t* trackIds, size_t trackIdCount, const char* playlistName, const uint64_t playlistId = 0) = 0; + virtual uint64_t SavePlaylistWithExternalIds( + const char** externalIds, + size_t externalIdCount, + const char* playlistName, + const uint64_t playlistId = 0) = 0; + virtual bool RenamePlaylist( const uint64_t playlistId, const char* playlistName) = 0; diff --git a/src/plugins/websocket_remote/Constants.h b/src/plugins/websocket_remote/Constants.h index 2283fa9c6..c81263e31 100644 --- a/src/plugins/websocket_remote/Constants.h +++ b/src/plugins/websocket_remote/Constants.h @@ -80,6 +80,7 @@ namespace key { static const std::string playing_track = "playing_track"; static const std::string title = "title"; static const std::string external_id = "external_id"; + static const std::string external_ids = "external_ids"; static const std::string filename = "filename"; static const std::string duration = "duration"; static const std::string artist = "artist"; diff --git a/src/plugins/websocket_remote/WebSocketServer.cpp b/src/plugins/websocket_remote/WebSocketServer.cpp index b86fb243f..7de95afa8 100644 --- a/src/plugins/websocket_remote/WebSocketServer.cpp +++ b/src/plugins/websocket_remote/WebSocketServer.cpp @@ -785,32 +785,79 @@ void WebSocketServer::RespondWithCurrentTime(connection_hdl connection, json& re void WebSocketServer::RespondWithSavePlaylist(connection_hdl connection, json& request) { auto& options = request[message::options]; - json& ids = options[key::ids]; - if (ids.is_array()) { - int64_t id = options.value(key::playlist_id, 0); - std::string name = options.value(key::playlist_name, ""); + /* by int64 id (faster, but less reliable) */ + if (options.find(key::ids) != options.end()) { + json& ids = options[key::ids]; - size_t count = ids.size(); - int64_t* idArray = new int64_t[count]; + if (ids.is_array()) { + int64_t id = options.value(key::playlist_id, 0); + std::string name = options.value(key::playlist_name, ""); - if (count > 0) { - std::copy(ids.begin(), ids.end(), idArray); - } + size_t count = ids.size(); + int64_t* idArray = new int64_t[count]; - uint64_t newPlaylistId = this->context.dataProvider - ->SavePlaylist(idArray, count, name.c_str(), id); + if (count > 0) { + std::copy(ids.begin(), ids.end(), idArray); + } - delete[] idArray; + uint64_t newPlaylistId = this->context.dataProvider + ->SavePlaylistWithIds(idArray, count, name.c_str(), id); - if (newPlaylistId != 0) { - this->RespondWithOptions(connection, request, { - { key::playlist_id, newPlaylistId } - }); - } - else { - this->RespondWithFailure(connection, request); + delete[] idArray; + + if (newPlaylistId != 0) { + this->RespondWithOptions(connection, request, { + { key::playlist_id, newPlaylistId } + }); + } + else { + this->RespondWithFailure(connection, request); + } } } + /* by external id (slower, more reliable) */ + else if (options.find(key::external_ids) != options.end()) { + json& externalIds = options[key::external_ids]; + + if (externalIds.is_array()) { + int64_t id = options.value(key::playlist_id, 0); + std::string name = options.value(key::playlist_name, ""); + + size_t count = externalIds.size(); + char** externalIdArray = (char**) malloc(count * sizeof(char*)); + + for (size_t i = 0; i < count; i++) { + std::string externalId = externalIds[i]; + size_t size = externalId.size(); + externalIdArray[i] = (char*) malloc(size + 1); + strncpy(externalIdArray[i], externalId.c_str(), size); + externalIdArray[i][size] = 0; + } + + uint64_t newPlaylistId = this->context.dataProvider + ->SavePlaylistWithExternalIds( + (const char**) externalIdArray, + count, + name.c_str(), + id); + + for (size_t i = 0; i < count; i++) { + free(externalIdArray[i]); + } + + free(externalIdArray); + + if (newPlaylistId != 0) { + this->RespondWithOptions(connection, request, { + { key::playlist_id, newPlaylistId } + }); + } + else { + this->RespondWithFailure(connection, request); + } + } + } + /* no id list or external id list */ else { this->RespondWithInvalidRequest( connection, request[message::name], request[message::id]);