3 * (c) 2005-2009 by Avuton Olrich <avuton@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include <sys/types.h>
30 #include "libmpdclient.h"
34 #define MPD_OUTPUT_STATE_DISABLED 0
35 #define LINELENGTH FILENAME_MAX+50 /* 50 to give adequate amount for command and args */
36 #define NEXTWORD(word) { word = strtok(NULL,":"); while(isspace(word[0])) { word++; } }
37 #define NEXTINT(ret,word) { NEXTWORD(word); ret = atoi(word); }
39 static struct Config
{
48 static void readandexit(char *filename
, char *dirname
)
52 access(dirname
, F_OK
);
53 check_perror(errno
, "Cannot read state file directory");
55 access(dirname
, X_OK
);
56 check_perror(errno
, "Cannot read state file directory");
58 access(dirname
, R_OK
);
59 check_perror(errno
, "Cannot read state file directory");
61 access(filename
, F_OK
);
62 check_perror(errno
, "Cannot read state file");
65 static void restore_playlist(mpd_Connection
* conn
,
67 char *word
, char buffer
[LINELENGTH
], int songnum
)
73 while (fgets(buffer
, LINELENGTH
, fp
) &&
74 strcmp(word
, "playlist_end\n") != 0 && strchr(buffer
, ':')) {
75 if (config
.nplaylist
== 1 && songnum
!= curnum
) {
80 * strtok_r would be called here, but this should
81 * be a bit more efficent since we discard the number.
83 string
= strchr(buffer
, ':');
86 string
[strlen(string
) - 1] = '\0';
87 /* number = atoi(buffer); */
88 mpd_sendAddCommand(conn
, string
);
93 static void restore_outputs(mpd_Connection
* conn
,
94 FILE * fp
, char *word
, char buffer
[LINELENGTH
])
96 int number
, namlen
, stat
;
99 while (fgets(buffer
, LINELENGTH
, fp
) &&
100 strcmp(word
, "outputs_end\n") != 0 && strchr(buffer
, ':')) {
101 number
= atoi(buffer
);
103 status
= strchr(buffer
, ':');
108 name
= strchr(status
, ':');
112 namlen
= (strlen(name
) - 1);
113 if (name
[namlen
] == '\n') {
117 if (stat
== MPD_OUTPUT_STATE_DISABLED
) {
118 mpd_sendDisableOutputCommand(conn
, number
);
120 mpd_sendEnableOutputCommand(conn
, number
);
125 static void handle_state_contents(mpd_Connection
* conn
, FILE * fp
)
127 /* conv is a temporary converting variable */
131 * We need these stored, incase the state
135 int seconds
= MPD_PLAY_AT_BEGINNING
;
139 char buffer
[LINELENGTH
];
142 while (fgets(buffer
, LINELENGTH
, fp
) && buffer
!= NULL
) {
143 word
= strtok(buffer
, ":");
145 /* Special cases first */
146 /* notice the strcmp ! ==0 */
147 if (config
.amend
== 1 && strcmp(word
, "playlist_begin\n")) {
149 } else if (config
.noutput
== 1 &&
150 strcmp(word
, "outputs_begin\n") == 0) {
152 } else if (strcmp(word
, "state") == 0) {
154 if (strncmp(word
, "play", strlen("play")) == 0) {
155 state
= MPD_STATUS_STATE_PLAY
;
156 } else if (strncmp(word
, "pause", strlen("pause")) == 0) {
157 state
= MPD_STATUS_STATE_PAUSE
;
158 } else if (strncmp(word
, "stop", strlen("stop")) == 0) {
159 state
= MPD_STATUS_STATE_STOP
;
161 puts("Invalid state\n");
164 } else if (strcmp(word
, "current") == 0) {
165 NEXTINT(songnum
, word
);
166 } else if (strcmp(word
, "volume") == 0) {
167 /* Setvol to 0, try to reduce the seeking sounds */
168 mpd_sendSetvolCommand(conn
, volume
);
170 NEXTINT(volume
, word
);
171 } else if (strcmp(word
, "time") == 0) {
172 NEXTINT(seconds
, word
);
173 } else if (strcmp(word
, "random") == 0) {
175 mpd_sendRandomCommand(conn
, conv
);
176 } else if (strcmp(word
, "repeat") == 0) {
178 mpd_sendRepeatCommand(conn
, conv
);
179 } else if (strcmp(word
, "single") == 0) {
181 mpd_sendSingleCommand(conn
, conv
);
182 } else if (strcmp(word
, "consume") == 0) {
184 mpd_sendConsumeCommand(conn
, conv
);
185 } else if (strcmp(word
, "crossfade") == 0) {
187 mpd_sendCrossfadeCommand(conn
, conv
);
188 } else if (strcmp(word
, "playlist_begin\n") == 0) {
189 if (config
.amend
== 0) {
190 mpd_sendClearCommand(conn
);
192 restore_playlist(conn
, fp
, word
, buffer
, songnum
);
193 } else if (strcmp(word
, "outputs_begin\n") == 0) {
194 restore_outputs(conn
, fp
, word
, buffer
);
196 word
= strtok(NULL
, ":");
199 if (config
.nplaylist
== 1) {
200 mpd_sendSeekCommand(conn
, 0, seconds
);
201 } else if (state
!= MPD_STATUS_STATE_STOP
&& config
.amend
== 0) {
202 mpd_sendSeekCommand(conn
, songnum
, seconds
);
203 if (state
== MPD_STATUS_STATE_PAUSE
) {
204 mpd_sendPauseCommand(conn
, 1);
209 mpd_sendSetvolCommand(conn
, volume
);
213 __attribute__ ((noreturn
))
214 static void print_usage(int exitnum
)
216 printf("Usage: state-restore [OPTION] [STATE NAME]");
217 printf("\nRestore a MPD state.");
218 printf("\nExample: state-restore -a my_cool_state");
221 ("\n -a, --amend\t\tAmend the playlist to the current MPD playlist, ignoring the actual state.");
222 printf("\n -o, --no-outputs\tOmit the outputs when restoring");
223 printf("\n -p, --no-playlist\tOnly restore the playing song");
225 ("\n -w, --wait=TIMEOUT\tWait until the music player has stopped to restore the playlist. Check MPD every TIMEOUT seconds.");
226 printf("\n\nReport bugs to Avuton Olrich <avuton@gmail.com>");
227 printf("\nSee 'man 1 state-restore' for more information\n");
231 static FILE *get_file(void)
233 char filename
[FILENAME_MAX
];
234 char dirname
[FILENAME_MAX
];
237 sprintf(dirname
, "%s/.mpd_states", get_home_dir());
239 if (!(config
.extopts
)) {
240 sprintf(filename
, "%s/default", dirname
);
242 sprintf(filename
, "%s/%s", dirname
, config
.extopts
);
245 fp
= fopen(filename
, "r");
247 readandexit(filename
, dirname
);
252 static void parse_args(int argc
, char **argv
)
255 static struct option long_options
[] = {
256 {"amend", 0, 0, 'a'},
258 {"no-outputs", 0, 0, 'o'},
259 {"no-playlist", 0, 0, 'p'},
265 int option_index
= 0;
267 c
= getopt_long(argc
, argv
, "ahopw:",
268 long_options
, &option_index
);
282 config
.nplaylist
= 1;
286 config
.wait
= atoi(optarg
);
288 if (config
.wait
< 1) {
289 puts("Wait time must be longer than 0");
293 puts("Wait time must be a digit between 1 and 65535");
297 * This really shouldn't be possible, but let's not take the chance
298 * this should be handled by getopts
301 puts("Unknown Error!");
315 if (config
.amend
&& config
.nplaylist
) {
316 puts("Incompatible options");
321 if ((argc
- optind
) == 1) {
322 config
.extopts
= argv
[optind
++];
323 } else if ((argc
- optind
) > 1) {
324 puts("Too many arguments, only restore one state at a time.");
330 static void handle_wait(mpd_Connection
* conn
)
336 conn
= setup_connection(NULL
, NULL
);
338 mpd_sendStatusCommand(conn
);
339 status
= mpd_getStatus(conn
);
340 if (status
->state
== MPD_STATUS_STATE_STOP
) {
343 printErrorAndExit(conn
);
344 mpd_finishCommand(conn
);
346 if (config
.wait
== 0) {
351 mpd_freeStatus(status
);
354 int main(int argc
, char **argv
)
357 mpd_Connection
*conn
;
359 parse_args(argc
, argv
);
363 conn
= setup_connection(NULL
, NULL
);
364 printErrorAndExit(conn
);
366 if (config
.wait
> 0) {
370 mpd_sendCommandListBegin(conn
);
371 handle_state_contents(conn
, fp
);
372 mpd_sendCommandListEnd(conn
);
373 printErrorAndExit(conn
);
374 mpd_finishCommand(conn
);
375 mpd_closeConnection(conn
);