diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx
index e0b80bd2c..1b72d0ad8 100644
--- a/src/PlaylistFile.cxx
+++ b/src/PlaylistFile.cxx
@@ -34,7 +34,6 @@
 #include "config/Defaults.hxx"
 #include "Idle.hxx"
 #include "fs/Limits.hxx"
-#include "fs/AllocatedPath.hxx"
 #include "fs/Traits.hxx"
 #include "fs/FileSystem.hxx"
 #include "fs/FileInfo.hxx"
@@ -173,11 +172,8 @@ ListPlaylistFiles()
 }
 
 static void
-SavePlaylistFile(const PlaylistFileContents &contents, const char *utf8path)
+SavePlaylistFile(Path path_fs, const PlaylistFileContents &contents)
 {
-	assert(utf8path != nullptr);
-
-	const auto path_fs = spl_map_to_fs(utf8path);
 	assert(!path_fs.IsNull());
 
 	FileOutputStream fos(path_fs);
@@ -191,12 +187,11 @@ SavePlaylistFile(const PlaylistFileContents &contents, const char *utf8path)
 	fos.Commit();
 }
 
-PlaylistFileContents
-LoadPlaylistFile(const char *utf8path)
+static PlaylistFileContents
+LoadPlaylistFile(Path path_fs)
 try {
 	PlaylistFileContents contents;
 
-	const auto path_fs = spl_map_to_fs(utf8path);
 	assert(!path_fs.IsNull());
 
 	TextFile file(path_fs);
@@ -251,16 +246,31 @@ try {
 	throw;
 }
 
-void
-spl_move_index(const char *utf8path, unsigned src, unsigned dest)
+static PlaylistFileContents
+MaybeLoadPlaylistFile(Path path_fs, PlaylistFileEditor::LoadMode load_mode)
+try {
+	if (load_mode == PlaylistFileEditor::LoadMode::NO)
+		return {};
+
+	return LoadPlaylistFile(path_fs);
+} catch (const PlaylistError &error) {
+	if (error.GetCode() == PlaylistResult::NO_SUCH_LIST &&
+	    load_mode == PlaylistFileEditor::LoadMode::TRY)
+		return {};
+
+	throw;
+ }
+
+PlaylistFileEditor::PlaylistFileEditor(const char *name_utf8,
+				       LoadMode load_mode)
+	:path(spl_map_to_fs(name_utf8)),
+	 contents(MaybeLoadPlaylistFile(path, load_mode))
 {
-	if (src == dest)
-		/* this doesn't check whether the playlist exists, but
-		   what the hell.. */
-		return;
-
-	auto contents = LoadPlaylistFile(utf8path);
+}
 
+void
+PlaylistFileEditor::MoveIndex(unsigned src, unsigned dest)
+{
 	if (src >= contents.size() || dest >= contents.size())
 		throw PlaylistError(PlaylistResult::BAD_RANGE, "Bad range");
 
@@ -270,12 +280,37 @@ spl_move_index(const char *utf8path, unsigned src, unsigned dest)
 
 	const auto dest_i = std::next(contents.begin(), dest);
 	contents.insert(dest_i, std::move(value));
+}
 
-	SavePlaylistFile(contents, utf8path);
+void
+PlaylistFileEditor::RemoveIndex(unsigned i)
+{
+	if (i >= contents.size())
+		throw PlaylistError(PlaylistResult::BAD_RANGE, "Bad range");
 
+	contents.erase(std::next(contents.begin(), i));
+}
+
+void
+PlaylistFileEditor::Save()
+{
+	SavePlaylistFile(path, contents);
 	idle_add(IDLE_STORED_PLAYLIST);
 }
 
+void
+spl_move_index(const char *utf8path, unsigned src, unsigned dest)
+{
+	if (src == dest)
+		/* this doesn't check whether the playlist exists, but
+		   what the hell.. */
+		return;
+
+	PlaylistFileEditor editor(utf8path, PlaylistFileEditor::LoadMode::YES);
+	editor.MoveIndex(src, dest);
+	editor.Save();
+}
+
 void
 spl_clear(const char *utf8path)
 {
@@ -317,15 +352,9 @@ spl_delete(const char *name_utf8)
 void
 spl_remove_index(const char *utf8path, unsigned pos)
 {
-	auto contents = LoadPlaylistFile(utf8path);
-
-	if (pos >= contents.size())
-		throw PlaylistError(PlaylistResult::BAD_RANGE, "Bad range");
-
-	contents.erase(std::next(contents.begin(), pos));
-
-	SavePlaylistFile(contents, utf8path);
-	idle_add(IDLE_STORED_PLAYLIST);
+	PlaylistFileEditor editor(utf8path, PlaylistFileEditor::LoadMode::YES);
+	editor.RemoveIndex(pos);
+	editor.Save();
 }
 
 void
diff --git a/src/PlaylistFile.hxx b/src/PlaylistFile.hxx
index b156c73e4..2f1c1bc04 100644
--- a/src/PlaylistFile.hxx
+++ b/src/PlaylistFile.hxx
@@ -20,6 +20,8 @@
 #ifndef MPD_PLAYLIST_FILE_HXX
 #define MPD_PLAYLIST_FILE_HXX
 
+#include "fs/AllocatedPath.hxx"
+
 #include <vector>
 #include <string>
 
@@ -27,12 +29,37 @@ struct ConfigData;
 class DetachedSong;
 class SongLoader;
 class PlaylistVector;
-class AllocatedPath;
 
 typedef std::vector<std::string> PlaylistFileContents;
 
 extern bool playlist_saveAbsolutePaths;
 
+class PlaylistFileEditor {
+	const AllocatedPath path;
+
+	PlaylistFileContents contents;
+
+public:
+	enum class LoadMode {
+		NO,
+		YES,
+		TRY,
+	};
+
+	/**
+	 * Throws on error.
+	 */
+	explicit PlaylistFileEditor(const char *name_utf8, LoadMode load_mode);
+
+	void MoveIndex(unsigned src, unsigned dest);
+	void RemoveIndex(unsigned i);
+
+	void Save();
+
+private:
+	void Load();
+};
+
 /**
  * Perform some global initialization, e.g. load configuration values.
  */
@@ -55,9 +82,6 @@ spl_map_to_fs(const char *name_utf8);
 PlaylistVector
 ListPlaylistFiles();
 
-PlaylistFileContents
-LoadPlaylistFile(const char *utf8path);
-
 void
 spl_move_index(const char *utf8path, unsigned src, unsigned dest);