mirror of
https://github.com/clangen/musikcube.git
synced 2024-10-02 13:02:35 +00:00
Added the ability for remote clients to create, rename, and delete
playlists. Note that this functionality was not added to the remote clients yet, only to the core SDK and to the websocket plugin. Specifically, updated ISimpleDataProvider interface with the following methods: 1. SavePlaylist() 2. RenamePlaylist() 3. DeletePlaylist() Then added the following messages to the server plugin: 1. save_playlist 2. rename_playlist 3. delete_playlist
This commit is contained in:
parent
50f4551b2c
commit
0fcd05dc2c
@ -114,7 +114,7 @@ void LocalLibrary::Close() {
|
||||
std::thread* thread = nullptr;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
std::unique_lock<std::recursive_mutex> lock(this->mutex);
|
||||
|
||||
delete this->indexer;
|
||||
this->indexer = nullptr;
|
||||
@ -159,7 +159,7 @@ int LocalLibrary::Enqueue(QueryPtr query, unsigned int options, Callback callbac
|
||||
LocalQueryPtr localQuery = std::dynamic_pointer_cast<LocalQuery>(query);
|
||||
|
||||
if (localQuery) {
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
std::unique_lock<std::recursive_mutex> lock(this->mutex);
|
||||
|
||||
if (this->exit) { /* closed */
|
||||
return -1;
|
||||
@ -189,7 +189,7 @@ int LocalLibrary::Enqueue(QueryPtr query, unsigned int options, Callback callbac
|
||||
|
||||
|
||||
LocalLibrary::QueryContextPtr LocalLibrary::GetNextQuery() {
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
std::unique_lock<std::recursive_mutex> lock(this->mutex);
|
||||
while (!this->queryQueue.size() && !this->exit) {
|
||||
this->queueCondition.wait(lock);
|
||||
}
|
||||
|
@ -120,8 +120,8 @@ namespace musik { namespace core { namespace library {
|
||||
std::string name;
|
||||
|
||||
std::thread* thread;
|
||||
std::condition_variable queueCondition;
|
||||
std::mutex mutex;
|
||||
std::condition_variable_any queueCondition;
|
||||
std::recursive_mutex mutex;
|
||||
std::atomic<bool> exit;
|
||||
|
||||
core::IIndexer *indexer;
|
||||
|
@ -40,8 +40,10 @@
|
||||
#include <core/library/query/local/AlbumListQuery.h>
|
||||
#include <core/library/query/local/CategoryListQuery.h>
|
||||
#include <core/library/query/local/CategoryTrackListQuery.h>
|
||||
#include <core/library/query/local/DeletePlaylistQuery.h>
|
||||
#include <core/library/query/local/SearchTrackListQuery.h>
|
||||
#include <core/library/query/local/GetPlaylistQuery.h>
|
||||
#include <core/library/query/local/SavePlaylistQuery.h>
|
||||
#include <core/library/query/local/TrackMetadataQuery.h>
|
||||
#include <core/library/track/LibraryTrack.h>
|
||||
#include <core/library/track/RetainedTrack.h>
|
||||
@ -210,3 +212,94 @@ IMetadataMapList* LocalSimpleDataProvider::QueryAlbums(
|
||||
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,
|
||||
const uint64_t playlistId)
|
||||
{
|
||||
if (playlistId == 0 && (!name || !strlen(name))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
std::shared_ptr<TrackList> sharedTrackList =
|
||||
std::make_shared<TrackList>(this->library, trackIds, trackIdCount);
|
||||
|
||||
/* replacing (and optionally renaming) an existing playlist */
|
||||
if (playlistId != 0) {
|
||||
std::shared_ptr<SavePlaylistQuery> query =
|
||||
SavePlaylistQuery::Replace(playlistId, sharedTrackList);
|
||||
|
||||
this->library->Enqueue(query, ILibrary::QuerySynchronous);
|
||||
|
||||
if (query->GetStatus() == IQuery::Finished) {
|
||||
if (strlen(name)) {
|
||||
query = SavePlaylistQuery::Rename(playlistId, name);
|
||||
|
||||
this->library->Enqueue(query, ILibrary::QuerySynchronous);
|
||||
|
||||
if (query->GetStatus() == IQuery::Finished) {
|
||||
return playlistId;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return playlistId;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::shared_ptr<SavePlaylistQuery> query =
|
||||
SavePlaylistQuery::Save(name, sharedTrackList);
|
||||
|
||||
this->library->Enqueue(query, ILibrary::QuerySynchronous);
|
||||
|
||||
if (query->GetStatus() == IQuery::Finished) {
|
||||
return query->GetPlaylistId();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
musik::debug::err(TAG, "SavePlaylist failed");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool LocalSimpleDataProvider::RenamePlaylist(const uint64_t playlistId, const char* name)
|
||||
{
|
||||
try {
|
||||
std::shared_ptr<SavePlaylistQuery> query =
|
||||
SavePlaylistQuery::Rename(playlistId, name);
|
||||
|
||||
this->library->Enqueue(query, ILibrary::QuerySynchronous);
|
||||
|
||||
if (query->GetStatus() == IQuery::Finished) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
musik::debug::err(TAG, "RenamePlaylist failed");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LocalSimpleDataProvider::DeletePlaylist(const uint64_t playlistId) {
|
||||
try {
|
||||
std::shared_ptr<DeletePlaylistQuery> query =
|
||||
std::make_shared<DeletePlaylistQuery>(playlistId);
|
||||
|
||||
this->library->Enqueue(query, ILibrary::QuerySynchronous);
|
||||
|
||||
if (query->GetStatus() == IQuery::Finished) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
musik::debug::err(TAG, "DeletePlaylist failed");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -49,11 +49,13 @@ namespace musik { namespace core { namespace db { namespace local {
|
||||
QueryTracks(
|
||||
const char* query = "",
|
||||
int limit = -1,
|
||||
int offset = 0);
|
||||
int offset = 0) override;
|
||||
|
||||
virtual musik::core::sdk::IRetainedTrack* QueryTrackById(int64_t trackId);
|
||||
virtual musik::core::sdk::IRetainedTrack*
|
||||
QueryTrackById(int64_t trackId) override;
|
||||
|
||||
virtual musik::core::sdk::IRetainedTrack* QueryTrackByExternalId(const char* externalId);
|
||||
virtual musik::core::sdk::IRetainedTrack*
|
||||
QueryTrackByExternalId(const char* externalId) override;
|
||||
|
||||
virtual musik::core::sdk::ITrackList*
|
||||
QueryTracksByCategory(
|
||||
@ -61,20 +63,32 @@ namespace musik { namespace core { namespace db { namespace local {
|
||||
int64_t selectedId,
|
||||
const char* filter = "",
|
||||
int limit = -1,
|
||||
int offset = 0);
|
||||
int offset = 0) override;
|
||||
|
||||
virtual musik::core::sdk::IMetadataValueList*
|
||||
QueryCategory(
|
||||
const char* type,
|
||||
const char* filter = "");
|
||||
const char* filter = "") override;
|
||||
|
||||
virtual musik::core::sdk::IMetadataMapList*
|
||||
QueryAlbums(const char* filter = "");
|
||||
QueryAlbums(const char* filter = "") override;
|
||||
|
||||
virtual musik::core::sdk::IMetadataMapList* QueryAlbums(
|
||||
const char* categoryIdName,
|
||||
int64_t categoryIdValue,
|
||||
const char* filter = "");
|
||||
const char* filter = "") override;
|
||||
|
||||
virtual uint64_t SavePlaylist(
|
||||
int64_t trackIds[],
|
||||
size_t trackIdCount,
|
||||
const char* name,
|
||||
const uint64_t playlistId = 0) override;
|
||||
|
||||
virtual bool RenamePlaylist(
|
||||
const uint64_t playlistId,
|
||||
const char* name) override;
|
||||
|
||||
virtual bool DeletePlaylist(const uint64_t playlistId) override;
|
||||
|
||||
private:
|
||||
musik::core::ILibraryPtr library;
|
||||
|
@ -178,6 +178,10 @@ SavePlaylistQuery::SavePlaylistQuery(
|
||||
SavePlaylistQuery::~SavePlaylistQuery() {
|
||||
}
|
||||
|
||||
int64_t SavePlaylistQuery::GetPlaylistId() const {
|
||||
return playlistId;
|
||||
}
|
||||
|
||||
bool SavePlaylistQuery::AddTracksToPlaylist(
|
||||
musik::core::db::Connection &db,
|
||||
int64_t playlistId,
|
||||
@ -245,17 +249,17 @@ bool SavePlaylistQuery::CreatePlaylist(musik::core::db::Connection &db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t playlistId = db.LastInsertedId();
|
||||
this->playlistId = db.LastInsertedId();
|
||||
|
||||
/* add tracks to playlist */
|
||||
if (this->tracks) {
|
||||
if (!this->AddTracksToPlaylist(db, playlistId, this->tracks)) {
|
||||
if (!this->AddTracksToPlaylist(db, this->playlistId, this->tracks)) {
|
||||
transaction.Cancel();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!this->AddCategoryTracksToPlaylist(db, playlistId)) {
|
||||
if (!this->AddCategoryTracksToPlaylist(db, this->playlistId)) {
|
||||
transaction.Cancel();
|
||||
return false;
|
||||
}
|
||||
|
@ -76,6 +76,8 @@ namespace musik { namespace core { namespace db { namespace local {
|
||||
|
||||
virtual ~SavePlaylistQuery();
|
||||
|
||||
int64_t GetPlaylistId() const;
|
||||
|
||||
protected:
|
||||
virtual bool OnRun(musik::core::db::Connection &db);
|
||||
|
||||
|
@ -63,6 +63,11 @@ TrackList::TrackList(TrackList* other)
|
||||
this->library = library;
|
||||
}
|
||||
|
||||
TrackList::TrackList(ILibraryPtr library, int64_t trackIds[], size_t trackIdCount)
|
||||
: library(library) {
|
||||
this->ids.insert(this->ids.end(), &trackIds[0], &trackIds[trackIdCount]);
|
||||
}
|
||||
|
||||
TrackList::~TrackList() {
|
||||
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ namespace musik { namespace core {
|
||||
public:
|
||||
TrackList(ILibraryPtr library);
|
||||
TrackList(TrackList* other);
|
||||
TrackList(ILibraryPtr library, int64_t trackIds[], size_t trackIdCount);
|
||||
|
||||
virtual ~TrackList();
|
||||
|
||||
/* ITrackList */
|
||||
|
@ -69,6 +69,18 @@ namespace musik { namespace core { namespace sdk {
|
||||
const char* categoryIdName,
|
||||
int64_t categoryIdValue,
|
||||
const char* filter = "") = 0;
|
||||
|
||||
virtual uint64_t SavePlaylist(
|
||||
int64_t trackIds[],
|
||||
size_t trackIdCount,
|
||||
const char* playlistName,
|
||||
const uint64_t playlistId = 0) = 0;
|
||||
|
||||
virtual bool RenamePlaylist(
|
||||
const uint64_t playlistId,
|
||||
const char* playlistName) = 0;
|
||||
|
||||
virtual bool DeletePlaylist(const uint64_t playlistId) = 0;
|
||||
};
|
||||
|
||||
} } }
|
||||
|
@ -110,6 +110,8 @@ namespace key {
|
||||
static const std::string relative = "relative";
|
||||
static const std::string password = "password";
|
||||
static const std::string authenticated = "authenticated";
|
||||
static const std::string playlist_id = "playlist_id";
|
||||
static const std::string playlist_name = "playlist_name";
|
||||
}
|
||||
|
||||
namespace value {
|
||||
@ -151,6 +153,9 @@ namespace request {
|
||||
static const std::string play_tracks_by_category = "play_tracks_by_category";
|
||||
static const std::string query_play_queue_tracks = "query_play_queue_tracks";
|
||||
static const std::string get_environment = "get_environment";
|
||||
static const std::string save_playlist = "save_playlist";
|
||||
static const std::string rename_playlist = "rename_playlist";
|
||||
static const std::string delete_playlist = "delete_playlist";
|
||||
}
|
||||
|
||||
namespace fragment {
|
||||
|
@ -330,6 +330,18 @@ void WebSocketServer::HandleRequest(connection_hdl connection, json& request) {
|
||||
this->RespondWithCurrentTime(connection, request);
|
||||
return;
|
||||
}
|
||||
else if (name == request::save_playlist) {
|
||||
this->RespondWithSavePlaylist(connection, request);
|
||||
return;
|
||||
}
|
||||
else if (name == request::rename_playlist) {
|
||||
this->RespondWithRenamePlaylist(connection, request);
|
||||
return;
|
||||
}
|
||||
else if (name == request::delete_playlist) {
|
||||
this->RespondWithDeletePlaylist(connection, request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->RespondWithInvalidRequest(connection, name, id);
|
||||
@ -770,6 +782,57 @@ 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, "");
|
||||
|
||||
size_t count = ids.size();
|
||||
int64_t* idArray = new int64_t[count];
|
||||
std::copy(ids.begin(), ids.end(), idArray);
|
||||
|
||||
uint64_t newPlaylistId = this->context.dataProvider
|
||||
->SavePlaylist(idArray, count, name.c_str(), id);
|
||||
|
||||
delete[] idArray;
|
||||
|
||||
if (newPlaylistId != 0) {
|
||||
this->RespondWithOptions(connection, request, {
|
||||
{ key::playlist_id, newPlaylistId }
|
||||
});
|
||||
}
|
||||
else {
|
||||
this->RespondWithFailure(connection, request);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->RespondWithInvalidRequest(
|
||||
connection, request[message::name], request[message::id]);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketServer::RespondWithRenamePlaylist(connection_hdl connection, json& request) {
|
||||
auto& options = request[message::options];
|
||||
int64_t id = options[key::playlist_id];
|
||||
std::string name = options[key::playlist_name];
|
||||
|
||||
this->context.dataProvider->RenamePlaylist(id, name.c_str())
|
||||
? this->RespondWithSuccess(connection, request)
|
||||
: this->RespondWithFailure(connection, request);
|
||||
}
|
||||
|
||||
void WebSocketServer::RespondWithDeletePlaylist(connection_hdl connection, json& request) {
|
||||
auto& options = request[message::options];
|
||||
int64_t id = options[key::playlist_id];
|
||||
|
||||
this->context.dataProvider->DeletePlaylist(id)
|
||||
? this->RespondWithSuccess(connection, request)
|
||||
: this->RespondWithFailure(connection, request);
|
||||
}
|
||||
|
||||
void WebSocketServer::BroadcastPlaybackOverview() {
|
||||
{
|
||||
auto rl = connectionLock.Read();
|
||||
|
@ -146,6 +146,9 @@ class WebSocketServer {
|
||||
void RespondWithPlayTracksByCategory(connection_hdl connection, json& request);
|
||||
void RespondWithEnvironment(connection_hdl connection, json& request);
|
||||
void RespondWithCurrentTime(connection_hdl connection, json& request);
|
||||
void RespondWithSavePlaylist(connection_hdl connection, json& request);
|
||||
void RespondWithRenamePlaylist(connection_hdl connection, json& request);
|
||||
void RespondWithDeletePlaylist(connection_hdl connection, json& request);
|
||||
|
||||
void BroadcastPlaybackOverview();
|
||||
void BroadcastPlayQueueChanged();
|
||||
|
Loading…
Reference in New Issue
Block a user