diff --git a/doc/protocol.rst b/doc/protocol.rst index 69bdada87..9d8125698 100644 --- a/doc/protocol.rst +++ b/doc/protocol.rst @@ -1008,10 +1008,21 @@ remote playlists (absolute URI with a supported scheme). .. _command_save: -:command:`save {NAME}` +:command:`save {NAME} [MODE]` Saves the queue to `NAME.m3u` in the playlist directory. + ``MODE`` [#since_0_24]_ + Optional argument. One of `create`, `append`, or `replace`. + + `create` + The default. Create a new playlist. + Fail if a playlist with name ``NAME`` already exists. + + `append`, `replace` + Append or replace an existing playlist. + Fail if a playlist with name ``NAME`` doesn\'t already exist. + The music database ================== diff --git a/src/PlaylistSave.cxx b/src/PlaylistSave.cxx index 6bcedb2d7..73157ca79 100644 --- a/src/PlaylistSave.cxx +++ b/src/PlaylistSave.cxx @@ -84,16 +84,25 @@ playlist_print_uri(BufferedOutputStream &os, const char *uri) } void -spl_save_queue(const char *name_utf8, const Queue &queue) +spl_save_queue(const char *name_utf8, PlaylistSaveMode save_mode, const Queue &queue) { const auto path_fs = spl_map_to_fs(name_utf8); assert(!path_fs.IsNull()); - if (FileExists(path_fs)) - throw PlaylistError(PlaylistResult::LIST_EXISTS, - "Playlist already exists"); + if (save_mode == PlaylistSaveMode::CREATE) { + if (FileExists(path_fs)) { + throw PlaylistError(PlaylistResult::LIST_EXISTS, "Playlist already exists"); + } + } + else if (!FileExists(path_fs)) { + throw PlaylistError(PlaylistResult::NO_SUCH_LIST, "No such playlist"); + } + + FileOutputStream fos(path_fs, + save_mode == PlaylistSaveMode::APPEND + ? FileOutputStream::Mode::APPEND_EXISTING + : FileOutputStream::Mode::CREATE); - FileOutputStream fos(path_fs); BufferedOutputStream bos(fos); for (unsigned i = 0; i < queue.GetLength(); i++) @@ -106,7 +115,7 @@ spl_save_queue(const char *name_utf8, const Queue &queue) } void -spl_save_playlist(const char *name_utf8, const playlist &playlist) +spl_save_playlist(const char *name_utf8, PlaylistSaveMode save_mode, const playlist &playlist) { - spl_save_queue(name_utf8, playlist.queue); + spl_save_queue(name_utf8, save_mode, playlist.queue); } diff --git a/src/PlaylistSave.hxx b/src/PlaylistSave.hxx index eebd9ed14..f0063783f 100644 --- a/src/PlaylistSave.hxx +++ b/src/PlaylistSave.hxx @@ -31,16 +31,19 @@ playlist_print_song(BufferedOutputStream &os, const DetachedSong &song); void playlist_print_uri(BufferedOutputStream &os, const char *uri); -/** - * Saves a queue object into a stored playlist file. - */ +enum class PlaylistSaveMode { + CREATE, + APPEND, + REPLACE +}; + void -spl_save_queue(const char *name_utf8, const Queue &queue); +spl_save_queue(const char *name_utf8, PlaylistSaveMode save_mode, const Queue &queue); /** * Saves a playlist object into a stored playlist file. */ void -spl_save_playlist(const char *name_utf8, const playlist &playlist); +spl_save_playlist(const char *name_utf8, PlaylistSaveMode save_mode, const playlist &playlist); #endif diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx index 30f698640..ca2f29a9a 100644 --- a/src/command/AllCommands.cxx +++ b/src/command/AllCommands.cxx @@ -184,7 +184,7 @@ static constexpr struct command commands[] = { handle_replay_gain_status }, { "rescan", PERMISSION_CONTROL, 0, 1, handle_rescan }, { "rm", PERMISSION_CONTROL, 1, 1, handle_rm }, - { "save", PERMISSION_CONTROL, 1, 1, handle_save }, + { "save", PERMISSION_CONTROL, 1, 2, handle_save }, #ifdef ENABLE_DATABASE { "search", PERMISSION_READ, 1, -1, handle_search }, { "searchadd", PERMISSION_ADD, 1, -1, handle_searchadd }, diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx index b6311b4f9..c3654d30f 100644 --- a/src/command/PlaylistCommands.cxx +++ b/src/command/PlaylistCommands.cxx @@ -66,7 +66,22 @@ print_spl_list(Response &r, const PlaylistVector &list) CommandResult handle_save(Client &client, Request args, [[maybe_unused]] Response &r) { - spl_save_playlist(args.front(), client.GetPlaylist()); + PlaylistSaveMode mode = PlaylistSaveMode::CREATE; + + const char *mode_arg = args.GetOptional(1); + if (mode_arg != nullptr) { + if (StringIsEqual(mode_arg, "create")) + mode = PlaylistSaveMode::CREATE; + else if (StringIsEqual(mode_arg, "append")) + mode = PlaylistSaveMode::APPEND; + else if (StringIsEqual(mode_arg, "replace")) + mode = PlaylistSaveMode::REPLACE; + else + throw std::invalid_argument("Unrecognized save mode, expected one of 'create', 'append', 'replace'"); + } + + spl_save_playlist(args.front(), mode, client.GetPlaylist()); + return CommandResult::OK; }