diff options
Diffstat (limited to '')
-rw-r--r-- | src/playlist.c | 1236 |
1 files changed, 1236 insertions, 0 deletions
diff --git a/src/playlist.c b/src/playlist.c new file mode 100644 index 000000000..eec721cfb --- /dev/null +++ b/src/playlist.c @@ -0,0 +1,1236 @@ +/* the Music Player Daemon (MPD) + * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu) + * 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 "playlist.h" +#include "player.h" +#include "command.h" +#include "ls.h" +#include "tag.h" +#include "conf.h" +#include "directory.h" +#include "stats.h" +#include "log.h" +#include "path.h" +#include "utils.h" +#include "sig_handlers.h" + +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> + +#define BITS_FOR_VERSION 31 + +#define PLAYLIST_COMMENT '#' + +#define PLAYLIST_STATE_STOP 0 +#define PLAYLIST_STATE_PLAY 1 + +#define PLAYLIST_PREV_UNLESS_ELAPSED 10 + +#define PLAYLIST_STATE_FILE_STATE "state: " +#define PLAYLIST_STATE_FILE_RANDOM "random: " +#define PLAYLIST_STATE_FILE_REPEAT "repeat: " +#define PLAYLIST_STATE_FILE_CURRENT "current: " +#define PLAYLIST_STATE_FILE_TIME "time: " +#define PLAYLIST_STATE_FILE_CROSSFADE "crossfade: " +#define PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "playlist_begin" +#define PLAYLIST_STATE_FILE_PLAYLIST_END "playlist_end" + +#define PLAYLIST_STATE_FILE_STATE_PLAY "play" +#define PLAYLIST_STATE_FILE_STATE_PAUSE "pause" +#define PLAYLIST_STATE_FILE_STATE_STOP "stop" + +#define PLAYLIST_BUFFER_SIZE 2*MAXPATHLEN + +typedef struct _Playlist { + Song ** songs; + int * order; + int length; + int current; + int queued; + int repeat; + int random; + unsigned long version; +} Playlist; + +Playlist playlist; +int playlist_state = PLAYLIST_STATE_STOP; +int playlist_max_length; +int playlist_stopOnError; +int playlist_errorCount = 0; +int playlist_queueError; +int playlist_noGoToNext = 0; + +int playlist_saveAbsolutePaths; + +char * playlist_stateFile = NULL; + +void swapOrder(int a, int b); +int playPlaylistOrderNumber(FILE * fp, int orderNum); +void randomizeOrder(int start, int end); + +void incrPlaylistVersion() { + static unsigned long max = ((unsigned long)1<<BITS_FOR_VERSION)-1; + playlist.version++; + if(playlist.version>=max) playlist.version = 0; +} + +void initPlaylist() { + char * test; + + playlist.length = 0; + playlist.repeat = 0; + playlist.version = 0; + playlist.random = 0; + playlist.queued = -1; + + blockTermSignal(); + + playlist_max_length = strtol((getConf())[CONF_MAX_PLAYLIST_LENGTH],&test,10); + if(*test!='\0') { + ERROR("max playlist length \"%s\" is not an integer\n", + (getConf())[CONF_MAX_PLAYLIST_LENGTH]); + exit(-1); + } + + if(strcmp("yes",(getConf())[CONF_SAVE_ABSOLUTE_PATHS_IN_PLAYLISTS]) + ==0) { + playlist_saveAbsolutePaths = 1; + } + else if(strcmp("no",(getConf())[CONF_SAVE_ABSOLUTE_PATHS_IN_PLAYLISTS]) + ==0) { + playlist_saveAbsolutePaths = 0; + } + else { + ERROR("save_absolute_paths_in_playlist \"%s\" is not yes or " + "no\n", + (getConf())[CONF_SAVE_ABSOLUTE_PATHS_IN_PLAYLISTS]); + exit(-1); + } + + playlist.songs = malloc(sizeof(Song *)*playlist_max_length); + playlist.order = malloc(sizeof(Song *)*playlist_max_length); + + memset(playlist.songs,(int)NULL,sizeof(char *)*playlist_max_length); + + srand(time(NULL)); + + if(getConf()[CONF_STATE_FILE]) { + playlist_stateFile = getConf()[CONF_STATE_FILE]; + } + + unblockTermSignal(); +} + +void finishPlaylist() { + stopPlaylist(stderr); + clearPlaylist(stderr); + free(playlist.songs); + free(playlist.order); +} + +int clearPlaylist(FILE * fp) { + int i; + + if(stopPlaylist(fp)<0) return -1; + + blockTermSignal(); + for(i=0;i<playlist.length;i++) playlist.songs[i] = NULL; + playlist.length = 0; + unblockTermSignal(); + + incrPlaylistVersion(); + + return 0; +} + +int showPlaylist(FILE * fp) { + int i; + + for(i=0;i<playlist.length;i++) { + myfprintf(fp,"%i:%s\n",i,(playlist.songs[i])->utf8file); + } + + return 0; +} + +void savePlaylistState() { + if(playlist_stateFile) { + FILE * fp; + + blockTermSignal(); + while(!(fp = fopen(playlist_stateFile,"w")) && errno==EINTR); + if(!fp) { + ERROR("problems opening state file \"%s\" for " + "writing\n",playlist_stateFile); + return; + } + + myfprintf(fp,"%s",PLAYLIST_STATE_FILE_STATE); + switch(playlist_state) { + case PLAYLIST_STATE_PLAY: + switch(getPlayerState()) { + case PLAYER_STATE_PAUSE: + myfprintf(fp,"%s\n", + PLAYLIST_STATE_FILE_STATE_PAUSE); + break; + default: + myfprintf(fp,"%s\n", + PLAYLIST_STATE_FILE_STATE_PLAY); + } + myfprintf(fp,"%s%i\n",PLAYLIST_STATE_FILE_CURRENT, + playlist.order[playlist.current]); + myfprintf(fp,"%s%i\n",PLAYLIST_STATE_FILE_TIME, + getPlayerElapsedTime()); + break; + default: + myfprintf(fp,"%s\n",PLAYLIST_STATE_FILE_STATE_STOP); + break; + } + myfprintf(fp,"%s%i\n",PLAYLIST_STATE_FILE_RANDOM, + playlist.random); + myfprintf(fp,"%s%i\n",PLAYLIST_STATE_FILE_REPEAT, + playlist.repeat); + myfprintf(fp,"%s%i\n",PLAYLIST_STATE_FILE_CROSSFADE, + (int)(getPlayerCrossFade())); + myfprintf(fp,"%s\n",PLAYLIST_STATE_FILE_PLAYLIST_BEGIN); + showPlaylist(fp); + myfprintf(fp,"%s\n",PLAYLIST_STATE_FILE_PLAYLIST_END); + + while(fclose(fp) && errno==EINTR); + unblockTermSignal(); + } +} + +void loadPlaylistFromStateFile(FILE * fp, char * buffer, int state, int current, + int time) +{ + char * temp; + int song; + + if(!myFgets(buffer,PLAYLIST_BUFFER_SIZE,fp)) { + ERROR("error parsing state file \"%s\"\n",playlist_stateFile); + exit(-1); + } + while(strcmp(buffer,PLAYLIST_STATE_FILE_PLAYLIST_END)) { + song = atoi(strtok(buffer,":")); + if(!(temp = strtok(NULL,""))) { + ERROR("error parsing state file \"%s\"\n", + playlist_stateFile); + exit(-1); + } + if(addToPlaylist(stderr,temp)==0 && current==song) { + if(state!=PLAYER_STATE_STOP) { + playPlaylist(stderr,playlist.length-1,0); + } + if(state==PLAYER_STATE_PAUSE) { + playerPause(stderr); + seekSongInPlaylist(stderr,playlist.length-1, + time); + } + } + if(!myFgets(buffer,PLAYLIST_BUFFER_SIZE,fp)) { + ERROR("error parsing state file \"%s\"\n", + playlist_stateFile); + exit(-1); + } + } +} + +void readPlaylistState() { + if(playlist_stateFile) { + FILE * fp; + struct stat st; + int current = -1; + int time = 0; + int state = PLAYER_STATE_STOP; + char buffer[PLAYLIST_BUFFER_SIZE]; + + if(stat(playlist_stateFile,&st)<0) return; + if(!S_ISREG(st.st_mode)) { + ERROR("state file \"%s\" is not a regular " + "file\n",playlist_stateFile); + exit(-1); + } + + fp = fopen(playlist_stateFile,"r"); + if(!fp) { + ERROR("problems opening state file \"%s\" for " + "reading\n",playlist_stateFile); + exit(-1); + } + + while(myFgets(buffer,PLAYLIST_BUFFER_SIZE,fp)) { + if(strncmp(buffer,PLAYLIST_STATE_FILE_STATE, + strlen(PLAYLIST_STATE_FILE_STATE))==0) { + if(strcmp(&(buffer + [strlen(PLAYLIST_STATE_FILE_STATE)]), + PLAYLIST_STATE_FILE_STATE_PLAY)==0) { + state = PLAYER_STATE_PLAY; + } + else if(strcmp(&(buffer + [strlen(PLAYLIST_STATE_FILE_STATE)]), + PLAYLIST_STATE_FILE_STATE_PAUSE)==0) { + state = PLAYER_STATE_PAUSE; + } + } + else if(strncmp(buffer,PLAYLIST_STATE_FILE_TIME, + strlen(PLAYLIST_STATE_FILE_TIME))==0) { + time = atoi(&(buffer + [strlen(PLAYLIST_STATE_FILE_TIME)])); + } + else if(strncmp(buffer,PLAYLIST_STATE_FILE_REPEAT, + strlen(PLAYLIST_STATE_FILE_REPEAT))==0) { + if(strcmp(&(buffer + [strlen(PLAYLIST_STATE_FILE_REPEAT)]), + "1")==0) { + setPlaylistRepeatStatus(stderr,1); + } + else setPlaylistRepeatStatus(stderr,0); + } + else if(strncmp(buffer,PLAYLIST_STATE_FILE_CROSSFADE, + strlen(PLAYLIST_STATE_FILE_CROSSFADE))==0) { + setPlayerCrossFade(atoi(&(buffer[strlen( + PLAYLIST_STATE_FILE_CROSSFADE)]))); + } + else if(strncmp(buffer,PLAYLIST_STATE_FILE_RANDOM, + strlen(PLAYLIST_STATE_FILE_RANDOM))==0) { + if(strcmp(&(buffer + [strlen(PLAYLIST_STATE_FILE_RANDOM)]), + "1")==0) { + setPlaylistRandomStatus(stderr,1); + } + else setPlaylistRandomStatus(stderr,0); + } + else if(strncmp(buffer,PLAYLIST_STATE_FILE_CURRENT, + strlen(PLAYLIST_STATE_FILE_CURRENT))==0) { + if(strlen(buffer)== + strlen(PLAYLIST_STATE_FILE_CURRENT)) { + ERROR("error parsing state " + "file \"%s\"\n", + playlist_stateFile); + exit(-1); + } + current = atoi(&(buffer + [strlen(PLAYLIST_STATE_FILE_CURRENT)])); + } + else if(strncmp(buffer, + PLAYLIST_STATE_FILE_PLAYLIST_BEGIN, + strlen(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN) + )==0) { + if(state==PLAYER_STATE_STOP) current = -1; + loadPlaylistFromStateFile(fp,buffer,state, + current,time); + } + } + + fclose(fp); + } +} + +int playlistInfo(FILE * fp,int song) { + MpdTag * tag; + int i; + int begin = 0; + int end = playlist.length; + + if(song>=0) { + begin = song; + end = song+1; + } + if(song>=playlist.length) { + myfprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR); + return -1; + } + + for(i=begin;i<end;i++) { + myfprintf(fp,"file: %s\n",(playlist.songs[i])->utf8file); + if((tag = (playlist.songs[i])->tag)) { + printMpdTag(fp,tag); + } + } + + return 0; +} + +void swapSongs(int song1, int song2) { + Song * temp; + + temp = playlist.songs[song1]; + playlist.songs[song1] = playlist.songs[song2]; + playlist.songs[song2] = temp; +} + +void queueNextSongInPlaylist() { + if(playlist.current<playlist.length-1) { + playlist.queued = playlist.current+1; + DEBUG("playlist: queue song %i:\"%s\"\n", + playlist.queued, + playlist.songs[playlist.order[ + playlist.queued]]->utf8file); + if(queueSong(playlist.songs[playlist.order[ + playlist.queued]]->utf8file)<0) { + playlist.queued = -1; + playlist_queueError = 1; + } + } + else if(playlist.length && playlist.repeat) { + if(playlist.length>1 && playlist.random) { + randomizeOrder(0,playlist.length); + } + playlist.queued = 0; + DEBUG("playlist: queue song %i:\"%s\"\n", + playlist.queued, + playlist.songs[playlist.order[ + playlist.queued]]->utf8file); + if(queueSong(playlist.songs[playlist.order[ + playlist.queued]]->utf8file)<0) { + playlist.queued = -1; + playlist_queueError = 1; + } + } +} + +void syncPlaylistWithQueue(int queue) { + if(queue && getPlayerQueueState()==PLAYER_QUEUE_BLANK) { + queueNextSongInPlaylist(); + } + else if(getPlayerQueueState()==PLAYER_QUEUE_DECODE) { + if(playlist.queued!=-1) setQueueState(PLAYER_QUEUE_PLAY); + else setQueueState(PLAYER_QUEUE_STOP); + } + else if(getPlayerQueueState()==PLAYER_QUEUE_EMPTY) { + setQueueState(PLAYER_QUEUE_BLANK); + if(playlist.queued>=0) { + DEBUG("playlist: now playing queued song\n"); + playlist.current = playlist.queued; + } + playlist.queued = -1; + if(queue) queueNextSongInPlaylist(); + } +} + +void lockPlaylistInteraction() { + if(getPlayerQueueState()==PLAYER_QUEUE_PLAY || + getPlayerQueueState()==PLAYER_QUEUE_FULL) { + playerQueueLock(); + syncPlaylistWithQueue(0); + } +} + +void unlockPlaylistInteraction() { + playerQueueUnlock(); +} + +void clearPlayerQueue() { + playlist.queued = -1; + switch(getPlayerQueueState()) { + case PLAYER_QUEUE_FULL: + DEBUG("playlist: dequeue song\n"); + setQueueState(PLAYER_QUEUE_BLANK); + break; + case PLAYER_QUEUE_PLAY: + DEBUG("playlist: stop decoding queued song\n"); + setQueueState(PLAYER_QUEUE_STOP); + break; + } +} + +int addToPlaylist(FILE * fp, char * file) { + Song * song; + + DEBUG("add to playlist: %s\n",file); + + if(!(song = getSong(file))) { + myfprintf(fp,"%s \"%s\" is not in the music db\n",COMMAND_RESPOND_ERROR,file); + return -1; + } + + return addSongToPlaylist(fp,song); +} + +int addSongToPlaylist(FILE * fp, Song * song) { + if(playlist.length==playlist_max_length) { + myfprintf(fp,"%s playlist is at the max size\n",COMMAND_RESPOND_ERROR); + return -1; + } + + if(playlist_state==PLAYLIST_STATE_PLAY) { + if(playlist.queued>=0 && playlist.current==playlist.length-1) { + lockPlaylistInteraction(); + clearPlayerQueue(); + unlockPlaylistInteraction(); + } + } + + playlist.songs[playlist.length] = song; + playlist.order[playlist.length] = playlist.length; + playlist.length++; + + if(playlist.random) { + int swap; + int start; + if(playlist_state==PLAYLIST_STATE_STOP) start = 0; + else if(playlist.queued>0) start = playlist.queued+1; + else start = playlist.current+1; + swap = rand()%(playlist.length-start); + swap+=start; + swapOrder(playlist.length-1,swap); + } + + incrPlaylistVersion(); + + return 0; +} + +int swapSongsInPlaylist(FILE * fp, int song1, int song2) { + int queuedSong = -1; + int currentSong = -1; + + if(song1<0 || song1>=playlist.length) { + fprintf(fp,"%s \"%i\" is not in the playlist\n", + COMMAND_RESPOND_ERROR,song1); + return -1; + } + if(song2<0 || song2>=playlist.length) { + fprintf(fp,"%s \"%i\" is not in the playlist\n", + COMMAND_RESPOND_ERROR,song2); + return -1; + } + + blockTermSignal(); + + if(playlist_state==PLAYLIST_STATE_PLAY) { + if(playlist.queued>=0) { + queuedSong = playlist.order[playlist.queued]; + } + currentSong = playlist.order[playlist.current]; + + if(queuedSong==song1 || queuedSong==song2 || currentSong==song1 + || currentSong==song2) + { + lockPlaylistInteraction(); + clearPlayerQueue(); + unlockPlaylistInteraction(); + } + } + + swapSongs(song1,song2); + if(playlist.random) { + int i; + int k; + int j = -1; + for(i=0;playlist.order[i]!=song1;i++) { + if(playlist.order[i]==song2) j = i; + } + k = i; + for(;j==-1;i++) if(playlist.order[i]==song2) j = i; + swapOrder(k,j); + } + else { + if(playlist.current==song1) playlist.current = song2; + else if(playlist.current==song2) playlist.current = song1; + } + + unblockTermSignal(); + + incrPlaylistVersion(); + + return 0; +} + +int deleteFromPlaylist(FILE * fp, int song) { + int i; + int songOrder; + + if(song<0) { + myfprintf(fp,"%s need a positive integer\n",COMMAND_RESPOND_ERROR); + return -1; + } + if(song>=playlist.length) { + myfprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR); + return -1; + } + + blockTermSignal(); + if(playlist_state==PLAYLIST_STATE_PLAY) { + if(playlist.queued>=0 && (playlist.order[playlist.queued]==song + || playlist.order[playlist.current]==song)) + { + lockPlaylistInteraction(); + clearPlayerQueue(); + unlockPlaylistInteraction(); + } + } + + /* delete song from songs array */ + for(i=song;i<playlist.length-1;i++) { + playlist.songs[i] = playlist.songs[i+1]; + } + /* now find it in the order array */ + for(i=0;i<playlist.length-1;i++) { + if(playlist.order[i]==song) break; + } + songOrder = i; + /* delete the entry from the order array */ + for(;i<playlist.length-1;i++) playlist.order[i] = playlist.order[i+1]; + /* readjust values in the order array */ + for(i=0;i<playlist.length-1;i++) { + if(playlist.order[i]>song) playlist.order[i]--; + } + /* now take care of other misc stuff */ + playlist.songs[playlist.length-1] = NULL; + playlist.length--; + + unblockTermSignal(); + + incrPlaylistVersion(); + + if(playlist_state!=PLAYLIST_STATE_STOP && playlist.current==songOrder) { + /*if(playlist.current>=playlist.length) return playerStop(fp); + else return playPlaylistOrderNumber(fp,playlist.current);*/ + playerStop(stderr); + playlist_noGoToNext = 1; + } + else if(playlist_state!=PLAYLIST_STATE_STOP && + playlist.current>songOrder) { + playlist.current--; + } + + if(playlist_state!=PLAYLIST_STATE_STOP && playlist.queued>songOrder) { + playlist.queued--; + } + + return 0; +} + +void deleteASongFromPlaylist(Song * song) { + int i; + + for(i=0;i<playlist.length;i++) { + if(song==playlist.songs[i]) { + deleteFromPlaylist(stderr,i); + } + } +} + +void deleteSongsFromPlaylist(SongList * songList) { + ListNode * node = songList->firstNode; + Song * song; + + while(node) { + song = (Song *)node->data; + deleteASongFromPlaylist(song); + node = node->nextNode; + } +} + +int stopPlaylist(FILE * fp) { + DEBUG("playlist: stop\n"); + if(playerStop(fp)<0) return -1; + playerCloseAudio(); + playlist.queued = -1; + playlist_state = PLAYLIST_STATE_STOP; + playlist_noGoToNext = 0; + /*stats.playTime+=getPlayerElapsedTime();*/ + if(playlist.random) randomizeOrder(0,playlist.length-1); + return 0; +} + +int playPlaylistOrderNumber(FILE * fp, int orderNum) { + /*stats.songsPlayed++;*/ + + if(playerStop(fp)<0) return -1; + + playlist_state = PLAYLIST_STATE_PLAY; + playlist_noGoToNext = 0; + playlist.queued = -1; + playlist_queueError = 0; + playlist.current = orderNum; + + DEBUG("playlist: play %i:\"%s\"\n",orderNum, + (playlist.songs[playlist.order[orderNum]])->utf8file); + + if(playerPlay(fp,(playlist.songs[playlist.order[orderNum]])-> + utf8file)<0) + { + stopPlaylist(fp); + return -1; + } + + return 0; +} + +int playPlaylist(FILE * fp, int song, int stopOnError) { + int i = song; + + clearPlayerError(); + + if(song==-1) i = 0; + else if(song<0) { + myfprintf(fp,"%s need integer >= -1\n",COMMAND_RESPOND_ERROR); + playlist_state = PLAYLIST_STATE_STOP; + return -1; + } + if(!playlist.length) { + myfprintf(fp,"%s playlist is empty\n",COMMAND_RESPOND_ERROR); + playlist_state = PLAYLIST_STATE_STOP; + return -1; + } + else if(song>=playlist.length) { + myfprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR); + playlist_state = PLAYLIST_STATE_STOP; + return -1; + } + + if(playlist.random) { + /*if(song == -1 && playlist_state==PLAYLIST_STATE_PLAY) { + randomizeOrder(0,playlist.length-1); + } + else {*/ + if(song>=0) for(i=0;song!=playlist.order[i];i++); + if(playlist_state==PLAYLIST_STATE_STOP) { + playlist.current = 0; + } + swapOrder(i,playlist.current); + i = playlist.current; + /*}*/ + } + + playlist_stopOnError = stopOnError; + playlist_errorCount = 0; + + return playPlaylistOrderNumber(fp,i); +} + +void syncPlayerAndPlaylist() { + if(playlist_state!=PLAYLIST_STATE_PLAY) return; + + if(getPlayerState()==PLAYER_STATE_STOP) playPlaylistIfPlayerStopped(); + else syncPlaylistWithQueue(!playlist_queueError); +} + +int currentSongInPlaylist(FILE * fp) { + if(playlist_state!=PLAYLIST_STATE_PLAY) return 0; + + playlist_stopOnError = 0; + + syncPlaylistWithQueue(0); + + if(playlist.current<playlist.length) { + return playPlaylistOrderNumber(fp,playlist.current); + } + else return stopPlaylist(fp);; + + return 0; +} + +int nextSongInPlaylist(FILE * fp) { + if(playlist_state!=PLAYLIST_STATE_PLAY) return 0; + + syncPlaylistWithQueue(0); + + playlist_stopOnError = 0; + + if(playlist.current<playlist.length-1) { + /*stats.playTime+=getPlayerElapsedTime();*/ + return playPlaylistOrderNumber(fp,playlist.current+1); + } + else if(playlist.length && playlist.repeat) { + /*stats.playTime+=getPlayerElapsedTime();*/ + if(playlist.random) randomizeOrder(0,playlist.length-1); + return playPlaylistOrderNumber(fp,0); + } + else { + return stopPlaylist(fp);; + } + + return 0; +} + +void playPlaylistIfPlayerStopped() { + if(getPlayerState()==PLAYER_STATE_STOP) { + int error = getPlayerError(); + + if(error==PLAYER_ERROR_NOERROR) playlist_errorCount = 0; + else playlist_errorCount++; + + if(playlist_state==PLAYLIST_STATE_PLAY && ( + (playlist_stopOnError && + error!=PLAYER_ERROR_NOERROR) || + error==PLAYER_ERROR_AUDIO || + error==PLAYER_ERROR_SYSTEM || + playlist_errorCount>=playlist.length)) { + stopPlaylist(stderr); + } + else if(playlist_noGoToNext) currentSongInPlaylist(stderr); + else nextSongInPlaylist(stderr); + } +} + +int getPlaylistRepeatStatus() { + return playlist.repeat; +} + +int getPlaylistRandomStatus() { + return playlist.random; +} + +int setPlaylistRepeatStatus(FILE * fp, int status) { + if(status!=0 && status!=1) { + myfprintf(fp,"%s \"%i\" is not 0 or 1\n",COMMAND_RESPOND_ERROR,status); + return -1; + } + + if(playlist_state==PLAYLIST_STATE_PLAY) { + if(playlist.repeat && !status && playlist.queued==0) { + lockPlaylistInteraction(); + clearPlayerQueue(); + unlockPlaylistInteraction(); + } + } + + playlist.repeat = status; + + return 0; +} + +int moveSongInPlaylist(FILE * fp, int from, int to) { + int i; + Song * tmpSong; + int queuedSong = -1; + int currentSong = -1; + + if(from<0 || from>=playlist.length) { + fprintf(fp,"%s \"%i\" is not a song in the playlist\n", + COMMAND_RESPOND_ERROR,from); + return -1; + } + + if(to<0 || to>=playlist.length) { + fprintf(fp,"%s \"%i\" is not a song in the playlist\n", + COMMAND_RESPOND_ERROR,to); + return -1; + } + + blockTermSignal(); + + if(playlist_state==PLAYLIST_STATE_PLAY) { + if(playlist.queued>=0) { + queuedSong = playlist.order[playlist.queued]; + } + currentSong = playlist.order[playlist.current]; + if(queuedSong==from || queuedSong==to || currentSong==from || + currentSong==to) + { + lockPlaylistInteraction(); + clearPlayerQueue(); + unlockPlaylistInteraction(); + } + } + + tmpSong = playlist.songs[from]; + /* move songs to one less in from->to */ + for(i=from;i<to;i++) playlist.songs[i] = playlist.songs[i+1]; + /* move songs to one more in to->from */ + for(i=from;i>to;i--) playlist.songs[i] = playlist.songs[i-1]; + /* put song at _to_ */ + playlist.songs[to] = tmpSong; + /* now deal with order */ + if(playlist.random) { + for(i=0;i<playlist.length;i++) { + if(playlist.order[i]>from && playlist.order[i]<=to) { + playlist.order[i]--; + } + else if(playlist.order[i]<from && + playlist.order[i]>=to) { + playlist.order[i]++; + } + else if(from==playlist.order[i]) { + playlist.order[i] = to; + } + } + } + else if(playlist.current==from) playlist.current = to; + else if(playlist.current>from && playlist.current<=to) { + playlist.current--; + } + else if(playlist.current>=to && playlist.current<from) { + playlist.current++; + } + + unblockTermSignal(); + + incrPlaylistVersion(); + + return 0; +} + +void orderPlaylist() { + int i; + + playlist.current = playlist.order[playlist.current]; + + blockTermSignal(); + if(playlist_state==PLAYLIST_STATE_PLAY) { + if(playlist.queued>=0) { + lockPlaylistInteraction(); + clearPlayerQueue(); + unlockPlaylistInteraction(); + } + } + + for(i=0;i<playlist.length;i++) { + playlist.order[i] = i; + } + + unblockTermSignal(); +} + +void swapOrder(int a, int b) { + int bak = playlist.order[a]; + playlist.order[a] = playlist.order[b]; + playlist.order[b] = bak; +} + +void randomizeOrder(int start,int end) { + int i; + int ri; + + DEBUG("playlist: randomize from %i to %i\n",start,end); + + blockTermSignal(); + + if(playlist_state==PLAYLIST_STATE_PLAY) { + if(playlist.queued>=start && playlist.queued<=end) { + lockPlaylistInteraction(); + clearPlayerQueue(); + unlockPlaylistInteraction(); + } + } + + for(i=start;i<=end;i++) { + ri = rand()%(end-start+1)+start; + if(ri==playlist.current) playlist.current = i; + else if(i==playlist.current) playlist.current = ri; + swapOrder(i,ri); + } + + unblockTermSignal(); +} + +int setPlaylistRandomStatus(FILE * fp, int status) { + int statusWas = playlist.random; + + if(status!=0 && status!=1) { + myfprintf(fp,"%s \"%i\" is not 0 or 1\n",COMMAND_RESPOND_ERROR,status); + return -1; + } + + playlist.random = status; + + if(status!=statusWas) { + if(playlist.random) { + if(playlist_state==PLAYLIST_STATE_PLAY) { + randomizeOrder(playlist.current+1, + playlist.length-1); + } + else randomizeOrder(0,playlist.length-1); + } + else orderPlaylist(); + } + + return 0; +} + +int previousSongInPlaylist(FILE * fp) { + if(playlist_state!=PLAYLIST_STATE_PLAY) return 0; + + syncPlaylistWithQueue(0); + + if (getPlayerElapsedTime()>PLAYLIST_PREV_UNLESS_ELAPSED) { + return playPlaylistOrderNumber(fp,playlist.current); + } + else { + if(playlist.current>0) { + return playPlaylistOrderNumber(fp,playlist.current-1); + } + else if(playlist.repeat) { + return playPlaylistOrderNumber(fp,playlist.length-1); + } + else { + return playPlaylistOrderNumber(fp,playlist.current); + } + } + + return 0; +} + +int shufflePlaylist(FILE * fp) { + int i; + int ri; + + if(playlist.length>1) { + blockTermSignal(); + if(playlist_state==PLAYLIST_STATE_PLAY) { + lockPlaylistInteraction(); + clearPlayerQueue(); + unlockPlaylistInteraction(); + /* put current playing song first */ + swapSongs(0,playlist.order[playlist.current]); + if(playlist.random) { + int j; + for(j=0;0!=playlist.order[j];j++); + playlist.current = j; + } + else playlist.current = 0; + i = 1; + } + else i = 0; + /* shuffle the rest of the list */ + for(;i<playlist.length;i++) { + ri = rand()%(playlist.length-1)+1; + swapSongs(i,ri); + } + unblockTermSignal(); + + incrPlaylistVersion(); + } + + return 0; +} + +int deletePlaylist(FILE * fp, char * utf8file) { + char * file = utf8ToFsCharset(utf8file); + char * rfile = malloc(strlen(file)+strlen(".")+ + strlen(PLAYLIST_FILE_SUFFIX)+1); + char * actualFile; + + strcpy(rfile,file); + strcat(rfile,"."); + strcat(rfile,PLAYLIST_FILE_SUFFIX); + + if((actualFile = rpp2app(rfile)) && isPlaylist(actualFile)) free(rfile); + else { + free(rfile); + myfprintf(fp,"%s playlist \"%s\" not found\n", + COMMAND_RESPOND_ERROR,utf8file); + return -1; + } + + if(unlink(actualFile)<0) { + myfprintf(fp,"%s problems deleting file\n",COMMAND_RESPOND_ERROR); + return -1; + } + + return 0; +} + +int savePlaylist(FILE * fp, char * utf8file) { + FILE * fileP; + int i; + struct stat st; + char * file; + char * rfile; + char * actualFile; + + if(strstr(utf8file,"/")) { + myfprintf(fp,"%s cannot save \"%s\", saving playlists to " + "subdirectories is not supported\n", + COMMAND_RESPOND_ERROR,utf8file); + return -1; + } + + file = strdup(utf8ToFsCharset(utf8file)); + + rfile = malloc(strlen(file)+strlen(".")+ + strlen(PLAYLIST_FILE_SUFFIX)+1); + + strcpy(rfile,file); + strcat(rfile,"."); + strcat(rfile,PLAYLIST_FILE_SUFFIX); + + free(file); + + actualFile = rpp2app(rfile); + + free(rfile); + + if(0==stat(actualFile,&st)) { + myfprintf(fp,"%s A file or directory already exists with the name \"%s\"\n",COMMAND_RESPOND_ERROR,utf8file); + return -1; + } + + + while(!(fileP = fopen(actualFile,"w")) && errno==EINTR); + if(fileP==NULL) { + myfprintf(fp,"%s Problems opening file\n",COMMAND_RESPOND_ERROR); + return -1; + } + + for(i=0;i<playlist.length;i++) { + if(playlist_saveAbsolutePaths) { + myfprintf(fileP,"%s%s\n",musicDir, + utf8ToFsCharset((playlist.songs[i])->utf8file)); + } + else myfprintf(fileP,"%s\n", + utf8ToFsCharset((playlist.songs[i])->utf8file)); + } + + while(fclose(fileP) && errno==EINTR); + + return 0; +} + +int loadPlaylist(FILE * fp, char * utf8file) { + FILE * fileP; + char s[MAXPATHLEN*1]; + int slength = 0; + char * temp = strdup(utf8ToFsCharset(utf8file)); + char * rfile = malloc(strlen(temp)+strlen(".")+ + strlen(PLAYLIST_FILE_SUFFIX)+1); + char * actualFile; + char * parent = parentPath(temp); + int parentlen = strlen(parent); + char * erroredFile = NULL; + + strcpy(rfile,temp); + strcat(rfile,"."); + strcat(rfile,PLAYLIST_FILE_SUFFIX); + + free(temp); + + if((actualFile = rpp2app(rfile)) && isPlaylist(actualFile)) free(rfile); + else { + free(rfile); + myfprintf(fp,"%s playlist \"%s\" not found\n", + COMMAND_RESPOND_ERROR,utf8file); + return -1; + } + + while(!(fileP = fopen(actualFile,"r")) && errno==EINTR); + if(fileP==NULL) { + myfprintf(fp,"%s Problems opening file \"%s\"\n", + COMMAND_RESPOND_ERROR,utf8file); + return -1; + } + + while((s[slength] = fgetc(fileP))!=EOF) { + if(s[slength]=='\n' || s[slength]=='\0') { + s[slength] = '\0'; + if(strncmp(s,musicDir,strlen(musicDir))==0) { + strcpy(s,&(s[strlen(musicDir)])); + } + else if(parentlen) { + temp = strdup(s); + memset(s,0,MAXPATHLEN+1); + strcpy(s,parent); + strncat(s,"/",MAXPATHLEN-parentlen); + strncat(s,temp,MAXPATHLEN-parentlen-1); + if(strlen(s)>=MAXPATHLEN) { + myfprintf(fp,"%s \"%s\" too long\n",COMMAND_RESPOND_ERROR,temp); + free(temp); + while(fclose(fileP) && errno==EINTR); + if(erroredFile) free(erroredFile); + return -1; + } + free(temp); + } + slength = 0; + temp = strdup(fsCharsetToUtf8(s)); + if(s[0]==PLAYLIST_COMMENT && !getSong(temp)) { + free(temp); + continue; + } + if((addToPlaylist(stderr,temp))<0) { + if(!erroredFile) erroredFile = strdup(temp); + } + free(temp); + } + else if(slength==MAXPATHLEN) { + s[slength] = '\0'; + myfprintf(fp,"%s \"%s\" too long\n",COMMAND_RESPOND_ERROR,s); + while(fclose(fileP) && errno==EINTR); + if(erroredFile) free(erroredFile); + return -1; + } + else if(s[slength]!='\r') slength++; + } + + while(fclose(fileP) && errno==EINTR); + + if(erroredFile) { + myfprintf(fp,"%s can't add file \"%s\"\n",COMMAND_RESPOND_ERROR, + erroredFile); + free(erroredFile); + return -1; + } + + return 0; +} + +int getPlaylistCurrentSong() { + return playlist.order[playlist.current]; +} + +unsigned long getPlaylistVersion() { + return playlist.version; +} + +int getPlaylistLength() { + return playlist.length; +} + +int seekSongInPlaylist(FILE * fp, int song, float time) { + int i = song; + + if(song<0) { + myfprintf(fp,"%s need integer >= -1\n",COMMAND_RESPOND_ERROR); + return -1; + } + if(!playlist.length) { + myfprintf(fp,"%s playlist is empty\n",COMMAND_RESPOND_ERROR); + return -1; + } + else if(song>=playlist.length) { + myfprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR); + return -1; + } + + if(playlist.random) for(i=0;song!=playlist.order[i];i++); + + clearPlayerError(); + playlist_stopOnError = 1; + playlist_errorCount = 0; + + if(playlist_state == PLAYLIST_STATE_PLAY) { + if(playlist.queued>=0) { + lockPlaylistInteraction(); + clearPlayerQueue(); + unlockPlaylistInteraction(); + } + } + else if(playPlaylistOrderNumber(fp,i)<0) return -1; + + if(playlist.current!=i) if(playPlaylistOrderNumber(fp,i)<0) return -1; + return playerSeek(fp,playlist.songs[playlist.order[i]]->utf8file,time); +} |