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