1 /*
2 * Copyright (C) 2009-2015 Christian Heckendorf <heckendorfc@gmail.com>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "player.h"
19 #include "insert.h"
20 #include "defs.h"
21 #include "dbact.h"
22 #include "util.h"
23 #include "admin.h"
24 #include "sndutil.h"
25 #include "plugins/plugin.h"
26
27 pthread_mutex_t actkey;
28
29 struct play_song_args{
30 struct playerHandles *ph;
31 struct playercontrolarg *pca;
32 int songid;
33 int totaltime;
34 int filetype;
35 char location[250];
36 struct pluginitem *pi_ptr;
37 };
38
initList(int list,char * query)39 static int initList(int list, char *query){
40 // If we shuffled, TempPlaylistSong will be a temp table. list will be 0.
41 if(list){ /* Should be unused */
42 sprintf(query,"CREATE TEMP VIEW IF NOT EXISTS TempPlaylistSong AS SELECT PlaylistSongID, SongID, \"Order\" FROM PlaylistSong WHERE PlaylistID=%d ORDER BY \"Order\"",list);
43 harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
44 sprintf(query,"SELECT Song.SongID, \"Order\" FROM Song, PlaylistSong WHERE Song.SongID=PlaylistSong.SongID AND PlaylistSong.PlaylistID=%d ORDER BY \"Order\"",list);
45 }
46 else{
47 sprintf(query,"SELECT Song.SongID,\"Order\" FROM Song, TempPlaylistSong WHERE Song.SongID=TempPlaylistSong.SongID ORDER BY \"Order\" LIMIT 1");
48 }
49 return 0;
50 }
51
play_handle_key(char * key)52 static int play_handle_key(char *key){
53 if(*key==KEY_QUIT)
54 return 1;
55 pthread_mutex_lock(&actkey);
56 *key=KEY_NULL;
57 pthread_mutex_unlock(&actkey);
58
59 return 0;
60 }
61
play_handle_SongPubInfo(void * data,int col_count,char ** row,char ** titles)62 static int play_handle_SongPubInfo(void *data, int col_count, char **row, char **titles){
63 struct play_song_args *psargs=(struct play_song_args *)data;
64
65 pthread_mutex_lock(&outstatus);
66 printSongPubInfo(row);
67 pthread_mutex_unlock(&outstatus);
68
69 psargs->totaltime=(int)strtol(row[5],NULL,10);
70 psargs->ph->pflag->rating=(int)strtol(row[6],NULL,10);
71 psargs->filetype=(int)strtol(row[4],NULL,10);
72 sprintf(psargs->location,"%s",row[1]);
73
74 return 0;
75 }
76
play_handle_plugin(struct play_song_args * psargs)77 static int play_handle_plugin(struct play_song_args *psargs){
78 int ret=-1;
79
80 //findPluginIDByType(0); // Reset
81 //while((psargs->pi_ptr=findPluginByID(psargs->ph->plugin_head,findPluginIDByType(psargs->filetype)))){
82 while(psargs->filetype<PLUGIN_NULL && (psargs->pi_ptr=plugin_head[psargs->filetype])){
83 if((psargs->ph->ffd=psargs->pi_ptr->modopen(psargs->location,"rb"))!=NULL){
84 psargs->pca->decoder=psargs->pi_ptr;
85 ret=psargs->pi_ptr->modplay(psargs->ph,psargs->pca->key,&psargs->totaltime);
86 psargs->pi_ptr->modclose(psargs->ph->ffd);
87 psargs->pca->decoder=NULL;
88 break;
89 }
90 else{
91 fprintf(stderr,"Failed to open file\n");
92 return 0;
93 }
94 }
95 //if(psargs->filetype>=PLUGIN_NULL || !psargs->pi_ptr)
96 //ret=-1;
97
98 return ret;
99 }
100
play_update_stats(struct play_song_args * psargs,int ret)101 static void play_update_stats(struct play_song_args *psargs, int ret){
102 char query[300];
103
104 if(ret!=DEC_RET_ERROR){ // Update stats
105 switch(ret){
106 case DEC_RET_SUCCESS: // Normal play.
107 if(psargs->totaltime>0)
108 sprintf(query,"UPDATE Song SET PlayCount=PlayCount+1, LastPlay=%d, Length=%d WHERE SongID=%d",(int)time(NULL),psargs->totaltime,psargs->songid);
109 else
110 sprintf(query,"UPDATE Song SET PlayCount=PlayCount+1, LastPlay=%d, WHERE SongID=%d",(int)time(NULL),psargs->songid);
111 break;
112 case DEC_RET_NEXT: // Next key
113 sprintf(query,"UPDATE Song SET SkipCount=SkipCount+1, LastPlay=%d WHERE SongID=%d",(int)time(NULL),psargs->songid);
114 break;
115 case DEC_RET_NEXT_NOUP: // Next key without update[playcount]
116 default:
117 sprintf(query,"UPDATE Song SET LastPlay=%d WHERE SongID=%d",(int)time(NULL),psargs->songid);
118 break;
119 }
120 debug(3,query);
121 harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
122 }
123 sprintf(query,"UPDATE Song SET Rating=%d WHERE SongID=%d",psargs->ph->pflag->rating,psargs->songid);
124 harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
125 }
126
play_next_song_query(char * query,struct playercontrolarg * pca)127 void play_next_song_query(char *query, struct playercontrolarg *pca){
128 sprintf(query,"SELECT Song.SongID,\"Order\" FROM Song NATURAL JOIN TempPlaylistSong WHERE \"Order\">=%d ORDER BY \"Order\" LIMIT 1",pca->next_order);
129 }
130
play_song(void * data,int col_count,char ** row,char ** titles)131 int play_song(void *data, int col_count, char **row, char **titles){
132 struct play_song_args *psargs=(struct play_song_args *)data;
133 char query[200];
134
135 psargs->songid=(int)strtol(row[0],NULL,10);
136 psargs->pca->next_order=psargs->pca->cur_order=strtol(row[1],NULL,10);
137 psargs->pca->next_order++;
138
139 sprintf(query,"SELECT * FROM SongPubInfo WHERE SongID=%d LIMIT 1",psargs->songid);
140 if(harp_sqlite3_exec(conn,query,play_handle_SongPubInfo,psargs,NULL)!=SQLITE_OK)
141 return 0;
142
143 return 1;
144 }
145
setPause(int pause,struct playerflag * pflag)146 static void setPause(int pause,struct playerflag *pflag){
147 pflag->pause=pause;
148 pflag->pausec=pause?'P':32;
149 }
150
printStatus(struct playerstatusarg * psa)151 static void printStatus(struct playerstatusarg *psa){
152 fprintf(stdout,"\r [%c %c][%ds of %ds (%d%%)]%s", psa->pflag->pausec, psa->pflag->mutec, psa->outdetail->curtime, psa->outdetail->totaltime, psa->outdetail->percent<-1?-1:psa->outdetail->percent,psa->outdetail->tail);
153 fflush(stdout);
154 }
155
playerStatus(void * arg)156 static void playerStatus(void *arg){
157 struct playerstatusarg *psa=(struct playerstatusarg*)arg;
158 while(1){
159 if(psa->pflag->update){
160 printStatus(psa);
161 }
162 usleep(STATUS_REFRESH_INTERVAL);
163 }
164 }
165
playerControl(void * arg)166 static void playerControl(void *arg){
167 char temp;
168 struct playercontrolarg *pca=(struct playercontrolarg*)arg;
169
170 // Set new term settings
171 struct termios orig,new;
172 new=pca->orig;
173 new.c_lflag&=(~ICANON);
174 new.c_lflag&=(~ECHO);
175 new.c_cc[VTIME]=0;
176 new.c_cc[VMIN]=1;
177 tcsetattr(0,TCSANOW,&new);
178
179 while(1){
180 temp=fgetc(stdin);
181 if(getSystemKey(temp,pca))
182 continue;
183 pthread_mutex_lock(&actkey);
184 *pca->key=temp;
185 pthread_mutex_unlock(&actkey);
186 if(temp==KEY_QUIT){
187 debug(2,"playerControl found quit");
188 break;
189 }
190 }
191 debug(2,"playerControl quitting");
192
193 // Reset term settings
194 tcsetattr(0,TCSANOW,&orig);
195
196 pthread_exit((void *) 0);
197 }
198
player(int list)199 int player(int list){//list - playlist number
200 unsigned int max_list_order;
201 int oldupdate;
202 int ret;
203 char *query; // Why aren't we using []?
204
205 // Create playerControl thread
206 char key=KEY_NULL;
207 struct play_song_args psargs;
208 struct playercontrolarg pca;
209 struct playerstatusarg psa;
210 struct playerHandles ph;
211 struct playerflag pflag={0,0,1,0,DEC_RET_SUCCESS,32,32};
212 struct outputdetail outdetail;
213 bzero(&outdetail,sizeof(outdetail));
214
215 if(!(query=malloc(sizeof(char)*320))){
216 debug(2,"Malloc failed (player query).");
217 return 1;
218 }
219 else{
220 char small_query[128];
221
222 if(initList(list,query))
223 return 0;
224
225 sprintf(small_query,"SELECT MAX(\"Order\") FROM TempPlaylistSong");
226 harp_sqlite3_exec(conn,small_query,uint_return_cb,&max_list_order,NULL);
227
228 if(!(ph.plugin_head=plugin_head)){
229 //fprintf(stderr,"No plugins found. Please add them with harp -a\n");
230 fprintf(stderr,"No plugins found.\n");
231 return 0;
232 }
233 }
234
235
236 ph.ffd=NULL;
237 ph.device=arglist[ADEVICE].subarg;
238 ph.dechandle=NULL;
239 psa.pflag=ph.pflag=&pflag;
240 psa.outdetail=ph.outdetail=&outdetail;
241 pca.ph=&ph;
242 tcgetattr(0,&pca.orig);
243 pca.key=&key;
244 psargs.ph=&ph;
245 psargs.pca=&pca;
246 oldupdate=1;
247
248 pthread_t control_thread,status_thread;
249 pthread_mutex_init(&actkey,NULL);
250 pthread_mutex_init(&outstatus,NULL);
251 pthread_create(&control_thread,NULL,(void *)&playerControl,(void*)&pca);
252 pthread_create(&status_thread,NULL,(void *)&playerStatus,(void*)&psa);
253
254 // Play the list!
255 if(snd_init(&ph)){
256 fprintf(stderr,"\nsnd_init failed\n");
257 free(query);
258 return 1;
259 }
260
261 // Run the song playing query. Loop for jumping.
262 while(harp_sqlite3_exec(conn,query,play_song,&psargs,NULL)==SQLITE_ABORT){
263 bzero(&outdetail,sizeof(outdetail));
264 psargs.ph->pflag->update=oldupdate;
265 if((ret=play_handle_plugin(&psargs))<1){
266 if(ret<0) /* No plugins */
267 break;
268 else{ /* Error opening file */
269 if(play_handle_key(psargs.pca->key))
270 break;
271 play_next_song_query(query,&pca);
272 continue;
273 }
274 }
275 oldupdate=psargs.ph->pflag->update;
276 psargs.ph->pflag->update=0;
277 printf("\n");
278
279 psargs.ph->ffd=NULL;
280 psargs.ph->dechandle=NULL;
281 psargs.ph->pflag->exit=DEC_RET_SUCCESS;
282
283 play_update_stats(&psargs,ret);
284
285 if(play_handle_key(psargs.pca->key))
286 break;
287
288 if(arglist[AREPEAT].active>0 &&
289 pca.next_order==pca.cur_order+1 && // Not jumping back
290 pca.next_order>max_list_order){
291 pca.next_order=1;
292 if(arglist[AREPEAT].subarg!=NULL)
293 arglist[AREPEAT].active--;
294 }
295
296 play_next_song_query(query,&pca);
297 }
298
299 snd_close(&ph);
300 pthread_cancel(status_thread);
301 pthread_cancel(control_thread);
302 // Reset term settings
303 tcsetattr(0,TCSANOW,&pca.orig);
304 pthread_mutex_destroy(&outstatus);
305 pthread_mutex_destroy(&actkey);
306 printf("\rExiting.\n");
307 free(query);
308 return 0;
309 }
310
writelist_file(char * com,struct playercontrolarg * pca)311 static void writelist_file(char *com, struct playercontrolarg *pca){
312 int y,limit;
313 unsigned int order;
314 FILE *ffd;
315 struct dbitem dbi;
316 char query[200],filename[30];
317 dbiInit(&dbi);
318
319 for(y=1;y<ADV_COM_ARG_LEN && com[y] && (com[y]<'0' || com[y]>'9');y++);
320 limit=(int)strtol(&com[y],NULL,10);
321 if(limit<=0)limit=50;
322 switch(com[1]){
323 case 'h':
324 sprintf(query,"SELECT \"Order\" AS \"#\",SongID,Title,Location,Rating,PlayCount,SkipCount,LastPlay FROM Song NATURAL JOIN TempPlaylistSong ORDER BY \"Order\" LIMIT %d",limit);
325 break;
326 case 't':
327 harp_sqlite3_exec(conn,query,uint_return_cb,&order,NULL);
328 sprintf(query,"SELECT \"Order\" AS \"#\",SongID,Title,Location,Rating,PlayCount,SkipCount,LastPlay FROM Song NATURAL JOIN TempPlaylistSong ORDER BY \"Order\" LIMIT %d",limit);
329 break;
330 case 'r':
331 default:
332 harp_sqlite3_exec(conn,query,uint_return_cb,&order,NULL);
333 sprintf(query,"SELECT \"Order\" AS \"#\",SongID,Title,Location,Rating,PlayCount,SkipCount,LastPlay FROM Song NATURAL JOIN TempPlaylistSong ORDER BY \"Order\" LIMIT %d",limit);
334 break;
335 }
336 sprintf(filename,"harp_stats_%d.csv",(int)time(NULL));
337 if((ffd=fopen(filename,"w"))==NULL){
338 fprintf(stderr,"Failed to open file\n");
339 return;
340 }
341 fputs("ORDER\tID\tTITLE\tLOCATION\tRATING\tPLAYCOUNT\tSKIPCOUNT\tLASTPLAY\n",ffd);
342 harp_sqlite3_exec(conn,query,write_stats_cb,ffd,NULL);
343 fclose(ffd);
344 }
345
writelist_db(char * com,struct playercontrolarg * pca)346 static void writelist_db(char *com, struct playercontrolarg *pca){
347 char query[200];
348 unsigned int pid=0;
349
350 sprintf(query,"SELECT PlaylistID FROM Playlist WHERE Title='"SAVED_PLAYLIST_NAME"' LIMIT 1");
351 harp_sqlite3_exec(conn,query,uint_return_cb,&pid,NULL);
352
353 if(!pid){
354 sprintf(query,SAVED_PLAYLIST_NAME);
355 pid=getPlaylist(query);
356 }
357
358 sprintf(query,"DELETE FROM PlaylistSong WHERE PlaylistID=%d",pid);
359 harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
360 sprintf(query,"INSERT INTO PlaylistSong (PlaylistID,SongID,\"Order\") SELECT %d,SongID,\"Order\" FROM TempPlaylistSong",pid);
361 harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
362 }
363
writelist(char * com,struct playercontrolarg * pca)364 static void writelist(char *com, struct playercontrolarg *pca){
365 if(com[1]=='f')
366 writelist_file(com,pca);
367 else
368 writelist_db(com,pca);
369 }
370
advseek(char * com,struct playercontrolarg * pca)371 static void advseek(char *com, struct playercontrolarg *pca){
372 int y;
373 for(y=1;y<ADV_COM_ARG_LEN && com[y] && (com[y]<'0' || com[y]>'9');y++);
374 int time=(int)strtol(&com[y],NULL,10);
375 if(time<=0)return;
376
377 if(com[0]==KEY_SEEK_DN)
378 time*=-1;
379
380 if(!pca->decoder)return;
381 setPause(0,pca->ph->pflag);
382 pca->decoder->modseek(pca->ph,time);
383 }
384
jump(char * com,struct playercontrolarg * pca)385 static void jump(char *com, struct playercontrolarg *pca){
386 char query[200];
387 int y,dest,max_dest;
388
389 sprintf(query,"SELECT MAX(\"Order\") FROM TempPlaylistSong");
390 harp_sqlite3_exec(conn,query,uint_return_cb,&max_dest,NULL);
391
392 for(y=1;y<ADV_COM_ARG_LEN && com[y] && (com[y]<'0' || com[y]>'9');y++);
393 if((dest=(int)strtol(&com[y],NULL,10))<=0)return;
394
395 if(com[1]=='s'){ // Jump by SongID. Find the Order first,
396 sprintf(query,"SELECT \"Order\" FROM TempPlaylistSong WHERE SongID=%d",dest);
397 debug(3,query);
398 dest=0;
399 harp_sqlite3_exec(conn,query,uint_return_cb,&dest,NULL);
400 }
401 if(dest<=0 || dest>max_dest)return;
402
403 pca->next_order=dest;
404
405 pthread_mutex_lock(&actkey);
406 *pca->key=(*com=='j')?KEY_NEXT:KEY_NEXT_NOUP;
407 pthread_mutex_unlock(&actkey);
408 pca->ph->pflag->exit=*pca->key;
409 setPause(0,pca->ph->pflag);
410 }
411
listtemp(char * com,struct playercontrolarg * pca)412 static void listtemp(char *com, struct playercontrolarg *pca){
413 char query[200];
414 int y,limit,exception[10];
415 unsigned int order;
416 for(y=2;y<10;y++)exception[y]=listconf.exception;
417 exception[0]=exception[1]=1;
418
419 for(y=1;y<ADV_COM_ARG_LEN && com[y] && (com[y]<'0' || com[y]>'9');y++);
420 limit=(int)strtol(&com[y],NULL,10);
421 if(limit<=0)limit=30;
422 switch(com[1]){
423 case 'h': // Head
424 sprintf(query,"SELECT \"Order\" AS \"#\",SongID,SongTitle,AlbumTitle,ArtistName FROM TempPlaylistSong NATURAL JOIN SongPubInfo ORDER BY \"Order\" LIMIT %d",limit);
425 break;
426 case 't': // Tail
427 harp_sqlite3_exec(conn,"SELECT MAX(\"Order\") FROM TempPlaylistSong",uint_return_cb,&order,NULL);
428 sprintf(query,"SELECT \"Order\" AS \"#\",SongID,SongTitle,AlbumTitle,ArtistName FROM TempPlaylistSong NATURAL JOIN SongPubInfo WHERE \"Order\">%d ORDER BY \"Order\" LIMIT %d",order-limit,limit);
429 break;
430 case 'r': // Relative
431 default:
432 sprintf(query,"SELECT \"Order\" AS \"#\",SongID,SongTitle,AlbumTitle,ArtistName FROM TempPlaylistSong NATURAL JOIN SongPubInfo WHERE \"Order\">=%d ORDER BY \"Order\" LIMIT %d",pca->cur_order,limit);
433 break;
434 }
435 debug(3,query);
436 doTitleQuery(query,exception,listconf.maxwidth);
437 }
438
439
remitem_order_shift_cb(void * arg,int col_count,char ** row,char ** titles)440 static int remitem_order_shift_cb(void *arg, int col_count, char **row, char **titles){
441 struct playercontrolarg *pca=(struct playercontrolarg *)arg;
442 char query[150];
443 int order=(int)strtol(*row,NULL,10);
444 if(order>0){
445 if(pca->cur_order>=order)pca->cur_order--;
446 if(pca->next_order>=order)pca->next_order--;
447 }
448 sprintf(query,"UPDATE TempPlaylistSong SET \"Order\"=\"Order\"-1 WHERE \"Order\">=%s",*row);
449 debug(3,query);
450 harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
451 return 0;
452 }
453
remitem(char * com,struct playercontrolarg * pca)454 static void remitem(char *com, struct playercontrolarg *pca){
455 char query[250];
456 int x,order=0;
457 struct IDList id_struct;
458
459 if(com[1]=='o'){
460 for(x=2;x<ADV_COM_ARG_LEN && com[x] && (com[x]<'0' || com[x]>'9');x++);
461 if((com[x]<'0' && com[x]>'9') || (order=(int)strtol(com+x,NULL,10))<1){
462 fprintf(stderr,"Improper command format\n");
463 return;
464 }
465 sprintf(query,"DELETE FROM TempPlaylistSong WHERE \"Order\"=%d",order);
466 harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
467 printf("Removed %d songs.\n",sqlite3_changes(conn));
468 sprintf(query,"UPDATE TempPlaylistSong SET \"Order\"=\"Order\"-1 WHERE \"Order\">%d",order);
469 harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
470 if(pca->cur_order>order)pca->cur_order--;
471 if(pca->next_order>order)pca->next_order--;
472 }
473 else{
474 for(x=2;x<ADV_COM_ARG_LEN && com[x] && com[x]==' ';x++);
475 if(getGroupSongIDs(com+x,ADV_COM_ARG_LEN,&id_struct)==HARP_RET_ERR)
476 return;
477
478 sprintf(query,"SELECT DISTINCT \"Order\",SongID FROM TempPlaylistSong WHERE SongID IN (SELECT SelectID FROM TempSelect WHERE TempID=%d) ORDER BY \"Order\" DESC",id_struct.tempselectid);
479 harp_sqlite3_exec(conn,query,remitem_order_shift_cb,pca,NULL);
480
481 sprintf(query,"DELETE FROM TempPlaylistSong WHERE SongID IN (SELECT SelectID FROM TempSelect WHERE TempID=%d)",id_struct.tempselectid);
482 harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
483
484 printf("Removed %d songs.\n",id_struct.length);
485 cleanTempSelect(id_struct.tempselectid);
486 free(id_struct.songid);
487 }
488 }
489
additem(char * com,struct playercontrolarg * pca)490 static void additem(char *com, struct playercontrolarg *pca){
491 struct IDList id_struct;
492 int x=1;
493 int dest=0;
494 char query[150],cb_query[150];
495 struct insert_tps_arg data;
496
497 if(com[1]=='o'){
498 for(x=2;x<ADV_COM_ARG_LEN && com[x] && com[x]==' ';x++);
499 if((com[x]<'0' && com[x]>'9') || (dest=(int)strtol(com+x,NULL,10))<1){
500 fprintf(stderr,"Improper command format\n");
501 return;
502 }
503 for(;x<ADV_COM_ARG_LEN && com[x] && com[x]>='0' && com[x]<='9';x++);
504 }
505
506 for(;x<ADV_COM_ARG_LEN && com[x] && com[x]==' ';x++);
507
508 if(getGroupSongIDs(com+x,ADV_COM_ARG_LEN,&id_struct)==HARP_RET_ERR)
509 return;
510
511 if(dest<1)
512 dest=pca->next_order;
513
514 sprintf(query,"UPDATE TempPlaylistSong SET \"Order\"=\"Order\"+%d WHERE \"Order\">=%d",id_struct.length,dest);
515 printf("Added %d songs starting at order %d.\n",id_struct.length,dest);
516 harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
517 if(pca->cur_order>=dest)pca->cur_order+=id_struct.length;
518 if(pca->next_order>dest)pca->next_order+=id_struct.length;
519
520 data.query=cb_query;
521 data.order=dest;
522 data.count=0;
523 sprintf(query,"SELECT SelectID FROM TempSelect WHERE TempID=%d",id_struct.tempselectid);
524 harp_sqlite3_exec(conn,query,batch_tempplaylistsong_insert_cb,&data,NULL);
525
526 cleanTempSelect(id_struct.tempselectid);
527 free(id_struct.songid);
528 }
529
530 struct adv_list_item{
531 char key;
532 void (*func)(char *, struct playercontrolarg *);
533 }adv_list[]={
534 {'l',listtemp},// List the playlist
535 {'w',writelist},// Write out information about the playlist
536 {'j',jump},// Jump to a different song
537 {'J',jump},// Jump to a different song
538 {KEY_SEEK_DN,advseek},// Seek with specified value
539 {KEY_SEEK_UP,advseek},// Seek with specified value
540 {'a',additem},
541 {'r',remitem},
542 {0,NULL}
543 };
544
getCommand(struct playercontrolarg * pca)545 static void getCommand(struct playercontrolarg *pca){
546 struct adv_list_item *list=adv_list;
547 struct termios old;
548 char *ptr,com[ADV_COM_ARG_LEN];
549 int x,oldupdate=pca->ph->pflag->update;
550 struct playerstatusarg psa={pca->ph->pflag,pca->ph->outdetail};
551
552 tcsetattr(0,TCSANOW,&pca->orig);
553 bzero(com,ADV_COM_ARG_LEN);
554 ptr=com;
555 psa.pflag->update=0;
556 pthread_mutex_lock(&outstatus);
557 for(x=0;psa.outdetail->tail[x];x++)psa.outdetail->tail[x]=' ';
558 pthread_mutex_unlock(&outstatus);
559 printStatus(&psa);
560 pthread_mutex_lock(&outstatus);
561 sprintf(psa.outdetail->tail,":");
562 pthread_mutex_unlock(&outstatus);
563 printStatus(&psa);
564
565 if(fgets(com,ADV_COM_ARG_LEN,stdin)){
566 int x;
567
568 for(x=0;x<ADV_COM_ARG_LEN && com[x]!='\n' && com[x];x++);
569 if(x<ADV_COM_ARG_LEN)com[x]=0;
570
571 while(list->key){
572 if(*com==list->key)
573 list->func(ptr,pca);
574 list++;
575 }
576 }
577 tcgetattr(0,&old);
578 old.c_lflag&=(~ICANON);
579 old.c_lflag&=(~ECHO);
580 old.c_cc[VTIME]=0;
581 old.c_cc[VMIN]=1;
582 tcsetattr(0,TCSANOW,&old);
583 pthread_mutex_lock(&outstatus);
584 sprintf(psa.outdetail->tail,"\r");
585 pthread_mutex_unlock(&outstatus);
586 pca->ph->pflag->update=oldupdate;
587 }
588
getSystemKey(char key,struct playercontrolarg * pca)589 int getSystemKey(char key, struct playercontrolarg *pca){
590 int oldrating;
591 char tail[OUTPUT_TAIL_SIZE];
592 switch(key){
593 case KEY_VOLUP:
594 changeVolume(pca->ph,5);
595 break;
596 case KEY_VOLDN:
597 changeVolume(pca->ph,-5);
598 break;
599 case KEY_MUTE:
600 toggleMute(pca->ph,&pca->ph->pflag->mute);
601 pca->ph->pflag->mutec=pca->ph->pflag->mutec==32?'M':32;
602 break;
603 case KEY_PAUSE:
604 setPause(!pca->ph->pflag->pause,pca->ph->pflag);
605 break;
606 case KEY_PREV:
607 if(!pca->decoder)break;
608 setPause(0,pca->ph->pflag);
609 pca->decoder->modseek(pca->ph,0);
610 break;
611 case KEY_SEEK_UP:
612 if(!pca->decoder)break;
613 setPause(0,pca->ph->pflag);
614 pca->decoder->modseek(pca->ph,20);
615 break;
616 case KEY_SEEK_DN:
617 if(!pca->decoder)break;
618 setPause(0,pca->ph->pflag);
619 pca->decoder->modseek(pca->ph,-20);
620 break;
621 case KEY_RATEUP:
622 oldrating=pca->ph->pflag->rating;
623 pca->ph->pflag->rating=pca->ph->pflag->rating==10?10:pca->ph->pflag->rating+1;
624 pthread_mutex_lock(&outstatus);
625 sprintf(tail,"Rating: %d/10 -> %d/10",oldrating,pca->ph->pflag->rating);
626 pthread_mutex_unlock(&outstatus);
627 addStatusTail(tail,pca->ph->outdetail);
628 break;
629 case KEY_RATEDN:
630 oldrating=pca->ph->pflag->rating;
631 pca->ph->pflag->rating=pca->ph->pflag->rating==0?0:pca->ph->pflag->rating-1;
632 pthread_mutex_lock(&outstatus);
633 sprintf(tail,"Rating: %d/10 -> %d/10",oldrating,pca->ph->pflag->rating);
634 pthread_mutex_unlock(&outstatus);
635 addStatusTail(tail,pca->ph->outdetail);
636 break;
637 case KEY_COMMAND:
638 getCommand(pca);
639 break;
640 case KEY_QUIT:
641 pca->ph->pflag->exit=DEC_RET_ERROR;
642 setPause(0,pca->ph->pflag);
643 return 0;
644 case KEY_NEXT:
645 pca->ph->pflag->exit=DEC_RET_NEXT;
646 setPause(0,pca->ph->pflag);
647 break;
648 case KEY_NEXT_NOUP:
649 pca->ph->pflag->exit=DEC_RET_NEXT_NOUP;
650 setPause(0,pca->ph->pflag);
651 default:break;
652 }
653 return 1;
654 }
655