Committing pat's rewrite of the stored playlist code. This also adds two
new commands: playlistmove and playlistdelete. git-svn-id: https://svn.musicpd.org/mpd/trunk@6116 09075e82-0dd4-0310-85a5-a0d7c8717e4f
This commit is contained in:
		| @@ -18,6 +18,8 @@ ver 0.13.0 (2007/??/??) | ||||
| * Fix a bug where no ACK was returned if loading a playlist failed | ||||
| * Fix a bug where db_update in stats would be 0 after initial database creation | ||||
| * New count command for getting stats on found songs (similar to "find") | ||||
| * New playlistmove command for moving songs in stored playlists | ||||
| * New playlistdelete command for deleting songs from stored playlists | ||||
| * Lots of bug fixes, cleaned up code, and performance improvements | ||||
|  | ||||
| ver 0.12.2 (2007/3/20) | ||||
|   | ||||
							
								
								
									
										8
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								TODO
									
									
									
									
									
								
							| @@ -40,12 +40,8 @@ | ||||
| 	*) abstract out state code from playlist.c | ||||
| 	*) put MPD Version in statefile | ||||
|  | ||||
| *) rewrite saved playlist code | ||||
| 	*) abstract out saved playlists from playlist.c | ||||
| 	*) new commands | ||||
| 		*) replace <playlist> /* replace current playlist | ||||
| 						with saved playlist and | ||||
| 						keep playing */ | ||||
| *) add playlistreplace command (replace current playlist with saved playlist | ||||
|                                 and keep playing) | ||||
|  | ||||
| *) add command for inserting songs in a specific position | ||||
|  | ||||
|   | ||||
| @@ -74,7 +74,8 @@ mpd_headers = \ | ||||
| 	utils.h \ | ||||
| 	volume.h \ | ||||
| 	zeroconf.h \ | ||||
| 	locate.h | ||||
| 	locate.h \ | ||||
| 	storedPlaylist.h | ||||
|  | ||||
|  | ||||
| mpd_SOURCES = \ | ||||
| @@ -125,7 +126,8 @@ mpd_SOURCES = \ | ||||
| 	volume.c \ | ||||
| 	utf8.c \ | ||||
| 	zeroconf.c \ | ||||
| 	locate.c | ||||
| 	locate.c \ | ||||
| 	storedPlaylist.c | ||||
|  | ||||
|  | ||||
| mpd_CFLAGS = $(MPD_CFLAGS) | ||||
|   | ||||
| @@ -30,6 +30,7 @@ | ||||
| #include "log.h" | ||||
| #include "tag.h" | ||||
| #include "utils.h" | ||||
| #include "storedPlaylist.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdarg.h> | ||||
| @@ -95,6 +96,8 @@ | ||||
| #define COMMAND_PLAYLISTADD	"playlistadd" | ||||
| #define COMMAND_PLAYLISTFIND	"playlistfind" | ||||
| #define COMMAND_PLAYLISTSEARCH	"playlistsearch" | ||||
| #define COMMAND_PLAYLISTMOVE	"playlistmove" | ||||
| #define COMMAND_PLAYLISTDELETE	"playlistdelete" | ||||
| #define COMMAND_TAGTYPES	"tagtypes" | ||||
| #define COMMAND_COUNT		"count" | ||||
|  | ||||
| @@ -578,6 +581,43 @@ static int handlePlaylistSearch(int fd, int *permission, int argc, char *argv[]) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int handlePlaylistDelete(int fd, int *permission, int argc, char *argv[]) { | ||||
| 	char *playlist = argv[1]; | ||||
| 	int from; | ||||
| 	char *test; | ||||
|  | ||||
| 	from = strtol(argv[2], &test, 10); | ||||
| 	if (*test != '\0') { | ||||
| 		commandError(fd, ACK_ERROR_ARG, | ||||
| 			     "\"%s\" is not a integer", argv[2]); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return removeOneSongFromStoredPlaylistByPath(fd, playlist, from); | ||||
| } | ||||
|  | ||||
| static int handlePlaylistMove(int fd, int *permission, int argc, char *argv[]) | ||||
| { | ||||
| 	char *playlist = argv[1]; | ||||
| 	int from, to; | ||||
| 	char *test; | ||||
|  | ||||
| 	from = strtol(argv[2], &test, 10); | ||||
| 	if (*test != '\0') { | ||||
| 		commandError(fd, ACK_ERROR_ARG, | ||||
| 			     "\"%s\" is not a integer", argv[2]); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	to = strtol(argv[3], &test, 10); | ||||
| 	if (*test != '\0') { | ||||
| 		commandError(fd, ACK_ERROR_ARG, | ||||
| 			     "\"%s\" is not a integer", argv[3]); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return moveSongInStoredPlaylistByPath(fd, playlist, from, to); | ||||
| } | ||||
|  | ||||
| static int listHandleUpdate(int fd, | ||||
| 			    int *permission, | ||||
| 			    int argc, | ||||
| @@ -1072,6 +1112,8 @@ void initCommands(void) | ||||
| 	addCommand(COMMAND_PLAYLISTADD,      PERMISSION_CONTROL, 2,   2,   handlePlaylistAdd,          NULL); | ||||
| 	addCommand(COMMAND_PLAYLISTFIND,     PERMISSION_READ,    2,   -1,  handlePlaylistFind,         NULL); | ||||
| 	addCommand(COMMAND_PLAYLISTSEARCH,   PERMISSION_READ,    2,   -1,  handlePlaylistSearch,       NULL); | ||||
| 	addCommand(COMMAND_PLAYLISTMOVE,     PERMISSION_CONTROL, 3,   3,   handlePlaylistMove,         NULL); | ||||
| 	addCommand(COMMAND_PLAYLISTDELETE,   PERMISSION_CONTROL, 2,   2,   handlePlaylistDelete,       NULL); | ||||
| 	addCommand(COMMAND_TAGTYPES,         PERMISSION_READ,    0,   0,   handleTagTypes,             NULL); | ||||
| 	addCommand(COMMAND_COUNT,            PERMISSION_READ,    2,   -1,  handleCount,                NULL); | ||||
|  | ||||
|   | ||||
| @@ -26,6 +26,7 @@ | ||||
| #include "tag.h" | ||||
| #include "tagTracker.h" | ||||
| #include "log.h" | ||||
| #include "storedPlaylist.h" | ||||
|  | ||||
| typedef struct _ListCommandItem { | ||||
| 	mpd_sint8 tagType; | ||||
| @@ -177,7 +178,9 @@ static int directoryAddSongToPlaylist(int fd, Song * song, void *data) | ||||
|  | ||||
| static int directoryAddSongToStoredPlaylist(int fd, Song *song, void *data) | ||||
| { | ||||
| 	return addSongToStoredPlaylist(fd, song, (char *)data); | ||||
| 	if (appendSongToStoredPlaylistByPath(fd, (char *)data, song) != 0) | ||||
| 		return -1; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int addAllIn(int fd, char *name) | ||||
|   | ||||
							
								
								
									
										359
									
								
								src/playlist.c
									
									
									
									
									
								
							
							
						
						
									
										359
									
								
								src/playlist.c
									
									
									
									
									
								
							| @@ -28,6 +28,7 @@ | ||||
| #include "utils.h" | ||||
| #include "sig_handlers.h" | ||||
| #include "state_file.h" | ||||
| #include "storedPlaylist.h" | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| @@ -37,8 +38,6 @@ | ||||
| #include <unistd.h> | ||||
| #include <time.h> | ||||
|  | ||||
| #define PLAYLIST_COMMENT	'#' | ||||
|  | ||||
| #define PLAYLIST_STATE_STOP		0 | ||||
| #define PLAYLIST_STATE_PLAY		1 | ||||
|  | ||||
| @@ -64,21 +63,6 @@ | ||||
| #define DEFAULT_PLAYLIST_MAX_LENGTH		(1024*16) | ||||
| #define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS	0 | ||||
|  | ||||
| typedef struct _Playlist { | ||||
| 	Song **songs; | ||||
| 	/* holds version a song was modified on */ | ||||
| 	mpd_uint32 *songMod; | ||||
| 	int *order; | ||||
| 	int *positionToId; | ||||
| 	int *idToPosition; | ||||
| 	int length; | ||||
| 	int current; | ||||
| 	int queued; | ||||
| 	int repeat; | ||||
| 	int random; | ||||
| 	mpd_uint32 version; | ||||
| } Playlist; | ||||
|  | ||||
| static Playlist playlist; | ||||
| static int playlist_state = PLAYLIST_STATE_STOP; | ||||
| static int playlist_max_length = DEFAULT_PLAYLIST_MAX_LENGTH; | ||||
| @@ -87,7 +71,7 @@ static int playlist_errorCount; | ||||
| static int playlist_queueError; | ||||
| static int playlist_noGoToNext; | ||||
|  | ||||
| static int playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS; | ||||
| int playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS; | ||||
|  | ||||
| static void swapOrder(int a, int b); | ||||
| static int playPlaylistOrderNumber(int fd, int orderNum); | ||||
| @@ -238,39 +222,7 @@ int clearPlaylist(int fd) | ||||
|  | ||||
| int clearStoredPlaylist(int fd, char *utf8file) | ||||
| { | ||||
| 	int fileD; | ||||
| 	char *file; | ||||
| 	char *rfile; | ||||
| 	char *actualFile; | ||||
|  | ||||
| 	if (strstr(utf8file, "/")) { | ||||
| 		commandError(fd, ACK_ERROR_ARG, | ||||
| 		             "cannot clear \"%s\", saving playlists to " | ||||
| 		             "subdirectories is not supported", utf8file); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	file = utf8ToFsCharset(utf8file); | ||||
|  | ||||
| 	rfile = xmalloc(strlen(file) + strlen(".") + | ||||
| 	                strlen(PLAYLIST_FILE_SUFFIX) + 1); | ||||
|  | ||||
| 	strcpy(rfile, file); | ||||
| 	strcat(rfile, "."); | ||||
| 	strcat(rfile, PLAYLIST_FILE_SUFFIX); | ||||
|  | ||||
| 	actualFile = rpp2app(rfile); | ||||
|  | ||||
| 	free(rfile); | ||||
|  | ||||
| 	while ((fileD = open(actualFile, O_WRONLY | O_TRUNC | O_CREAT)) == -1 | ||||
| 	       && errno == EINTR); | ||||
| 	if (fileD == -1) { | ||||
| 		commandError(fd, ACK_ERROR_SYSTEM, "problems opening file"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	while (close(fileD) == -1 && errno == EINTR); | ||||
|  | ||||
| 	removeAllFromStoredPlaylistByPath(fd, utf8file); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -643,7 +595,8 @@ int addToStoredPlaylist(int fd, char *url, char *utf8file) | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return addSongToStoredPlaylist(fd, song, utf8file); | ||||
| 	appendSongToStoredPlaylistByPath(fd, utf8file, song); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int addSongToPlaylist(int fd, Song * song, int printId) | ||||
| @@ -698,52 +651,6 @@ int addSongToPlaylist(int fd, Song * song, int printId) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int addSongToStoredPlaylist(int fd, Song *song, char *utf8file) | ||||
| { | ||||
| 	FILE *fileP; | ||||
| 	char *file; | ||||
| 	char *rfile; | ||||
| 	char *actualFile; | ||||
| 	char *url; | ||||
|  | ||||
| 	if (strstr(utf8file, "/")) { | ||||
| 		commandError(fd, ACK_ERROR_ARG, | ||||
| 		             "cannot add to \"%s\", saving playlists to " | ||||
| 		             "subdirectories is not supported", utf8file); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	file = utf8ToFsCharset(utf8file); | ||||
|  | ||||
| 	rfile = xmalloc(strlen(file) + strlen(".") + | ||||
| 	                strlen(PLAYLIST_FILE_SUFFIX) + 1); | ||||
|  | ||||
| 	strcpy(rfile, file); | ||||
| 	strcat(rfile, "."); | ||||
| 	strcat(rfile, PLAYLIST_FILE_SUFFIX); | ||||
|  | ||||
| 	actualFile = rpp2app(rfile); | ||||
|  | ||||
| 	free(rfile); | ||||
|  | ||||
| 	while (!(fileP = fopen(actualFile, "a")) && errno == EINTR); | ||||
| 	if (fileP == NULL) { | ||||
| 		commandError(fd, ACK_ERROR_SYSTEM, "problems opening file"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	url = utf8ToFsCharset(getSongUrl(song)); | ||||
|  | ||||
| 	if (playlist_saveAbsolutePaths && song->type == SONG_TYPE_FILE) | ||||
| 		fprintf(fileP, "%s\n", rmp2amp(url)); | ||||
| 	else | ||||
| 		fprintf(fileP, "%s\n", url); | ||||
|  | ||||
| 	while (fclose(fileP) && errno == EINTR); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int swapSongsInPlaylist(int fd, int song1, int song2) | ||||
| { | ||||
| 	int queuedSong = -1; | ||||
| @@ -1407,59 +1314,17 @@ int deletePlaylist(int fd, char *utf8file) | ||||
|  | ||||
| int savePlaylist(int fd, char *utf8file) | ||||
| { | ||||
| 	FILE *fileP; | ||||
| 	int i; | ||||
| 	struct stat st; | ||||
| 	char *file; | ||||
| 	char *rfile; | ||||
| 	char *actualFile; | ||||
| 	StoredPlaylist *sp = newStoredPlaylist(utf8file, fd, 0); | ||||
| 	if (!sp) | ||||
| 		return -1; | ||||
|  | ||||
| 	if (strstr(utf8file, "/")) { | ||||
| 		commandError(fd, ACK_ERROR_ARG, | ||||
| 			     "cannot save \"%s\", saving playlists to " | ||||
| 			     "subdirectories is not supported", utf8file); | ||||
| 	appendPlaylistToStoredPlaylist(sp, &playlist); | ||||
| 	if (writeStoredPlaylist(sp) != 0) { | ||||
| 		freeStoredPlaylist(sp); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	file = utf8ToFsCharset(utf8file); | ||||
|  | ||||
| 	rfile = xmalloc(strlen(file) + strlen(".") + | ||||
| 		       strlen(PLAYLIST_FILE_SUFFIX) + 1); | ||||
|  | ||||
| 	strcpy(rfile, file); | ||||
| 	strcat(rfile, "."); | ||||
| 	strcat(rfile, PLAYLIST_FILE_SUFFIX); | ||||
|  | ||||
| 	actualFile = rpp2app(rfile); | ||||
|  | ||||
| 	free(rfile); | ||||
|  | ||||
| 	if (0 == stat(actualFile, &st)) { | ||||
| 		commandError(fd, ACK_ERROR_EXIST, "a file or directory already " | ||||
| 			     "exists with the name \"%s\"", utf8file); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	while (!(fileP = fopen(actualFile, "w")) && errno == EINTR) ; | ||||
| 	if (fileP == NULL) { | ||||
| 		commandError(fd, ACK_ERROR_SYSTEM, "problems opening file"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 0; i < playlist.length; i++) { | ||||
| 		if (playlist_saveAbsolutePaths && | ||||
| 		    playlist.songs[i]->type == SONG_TYPE_FILE) { | ||||
| 			fprintf(fileP, "%s\n", | ||||
| 				  rmp2amp(utf8ToFsCharset | ||||
| 					  ((getSongUrl(playlist.songs[i]))))); | ||||
| 		} else { | ||||
| 			fprintf(fileP, "%s\n", | ||||
| 			        utf8ToFsCharset(getSongUrl(playlist.songs[i]))); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	while (fclose(fileP) && errno == EINTR) ; | ||||
|  | ||||
| 	freeStoredPlaylist(sp); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -1528,156 +1393,70 @@ int getPlaylistSongId(int song) | ||||
| 	return playlist.positionToId[song]; | ||||
| } | ||||
|  | ||||
| static int PlaylistIterFunc(int fd, char *utf8file, | ||||
| 			    void (*IterFunc) (int fd, char *utf8_file, | ||||
| 					      char **errored_File)) | ||||
| { | ||||
| 	FILE *fileP; | ||||
| 	char s[MAXPATHLEN + 1]; | ||||
| 	int slength = 0; | ||||
| 	char *temp = utf8ToFsCharset(utf8file); | ||||
| 	char *rfile = xmalloc(strlen(temp) + strlen(".") + | ||||
| 			     strlen(PLAYLIST_FILE_SUFFIX) + 1); | ||||
| 	char *actualFile; | ||||
| 	char *parent = parentPath(temp); | ||||
| 	int parentlen = strlen(parent); | ||||
| 	char *erroredFile = NULL; | ||||
| 	int tempInt; | ||||
| 	int commentCharFound = 0; | ||||
|  | ||||
| 	strcpy(rfile, temp); | ||||
| 	strcat(rfile, "."); | ||||
| 	strcat(rfile, PLAYLIST_FILE_SUFFIX); | ||||
|  | ||||
| 	if ((actualFile = rpp2app(rfile)) && isPlaylist(actualFile)) | ||||
| 		free(rfile); | ||||
| 	else { | ||||
| 		free(rfile); | ||||
| 		commandError(fd, ACK_ERROR_NO_EXIST, | ||||
| 			     "playlist \"%s\" not found", utf8file); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	while (!(fileP = fopen(actualFile, "r")) && errno == EINTR) ; | ||||
| 	if (fileP == NULL) { | ||||
| 		commandError(fd, ACK_ERROR_SYSTEM, | ||||
| 			     "problems opening file \"%s\"", utf8file); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	while ((tempInt = fgetc(fileP)) != EOF) { | ||||
| 		s[slength] = tempInt; | ||||
| 		if (s[slength] == '\n' || s[slength] == '\0') { | ||||
| 			commentCharFound = 0; | ||||
| 			s[slength] = '\0'; | ||||
| 			if (s[0] == PLAYLIST_COMMENT) { | ||||
| 				commentCharFound = 1; | ||||
| 			} | ||||
| 			if (strncmp(s, musicDir, strlen(musicDir)) == 0) { | ||||
| 				strcpy(s, &(s[strlen(musicDir)])); | ||||
| 			} else if (parentlen) { | ||||
| 				temp = xstrdup(s); | ||||
| 				memset(s, 0, MAXPATHLEN + 1); | ||||
| 				strcpy(s, parent); | ||||
| 				strncat(s, "/", MAXPATHLEN - parentlen); | ||||
| 				strncat(s, temp, MAXPATHLEN - parentlen - 1); | ||||
| 				if (strlen(s) >= MAXPATHLEN) { | ||||
| 					commandError(fd, | ||||
| 						     ACK_ERROR_PLAYLIST_LOAD, | ||||
| 						     "\"%s\" too long", temp); | ||||
| 					free(temp); | ||||
| 					while (fclose(fileP) | ||||
| 					       && errno == EINTR) ; | ||||
| 					if (erroredFile) | ||||
| 						free(erroredFile); | ||||
| 					return -1; | ||||
| 				} | ||||
| 				free(temp); | ||||
| 			} | ||||
| 			slength = 0; | ||||
| 			temp = fsCharsetToUtf8(s); | ||||
| 			if (!temp) | ||||
| 				continue; | ||||
| 			if (!commentCharFound) { | ||||
| 				/* using temp directly should be safe, | ||||
| 				 * for our current IterFunction set | ||||
| 				 * but just in case, we copy to s */ | ||||
| 				strcpy(s, temp); | ||||
| 				IterFunc(fd, s, &erroredFile); | ||||
| 			} | ||||
| 		} else if (slength == MAXPATHLEN) { | ||||
| 			s[slength] = '\0'; | ||||
| 			commandError(fd, ACK_ERROR_PLAYLIST_LOAD, | ||||
| 				     "line in \"%s\" is too long", utf8file); | ||||
| 			ERROR("line \"%s\" in playlist \"%s\" is too long\n", | ||||
| 			      s, utf8file); | ||||
| 			while (fclose(fileP) && errno == EINTR) ; | ||||
| 			if (erroredFile) | ||||
| 				free(erroredFile); | ||||
| 			return -1; | ||||
| 		} else if (s[slength] != '\r') | ||||
| 			slength++; | ||||
| 	} | ||||
|  | ||||
| 	while (fclose(fileP) && errno == EINTR) ; | ||||
|  | ||||
| 	if (erroredFile) { | ||||
| 		commandError(fd, ACK_ERROR_PLAYLIST_LOAD, | ||||
| 			     "can't add file \"%s\"", erroredFile); | ||||
| 		free(erroredFile); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void PlaylistInfoPrintInfo(int fd, char *utf8file, char **erroredfile) | ||||
| { | ||||
| 	Song *song = getSongFromDB(utf8file); | ||||
| 	if (song) { | ||||
| 		printSongInfo(fd, song); | ||||
| 	} else { | ||||
| 		fdprintf(fd, SONG_FILE "%s\n", utf8file); | ||||
| 	} | ||||
| } | ||||
| static void PlaylistInfoPrint(int fd, char *utf8file, char **erroredfile) | ||||
| { | ||||
| 	fdprintf(fd, SONG_FILE "%s\n", utf8file); | ||||
| } | ||||
|  | ||||
| static void PlaylistLoadIterFunc(int fd, char *temp, char **erroredFile) | ||||
| { | ||||
| 	if (!getSongFromDB(temp) && !isRemoteUrl(temp)) { | ||||
|  | ||||
| 	} else if ((addToPlaylist(STDERR_FILENO, temp, 0)) < 0) { | ||||
| 		/* for windows compatibility, convert slashes */ | ||||
| 		char *temp2 = xstrdup(temp); | ||||
| 		char *p = temp2; | ||||
| 		while (*p) { | ||||
| 			if (*p == '\\') | ||||
| 				*p = '/'; | ||||
| 			p++; | ||||
| 		} | ||||
| 		if ((addToPlaylist(STDERR_FILENO, temp2, 0)) < 0) { | ||||
| 			if (!*erroredFile) { | ||||
| 				*erroredFile = xstrdup(temp); | ||||
| 			} | ||||
| 		} | ||||
| 		free(temp2); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int PlaylistInfo(int fd, char *utf8file, int detail) | ||||
| { | ||||
| 	if (detail) { | ||||
| 		return PlaylistIterFunc(fd, utf8file, PlaylistInfoPrintInfo); | ||||
| 	ListNode *node; | ||||
| 	StoredPlaylist *sp = loadStoredPlaylist(utf8file, fd); | ||||
| 	if (sp == NULL) | ||||
| 		return -1; | ||||
|  | ||||
| 	node = sp->list->firstNode; | ||||
| 	while (node != NULL) { | ||||
| 		char *temp = node->data; | ||||
| 		int wrote = 0; | ||||
|  | ||||
| 		if (detail) { | ||||
| 			Song *song = getSongFromDB(temp); | ||||
| 			if (song) { | ||||
| 				printSongInfo(fd, song); | ||||
| 				wrote = 1; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (!wrote) { | ||||
| 			fdprintf(fd, SONG_FILE "%s\n", temp); | ||||
| 		} | ||||
|  | ||||
| 		node = node->nextNode; | ||||
| 	} | ||||
| 	return PlaylistIterFunc(fd, utf8file, PlaylistInfoPrint); | ||||
|  | ||||
| 	freeStoredPlaylist(sp); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int loadPlaylist(int fd, char *utf8file) | ||||
| { | ||||
| 	return PlaylistIterFunc(fd, utf8file, PlaylistLoadIterFunc); | ||||
| 	ListNode *node; | ||||
| 	StoredPlaylist *sp = loadStoredPlaylist(utf8file, fd); | ||||
| 	if (sp == NULL) | ||||
| 		return -1; | ||||
|  | ||||
| 	node = sp->list->firstNode; | ||||
| 	while (node != NULL) { | ||||
| 		char *temp = node->data; | ||||
| 		if (!getSongFromDB(temp) && !isRemoteUrl(temp)) { | ||||
|  | ||||
| 		} else if ((addToPlaylist(STDERR_FILENO, temp, 0)) < 0) { | ||||
| 			/* for windows compatibility, convert slashes */ | ||||
| 			char *temp2 = xstrdup(temp); | ||||
| 			char *p = temp2; | ||||
| 			while (*p) { | ||||
| 				if (*p == '\\') | ||||
| 					*p = '/'; | ||||
| 				p++; | ||||
| 			} | ||||
| 			if ((addToPlaylist(STDERR_FILENO, temp2, 0)) < 0) { | ||||
| 				commandError(fd, ACK_ERROR_PLAYLIST_LOAD, | ||||
| 							"can't add file \"%s\"", temp2); | ||||
| 			} | ||||
| 			free(temp2); | ||||
| 		} | ||||
|  | ||||
| 		node = node->nextNode; | ||||
| 	} | ||||
|  | ||||
| 	freeStoredPlaylist(sp); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void searchForSongsInPlaylist(int fd, int numItems, LocateTagItem * items) | ||||
|   | ||||
| @@ -28,6 +28,24 @@ | ||||
| #include <time.h> | ||||
|  | ||||
| #define PLAYLIST_FILE_SUFFIX 	"m3u" | ||||
| #define PLAYLIST_COMMENT	'#' | ||||
|  | ||||
| typedef struct _Playlist { | ||||
| 	Song **songs; | ||||
| 	/* holds version a song was modified on */ | ||||
| 	mpd_uint32 *songMod; | ||||
| 	int *order; | ||||
| 	int *positionToId; | ||||
| 	int *idToPosition; | ||||
| 	int length; | ||||
| 	int current; | ||||
| 	int queued; | ||||
| 	int repeat; | ||||
| 	int random; | ||||
| 	mpd_uint32 version; | ||||
| } Playlist; | ||||
|  | ||||
| extern int playlist_saveAbsolutePaths; | ||||
|  | ||||
| void initPlaylist(void); | ||||
|  | ||||
| @@ -47,8 +65,6 @@ int addToStoredPlaylist(int fd, char *file, char *utf8file); | ||||
|  | ||||
| int addSongToPlaylist(int fd, Song * song, int printId); | ||||
|  | ||||
| int addSongToStoredPlaylist(int fd, Song *song, char *utf8file); | ||||
|  | ||||
| int showPlaylist(int fd); | ||||
|  | ||||
| int deleteFromPlaylist(int fd, int song); | ||||
|   | ||||
							
								
								
									
										473
									
								
								src/storedPlaylist.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										473
									
								
								src/storedPlaylist.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,473 @@ | ||||
| /* the Music Player Daemon (MPD) | ||||
|  * Copyright (C) 2007 by Warren Dukes (warren.dukes@gmail.com) | ||||
|  * This project's homepage is: http://www.musicpd.org | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "storedPlaylist.h" | ||||
| #include "log.h" | ||||
| #include "path.h" | ||||
| #include "utils.h" | ||||
| #include "playlist.h" | ||||
| #include "ack.h" | ||||
| #include "command.h" | ||||
|  | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| static char *utf8pathToFsPathInStoredPlaylist(const char *utf8path, int fd) | ||||
| { | ||||
| 	char *file; | ||||
| 	char *rfile; | ||||
| 	char *actualFile; | ||||
|  | ||||
| 	if (strstr(utf8path, "/")) { | ||||
| 		if (fd != -1) { | ||||
| 			commandError(fd, ACK_ERROR_ARG, | ||||
| 					"Cannot clear \"%s\", saving playlists to " | ||||
| 					"subdirectories is not supported", utf8path); | ||||
| 		} | ||||
|  | ||||
| 		ERROR("Cannot clear \"%s\", saving playlists to " | ||||
| 			  "subdirectories is not supported", utf8path); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	file = utf8ToFsCharset((char *)utf8path); | ||||
|  | ||||
| 	rfile = xmalloc(strlen(file) + strlen(".") + | ||||
| 	                strlen(PLAYLIST_FILE_SUFFIX) + 1); | ||||
|  | ||||
| 	strcpy(rfile, file); | ||||
| 	strcat(rfile, "."); | ||||
| 	strcat(rfile, PLAYLIST_FILE_SUFFIX); | ||||
|  | ||||
| 	actualFile = rpp2app(rfile); | ||||
|  | ||||
| 	free(rfile); | ||||
|  | ||||
| 	return actualFile; | ||||
| } | ||||
|  | ||||
| static unsigned int lengthOfStoredPlaylist(StoredPlaylist *sp) | ||||
| { | ||||
| 	return sp->list->numberOfNodes; | ||||
| } | ||||
|  | ||||
| static ListNode *nodeOfStoredPlaylist(StoredPlaylist *sp, int index) | ||||
| { | ||||
| 	int forward; | ||||
| 	ListNode *node; | ||||
| 	int i; | ||||
|  | ||||
| 	if (index >= lengthOfStoredPlaylist(sp) || index < 0) | ||||
| 		return NULL; | ||||
|  | ||||
| 	if (index > lengthOfStoredPlaylist(sp)/2) { | ||||
| 		forward = 0; | ||||
| 		node = sp->list->lastNode; | ||||
| 		i = lengthOfStoredPlaylist(sp) - 1; | ||||
| 	} else { | ||||
| 		forward = 1; | ||||
| 		node = sp->list->firstNode; | ||||
| 		i = 0; | ||||
| 	} | ||||
|  | ||||
| 	while (node != NULL) { | ||||
| 		if (i == index) | ||||
| 			return node; | ||||
|  | ||||
| 		if (forward) { | ||||
| 			i++; | ||||
| 			node = node->nextNode; | ||||
| 		} else { | ||||
| 			i--; | ||||
| 			node = node->prevNode; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| StoredPlaylist *newStoredPlaylist(const char *utf8name, int fd, int ignoreExisting) | ||||
| { | ||||
| 	struct stat buf; | ||||
| 	char *filename = NULL; | ||||
| 	StoredPlaylist *sp = calloc(1, sizeof(*sp)); | ||||
| 	if (!sp) | ||||
| 		return NULL; | ||||
|  | ||||
| 	if (utf8name) { | ||||
| 		filename = utf8pathToFsPathInStoredPlaylist(utf8name, fd); | ||||
|  | ||||
| 		if (filename && stat(filename, &buf) == 0 && ignoreExisting == 0) { | ||||
| 			if (fd != -1) | ||||
| 				commandError(fd, ACK_ERROR_EXIST, | ||||
| 						"a file or directory already " | ||||
| 						"exists with the name \"%s\"", utf8name); | ||||
|  | ||||
| 			ERROR("a file or directory already " | ||||
| 					"exists with the name \"%s\"", utf8name); | ||||
|  | ||||
| 			free(sp); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	sp->list = makeList(DEFAULT_FREE_DATA_FUNC, 0); | ||||
| 	sp->fd = fd; | ||||
|  | ||||
| 	if (filename) | ||||
| 		sp->fspath = strdup(filename); | ||||
|  | ||||
| 	return sp; | ||||
| } | ||||
|  | ||||
| StoredPlaylist *loadStoredPlaylist(const char *utf8path, int fd) | ||||
| { | ||||
| 	char *filename; | ||||
| 	StoredPlaylist *sp; | ||||
| 	FILE *file; | ||||
| 	char s[MAXPATHLEN + 1]; | ||||
| 	int slength = 0; | ||||
| 	char *temp = utf8ToFsCharset((char *)utf8path); | ||||
| 	char *parent = parentPath(temp); | ||||
| 	int parentlen = strlen(parent); | ||||
| 	int tempInt; | ||||
| 	int commentCharFound = 0; | ||||
|  | ||||
| 	filename = utf8pathToFsPathInStoredPlaylist(utf8path, fd); | ||||
| 	if (!filename) | ||||
| 		return NULL; | ||||
|  | ||||
| 	while (!(file = fopen(filename, "r")) && errno == EINTR); | ||||
| 	if (file == NULL) { | ||||
| 		if (fd != -1) { | ||||
| 			commandError(fd, ACK_ERROR_NO_EXIST, | ||||
| 					"Could not open file \"%s\": %s", filename, strerror(errno)); | ||||
| 		} | ||||
|  | ||||
| 		ERROR("Could not open file \"%s\": %s", filename, strerror(errno)); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	sp = newStoredPlaylist(utf8path, fd, 1); | ||||
| 	if (!sp) { | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	while ((tempInt = fgetc(file)) != EOF) { | ||||
| 		s[slength] = tempInt; | ||||
| 		if (s[slength] == '\n' || s[slength] == '\0') { | ||||
| 			commentCharFound = 0; | ||||
| 			s[slength] = '\0'; | ||||
| 			if (s[0] == PLAYLIST_COMMENT) { | ||||
| 				commentCharFound = 1; | ||||
| 			} | ||||
| 			if (strncmp(s, musicDir, strlen(musicDir)) == 0) { | ||||
| 				strcpy(s, &(s[strlen(musicDir)])); | ||||
| 			} else if (parentlen) { | ||||
| 				temp = xstrdup(s); | ||||
| 				memset(s, 0, MAXPATHLEN + 1); | ||||
| 				strcpy(s, parent); | ||||
| 				strncat(s, "/", MAXPATHLEN - parentlen); | ||||
| 				strncat(s, temp, MAXPATHLEN - parentlen - 1); | ||||
| 				if (strlen(s) >= MAXPATHLEN) { | ||||
| 					if (sp->fd != -1) { | ||||
| 						commandError(sp->fd, ACK_ERROR_PLAYLIST_LOAD, | ||||
| 								"\"%s\" too long", temp); | ||||
| 					} | ||||
|  | ||||
| 					ERROR("\"%s\" too long", temp); | ||||
| 					free(temp); | ||||
| 					goto out; | ||||
| 				} | ||||
| 				free(temp); | ||||
| 			} | ||||
| 			slength = 0; | ||||
| 			temp = fsCharsetToUtf8(s); | ||||
| 			if (!temp) | ||||
| 				continue; | ||||
| 			if (!commentCharFound) { | ||||
| 				insertInListWithoutKey(sp->list, strdup(s)); | ||||
| 			} | ||||
| 		} else if (slength == MAXPATHLEN) { | ||||
| 			s[slength] = '\0'; | ||||
| 			if (sp->fd != -1) { | ||||
| 				commandError(sp->fd, ACK_ERROR_PLAYLIST_LOAD, | ||||
| 						"line \"%s\" in playlist \"%s\" is too long\n", | ||||
| 						s, utf8path); | ||||
| 			} | ||||
| 			ERROR("line \"%s\" in playlist \"%s\" is too long\n", s, utf8path); | ||||
| 			goto out; | ||||
| 		} else if (s[slength] != '\r') | ||||
| 			slength++; | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	while (fclose(file) && errno == EINTR); | ||||
| 	return sp; | ||||
| } | ||||
|  | ||||
| void freeStoredPlaylist(StoredPlaylist *sp) | ||||
| { | ||||
| 	if (sp->list) | ||||
| 		freeList(sp->list); | ||||
| 	if (sp->fspath) | ||||
| 		free(sp->fspath); | ||||
|  | ||||
| 	free(sp); | ||||
| } | ||||
|  | ||||
| static int moveSongInStoredPlaylist(int fd, StoredPlaylist *sp, int src, int dest) | ||||
| { | ||||
| 	ListNode *srcNode, *destNode; | ||||
|  | ||||
| 	if (src >= lengthOfStoredPlaylist(sp) || dest >= lengthOfStoredPlaylist(sp) || src < 0 || dest < 0 || src == dest) { | ||||
| 		commandError(fd, ACK_ERROR_ARG, "argument out of range."); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	srcNode = nodeOfStoredPlaylist(sp, src); | ||||
| 	if (!srcNode) | ||||
| 		return -1; | ||||
|  | ||||
| 	destNode = nodeOfStoredPlaylist(sp, dest); | ||||
|  | ||||
| 	/* remove src */ | ||||
| 	if (srcNode->prevNode) | ||||
| 		srcNode->prevNode->nextNode = srcNode->nextNode; | ||||
| 	else | ||||
| 		sp->list->firstNode = srcNode->nextNode; | ||||
|  | ||||
| 	if (srcNode->nextNode) | ||||
| 		srcNode->nextNode->prevNode = srcNode->prevNode; | ||||
| 	else | ||||
| 		sp->list->lastNode = srcNode->prevNode; | ||||
|  | ||||
| 	/* this is all a bit complicated - but I tried to | ||||
| 	 * maintain the same order stuff is moved as in the | ||||
| 	 * real playlist */ | ||||
| 	if (dest == 0) { | ||||
| 		sp->list->firstNode->prevNode = srcNode; | ||||
| 		srcNode->nextNode = sp->list->firstNode; | ||||
| 		srcNode->prevNode = NULL; | ||||
| 		sp->list->firstNode = srcNode; | ||||
| 	} else if ((dest + 1) == lengthOfStoredPlaylist(sp)) { | ||||
| 		sp->list->lastNode->nextNode = srcNode; | ||||
| 		srcNode->nextNode = NULL; | ||||
| 		srcNode->prevNode = sp->list->lastNode; | ||||
| 		sp->list->lastNode = srcNode; | ||||
| 	} else { | ||||
| 		if (destNode == NULL) { | ||||
| 			/* this shouldn't be happening. */ | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| 		if (src > dest) { | ||||
| 			destNode->prevNode->nextNode = srcNode; | ||||
| 			srcNode->prevNode = destNode->prevNode; | ||||
| 			srcNode->nextNode = destNode; | ||||
| 			destNode->prevNode = srcNode; | ||||
| 		} else { | ||||
| 			destNode->nextNode->prevNode = srcNode; | ||||
| 			srcNode->prevNode = destNode; | ||||
| 			srcNode->nextNode = destNode->nextNode; | ||||
| 			destNode->nextNode = srcNode; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int moveSongInStoredPlaylistByPath(int fd, const char *utf8path, int src, int dest) | ||||
| { | ||||
| 	StoredPlaylist *sp = loadStoredPlaylist(utf8path, fd); | ||||
| 	if (!sp) { | ||||
| 		commandError(fd, ACK_ERROR_UNKNOWN, "could not open playlist."); | ||||
| 		freeStoredPlaylist(sp); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (moveSongInStoredPlaylist(fd, sp, src, dest) != 0) { | ||||
| 		freeStoredPlaylist(sp); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (writeStoredPlaylist(sp) != 0) { | ||||
| 		commandError(fd, ACK_ERROR_UNKNOWN, "failed to save playlist."); | ||||
| 		freeStoredPlaylist(sp); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	freeStoredPlaylist(sp); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Not used currently | ||||
| static void removeAllFromStoredPlaylist(StoredPlaylist *sp) | ||||
| { | ||||
| 	freeList(sp->list); | ||||
| 	sp->list = makeList(DEFAULT_FREE_DATA_FUNC, 0); | ||||
| } | ||||
| */ | ||||
|  | ||||
| int removeAllFromStoredPlaylistByPath(int fd, const char *utf8path) | ||||
| { | ||||
| 	char *filename; | ||||
| 	FILE *file; | ||||
|  | ||||
| 	filename = utf8pathToFsPathInStoredPlaylist(utf8path, fd); | ||||
| 	if (!filename) { | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	while (!(file = fopen(filename, "w")) && errno == EINTR); | ||||
| 	if (file == NULL) { | ||||
| 		if (fd != -1) { | ||||
| 			commandError(fd, ACK_ERROR_NO_EXIST, | ||||
| 					"Could not open file \"%s\": %s", filename, strerror(errno)); | ||||
| 		} | ||||
|  | ||||
| 		ERROR("Could not open file \"%s\": %s", filename, strerror(errno)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	while (fclose(file) != 0 && errno == EINTR); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int removeOneSongFromStoredPlaylist(int fd, StoredPlaylist *sp, int pos) | ||||
| { | ||||
| 	ListNode *node = nodeOfStoredPlaylist(sp, pos); | ||||
| 	if (!node) { | ||||
| 		commandError(fd, ACK_ERROR_ARG, "could not find song at position."); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	deleteNodeFromList(sp->list, node); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int removeOneSongFromStoredPlaylistByPath(int fd, const char *utf8path, int pos) | ||||
| { | ||||
| 	StoredPlaylist *sp = loadStoredPlaylist(utf8path, fd); | ||||
| 	if (!sp) { | ||||
| 		commandError(fd, ACK_ERROR_UNKNOWN, "could not open playlist."); | ||||
| 		freeStoredPlaylist(sp); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (removeOneSongFromStoredPlaylist(fd, sp, pos) != 0) { | ||||
| 		freeStoredPlaylist(sp); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (writeStoredPlaylist(sp) != 0) { | ||||
| 		commandError(fd, ACK_ERROR_UNKNOWN, "failed to save playlist."); | ||||
| 		freeStoredPlaylist(sp); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	freeStoredPlaylist(sp); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int writeStoredPlaylistToPath(StoredPlaylist *sp, const char *fspath) | ||||
| { | ||||
| 	FILE *file; | ||||
| 	ListNode *node; | ||||
|  | ||||
| 	if (fspath == NULL) | ||||
| 		return -1; | ||||
|  | ||||
| 	while (!(file = fopen(fspath, "w")) && errno == EINTR); | ||||
| 	if (file == NULL) { | ||||
| 		if (sp->fd != -1) { | ||||
| 			commandError(sp->fd, ACK_ERROR_NO_EXIST, | ||||
| 					"Could not open file \"%s\": %s", fspath, strerror(errno)); | ||||
| 		} | ||||
|  | ||||
| 		ERROR("Couldn't open file \"%s\": %s", fspath, strerror(errno)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	node = sp->list->firstNode; | ||||
| 	while (node != NULL) { | ||||
| 		fprintf(file, "%s\n", (char *)node->data); | ||||
| 		node = node->nextNode; | ||||
| 	} | ||||
|  | ||||
| 	while (fclose(file) != 0 && errno == EINTR); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int writeStoredPlaylist(StoredPlaylist *sp) | ||||
| { | ||||
| 	return writeStoredPlaylistToPath(sp, sp->fspath); | ||||
| } | ||||
|  | ||||
| static void appendSongToStoredPlaylist(StoredPlaylist *sp, Song *song) | ||||
| { | ||||
| 	char *s; | ||||
|  | ||||
| 	if (playlist_saveAbsolutePaths && song->type == SONG_TYPE_FILE) { | ||||
| 		s = rmp2amp(utf8ToFsCharset(getSongUrl(song))); | ||||
| 	} else { | ||||
| 		s = utf8ToFsCharset(getSongUrl(song)); | ||||
| 	} | ||||
|  | ||||
| 	insertInListWithoutKey(sp->list, strdup(s)); | ||||
| } | ||||
|  | ||||
| int appendSongToStoredPlaylistByPath(int fd, const char *utf8path, Song *song) | ||||
| { | ||||
| 	char *filename; | ||||
| 	FILE *file; | ||||
|  | ||||
| 	filename = utf8pathToFsPathInStoredPlaylist(utf8path, fd); | ||||
| 	if (!filename) { | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	while (!(file = fopen(filename, "a")) && errno == EINTR); | ||||
| 	if (file == NULL) { | ||||
| 		if (fd != -1) { | ||||
| 			commandError(fd, ACK_ERROR_NO_EXIST, | ||||
| 					"Could not open file \"%s\": %s", filename, strerror(errno)); | ||||
| 		} | ||||
|  | ||||
| 		ERROR("Could not open file \"%s\": %s", filename, strerror(errno)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (playlist_saveAbsolutePaths && song->type == SONG_TYPE_FILE) { | ||||
| 		fprintf(file, "%s\n", rmp2amp(utf8ToFsCharset(getSongUrl(song)))); | ||||
| 	} else { | ||||
| 		fprintf(file, "%s\n", utf8ToFsCharset(getSongUrl(song))); | ||||
| 	} | ||||
|  | ||||
| 	while (fclose(file) != 0 && errno == EINTR); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void appendPlaylistToStoredPlaylist(StoredPlaylist *sp, Playlist *playlist) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 0; i < playlist->length; i++) { | ||||
| 		appendSongToStoredPlaylist(sp, playlist->songs[i]); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										46
									
								
								src/storedPlaylist.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/storedPlaylist.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| /* the Music Player Daemon (MPD) | ||||
|  * Copyright (C) 2007 by Warren Dukes (warren.dukes@gmail.com) | ||||
|  * This project's homepage is: http://www.musicpd.org | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #ifndef STORED_PLAYLIST_H | ||||
| #define STORED_PLAYLIST_H | ||||
|  | ||||
| #include "song.h" | ||||
| #include "list.h" | ||||
| #include "playlist.h" | ||||
|  | ||||
| typedef struct _storedPlaylist { | ||||
| 	List *list; | ||||
| 	unsigned int length; | ||||
| 	char *fspath; | ||||
| 	int fd; | ||||
| } StoredPlaylist; | ||||
|  | ||||
| StoredPlaylist *newStoredPlaylist(const char *filename, int fd, int ignoreExisting); | ||||
| StoredPlaylist *loadStoredPlaylist(const char *utf8path, int fd); | ||||
| void freeStoredPlaylist(StoredPlaylist *sp); | ||||
|  | ||||
| int moveSongInStoredPlaylistByPath(int fd, const char *utf8path, int src, int dest); | ||||
| int removeAllFromStoredPlaylistByPath(int fd, const char *utf8path); | ||||
| int removeOneSongFromStoredPlaylistByPath(int fd, const char *utf8path, int pos); | ||||
|  | ||||
| int writeStoredPlaylist(StoredPlaylist *sp); | ||||
|  | ||||
| int appendSongToStoredPlaylistByPath(int fd, const char *utf8path, Song *song); | ||||
| void appendPlaylistToStoredPlaylist(StoredPlaylist *sp, Playlist *playlist); | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user
	 J. Alexander Treuman
					J. Alexander Treuman