1 /*
2  *  Copyright (C) 2009-2014  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 "edit.h"
19 #include "defs.h"
20 #include "dbact.h"
21 #include "util.h"
22 #include "portal.h"
23 #include "tree.h"
24 #include "insert.h"
25 #include "list.h"
26 
27 static void cleanOrphans();
28 
editSongName(char * args,void * data)29 static int editSongName(char *args, void *data){
30 	struct IDList *songids=(struct IDList *)data;
31 	if(!songids || !songids->songid){
32 		fprintf(stderr,"no data\n");
33 		return PORTAL_RET_PREV;
34 	}
35 	char query[200];
36 	int x;
37 
38 	if((x=getStdArgs(args,"Name: "))<0)return PORTAL_RET_PREV;
39 	args=&args[x];
40 
41 	if(songids->length>1 && !editWarn("This operation will alter multiple songs.")){
42 			printf("Aborted\n");
43 			return PORTAL_RET_PREV;
44 	}
45 
46 	for(x=0;x<songids->length;x++){
47 		sprintf(query,"UPDATE Song SET Title='%s' WHERE SongID=%d",args,songids->songid[x]);
48 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
49 	}
50 	return PORTAL_RET_PREV;
51 }
52 
editSongLocation(char * args,void * data)53 static int editSongLocation(char *args, void *data){
54 	struct IDList *songids=(struct IDList *)data;
55 	if(!songids || !songids->songid){
56 		fprintf(stderr,"no data\n");
57 		return PORTAL_RET_PREV;
58 	}
59 	char query[200];
60 	int x;
61 
62 	if((x=getStdArgs(args,"Location: "))<0)return PORTAL_RET_PREV;
63 	args=&args[x];
64 
65 	if(songids->length>1 && !editWarn("This operation will alter multiple songs.")){
66 			printf("Aborted\n");
67 			return PORTAL_RET_PREV;
68 	}
69 
70 	for(x=0;x<songids->length;x++){
71 		sprintf(query,"UPDATE Song SET Location='%s' WHERE SongID=%d",args,songids->songid[x]);
72 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
73 	}
74 	return PORTAL_RET_PREV;
75 }
76 
editSongArtist(char * args,void * data)77 static int editSongArtist(char *args, void *data){
78 	struct IDList *songids=(struct IDList *)data;
79 	if(!songids || !songids->songid){
80 		fprintf(stderr,"no data\n");
81 		return PORTAL_RET_PREV;
82 	}
83 	char query[200],*ptr;
84 	int x,albumid,artistid;
85 	struct dbitem dbi;
86 	dbiInit(&dbi);
87 
88 	if((x=getStdArgs(args,"Artist: "))<0)return PORTAL_RET_PREV;
89 	args=&args[x];
90 
91 	if((artistid=(int)strtol(args,NULL,10))<1)
92 		artistid=getArtist(args);
93 	sprintf(query,"SELECT Album.Title FROM Album,Song WHERE Album.AlbumID=Song.AlbumID AND SongID=%d",songids->songid[0]);
94 	debug(3,query);
95 	if(doQuery(query,&dbi)>0 && fetch_row(&dbi))
96 		albumid=getAlbum(dbi.row[0],artistid);
97 	else{
98 		debug(2,"getAlbum error in editSongArtist");
99 		dbiClean(&dbi);
100 		return PORTAL_RET_PREV;
101 	}
102 
103 	sprintf(query,"UPDATE Song SET AlbumID=%d WHERE SongID=",albumid);
104 	debug(3,query);
105 	for(x=0;query[x];x++);
106 	ptr=&query[x];
107 	for(x=0;x<songids->length;x++){
108 		sprintf(ptr,"%d",songids->songid[x]);
109 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
110 	}
111 	dbiClean(&dbi);
112 	return PORTAL_RET_PREV;
113 }
114 
editSongAlbum(char * args,void * data)115 static int editSongAlbum(char *args, void *data){
116 	struct IDList *songids=(struct IDList *)data;
117 	if(!songids || !songids->songid){
118 		fprintf(stderr,"no data\n");
119 		return PORTAL_RET_PREV;
120 	}
121 	char query[200],*ptr;
122 	int x,albumid,artistid;
123 	struct dbitem dbi;
124 	dbiInit(&dbi);
125 
126 	if((x=getStdArgs(args,"Album title: "))<0)return PORTAL_RET_PREV;
127 	args=&args[x];
128 
129 	sprintf(query,"SELECT ArtistID from Song NATURAL JOIN AlbumArtist WHERE SongID=%d",songids->songid[0]);
130 	debug(3,query);
131 	if(doQuery(query,&dbi)<1 || !fetch_row(&dbi) || (artistid=(int)strtol(dbi.row[0],NULL,10))<1){
132 		debug(2,"getArtist error in getAlbum");
133 		return PORTAL_RET_PREV;
134 	}
135 
136 	albumid=getAlbum(args,artistid);
137 
138 	sprintf(query,"UPDATE Song SET AlbumID=%d WHERE SongID=",albumid);
139 	debug(3,query);
140 	for(x=0;query[x];x++);
141 	ptr=&query[x];
142 	for(x=0;x<songids->length;x++){
143 		sprintf(ptr,"%d",songids->songid[x]);
144 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
145 	}
146 	dbiClean(&dbi);
147 	return PORTAL_RET_PREV;
148 }
149 
deleteSong(char * args,void * data)150 static int deleteSong(char *args, void *data){
151 	struct IDList *songids=(struct IDList *)data;
152 	if(!songids || !songids->songid){
153 		fprintf(stderr,"no data\n");
154 		return PORTAL_RET_PREV;
155 	}
156 	char query[100];
157 	int x;
158 	printf("Delete?: ");
159 	if(!fgets(args,PORTAL_ARG_LEN,stdin))return PORTAL_RET_PREV;
160 	if(*args=='y' || *args=='Y'){
161 		for(x=0;x<songids->length;x++){
162 			sprintf(query,"DELETE FROM Song WHERE SongID=%d",songids->songid[x]);
163 			harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
164 			sprintf(query,"DELETE FROM PlaylistSong WHERE SongID=%d",songids->songid[x]);
165 			harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
166 		}
167 		debug(1,"Songs deleted.");
168 		return PORTAL_RET_MAIN;
169 	}
170 	return PORTAL_RET_PREV;
171 }
172 
editSongTrack(char * args,void * data)173 static int editSongTrack(char *args, void *data){
174 	struct IDList *songids=(struct IDList *)data;
175 	if(!songids || !songids->songid){
176 		fprintf(stderr,"no data\n");
177 		return PORTAL_RET_PREV;
178 	}
179 	char query[100],*ptr;
180 	int x;
181 
182 	if((x=getStdArgs(args,"Track number: "))<0)return PORTAL_RET_PREV;
183 
184 	sprintf(query,"UPDATE Song SET Track=%d WHERE SongID=",(int)strtol(&args[x],NULL,10));
185 	for(x=0;query[x];x++);
186 	ptr=query+x;
187 
188 	for(x=0;x<songids->length;x++){
189 		sprintf(ptr,"%d",songids->songid[x]);
190 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
191 	}
192 	return PORTAL_RET_PREV;
193 }
194 
songActivation(char * args,void * data)195 static int songActivation(char *args, void *data){
196 	struct IDList *songids=(struct IDList *)data;
197 	if(!songids || !songids->songid){
198 		fprintf(stderr,"no data\n");
199 		return PORTAL_RET_PREV;
200 	}
201 	char query[100],*ptr;
202 	int x;
203 	for(x=1;x<PORTAL_ARG_LEN && args[x] && args[x]==' ';x++);
204 	if(args[x] && args[x]>='0' && args[x]<='9'){
205 		sprintf(query,"UPDATE Song SET Active=%d WHERE SongID=",(strtol(&args[x],NULL,10)>0?1:0));
206 		ptr=&query[38];
207 	}
208 	else{
209 		sprintf(query,"UPDATE Song SET Active=NOT(Active) WHERE SongID=");
210 		ptr=&query[48];
211 	}
212 	debug(3,query);
213 	for(x=0;x<songids->length;x++){
214 		sprintf(ptr,"%d",songids->songid[x]);
215 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
216 	}
217 	return PORTAL_RET_PREV;
218 }
219 
editAlbumArtist(char * args,void * data)220 static int editAlbumArtist(char *args, void *data){
221 	struct IDList *ids=(struct IDList *)data;
222 	if(!ids || !ids->songid){
223 		fprintf(stderr,"no data\n");
224 		return PORTAL_RET_PREV;
225 	}
226 	char query[200],*ptr;
227 	int x,artistid;
228 
229 	if((x=getStdArgs(args,"Artist: "))<0)return PORTAL_RET_PREV;
230 	args=&args[x];
231 
232 	artistid=getArtist(args);
233 	sprintf(query,"UPDATE AlbumArtist SET ArtistID=%d WHERE AlbumID=",artistid);
234 	for(x=0;query[x];x++);
235 	ptr=&query[x];
236 	debug(3,query);
237 	for(x=0;x<ids->length;x++){
238 		sprintf(ptr,"%d",ids->songid[x]);
239 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
240 	}
241 	cleanOrphans();
242 	return PORTAL_RET_PREV;
243 }
244 
editAlbumTitle(char * args,void * data)245 static int editAlbumTitle(char *args, void *data){
246 	struct IDList *ids=(struct IDList *)data;
247 	if(!ids || !ids->songid){
248 		fprintf(stderr,"no data\n");
249 		return PORTAL_RET_PREV;
250 	}
251 	char query[200];
252 	int x,albumid,artistid;
253 	struct dbitem dbi;
254 	dbiInit(&dbi);
255 
256 	if((x=getStdArgs(args,"Album title: "))<0)return PORTAL_RET_PREV;
257 	args=&args[x];
258 
259 	sprintf(query,"SELECT ArtistID from AlbumArtist WHERE AlbumID=%d",ids->songid[0]);
260 	debug(3,query);
261 	if(doQuery(query,&dbi)<1 || !fetch_row(&dbi) || (artistid=(int)strtol(dbi.row[0],NULL,10))<1){
262 		debug(2,"getArtist error in editAlbumTitle");
263 		return PORTAL_RET_PREV;
264 	}
265 
266 	albumid=getAlbum(args,artistid);
267 	for(x=0;x<ids->length;x++){
268 		sprintf(query,"UPDATE Song SET AlbumID=%d WHERE AlbumID=%d",albumid,ids->songid[x]);
269 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
270 		// The following queries will also be taken care of by cleanOrphans. Leave it for now.
271 		sprintf(query,"DELETE FROM AlbumArtist WHERE AlbumID=%d",ids->songid[x]);
272 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
273 		if(albumid!=ids->songid[x]){
274 			sprintf(query,"DELETE FROM Album WHERE AlbumID=%d",ids->songid[x]);
275 			harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
276 		}
277 		ids->songid[x]=albumid;
278 	}
279 	ids->length=1;
280 	dbiClean(&dbi);
281 	return PORTAL_RET_PREV;
282 }
283 
editAlbumDate(char * args,void * data)284 static int editAlbumDate(char *args, void *data){
285 	struct IDList *ids=(struct IDList *)data;
286 	if(!ids || !ids->songid){
287 		fprintf(stderr,"no data\n");
288 		return PORTAL_RET_PREV;
289 	}
290 	char query[200];
291 	int x,date;
292 
293 	if((x=getStdArgs(args,"Album date: "))<0)return PORTAL_RET_PREV;
294 	args=&args[x];
295 
296 	date=(int)strtol(args,NULL,10);
297 
298 	for(x=0;x<ids->length;x++){
299 		sprintf(query,"UPDATE Album SET \"Date\"=%d WHERE AlbumID=%d",date,ids->songid[x]);
300 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
301 	}
302 	return PORTAL_RET_PREV;
303 }
304 
editArtistName(char * args,void * data)305 static int editArtistName(char *args, void *data){
306 	struct IDList *ids=(struct IDList *)data;
307 	if(!ids || !ids->songid){
308 		fprintf(stderr,"no data\n");
309 		return PORTAL_RET_PREV;
310 	}
311 	char query[200];
312 	int x,artistid;
313 	struct dbitem dbi;
314 	dbiInit(&dbi);
315 
316 	if((x=getStdArgs(args,"Artist: "))<0)return PORTAL_RET_PREV;
317 	args=&args[x];
318 
319 	artistid=getArtist(args);
320 	for(x=0;x<ids->length;x++){
321 		sprintf(query,"UPDATE AlbumArtist SET ArtistID=%d WHERE ArtistID=%d",artistid,ids->songid[x]);
322 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
323 		if(ids->songid[x]!=artistid){
324 			sprintf(query,"DELETE FROM Artist WHERE ArtistID=%d",ids->songid[x]);
325 			harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
326 		}
327 		ids->songid[x]=artistid;
328 	}
329 	ids->length=1;
330 	dbiClean(&dbi);
331 	return PORTAL_RET_PREV;
332 }
333 
editPlaylistName(char * args,void * data)334 static int editPlaylistName(char *args, void *data){
335 	struct IDList *ids=(struct IDList *)data;
336 	if(!ids || !ids->songid){
337 		fprintf(stderr,"no data\n");
338 		return PORTAL_RET_PREV;
339 	}
340 	char query[200];
341 	int x;
342 
343 	if((x=getStdArgs(args,"Name: "))<0)return PORTAL_RET_PREV;
344 	args=&args[x];
345 
346 	if(ids->length>1 && !editWarn("This operation will alter only the first playlist in the list.")){
347 			printf("Aborted\n");
348 			return PORTAL_RET_PREV;
349 	}
350 
351 	sprintf(query,"UPDATE Playlist SET Title='%s' WHERE PlaylistID=%d",args,ids->songid[0]);
352 	harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
353 
354 	return PORTAL_RET_PREV;
355 }
356 
deletePlaylist(char * args,void * data)357 static int deletePlaylist(char *args, void *data){
358 	struct IDList *ids=(struct IDList *)data;
359 	if(!ids || !ids->songid){
360 		fprintf(stderr,"no data\n");
361 		return PORTAL_RET_PREV;
362 	}
363 	char query[200];
364 	int x;
365 
366 	printf("Delete playlist?: ");
367 	if(!fgets(args,PORTAL_ARG_LEN,stdin))return PORTAL_RET_PREV;
368 	if(*args=='y' || *args=='Y'){
369 		for(x=0;x<ids->length;x++){
370 			sprintf(query,"DELETE FROM PlaylistSong WHERE PlaylistID=%d",ids->songid[x]);
371 			harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
372 			if(ids->songid[x]!=1){ // If Library: delete playlistsongs but leave playlist
373 				sprintf(query,"DELETE FROM Playlist WHERE PlaylistID=%d",ids->songid[x]);
374 				harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
375 			}
376 		}
377 		return PORTAL_RET_MAIN;
378 	}
379 	return PORTAL_RET_PREV;
380 }
381 
editPlaylistSongAdd(char * args,void * data)382 static int editPlaylistSongAdd(char *args, void *data){
383 	struct IDList *ids=(struct IDList *)data;
384 	if(!ids || !ids->songid){
385 		fprintf(stderr,"no data\n");
386 		return PORTAL_RET_PREV;
387 	}
388 	char query[200];
389 	struct dbitem dbi;
390 	dbiInit(&dbi);
391 	int x,order,songid;
392 
393 	if((x=getStdArgs(args,"Song: "))<0)return PORTAL_RET_PREV;
394 	args=&args[x];
395 
396 	*arglist[ATYPE].subarg='s';
397 	if((songid=getID(args))<1){
398 		fprintf(stderr,"No song found.\n");
399 		return PORTAL_RET_PREV;
400 	}
401 	*arglist[ATYPE].subarg='p';
402 
403 	for(x=0;x<ids->length;x++){
404 		sprintf(query,"SELECT \"Order\" FROM PlaylistSong WHERE PlaylistID=%d ORDER BY \"Order\" DESC LIMIT 1",ids->songid[x]);
405 		doQuery(query,&dbi);
406 		if(fetch_row(&dbi))
407 			order=(int)strtol(dbi.row[0],NULL,10)+1;
408 		else
409 			order=1;
410 
411 		sprintf(query,"INSERT INTO PlaylistSong(PlaylistID,SongID,\"Order\") VALUES (%d,%d,%d)",ids->songid[x],songid,order);
412 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
413 	}
414 	dbiClean(&dbi);
415 	return PORTAL_RET_PREV;
416 }
417 
editPlaylistSongDelete(char * args,void * data)418 static int editPlaylistSongDelete(char *args, void *data){
419 	struct IDList *ids=(struct IDList *)data;
420 	if(!ids || !ids->songid){
421 		fprintf(stderr,"no data\n");
422 		return PORTAL_RET_PREV;
423 	}
424 	char query[200];
425 	int x;
426 
427 	if((x=getStdArgs(args,"Song order: "))<0)return PORTAL_RET_PREV;
428 	args=&args[x];
429 
430 	if(ids->length>1 && !editWarn("This operation will alter only the first playlist in the list.")){
431 			printf("Aborted\n");
432 			return PORTAL_RET_PREV;
433 	}
434 
435 	x=(int)strtol(args,NULL,10);
436 	sprintf(query,"DELETE FROM PlaylistSong WHERE \"Order\"=%d AND PlaylistID=%d",x,ids->songid[0]);
437 	debug(3,query);
438 	harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
439 	// Remove the empty place at the old order
440 	sprintf(query,"UPDATE PlaylistSong SET \"Order\"=\"Order\"-1 WHERE \"Order\">%d AND PlaylistID=%d",x,ids->songid[0]);
441 	harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
442 
443 	return PORTAL_RET_PREV;
444 }
445 
getPlaylistSongOrders(char * args,int * cur,int * new)446 static void getPlaylistSongOrders(char *args, int *cur, int*new){
447 	int x;
448 	*cur=*new=0;
449 	for(x=1;args[x] && (args[x]<'0' || args[x]>'9');x++);
450 	if(!args[x]){
451 		printf("Current order: ");
452 		if(!fgets(args,PORTAL_ARG_LEN,stdin) || *args=='\n'){
453 			printf("Aborted\n");
454 			*cur=*new=0;
455 			return;
456 		}
457 		cleanString(args);
458 		*cur=(int)strtol(args,NULL,10);
459 
460 		printf("New order: ");
461 		if(!fgets(args,PORTAL_ARG_LEN,stdin) || *args=='\n'){
462 			printf("Aborted\n");
463 			*cur=*new=0;
464 			return;
465 		}
466 		cleanString(args);
467 		*new=(int)strtol(args,NULL,10);
468 	}
469 	else{
470 		*cur=(int)strtol(&args[x],NULL,10);
471 
472 		// Find the end of the current order
473 		for(;args[x] && args[x]>='0' && args[x]<='9';x++);
474 		// Find the start of the new order
475 		for(;args[x] && (args[x]<'0' || args[x]>'9');x++);
476 		if(!args[x]){
477 			printf("New order: ");
478 			if(!fgets(args,PORTAL_ARG_LEN,stdin) || *args=='\n'){
479 				printf("Aborted\n");
480 				*cur=*new=0;
481 				return;
482 			}
483 			cleanString(args);
484 			x=0;
485 		}
486 		*new=(int)strtol(&args[x],NULL,10);
487 	}
488 }
489 
editPlaylistSongOrder(char * args,void * data)490 static int editPlaylistSongOrder(char *args, void *data){
491 	struct IDList *ids=(struct IDList *)data;
492 	if(!ids || !ids->songid){
493 		fprintf(stderr,"no data\n");
494 		return PORTAL_RET_PREV;
495 	}
496 	char query[200];
497 	int current_order,new_order;
498 
499 	if(ids->length>1 && !editWarn("This operation will alter only the first playlist in the list.")){
500 			printf("Aborted\n");
501 			return PORTAL_RET_PREV;
502 	}
503 
504 	getPlaylistSongOrders(args,&current_order,&new_order);
505 	if(current_order<1 || new_order<1){
506 		printf("Invalid order\n");
507 		return PORTAL_RET_PREV;
508 	}
509 
510 	// Move song out of the way. Order 0 should not be used.
511 	sprintf(query,"UPDATE PlaylistSong SET \"Order\"=0 WHERE PlaylistID=%d AND \"Order\"=%d",ids->songid[0],current_order);
512 	debug(3,query);
513 	harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
514 	// Remove the empty place at the old order
515 	sprintf(query,"UPDATE PlaylistSong SET \"Order\"=\"Order\"-1 WHERE \"Order\">%d AND PlaylistID=%d",current_order,ids->songid[0]);
516 	debug(3,query);
517 	harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
518 	// Make room for the song. TODO: find performance of ignoring the overlap vs testing for overlap in the query.
519 	sprintf(query,"UPDATE PlaylistSong SET \"Order\"=\"Order\"+1 WHERE \"Order\">%d AND PlaylistID=%d",new_order-1,ids->songid[0]);
520 	debug(3,query);
521 	harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
522 	// Change the song's order
523 	sprintf(query,"UPDATE PlaylistSong SET \"Order\"=%d WHERE PlaylistID=%d AND \"Order\"=0",new_order,ids->songid[0]);
524 	debug(3,query);
525 	harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
526 
527 	return PORTAL_RET_PREV;
528 }
529 
editPlaylistCreate(char * args,void * data)530 static int editPlaylistCreate(char *args, void *data){
531 	char *ptr;
532 	int x,pid,sid;
533 
534 	if((x=getStdArgs(args,"Playlist name: "))<0)return PORTAL_RET_PREV;
535 	args=&args[x];
536 
537 	pid=getPlaylist(args);
538 	if(pid<1){
539 		fprintf(stderr,"Error inserting playlist\n");
540 		return PORTAL_RET_PREV;
541 	}
542 	*arglist[ATYPE].subarg='s';
543 	while(printf("SongID: ") && fgets(args,PORTAL_ARG_LEN,stdin) && *args!='\n'){
544 		ptr=args;
545 		while(*(++ptr)!='\n');
546 		*ptr=0;
547 		if((sid=getID(args))<1)continue;
548 		//if((sid=verifySong(sid))>0) // I don't know if I like this or not. Disabled for now.
549 		getPlaylistSong(sid,pid);
550 	}
551 	return PORTAL_RET_MAIN;
552 }
553 
editGenreCreate(char * args,void * data)554 static int editGenreCreate(char *args, void *data){
555 	int x,gid;
556 
557 	if((x=getStdArgs(args,"Genre name: "))<0)return PORTAL_RET_PREV;
558 	args=&args[x];
559 
560 	gid=getCategory(args);
561 	if(gid<1){
562 		fprintf(stderr,"Error inserting genre\n");
563 		return PORTAL_RET_PREV;
564 	}
565 	return PORTAL_RET_MAIN;
566 }
567 
editGenreName(char * args,void * data)568 static int editGenreName(char *args, void *data){
569 	struct IDList *ids=(struct IDList *)data;
570 	if(!ids || !ids->songid){
571 		fprintf(stderr,"no data\n");
572 		return PORTAL_RET_PREV;
573 	}
574 	char query[200];
575 	int x;
576 
577 	if((x=getStdArgs(args,"Genre name: "))<0)return PORTAL_RET_PREV;
578 	args=&args[x];
579 
580 	for(x=0;x<ids->length;x++){
581 		sprintf(query,"UPDATE Category SET Name='%s' WHERE CategoryID=%d",args,ids->songid[x]);
582 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
583 	}
584 	return PORTAL_RET_PREV;
585 }
586 
editGenreParent(char * args,void * data)587 static int editGenreParent(char *args, void *data){
588 	struct IDList *ids=(struct IDList *)data;
589 	if(!ids || !ids->songid){
590 		fprintf(stderr,"no data\n");
591 		return PORTAL_RET_PREV;
592 	}
593 	char query[200];
594 	int x,gid;
595 
596 	if((x=getStdArgs(args,"Owner: "))<0)return PORTAL_RET_PREV;
597 	args=&args[x];
598 
599 	*arglist[ATYPE].subarg='g';
600 	if((gid=getID(args))<1){
601 		if(!strncasecmp(args,"none",4) || *args=='0')
602 			gid=0;
603 		else
604 			return PORTAL_RET_PREV;
605 	}
606 
607 	for(x=0;x<ids->length;x++){
608 		if(gid==ids->songid[x])continue;
609 		sprintf(query,"UPDATE Category SET ParentID=%d WHERE CategoryID=%d AND CategoryID NOT IN (SELECT ParentID FROM Category WHERE CategoryID=%d)",gid,ids->songid[x],gid);
610 		debug(3,query);
611 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
612 	}
613 	return PORTAL_RET_PREV;
614 }
615 
editGenreDelete(char * args,void * data)616 static int editGenreDelete(char *args, void *data){
617 	struct IDList *ids=(struct IDList *)data;
618 	if(!ids || !ids->songid){
619 		fprintf(stderr,"no data\n");
620 		return PORTAL_RET_PREV;
621 	}
622 	if(!editWarn("Delete genre?"))return PORTAL_RET_MAIN;
623 
624 	char query[200];
625 	int x;
626 
627 	for(x=0;x<ids->length;x++){
628 		if(ids->songid[x]==1)continue;
629 
630 		sprintf(query,"UPDATE Category SET ParentID=(SELECT ParentID FROM Category WHERE CategoryID=%d) WHERE ParentID=%d",ids->songid[x],ids->songid[x]);
631 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
632 		sprintf(query,"DELETE FROM Category WHERE CategoryID=%d",ids->songid[x]);
633 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
634 		sprintf(query,"DELETE FROM SongCategory WHERE CategoryID=%d",ids->songid[x]);
635 		harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
636 	}
637 	return PORTAL_RET_MAIN;
638 }
639 
editSongGenreAdd(char * args,void * data)640 static int editSongGenreAdd(char *args, void *data){
641 	struct IDList *ids=(struct IDList *)data;
642 	if(!ids || !ids->songid){
643 		fprintf(stderr,"no data\n");
644 		return PORTAL_RET_PREV;
645 	}
646 
647 	char query[300];
648 	int x,gid;
649 
650 	if((x=getStdArgs(args,"Genre to add: "))<0)return PORTAL_RET_PREV;
651 	args=&args[x];
652 
653 	*arglist[ATYPE].subarg='g';
654 	if((gid=getID(args))<1){
655 		fprintf(stderr,"Invalid genre\n");
656 		return PORTAL_RET_PREV;
657 	}
658 
659 	// Insert songs from list that are not already in the category
660 	sprintf(query,"INSERT INTO SongCategory(CategoryID,SongID) SELECT '%d',SongID FROM SongCategory WHERE SongID NOT IN (SELECT SongID FROM SongCategory WHERE CategoryID=%d) AND SongID IN (SELECT SelectID FROM TempSelect WHERE TempID=%d)",gid,gid,ids->tempselectid);
661 	debug(3,query);
662 	harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
663 	return PORTAL_RET_PREV;
664 }
665 
editSongGenreRemove(char * args,void * data)666 static int editSongGenreRemove(char *args, void *data){
667 	struct IDList *ids=(struct IDList *)data;
668 	if(!ids || !ids->songid){
669 		fprintf(stderr,"no data\n");
670 		return PORTAL_RET_PREV;
671 	}
672 
673 	char query[200];
674 	int x,gid;
675 
676 	if((x=getStdArgs(args,"Genre to remove: "))<0)return PORTAL_RET_PREV;
677 	args=&args[x];
678 
679 	*arglist[ATYPE].subarg='g';
680 	if((gid=getID(args))<1){
681 		fprintf(stderr,"Invalid genre\n");
682 		return PORTAL_RET_PREV;
683 	}
684 
685 	sprintf(query,"DELETE FROM SongCategory WHERE CategoryID=%d AND SongID IN (SELECT SelectID FROM TempSelect WHERE TempID=%d)",gid,ids->tempselectid);
686 	debug(3,query);
687 	harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
688 	return PORTAL_RET_PREV;
689 }
690 
cleanOrphans()691 static void cleanOrphans(){
692 	// Clean orphaned albums
693 	harp_sqlite3_exec(conn,"DELETE FROM Album WHERE AlbumID NOT IN (SELECT AlbumID FROM Song)",NULL,NULL,NULL);
694 	// Clean orphaned albumartists
695 	harp_sqlite3_exec(conn,"DELETE FROM AlbumArtist WHERE AlbumID NOT IN (SELECT AlbumID FROM Song)",NULL,NULL,NULL);
696 	// Clean orphaned artists
697 	harp_sqlite3_exec(conn,"DELETE FROM Artist WHERE ArtistID NOT IN (SELECT ArtistID FROM AlbumArtist)",NULL,NULL,NULL);
698 	// Put orphaned [category] songs in unknown
699 	harp_sqlite3_exec(conn,"INSERT INTO SongCategory (SongID,CategoryID) SELECT SongID,'1' FROM Song WHERE SongID NOT IN (SELECT SongID FROM SongCategory)",NULL,NULL,NULL);
700 }
701 
listSongs(char * args,void * data)702 static int listSongs(char *args, void *data){
703 	struct IDList *songids=(struct IDList *)data;
704 	if(!songids || !songids->songid){
705 		fprintf(stderr,"no data\n");
706 		return PORTAL_RET_PREV;
707 	}
708 	char query[200];
709 	int x=0;
710 
711 	int exception[10];
712 	for(x=0;x<10;x++)exception[x]=x<5?1:listconf.exception;
713 	sprintf(query,"SELECT SongID, SongTitle, SongTrack, Location, AlbumTitle AS Album, ArtistName AS Artist FROM SongPubInfo WHERE SongID IN (SELECT SelectID FROM TempSelect WHERE TempID=%d)",songids->tempselectid);
714 	doTitleQuery(query,exception,listconf.maxwidth);
715 	return PORTAL_RET_PREV;
716 }
717 
listAlbums(char * args,void * data)718 static int listAlbums(char *args, void *data){
719 	struct IDList *ids=(struct IDList *)data;
720 	if(!ids || !ids->songid){
721 		fprintf(stderr,"no data\n");
722 		return PORTAL_RET_PREV;
723 	}
724 	char query[200];
725 	int x=0;
726 
727 	int exception[10];
728 	for(x=0;x<10;x++)exception[x]=x<1?1:listconf.exception;
729 	sprintf(query,"SELECT AlbumID, Title, \"Date\" FROM Album WHERE AlbumID IN (SELECT SelectID FROM TempSelect WHERE TempID=%d)",ids->tempselectid);
730 	doTitleQuery(query,exception,listconf.maxwidth);
731 	return PORTAL_RET_PREV;
732 }
733 
listArtists(char * args,void * data)734 static int listArtists(char *args, void *data){
735 	struct IDList *ids=(struct IDList *)data;
736 	if(!ids || !ids->songid){
737 		fprintf(stderr,"no data\n");
738 		return PORTAL_RET_PREV;
739 	}
740 	char query[200];
741 	int x=0;
742 
743 	int exception[10];
744 	for(x=0;x<10;x++)exception[x]=x<1?1:listconf.exception;
745 	sprintf(query,"SELECT ArtistID, Name FROM Artist WHERE ArtistID IN (SELECT SelectID FROM TempSelect WHERE TempID=%d)",ids->tempselectid);
746 	doTitleQuery(query,exception,listconf.maxwidth);
747 	return PORTAL_RET_PREV;
748 }
749 
listPlaylists(char * args,void * data)750 static int listPlaylists(char *args, void *data){
751 	struct IDList *ids=(struct IDList *)data;
752 	if(!ids || !ids->songid){
753 		*arglist[ATYPE].subarg='p';
754 		listall();
755 		fprintf(stderr,"None selected.\n");
756 		return PORTAL_RET_PREV;
757 	}
758 	struct dbitem dbi;
759 	dbiInit(&dbi);
760 	char query[120];
761 	int x=0;
762 
763 	// List contents of playlist as well.
764 	if(args[1]=='C'){
765 		*arglist[ATYPE].subarg='p';
766 		list(ids->songid,ids->length);
767 		return PORTAL_RET_PREV;
768 	}
769 
770 	int exception[10];
771 	for(x=0;x<10;x++)exception[x]=x<1?1:listconf.exception;
772 	sprintf(query,"SELECT PlaylistID, Title FROM Playlist WHERE PlaylistID IN (SELECT SelectID FROM TempSelect WHERE TempID=%d)",ids->tempselectid);
773 	doTitleQuery(query,exception,listconf.maxwidth);
774 	return PORTAL_RET_PREV;
775 }
776 
listGenre(char * args,void * data)777 static int listGenre(char *args, void *data){
778 	struct IDList *ids=(struct IDList *)data;
779 	int x=0;
780 	if(!ids || !ids->songid){
781 		printGenreTree(0,(void *)tierCatPrint);
782 		fprintf(stderr,"None selected.\n");
783 		return PORTAL_RET_PREV;
784 	}
785 	// List contents of genre as well.
786 	if(args[1]=='C'){
787 		for(x=0;x<ids->length;x++){
788 			printGenreTree(ids->songid[x],(void *)tierChildPrint);
789 		}
790 	}
791 	else{
792 		for(x=0;x<ids->length;x++){
793 			printGenreTree(ids->songid[x],(void *)tierCatPrint);
794 		}
795 	}
796 	return PORTAL_RET_PREV;
797 }
798 
listSongGenre(char * args,void * data)799 static int listSongGenre(char *args, void *data){
800 	struct IDList *ids=(struct IDList *)data;
801 	int x=0;
802 	if(!ids || !ids->songid){
803 		fprintf(stderr,"None selected.\n");
804 		return PORTAL_RET_PREV;
805 	}
806 	char query[221];
807 	int exception[10];
808 	for(x=2;x<10;x++)exception[x]=listconf.exception;
809 	exception[0]=exception[1]=1;
810 
811 	// Print a list of genres that the songs belong to.
812 	sprintf(query,"SELECT CategoryID AS ID,Name FROM Category WHERE ID IN (SELECT DISTINCT CategoryID FROM SongCategory WHERE SongID IN (SELECT SelectID FROM TempSelect WHERE TempID=%d))",ids->tempselectid);
813 	debug(3,query);
814 	doTitleQuery(query,exception,listconf.maxwidth);
815 	return PORTAL_RET_PREV;
816 }
817 
songGenrePortal(char * args,void * data)818 static int songGenrePortal(char *args, void *data){
819 	struct IDList *ids=(struct IDList *)data;
820 	if(!ids || !ids->songid){
821 		fprintf(stderr,"None selected.\n");
822 		return PORTAL_RET_PREV;
823 	}
824 	struct commandOption portalOptions[]={
825 		{'L',listSongGenre,"List genres",ids},
826 		{'a',editSongGenreAdd,"Add songs to a genre",ids},
827 		{'r',editSongGenreRemove,"Remove songs from a genre",ids},
828 		{0,NULL,NULL,NULL}
829 	};
830 
831 	return portal(portalOptions,"Song-Genre");
832 }
833 
songPortal(char * args,void * data)834 static int songPortal(char *args, void *data){
835 	struct IDList sid_struct;
836 	struct IDList *id_struct=&sid_struct;
837 	struct commandOption portalOptions[]={
838 		{'L',listSongs,"List affected songs",id_struct},
839 		{'t',editSongName,"Change title",id_struct},
840 		{'k',editSongTrack,"Change track number",id_struct},
841 		{'l',editSongLocation,"Change location",id_struct},
842 		{'a',editSongAlbum,"Change album",id_struct},
843 		{'r',editSongArtist,"Change artist",id_struct},
844 		{'d',deleteSong,"Delete from database",id_struct},
845 		{'v',songActivation,"Toggle activation",id_struct},
846 		{'g',songGenrePortal,"Manage genre",id_struct},
847 		{0,NULL,NULL,NULL}
848 	};
849 	int x,*song_ids=malloc(sizeof(int));
850 	struct dbitem dbi;
851 	dbiInit(&dbi);
852 
853 
854 	for(x=1;x<PORTAL_ARG_LEN && args[x] && args[x]<'0';x++);
855 	if(!args[x]){
856 		fprintf(stderr,"Required argument not given.\n");
857 		return PORTAL_RET_PREV;
858 	}
859 
860 	if(getGroupSongIDs(args+x,PORTAL_ARG_LEN,id_struct)==HARP_RET_ERR)
861 		return PORTAL_RET_PREV;
862 
863 	x=portal(portalOptions,"Song");
864 	cleanTempSelect(id_struct->tempselectid);
865 	free(song_ids);
866 	return x;
867 }
868 
albumPortal(char * args,void * data)869 static int albumPortal(char *args, void *data){
870 	struct IDList ids_struct;
871 	struct IDList *ids=&ids_struct;
872 	struct commandOption portalOptions[]={
873 		{'L',listAlbums,"List affected alubms",ids},
874 		{'t',editAlbumTitle,"Change title",ids},
875 		{'r',editAlbumArtist,"Change artist",ids},
876 		{'d',editAlbumDate,"Change date",ids},
877 		{0,NULL,NULL,NULL}
878 	};
879 	int x;
880 
881 	for(x=1;x<PORTAL_ARG_LEN && args[x] && args[x]<'0';x++);
882 	if(!args[x]){
883 		fprintf(stderr,"Required argument not given.\n");
884 		return PORTAL_RET_PREV;
885 	}
886 
887 	arglist[ATYPE].subarg[0]='a';
888 
889 	if(!(ids->songid=getMulti(&args[x],&ids->length)))
890 		return PORTAL_RET_PREV;
891 	if(ids->songid[0]<1){
892 		fprintf(stderr,"No results found.\n");
893 		free(ids->songid);
894 		return PORTAL_RET_PREV;
895 	}
896 	ids->tempselectid=insertTempSelect(ids->songid,ids->length);
897 
898 	x=portal(portalOptions,"Album");
899 	cleanTempSelect(ids->tempselectid);
900 	free(ids->songid);
901 	return x;
902 }
903 
artistPortal(char * args,void * data)904 static int artistPortal(char *args, void *data){
905 	struct IDList ids_struct;
906 	struct IDList *ids=&ids_struct;
907 	struct commandOption portalOptions[]={
908 		{'L',listArtists,"List affected artists",ids},
909 		{'n',editArtistName,"Change name",ids},
910 		{0,NULL,NULL,NULL}
911 	};
912 	int x;
913 
914 	for(x=1;x<PORTAL_ARG_LEN && args[x] && args[x]<'0';x++);
915 	if(!args[x]){
916 		fprintf(stderr,"Required argument not given.\n");
917 		return PORTAL_RET_PREV;
918 	}
919 
920 	arglist[ATYPE].subarg[0]='r';
921 
922 	if(!(ids->songid=getMulti(&args[x],&ids->length)))
923 		return PORTAL_RET_PREV;
924 	if(ids->songid[0]<1){
925 		fprintf(stderr,"No results found.\n");
926 		return PORTAL_RET_PREV;
927 	}
928 	ids->tempselectid=insertTempSelect(ids->songid,ids->length);
929 
930 	x=portal(portalOptions,"Artist");
931 	cleanTempSelect(ids->tempselectid);
932 	free(ids->songid);
933 	return x;
934 }
935 
playlistPortal(char * args,void * data)936 static int playlistPortal(char *args, void *data){
937 	struct IDList ids_struct;
938 	struct IDList *ids=&ids_struct;
939 
940 	int x;
941 	for(x=1;x<PORTAL_ARG_LEN && args[x] && args[x]<'0';x++);
942 	if(args[x]){
943 		struct commandOption portalOptions[]={
944 			{'L',listPlaylists,"List affected playlist\nLC\tList the contents of the selected playlist",ids},
945 			{'c',editPlaylistCreate,"Create a playlist",NULL},
946 			{'n',editPlaylistName,"Change name",ids},
947 			{'d',deletePlaylist,"Delete playlist from database",ids},
948 			{'a',editPlaylistSongAdd,"Add a song",ids},
949 			{'r',editPlaylistSongDelete,"Remove a song from playlist",ids},
950 			{'o',editPlaylistSongOrder,"Change the order of a song",ids},
951 			{0,NULL,NULL,NULL}
952 		};
953 
954 		arglist[ATYPE].subarg[0]='p';
955 
956 		if(!(ids->songid=getMulti(&args[x],&ids->length)))
957 			return PORTAL_RET_PREV;
958 		if(ids->songid[0]<1){
959 			fprintf(stderr,"No results found.\n");
960 			return PORTAL_RET_PREV;
961 		}
962 		for(x=0;x<ids->length;x++){
963 			if(ids->songid[x]==1){
964 				fprintf(stderr,"You may not edit the Library\n");
965 				free(ids->songid);
966 				return PORTAL_RET_PREV;
967 			}
968 		}
969 		ids->tempselectid=insertTempSelect(ids->songid,ids->length);
970 		x=portal(portalOptions,"Playlist");
971 		cleanTempSelect(ids->tempselectid);
972 		free(ids->songid);
973 		return x;
974 	}
975 	else{
976 		struct commandOption portalOptions[]={
977 			{'L',listPlaylists,"Create a playlist",NULL},
978 			{'c',editPlaylistCreate,"Create a playlist",NULL},
979 			{0,NULL,NULL,NULL}
980 		};
981 		fprintf(stderr,"No playlist selected.\n");
982 		return portal(portalOptions,"Playlist");
983 	}
984 }
985 
genrePortal(char * args,void * data)986 static int genrePortal(char *args, void *data){
987 	struct IDList ids_struct;
988 	struct IDList *ids=&ids_struct;
989 	struct commandOption portalOptions[]={
990 		{'L',listGenre,"List affected genre\nLC\tList the contents of the selected genre",ids},
991 		{'n',editGenreName,"Change name of selected genre",ids},
992 		{'o',editGenreParent,"Change the owner of the genre",ids},
993 		{'d',editGenreDelete,"Delete genre from database",ids},
994 		{'a',editGenreCreate,"Add a new genre",NULL},
995 		{0,NULL,NULL,NULL}
996 	};
997 	int x;
998 
999 	for(x=1;x<PORTAL_ARG_LEN && args[x] && args[x]<'0';x++);
1000 	if(!args[x]){
1001 		fprintf(stderr,"No genre selected.\n");
1002 		struct commandOption portalOptions[]={
1003 			{'L',listGenre,"List all genres",NULL},
1004 			{'a',editGenreCreate,"Add a new genre",NULL},
1005 			{0,NULL,NULL,NULL}
1006 		};
1007 		return portal(portalOptions,"Genre");
1008 	}
1009 
1010 	arglist[ATYPE].subarg[0]='g';
1011 
1012 	if(!(ids->songid=getMulti(&args[x],&ids->length)))
1013 		return PORTAL_RET_PREV;
1014 	if(ids->songid[0]<1){
1015 		fprintf(stderr,"No results found.\n");
1016 		return PORTAL_RET_PREV;
1017 	}
1018 	ids->tempselectid=insertTempSelect(ids->songid,ids->length);
1019 
1020 	x=portal(portalOptions,"Genre");
1021 	cleanTempSelect(ids->tempselectid);
1022 	free(ids->songid);
1023 	return x;
1024 }
1025 
editPortal()1026 void editPortal(){
1027 	struct commandOption portalOptions[]={
1028 		{'s',songPortal,"Edit songs.\nsa\tEdit songs in an album (or albums)\nsr\tEdit songs in an artist (or artists)\nsg\tEdit songs in a genre (or genres)",NULL},
1029 		{'a',albumPortal,"Edit albums",NULL},
1030 		{'r',artistPortal,"Edit artists",NULL},
1031 		{'p',playlistPortal,"Edit playlists",NULL},
1032 		{'g',genrePortal,"Edit genres",NULL},
1033 		{0,NULL,NULL,NULL}
1034 	};
1035 	int ret=1;
1036 	printf("Enter a command. ? for command list.\n");
1037 	if(!arglist[ATYPE].subarg && !(arglist[ATYPE].subarg=malloc(sizeof(char)))){
1038 		debug(2,"Malloc failed (ATYPE subarg).");
1039 		return;
1040 	}
1041 	while(ret){
1042 		ret=portal(portalOptions,"Edit");
1043 		cleanOrphans();
1044 	}
1045 }
1046