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,¤t_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