static gboolean
save_playlist (RBGenericPlayerPlaylistSource *source)
{
	TotemPlParser *parser;
	TotemPlParserType playlist_type;
	RhythmDBQueryModel *query_model;
	char *name;
	char *temp_path;
	GError *error = NULL;
	RBGenericPlayerPlaylistSourcePrivate *priv = GET_PRIVATE (source);
	GFile *file;
	gboolean result;
	SavePlaylistData data;

	priv->save_playlist_id = 0;
	playlist_type = rb_generic_player_source_get_playlist_format (priv->player_source);

	g_object_get (source,
		      "name", &name,
		      "base-query-model", &query_model,
		      NULL);

	/* if we don't already have a name for this playlist, make one now */
	if (priv->playlist_path == NULL) {
		char *playlist_dir;
		char *mount_uri;
		char *filename;
		const char *ext;
		GFile *dir;
		GFile *playlist;

		ext = playlist_format_extension (playlist_type);

		if (name == NULL || name[0] == '\0') {
			/* now what? */
			filename = g_strdup_printf ("unnamed%s", ext);
		} else {
			filename = g_strdup_printf ("%s%s", name, ext);
		}

		playlist_dir = rb_generic_player_source_get_playlist_path (priv->player_source);
		mount_uri = rb_generic_player_source_get_mount_path (priv->player_source);

		dir = g_file_new_for_uri (mount_uri);
		if (playlist_dir != NULL) {
			GFile *pdir;

			pdir = g_file_resolve_relative_path (dir, playlist_dir);
			g_object_unref (dir);
			dir = pdir;
		}

		playlist = g_file_resolve_relative_path (dir, filename);
		priv->playlist_path = g_file_get_path (playlist);
		
		g_free (mount_uri);
		g_free (playlist_dir);

		g_object_unref (dir);
	}

	temp_path = g_strdup_printf ("%s%06X", priv->playlist_path, g_random_int_range (0, 0xFFFFFF));
	file = g_file_new_for_path (temp_path);

	parser = totem_pl_parser_new ();
	data.source = source;
	data.playlist_type = playlist_type;
#if TOTEM_PL_PARSER_CHECK_VERSION(2,29,1)
	data.playlist = totem_pl_playlist_new ();

	gtk_tree_model_foreach (GTK_TREE_MODEL (query_model),
				(GtkTreeModelForeachFunc) save_playlist_foreach,
				&data);
	if (rb_debug_matches ("totem_pl_parser_save", "totem-pl-parser.c")) {
		g_object_set (parser, "debug", TRUE, NULL);
	}

	result = totem_pl_parser_save (parser, data.playlist, file, name, playlist_type, &error);
	g_object_unref (data.playlist);
	data.playlist = NULL;
#else
	if (rb_debug_matches ("totem_pl_parser_write_with_title", "totem-pl-parser.c")) {
		g_object_set (parser, "debug", TRUE, NULL);
	}

	result = totem_pl_parser_write_with_title (parser,
						   GTK_TREE_MODEL (query_model),
						   (TotemPlParserIterFunc) save_playlist_entry,
						   temp_path,
						   name,
						   playlist_type,
						   &data,
						   &error);
#endif
	if (result == FALSE) {
		/* XXX report this more usefully */
		g_warning ("Playlist save failed: %s", error ? error->message : "<no error>");
	} else {
		GFile *dest;

		dest = g_file_new_for_path (priv->playlist_path);
		g_file_move (file, dest, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NO_FALLBACK_FOR_MOVE, NULL, NULL, NULL, &error);
		if (error != NULL) {
			/* XXX report this more usefully */
			g_warning ("moving %s => %s failed: %s", temp_path, priv->playlist_path, error->message);
		}

		g_object_unref (dest);
	}

	g_clear_error (&error);
	g_free (name);
	g_free (temp_path);
	g_object_unref (query_model);
	g_object_unref (parser);
	g_object_unref (file);

	return FALSE;
}
static gboolean
save_playlist (RBGenericPlayerPlaylistSource *source)
{
	TotemPlParser *parser;
	TotemPlParserType playlist_type;
	RhythmDBQueryModel *query_model;
	char *name;
	char *temp_uri;
	GError *error = NULL;
	RBGenericPlayerPlaylistSourcePrivate *priv = GET_PRIVATE (source);

	priv->save_playlist_id = 0;
	playlist_type = rb_generic_player_source_get_playlist_format (priv->player_source);

	g_object_get (source,
		      "name", &name,
		      "base-query-model", &query_model,
		      NULL);

	/* if we don't already have a name for this playlist, make one now */
	if (priv->playlist_path == NULL) {
		char *playlist_dir;
		char *mount_uri;
		char *filename;
		const char *ext;
		GFile *dir;
		GFile *playlist;

		ext = playlist_format_extension (playlist_type);

		if (name == NULL || name[0] == '\0') {
			/* now what? */
			filename = g_strdup_printf ("unnamed%s", ext);
		} else {
			filename = g_strdup_printf ("%s%s", name, ext);
		}

		playlist_dir = rb_generic_player_source_get_playlist_path (priv->player_source);
		mount_uri = rb_generic_player_source_get_mount_path (priv->player_source);

		dir = g_file_new_for_uri (mount_uri);
		if (playlist_dir != NULL) {
			GFile *pdir;

			pdir = g_file_resolve_relative_path (dir, playlist_dir);
			g_object_unref (dir);
			dir = pdir;
		}

		playlist = g_file_resolve_relative_path (dir, filename);
		priv->playlist_path = g_file_get_path (playlist);
		
		g_free (mount_uri);
		g_free (playlist_dir);

		g_object_unref (dir);
	}

	temp_uri = g_strdup_printf ("%s%06X", priv->playlist_path, g_random_int_range (0, 0xFFFFFF));

	parser = totem_pl_parser_new ();
	if (rb_debug_matches ("totem_pl_parser_write_with_title", "totem-pl-parser.c")) {
		g_object_set (parser, "debug", TRUE, NULL);
	}
	if (totem_pl_parser_write_with_title (parser,
					      GTK_TREE_MODEL (query_model),
					      (TotemPlParserIterFunc) save_playlist_entry,
					      temp_uri,
					      name,
					      playlist_type,
					      source,
					      &error) == FALSE) {
		/* XXX report this more usefully */
		g_warning ("Playlist save failed: %s", error->message);
	} else {
		GFile *dest;
		GFile *src;

		dest = g_file_new_for_path (priv->playlist_path);
		src = g_file_new_for_path (temp_uri);
		g_file_move (src, dest, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NO_FALLBACK_FOR_MOVE, NULL, NULL, NULL, &error);
		if (error != NULL) {
			/* XXX report this more usefully */
			g_warning ("Replacing playlist failed: %s", error->message);
		}

		g_object_unref (dest);
		g_object_unref (src);
	}

	g_clear_error (&error);
	g_free (name);
	g_free (temp_uri);
	g_object_unref (query_model);

	return FALSE;
}