1 /*
2 * Copyright (c) 2010, 2011 Ryan Flannery <ryan.flannery@gmail.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "e_commands.h"
18
19 const struct ecmd ECMD_PATH[] = {
20 { "init", ecmd_init, ecmd_help_init},
21 { "add", ecmd_add, ecmd_help_add },
22 { "addurl", ecmd_addurl, ecmd_help_addurl },
23 { "check", ecmd_check, ecmd_help_check },
24 { "rmfile", ecmd_rmfile, ecmd_help_rmfile },
25 { "rm", ecmd_rmfile, ecmd_help_rmfile },
26 { "update", ecmd_update, ecmd_help_update },
27 { "flush", ecmd_flush, ecmd_help_flush },
28 { "tag", ecmd_tag, ecmd_help_tag },
29 { "help", ecmd_help, NULL }
30 };
31 const int ECMD_PATH_SIZE = (sizeof(ECMD_PATH) / sizeof(struct ecmd));
32
33 int
ecmd_init(int argc,char * argv[])34 ecmd_init(int argc, char *argv[])
35 {
36 if (argc != 1)
37 errx(1, "usage: -e %s", argv[0]);
38
39 printf("Creating all necessary files and directories for vitunes...\n");
40 medialib_setup_files(vitunes_dir, db_file, playlist_dir);
41
42 printf("\nNow use 'vitunes -e add dir1 dir2 ...' to add files to vitunes.\n");
43 return 0;
44 }
45
46 int
ecmd_update(int argc,char * argv[])47 ecmd_update(int argc, char *argv[])
48 {
49 bool show_skipped = false;
50
51 if (argc == 2 && strcmp(argv[1], "-s") == 0)
52 show_skipped = true;
53
54 if (argc != 1 && !show_skipped)
55 errx(1, "usage: -e %s [-s]", argv[0]);
56
57 printf("Loading existing database...\n");
58 medialib_load(db_file, playlist_dir);
59
60 printf("Updating existing database...\n");
61 medialib_db_update(show_skipped);
62
63 medialib_destroy();
64 return 0;
65 }
66
67 int
ecmd_add(int argc,char * argv[])68 ecmd_add(int argc, char *argv[])
69 {
70 if (argc == 1)
71 errx(1, "usage: -e %s /path/to/filesORdirs [ ... ] ", argv[0]);
72
73 printf("Loading existing database...\n");
74 medialib_load(db_file, playlist_dir);
75
76 printf("Scanning directories for files to add to database...\n");
77 medialib_db_scan_dirs(argv + 1);
78
79 medialib_destroy();
80 return 0;
81 }
82
83 int
ecmd_addurl(int argc,char * argv[])84 ecmd_addurl(int argc, char *argv[])
85 {
86 meta_info *m;
87 bool found;
88 int found_idx;
89 char input[255];
90 int field, i;
91
92 if (argc != 2)
93 errx(1, "usage: -e %s filename|URL", argv[0]);
94
95 /* start new record, set filename */
96 m = mi_new();
97 m->is_url = true;
98 if ((m->filename = strdup(argv[1])) == NULL)
99 err(1, "%s: strdup failed (filename)", argv[0]);
100
101 /* get fields from user */
102 for (field = 0; field < MI_NUM_CINFO; field++) {
103
104 printf("%10.10s: ", MI_CINFO_NAMES[field]);
105 if (fgets(input, sizeof(input), stdin) == NULL) {
106 warnx("Operation canceled. Database unchanged.");
107 mi_free(m);
108 return 0;
109 }
110
111 if (input[strlen(input) - 1] == '\n')
112 input[strlen(input) - 1] = '\0';
113
114 if ((m->cinfo[field] = strdup(input)) == NULL)
115 err(1, "%s: strdup failed (field)", argv[0]);
116 }
117
118 /* load existing database and see if file/URL already exists */
119 medialib_load(db_file, playlist_dir);
120
121 /* does the URL already exist in the database? */
122 found = false;
123 found_idx = -1;
124 for (i = 0; i < mdb.library->nfiles && !found; i++) {
125 if (strcmp(m->filename, mdb.library->files[i]->filename) == 0) {
126 found = true;
127 found_idx = i;
128 }
129 }
130
131 if (found) {
132 printf("Warning: file/URL '%s' already in the database.\n", argv[0]);
133 printf("Do you want to replace the existing record? [y/n] ");
134
135 if (fgets(input, sizeof(input), stdin) == NULL
136 || (strcasecmp(input, "yes\n") != 0 && strcasecmp(input, "y\n") != 0)) {
137 warnx("Operation Canceled. Database unchanged.");
138 mi_free(m);
139 medialib_destroy();
140 return 0;
141 }
142
143 mi_sanitize(m);
144 playlist_file_replace(mdb.library, found_idx, m);
145 } else {
146 mi_sanitize(m);
147 playlist_files_append(mdb.library, &m, 1, false);
148 }
149
150 medialib_db_save(db_file);
151 medialib_destroy();
152
153 return 0;
154 }
155
156 int
ecmd_check(int argc,char * argv[])157 ecmd_check(int argc, char *argv[])
158 {
159 meta_info *mi;
160 bool show_raw, show_sanitized, show_database;
161 bool found;
162 char ch;
163 char realfile[PATH_MAX];
164 char **files;
165 int nfiles;
166 int f, i;
167
168 if (argc < 3)
169 errx(1, "usage: -e %s [-rsd] file1 [ file2 ... ]", argv[0]);
170
171 /* parse options to see which version to show */
172 show_raw = false;
173 show_sanitized = false;
174 show_database = false;
175 optreset = 1;
176 optind = 0;
177 while ((ch = getopt(argc, argv, "rsd")) != -1) {
178 switch (ch) {
179 case 'r':
180 show_raw = true;
181 break;
182 case 's':
183 show_sanitized = true;
184 break;
185 case 'd':
186 show_database = true;
187 break;
188 case 'h':
189 case '?':
190 default:
191 errx(1, "usage: -e %s [-rsd] file1 [ file2 ... ]", argv[0]);
192 }
193 }
194 files = argv + optind;
195 nfiles = argc - optind;
196
197 if (!show_raw && !show_sanitized && !show_database)
198 errx(1, "%s: must specify at least one of -r, -s, or -d", argv[0]);
199
200 if (nfiles == 0)
201 errx(1, "%s: must provide at least one file to check.", argv[0]);
202
203 /* scan through files... */
204 for (f = 0; f < nfiles; f++) {
205
206 printf("Checking: '%s'\n", files[f]);
207
208 /* show raw or sanitized information */
209 if (show_raw || show_sanitized) {
210 mi = mi_extract(files[f]);
211 if (mi == NULL)
212 warnx("Failed to extract any meta-information from '%s'", files[f]);
213 else {
214 /* show raw info */
215 if (show_raw) {
216 printf("\tThe RAW meta-information from the file is:\n");
217 for (i = 0; i < MI_NUM_CINFO; i++)
218 printf("\t%10.10s: '%s'\n", MI_CINFO_NAMES[i], mi->cinfo[i]);
219 }
220
221 /* show sanitized info */
222 if (show_sanitized) {
223 mi_sanitize(mi);
224 printf("\tThe SANITIZED meta-information from the file is:\n");
225 for (i = 0; i < MI_NUM_CINFO; i++)
226 printf("\t%10.10s: '%s'\n", MI_CINFO_NAMES[i], mi->cinfo[i]);
227 }
228 }
229 }
230
231 /* check if it's in the database */
232 if (show_database) {
233
234 /* get absolute filename */
235 if (realpath(files[f], realfile) == NULL) {
236 warn("%s: realpath failed for %s: skipping", argv[0], files[f]);
237 continue;
238 }
239
240 /* check if file is in database */
241 medialib_load(db_file, playlist_dir);
242
243 found = false;
244 mi = NULL;
245 for (i = 0; i < mdb.library->nfiles && !found; i++) {
246 if (strcmp(realfile, mdb.library->files[i]->filename) == 0) {
247 found = true;
248 mi = mdb.library->files[i];
249 }
250 }
251
252 if (!found)
253 warnx("File '%s' does NOT exist in the database", files[f]);
254 else {
255 printf("\tThe meta-information in the DATABASE is:\n");
256 for (i = 0; i < MI_NUM_CINFO; i++)
257 printf("\t%10.10s: '%s'\n", MI_CINFO_NAMES[i], mi->cinfo[i]);
258 }
259
260 medialib_destroy();
261 }
262
263 }
264
265 return 0;
266 }
267
268 int
ecmd_rmfile(int argc,char * argv[])269 ecmd_rmfile(int argc, char *argv[])
270 {
271 char *filename;
272 char input[255];
273 bool forced;
274 bool found;
275 int found_idx;
276 int i;
277
278 if (argc < 2 || argc > 3)
279 errx(1, "usage: -e %s [-f] filename|URL", argv[0]);
280
281 /* get filename and if this remove is forced */
282 forced = false;
283 if (argc == 3) {
284 if (strcmp(argv[1], "-f") != 0)
285 errx(1, "usage: -e %s [-f] filename|URL", argv[0]);
286 else
287 forced = true;
288
289 filename = argv[2];
290 } else
291 filename = argv[1];
292
293
294 /* load database and search for record */
295 medialib_load(db_file, playlist_dir);
296 found = false;
297 found_idx = -1;
298 for (i = 0; i < mdb.library->nfiles && !found; i++) {
299 if (strcmp(filename, mdb.library->files[i]->filename) == 0) {
300 found = true;
301 found_idx = i;
302 }
303 }
304
305 /* if not found then error */
306 if (!found) {
307 i = (forced ? 0 : 1);
308 errx(i, "%s: %s: No such file or URL", argv[0], filename);
309 }
310
311 /* if not forced, prompt user if they are sure */
312 if (!forced) {
313 printf("Are you sure you want to delete '%s'? [y/n] ", filename);
314 if (fgets(input, sizeof(input), stdin) == NULL
315 || (strcasecmp(input, "yes\n") != 0 && strcasecmp(input, "y\n") != 0))
316 errx(1, "%s: operation canceled. Database unchanged.", argv[0]);
317 }
318
319 playlist_files_remove(mdb.library, found_idx, 1, false);
320 medialib_db_save(db_file);
321 medialib_destroy();
322 return 0;
323 }
324
325 int
ecmd_flush(int argc,char * argv[])326 ecmd_flush(int argc, char *argv[])
327 {
328 char ch;
329 char *time_format = "%Y %m %d %H:%M:%S";
330
331 optreset = 1;
332 optind = 0;
333 while ((ch = getopt(argc, argv, "t:")) != -1) {
334 switch (ch) {
335 case 't':
336 if ((time_format = strdup(optarg)) == NULL)
337 err(1, "%s: strdup of time_format failed", argv[0]);
338 break;
339 case '?':
340 case 'h':
341 default:
342 errx(1, "usage: %s [-t format]", argv[0]);
343 }
344 }
345
346 medialib_load(db_file, playlist_dir);
347 medialib_db_flush(stdout, time_format);
348 medialib_destroy();
349 return 0;
350 }
351
352 int
ecmd_tag(int argc,char * argv[])353 ecmd_tag(int argc, char *argv[])
354 {
355 TagLib_File *tag_file;
356 TagLib_Tag *tag;
357 bool set_artist = false;
358 bool set_album = false;
359 bool set_title = false;
360 bool set_genre = false;
361 bool set_track = false;
362 bool set_year = false;
363 bool set_comment = false;
364 char *artist = NULL, *album = NULL, *title = NULL, *genre = NULL,
365 *comment = NULL;
366 const char *errstr = NULL;
367 unsigned int track = 0, year = 0;
368 char ch;
369 char **files;
370 int nfiles, f;
371
372 static struct option longopts[] = {
373 { "artist", required_argument, NULL, 'a' },
374 { "album", required_argument, NULL, 'A' },
375 { "title", required_argument, NULL, 't' },
376 { "genre", required_argument, NULL, 'g' },
377 { "track", required_argument, NULL, 'T' },
378 { "year", required_argument, NULL, 'y' },
379 { "comment", required_argument, NULL, 'c' },
380 { NULL, 0, NULL, 0 }
381 };
382
383 /* parse options and get list of files */
384 optreset = 1;
385 optind = 0;
386 while ((ch = getopt_long_only(argc, argv, "a:A:t:g:T:y:c:", longopts, NULL)) != -1) {
387 switch (ch) {
388 case 'a':
389 set_artist = true;
390 if ((artist = strdup(optarg)) == NULL)
391 err(1, "%s: strdup ARTIST failed", argv[0]);
392 break;
393 case 'A':
394 set_album = true;
395 if ((album = strdup(optarg)) == NULL)
396 err(1, "%s: strdup ALBUM failed", argv[0]);
397 break;
398 case 't':
399 set_title = true;
400 if ((title = strdup(optarg)) == NULL)
401 err(1, "%s: strdup TITLE failed", argv[0]);
402 break;
403 case 'g':
404 set_genre = true;
405 if ((genre = strdup(optarg)) == NULL)
406 err(1, "%s: strdup GENRE failed", argv[0]);
407 break;
408 case 'T':
409 set_track = true;
410 track = (unsigned int) strtonum(optarg, 0, INT_MAX, &errstr);
411 if (errstr != NULL)
412 errx(1, "%s: invalid track '%s': %s", argv[0], optarg, errstr);
413 break;
414 case 'y':
415 set_year = true;
416 year = (unsigned int) strtonum(optarg, 0, INT_MAX, &errstr);
417 if (errstr != NULL)
418 errx(1, "%s: invalid year '%s': %s", argv[0], optarg, errstr);
419 break;
420 case 'c':
421 set_comment = true;
422 if ((comment = strdup(optarg)) == NULL)
423 err(1, "%s: strdup COMMENT failed", argv[0]);
424 break;
425 case 'h':
426 case '?':
427 default:
428 errx(1, "usage: see 'vitunes -e help tag'");
429 }
430 }
431 files = argv + optind;
432 nfiles = argc - optind;
433
434 /* be verbose, indicate what we're setting... */
435 printf("Setting the following tags to all files:\n");
436 if (set_artist) printf("%10.10s => '%s'\n", "artist", artist);
437 if (set_album) printf("%10.10s => '%s'\n", "album", album);
438 if (set_title) printf("%10.10s => '%s'\n", "title", title);
439 if (set_genre) printf("%10.10s => '%s'\n", "genre", genre);
440 if (set_track) printf("%10.10s => %i\n", "track", track);
441 if (set_year) printf("%10.10s => %i\n", "year", year);
442 if (set_comment) printf("%10.10s => '%s'\n", "comment", comment);
443
444 if (!set_artist && !set_album && !set_title && !set_genre
445 && !set_track && !set_year && !set_comment)
446 errx(1, "%s: nothing to set! See 'vitunes -e help tag'", argv[0]);
447
448 if (nfiles == 0)
449 errx(1, "%s: must provide at least one file to tag.", argv[0]);
450
451 /* tag files ... */
452 taglib_set_strings_unicode(false);
453 for (f = 0; f < nfiles; f++) {
454 printf("tagging: '%s'\n", files[f]);
455
456 /* extract taglib stuff */
457 if ((tag_file = taglib_file_new(files[f])) == NULL) {
458 warnx("TagLib: failed to open file '%s': skipping.", files[f]);
459 printf(" => Causes: format not supported by TagLib or format doesn't support tags\n");
460 fflush(stdout);
461 continue;
462 }
463
464 tag = taglib_file_tag(tag_file);
465
466 /* apply changes */
467 if (set_artist) taglib_tag_set_artist(tag, artist);
468 if (set_album) taglib_tag_set_album(tag, album);
469 if (set_title) taglib_tag_set_title(tag, title);
470 if (set_genre) taglib_tag_set_genre(tag, genre);
471 if (set_track) taglib_tag_set_track(tag, track);
472 if (set_year) taglib_tag_set_year(tag, year);
473 if (set_comment) taglib_tag_set_comment(tag, comment);
474
475
476 /* save changes and cleanup */
477 taglib_file_save(tag_file);
478 taglib_tag_free_strings();
479 taglib_file_free(tag_file);
480 }
481
482 return 0;
483 }
484
485 void
ecmd_help_init(void)486 ecmd_help_init(void)
487 {
488 printf("\
489 VITUNES COMMAND:\n\tinit - initialize vitunes directories/database\n\n\
490 SYNOPSIS:\n\tinit\n\n\
491 DESCRIPTION:\n\
492 The init command is used to setup the initial files and directories\n\
493 used by vitunes. This includes:\n\n\
494 ~/.vitunes The vitunes core directory where everything\n\
495 is stored.\n\
496 ~/.vitunes/vitunes.db The library database containing the meta\n\
497 information of all media files.\n\
498 ~/.vitunes/playlists/ Directory where all playlists are stored.\n\n\
499 The database created is initially empty.\n\
500 If any of the above files/directories exists, nothing will be changed.\n\
501 This command takes no parameters.\n\n\
502 It is not strictly necessary to run this command to start using\n\
503 vitunes, as you could use an existing installation, or create the\n\
504 files/directories yourself. It is provided for convenience.\n\n\
505 EXAMPLE:\n\
506 $ vitunes -e init\n\n\
507 ");
508 }
509
510 void
ecmd_help_add(void)511 ecmd_help_add(void)
512 {
513 printf("\
514 VITUNES COMMAND:\n\tadd - add files to the vitunes database\n\n\
515 SYNOPSIS:\n\tadd /path/to/files1 [ path2 ... ]\n\n\
516 DESCRIPTION:\n\
517 The add command is used to add files to the database used by vitunes.\n\
518 For every file/directory provided as a parameter, vitunes will scan that\n\
519 file or directory (recursively) searching for media files that contain\n\
520 any meta information. Any such files found are added to the database\n\
521 used by vitunes. If any of the files found are already in the database\n\
522 then they will be re-scanned and any changes will be updated.\n\n\
523 Note that vitunes only maintains information about the file, and not the\n\
524 file itself. It does NOT move/copy/modify the files in its database in\n\
525 any way.\n\n\
526 The information vitunes stores for each file includes:\n\
527 * Filename * Track Number\n\
528 * Artist Name * Year\n\
529 * Album Name * Genre\n\
530 * Song/Video Title * Play Length (seconds)\n\n\
531 The filename stored is the absolute pathname obtained from realpath(3),\n\
532 and serves as the key-field within the database.\n\n\
533 If any file encountered has no meta information, it is NOT added to the\n\
534 database.\n\n\
535 EXAMPLE:\n\
536 $ vitunes -e add ~/music /usr/local/share/music\n\n\
537 ");
538 }
539
540 void
ecmd_help_addurl(void)541 ecmd_help_addurl(void)
542 {
543 printf("\
544 VITUNES COMMAND:\n\taddurl - add a URL (or other) to the vitunes database\n\n\
545 SYNOPSIS:\n\taddurl URL\n\n\
546 DESCRIPTION:\n\
547 To add non-standard-files to the vitunes database (things like URL's for\n\
548 Internet radio streams), one can use the addurl command. It takes a single\n\
549 parameter: the URL/filename to be added to the database. After that, you\n\
550 will be prompted to enter meta-information for each field vitunes indexes\n\
551 (artist, album, title, track, year, and genre). You may leave any/all of\n\
552 the fields blank.\n\n\
553 Basically, anything \"foo\" that mplayer can play by a simple:\n\
554 $ mplayer foo\n\
555 can be added to the database using this command. Although regular files\n\
556 could also be added using this command, the add command is preferred, as\n\
557 it attempts to extract meta information automatically.\n\n\
558 Note that files added to the database using the addurl command are NOT\n\
559 checked for updates during an update command.\n\n\
560 Note that the addurl command can also be used to change the meta-\n\
561 information of an existing URL within the database.\n\n\
562 EXAMPLE:\n\
563 $ vitunes -e addurl \"http://198.234.121.118:80\"\n\
564 Artist: WVXU Online Radio<ENTER>\n\
565 Album: Cincinnati Public Radio<ENTER>\n\
566 Title: NPR<ENTER>\n\
567 Track: <ENTER>\n\
568 Year: <ENTER>\n\
569 Genre: Radio<ENTER>\n\
570 Length: INF<ENTER>\n\n\
571 NOTES:\n\
572 When the vitunes database has to be re-built (because of say, an upgrade\n\
573 where the database format has changed, or you simply deleted your\n\
574 database), re-adding URL's can be tedious. To ease this, consider using\n\
575 a shell script such as the 'add_urls.sh' script found on the vitunes\n\
576 website for storing & adding all of your URL's. The script simply\n\
577 executes the addurl command with all meta-information provided. As an\n\
578 example, the above EXAMPLE could be automated as:\n\n\
579 #!/bin/sh\n\
580 echo \"WVXU Online Radio\\n\\\n\
581 Cincinnati Public Radio\\n\\\n\
582 NPR\\n\\\n\
583 \\n\\\n\
584 \\n\\\n\
585 Radio\\n\\\n\
586 INF\\n\" | vitunes -e addurl \"http://198.234.121.118:80\"\n\n\
587 ");
588 }
589
590 void
ecmd_help_check(void)591 ecmd_help_check(void)
592 {
593 printf("\
594 VITUNES COMMAND:\n\tcheck - check files for meta-info and if they're in the DB\n\n\
595 SYNOPSIS:\n\tcheck [-rsd] file1 [ file2 ... ]\n\n\
596 DESCRIPTION:\n\
597 The check command will scan each filename provided to see to see if\n\
598 vitunes can extract any meta-information, to see how vitunes \"sanitizes\"\n\
599 the meta-information (see below), or what information vitunes currently has\n\
600 in the database for this file.\n\n\
601 Note that at least one of the -r, -s, or -d flags must be present.\n\n\
602 The options are as follows:\n\n\
603 -r Show the raw information extracted directly from each file.\n\n\
604 -s Show the sanitized information after extracting it from each file.\n\
605 See the section SANITATION below for details on what this is.\n\n\
606 -d Load the vitunes database and check if the each exists within it.\n\
607 If so, show the information in the vitunes database.\n\n\
608 If multiple files are provided, each will be checked in order.\n\n\
609 SANITATION:\n\
610 By \"sanitation\" above, the following is meant: some media files have\n\
611 non-printable characters in their meta information for one reason or\n\
612 another (often, it's a faulty ripper/tagging application). Sometimes\n\
613 these non-printable characters can be control sequences used by [n]curses\n\
614 and thus cause problems with the curses display of vitunes. The way\n\
615 vitunes sanitizes such data is to replace all such control characters with\n\
616 an '?'.\n\n\
617 EXAMPLE:\n\
618 $ vitunes -e check -r /path/to/file.mp3\n\n\
619 ");
620 }
621
622 void
ecmd_help_rmfile(void)623 ecmd_help_rmfile(void)
624 {
625 printf("\
626 VITUNES COMMAND:\n\trm - remove a single file/URL from the vitunes database\n\n\
627 SYNOPSIS:\n\trm [-f] filename/URL\n\n\
628 DESCRIPTION:\n\
629 To remove a single file/URL from the vitunes database, the rm command may\n\
630 be used. It takes a single parameter: the filename/URL of the file to\n\
631 remove. Normally, you will be prompted if you are sure you want to\n\
632 remove the file as a safety measure. This prompt can be avoided using\n\
633 the '-f' flag, to force the removal.\n\n\
634 Note that to remove files, the full, absolute path to the file must be\n\
635 provided, as obtained from realpath(3).\n\n\
636 EXAMPLE:\n\
637 $ vitunes -e rm -f \"http://198.234.121.118/listen.pls\"\n\n\
638 ALIAS:\n\trmfile - As e-commands, \"rmfile\" and \"rm\" are interchangeable.\n\n\
639 ");
640 }
641
642 void
ecmd_help_update(void)643 ecmd_help_update(void)
644 {
645 printf("\
646 VITUNES COMMAND:\n\tupdate - update vitunes database\n\n\
647 SYNOPSIS:\n\tupdate [-s]\n\n\
648 DESCRIPTION:\n\
649 The update command loads the existing meta information database used\n\
650 by vitunes and for each media file listed in the database, the file is\n\
651 checked to see if it has been removed or modified since it was added\n\
652 to the database.\n\n\
653 If the file has been removed, it will be removed from the database. If\n\
654 the file has been modified, it's meta information will be extracted\n\
655 again and the database will be updated.\n\
656 Note that if there are errors while checking the file, the error will be\n\
657 reported but the file, and its meta information, will remain in the\n\
658 vitunes database.\n\n\
659 -s When present, files that are skipped because they have not\n\
660 been modified will also be reported to stdout. Normally,\n\
661 only files that are updated are reported.\n\n\
662 In short, anytime you remove/modify media files already in the vitunes\n\
663 database, you should run this command.\n\n\
664 NOTE ABOUT URLS:\n\
665 Note that files added to the database using the 'addurl' e-command will\n\
666 NOT be checked/updated in any way, for obvious reasons.\n\n\
667 EXAMPLE:\n\
668 $ vitunes -e update\n\n\
669 ");
670 }
671
672 void
ecmd_help_flush(void)673 ecmd_help_flush(void)
674 {
675 printf("\
676 VITUNES COMMAND:\n\tflush - dump output of vitunes database to terminal\n\n\
677 SYNOPSIS:\n\tflush [-t time-format]\n\n\
678 DESCRIPTION:\n\
679 The flush command simply outputs the contents of the meta-information\n\
680 database used by vitunes to stdout, in a fairly easy to read (but very\n\
681 easy to parse/grep through) format.\n\n\
682 The one optional parameter, time-format, can be any string acceptable\n\
683 to strftime(3) and is used when displaying the 'last-updated' field of\n\
684 each record in the database (when the file was last checked for meta-\n\
685 information).\n\n\
686 The format used is a simple comma-separated-value (CSV) one, where most\n\
687 fields (any that can contain spaces/commas) are within double quotes.\n\
688 The first line contains the field names, and those fields that are quoted\n\
689 are also quoted in this header row.\n\n\
690 EXAMPLE:\n\
691 $ vitunes -e flush\n\n\
692 To see which files were last updated this month:\n\
693 $ vitunes -e flush -t \"%%M %%Y\" | grep \"January 2010\"\n\n\
694 REFERENCES:\n\
695 For some quick sed(1) one-liners on how to parse CSV data like the\n\
696 output of this command, you can visit the following website:\n\
697 http://sed.sourceforge.net/sedfaq4.html\n\n\
698 ");
699 }
700
701 void
ecmd_help_tag(void)702 ecmd_help_tag(void)
703 {
704 printf("\
705 VITUNES COMMAND:\n\ttag - set meta-information tags to raw files\n\n\
706 SYNOPSIS:\n\ttag [--artist=string] [-a string] [--album=string] [-A value]\n\
707 \t [--title=string] [-t string] [--genre=string] [-g string]\n\
708 \t [--track=number] [-T number] [--year=number] [-y number]\n\
709 \t [--comment=string] [-c string]\n\
710 \t file1 [ file2 ... ]\n\n\
711 DESCRIPTION:\n\
712 The tag command is provided to add/change the meta-information tags of\n\
713 media files. The meta-information fields that can be set are: artist,\n\
714 album, title, genre, track, and year.\n\n\
715 Please note that this command only changes the meta-information in the\n\
716 raw files themselves and NOT in the vitunes database. To update the\n\
717 vitunes database after tagging, use the 'update' e-command.\n\n\
718 The tag command takes a series of field/value specifiers and any number\n\
719 of files. For each file specified, the given field will be set to the\n\
720 provided value.\n\n\
721 The options are as follows:\n\n\
722 --artist=string\n\
723 -a string Sets the artist field to the provided string.\n\n\
724 --album=string\n\
725 -A string Sets the album field to the provided string.\n\n\
726 --title=string\n\
727 -t string Sets the title field to the provided string.\n\n\
728 --genre=string\n\
729 -g string Sets the genre field to the provided string.\n\n\
730 --comment=string\n\
731 -c string Sets the comment field to the provided string.\n\n\
732 --track=number\n\
733 -T number Sets the track field to the provided number.\n\
734 Note that the number must be between 0 and\n\
735 INT_MAX.\n\n\
736 --year=number\n\
737 -y number Sets the year field to the provided number.\n\
738 Note that the number must be between 0 and\n\
739 INT_MAX.\n\n\
740 At least one tag option must be provided and at least one file must\n\
741 be provided.\n\n\
742 IMPORTANT NOTE:\n\
743 Just to reiterate a comment above, this command only changes the meta-\n\
744 information in the raw files themselves and NOT in the vitunes database.\n\
745 To update the vitunes database after tagging, use the 'update' or 'add'\n\
746 e-commands.\n\n\
747 EXAMPLE:\n\
748 CD rippers frequently pull information from CDDB (or other databases)\n\
749 where, for example, a \"The\" is missing from an artist/album name\n\
750 when it is, in fact, appropriate. Below is an example of correcting\n\
751 this and then updating the vitunes database:\n\n\
752 $ vitunes -e tag --artist=\"The White Stripes\" /path/to/De_Stijl/*\n\
753 $ vitunes -e update\n\n\
754 ");
755 }
756
757 int
ecmd_help(int argc,char * argv[])758 ecmd_help(int argc, char *argv[])
759 {
760 bool found;
761 int i;
762
763 if (argc > 2)
764 errx(1, "usage: -e %s [command]", argv[0]);
765
766 /* no help requested for a specific command, give a list of all e-cmds */
767 if (argc == 1) {
768 printf("\
769 The following is a list of e-commands supported by vitunes.\n\
770 Each command is executed by doing:\n\n\
771 $ vitunes -e COMMAND [args ...]\n\n\
772 The complete manual for each can be obtained by doing:\n\n\
773 $ vitunes -e help COMMAND\n\n\
774 The list of available commands are:\n\n\
775 COMMAND BRIEF DESCRIPTION\n\
776 ------- ------------------------------------------------------------\n\
777 init Create the initial files used by vitunes.\n\n\
778 update Load the existing database and check all files contained\n\
779 within to see they have been removed or modified. The\n\
780 library is updated accordingly.\n\n\
781 add Scan the list of provided files/directories for files to add\n\
782 to the database used by vitunes.\n\n\
783 addurl Add a URL to the database, where you provide your own meta\n\
784 information.\n\n\
785 check Check files to see what meta-information vitunes can extract,\n\
786 sanitize, and whether or not it's in the database.\n\n\
787 rm Remove a file/URL from the database.\n\n\
788 rmfile Alias for \"rm\".\n\n\
789 tag Add/modify meta-information tags of raw files.\n\n\
790 flush Load the existing database and dump it's information in an\n\
791 easy-to-parse format to stdout.\n\n\
792 help This command.\n\n\
793 ");
794 return 0;
795 }
796
797 /* if reach here, help fora specific command was requested */
798 found = false;
799 for (i = 0; i < ECMD_PATH_SIZE && !found; i++) {
800 if (strcmp(argv[1], "help") == 0) {
801 printf("You're a damn fool if you need help with help.\n");
802 found = true;
803 }
804 else if (strcmp(argv[1], ECMD_PATH[i].name) == 0) {
805 (ECMD_PATH[i].help)();
806 found = true;
807 }
808 }
809
810 if (!found)
811 printf("Unknown command \"%s\". See \"vitunes -e help\" for list.\n", argv[1]);
812
813 return 0;
814 }
815
816