diff options
-rw-r--r-- | NEWS | 20 | ||||
-rw-r--r-- | doc/idest.texi | 234 | ||||
-rw-r--r-- | examples/echo.scm | 18 | ||||
-rw-r--r-- | src/idop.c | 46 | ||||
-rw-r--r-- | src/main.c | 30 |
5 files changed, 227 insertions, 121 deletions
@@ -31,7 +31,7 @@ The --copy option can be used together with --set and --script. The --filter=FRAME-LIST option can be used in conjunction with --query, --copy and --delete. It abridges the scope of operation -to the fields from FRAME-LIST. The `--delete --filter=FRAME-LIST' +to fields from FRAME-LIST. The `--delete --filter=FRAME-LIST' is equivalent to `--delete=FRAME-LIST'. * New option --list-frames (-L) @@ -90,9 +90,8 @@ The full syntax is: NAME:QUAL Where NAME is a frame name or ID and QUAL stands for a list of -qualifiers separated by colons. Empty -qualifiers and '*' act as wildcards, matching any actual field value. -For example: +qualifiers separated by colons. Empty qualifiers and '*' act as +wildcards, matching any actual field value. For example: --query title,comment::my-comment --delete=comment:eng:my-comment @@ -129,6 +128,17 @@ frame qualifiers. For example, for "comment" (COMM) frames: the text is written condesc content descriptor +Unsupported or partially-supported frames contain a single property: +rawdata. The value of this property is a list of frame fields. Each +field is represented by a triplet (ORD TYPE VALUE), where ORD is the +ordinal number of that field in frame, TYPE is its type (integer) and +VALUE is its value. If TYPE is one of numeric types, VALUE is the +numeric value converted to string (string->number will bring it back +to number). If TYPE is a string type, VALUE contains the string in +the appropriate encoding. Otherwise, VALUE holds the field value as +a binary string. Each byte in such a string is represented by two +hexagesimal digits. For example, "AB\n" is represented as 41420A. + * Guile startup files. When run with the --script option, idest searches for files .idest.scm, @@ -145,7 +155,7 @@ This feature is disabled if the argument contains directory separators * Test script `echo.scm' -The test script `echo.scm' is installed into the program script +The test script `echo.scm' is installed in the program script directory. It allows you to check whether your scripts work in the expected way and correctly modify the frames. For example, to test the script `modify.scm' run diff --git a/doc/idest.texi b/doc/idest.texi index 37bdfec..f88b42a 100644 --- a/doc/idest.texi +++ b/doc/idest.texi @@ -37,13 +37,9 @@ Copyright @copyright{} 2009-2011 Sergey Poznyakoff Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no -Invariant Sections, with the Front-Cover texts being ``IdEst -- an -ID3 Edit and Scripting Tool'' and with the Back-Cover Texts as in (a) -below. A copy of the license is included in the section entitled -``GNU Free Documentation License''. - -(a) The Back-Cover Text is: ``You have freedom to copy and modify -this manual, like GNU software.'' +Invariant Sections, and without restrictions as to the Front and +Back-Cover texts. A copy of the license is included in the section +entitled ``GNU Free Documentation License''. @end copying @titlepage @@ -74,9 +70,10 @@ documents IdEst @value{VERSION}. @menu * Intro:: Introduction to ID3 Tags and @samp{IdEst} * Tag Versions:: ID3 Tag Versions +* Frames:: ID3 Frames * View:: Viewing Existing Tags * Modify:: Modifying Existing Tags -* Delete:: Deleting Tags +* Delete:: Deleting Tags and Frames * ID Versioning:: Storing Tags in Different ID3 Versions * Structure:: Examining File Structure * Scripting:: Scripting @@ -139,7 +136,7 @@ creating version 1 tags or converting version 2 to version 1. Properly speaking, the pure version 1 tag is seldom used. It is its modified version, called 1.1 which is used most often. - The version 1.1 tag contains the following fields: + The version 1.1 tag contains the following frames: @float Table, ID3v1 @caption{ID3v1.1 tag} @@ -156,11 +153,11 @@ modified version, called 1.1 which is used most often. @end float The last field, @samp{genre}, merits special notice. It is an ordinal -number of the genre in a predefined table of genres (@pxref{Genre +number of genre in a predefined table of genres (@pxref{Genre Codes}). When modifying or setting this tag, you should supply one of the values listed in that table (case-insensitive). If the value you supply is not found in that table, the value @samp{Other} will be -set. +used. @cindex ID3v2 @cindex ID3 version 2 @@ -183,6 +180,73 @@ corresponding to the ID3v1 fields, are: @end multitable @end float +@node Frames +@chapter ID3 Frames +@cindex frames +Each ID3 tag consists of frames. As described above, IDv1 tags +contain a fixed set of frames, whereas IDv2 tags can contain +any number of this. The frame @dfn{ID} is a four-character name +which identifies a frame. + +@cindex qualifiers +There are frames that can appear only once in a tag, and there are +such that can appear multiple times. These latter have some +additional fields which serve to discern between them. In +@command{idest} parlance we call these fields frame @dfn{qualifiers}. +The number and semantics of qualifiers are frame-dependent. For +example, the @samp{COMM} (comment) frame contains two qualifiers: +@dfn{language}, which holds a three-letter code of the language the +comment is written in, and @dfn{content descriptor}, which holds +arbitrary string describing the comment. + +@anchor{fully-qualified name} +@cindex fully-qualified frame name +There are two ways to address a frame: by its ID, and by its +fully-qualified name. Addressing the frame by its ID retrieves all +instances of that frame. A @dfn{fully-qualified name}, on the other +hand, provides a way to retrieve a particular instance of the frame. +A fully-qualified name consists of frame ID, followed by a colon and +a list of qualifier values, delimited with colons. For example, the +name @samp{COMM:eng:my-comment} will select the @samp{COMM} frame +which has @samp{eng} in its language field and @samp{my-comment} in +its content descriptor field. Any of qualifiers in a fully-qualified +name can be omitted. Such an empty qualifier works as a +@dfn{wildcard}, matching any value in the actual field. Thus, +@samp{COMM::my-comment} select the @samp{COMM} frames with content +descriptor @samp{my-comment}, no matter what their language. + +@xopindex{list-frames, introduced} +To examine the qualifiers a particular frame has, use the +@option{--list-frames} (@option{-L}) option. It lists all the supported +frames: + +@example +$ idest --list-frames +COMM:lang:condesc Comments +TALB Album/movie/show title +TBPM BPM (beats per minute) +TCOM Composer +... +@end example + +The output it produces consists of two columns: the first one shows +the frame ID and its qualifiers (if any). The second one contains a +short description of this frame purpose. + +@xopindex{filter, introduced} +To select one or several frames of interest, give their names as +argument (a comma-separated list) to the @option{--filter} +(@option{-F}) argument, e.g.: + +@example +$ idest --list --filter=COMM,TXXX +COMM:lang:condesc Comments +TXXX:descr User defined text information frame +@end example + +The @option{--filter} option is a standard way to abridge +@command{idest} operation to a subset of frames. + @node View @chapter Viewing Existing Tags @@ -203,42 +267,78 @@ genre: Folk @cindex query mode @xopindex{query, introduced} This operation mode is called @dfn{query mode}. By default, -@command{idest} shows all these fields in this order. If you wish to -display only a subset of them, use the @option{--query} option -(or @option{-q}, for short). It takes a comma-separated list of the -frame names to query. For example: +@command{idest} shows all these fields in this order. If there are +several comment fields, they will be shown in the fully-qualified +form, e.g.: @example -$ idest --query=artist,title,year file.mp3 +$ idest track01.mp3 +title: Plou i fa sol +album: Camins de Tarda +track: 3 +comment:eng:my: Comment text +comment:eng:encoder: lame +artist: Josep Tero +year: 1995 +genre: Folk +@end example + +@xopindex{filter, in query mode} +If you wish to display another frames, use the @option{--filter} +(@option{-F}) option: + +@example +$ idest --filter=artist,title,year file.mp3 artist: Joan Baez title: Diamonds & Rust year: 1975 @end example -If the long option form is used, as in the example above, then the -frame list must be separated from it by an equal sign, with no -surrounding white space. If the short option form is used, the list -must follow the option letter, with no white space in between, e.g.: +The names given in the filter list can be either IDv1 or IDv2 names, +@command{idest} will convert the to IDv2 automatically. + +Frames can also be given in a fully-qualified form, for example: @example -$ idest -qartist,title,year file.mp3 +$ idest --filter=title,comment::encoder track01.mp3 +title: Plou i fa sol +comment:eng:encoder: lame @end example -The frame list may contain either field names (@pxref{ID3v1}) or -the corresponding standard frame names (@pxref{ID3v2}). +You can also define a string which will be printed instead of the +frame name in the output. This string is given as a prefix to the +frame name. The two parts are delimited by a percent sign, e.g.: + +@example +$ idest --filter=Title%title,'Encoded by'%comment::encoder track01.mp3 +Title: Plou i fa sol +Encoded by: lame +@end example @anchor{describe} -@xopindex{describe, introduced} To describe frames in a verbose manner, use the @option{--describe} (@option{-D}) option'' @example -$ idest --describe -qartist,title,year file.mp3 +$ idest --describe --filter=artist,title,year file.mp3 Lead performer(s)/soloist(s): Joan Baez Title/songname/content description: Diamonds & Rust Recording time: 1975 @end example +@xopindex{query, introduced} +For compatibility with previous versions, the @option{--query} option +(or @option{-q}, for short) is supported. When used without argument +it forces the query mode. If argument is supplied, it must be in the +same format as for the @option{--filter} option and has the same +effect (e.g. @code{idest -qartist,title,year file.mp3}). + +If the long option form (@option{--query}) is used, then the frame +list must be separated from the option by an equal sign, with no +surrounding white space. If the short option form (@option{-q}) is +used, the list must follow the option letter, with no white space in +between. + @xopindex{latin1, introduced} @cindex UTF-8 @cindex ISO-8859-1 @@ -257,11 +357,11 @@ year: 1991 genre: @end example -Of course, @option{--latin1} and @option{--query} can be used -together: +Of course, @option{--latin1} and @option{--filter} (or +@option{--query}) can be used together: @example -$ idest --latin1 --query=title,artist,album track06.mp3 +$ idest --latin1 --filter=title,artist,album track06.mp3 title: D@'ona'm sa m@`a artist: Llu@'is Llach album: Torna aviat @@ -270,45 +370,12 @@ album: Torna aviat Future versions of @command{idest} will provide more sophisticated recoding facilities. -@anchor{comment frames} -@cindex comment frames, in query mode -@cindex content descriptor, in comment frames -@cindex language, in comment frames -If several frames match a queried frame name, @command{idest} -concatenates their values and displays them on a single line. Comment -frames are treated specially. Each comment frame has a @dfn{language -field}, which holds a three-letter code of the language the comment is -written in, and a @dfn{content descriptor} field, which is supposed to -help discern multiple comment entries. In the concatenated string, -each frame value is prefixed by the corresponding content -descriptor enclosed in square brackets. For example: - -@example -$ idest track01.mp3 -title: Cor i arbre -album: Fronteres -track: 1 -comment: [Bit_Rate]: 320 [Sample_Rate]: 44100 -artist: Josep Tero -year: 2009 -genre: -@end example - @anchor{all-frames query} @anchor{fully qualified comment} @xopindex{all, introduced} @cindex comment, fully qualified form There is a special option which instructs @command{idest} to output -all frames: the @option{--all} (@option{-a}) option. When it is used, -comment headers are output in @dfn{fully qualified} form, i.e.: - -@example -comment:@var{lang}:@var{descr} -@end example - -@noindent -where @var{lang} is the three-letter language code and @var{descr} is -the content descriptor. For example: +all frames: the @option{--all} (@option{-a}) option: @example $ idest --all track01.mp3 @@ -323,16 +390,6 @@ year: 2009 genre: @end example -You can also use fully qualified comment form in frame lists given -with the @option{--query} option, e.g.: - -@example -$ idest --all title,track,comment::Sample_Rate track01.mp3 -title: Cor i arbre -track: 1 -comment: [Sample_Rate]: track01.mp3 -@end example - @node Modify @chapter Modifying Existing Tags @@ -344,16 +401,26 @@ option. For example: $ idest --set artist='Jacques Brel' track01.mp3 @end example -Several @option{--set} options may be used together to set several -fields at once: +Several frames can be set at once. To do so you can either supply +a distinct @option{--set} option for each frame, or to give a +single @option{--set} option followed by as many frame assignments as +you need, for example: @example $ idest --set artist='Jacques Brel' \ --set title='Ne me quitte pas' track01.mp3 @end example -When setting comment frames, you can use fully qualified form -(@pxref{fully qualified comment}), e.g.: +@noindent +or + +@example +$ idest --set artist='Jacques Brel' \ + title='Ne me quitte pas' track01.mp3 +@end example + +You can use fully qualified form (@pxref{fully-qualified name}) for +frames that require it: @example $ idest --set comment:eng:My_comment='Noise reduction on' track01.mp3 @@ -377,7 +444,7 @@ $ idest --latin1 --set artist='Llu@'is Llach' *.mp3 @end example @node Delete -@chapter Deleting Tags +@chapter Deleting Tags and Frames @xopindex{delete, described} The @option{--delete} (@option{-d}) option instructs @command{idest} @@ -391,11 +458,20 @@ $ idest --delete *.mp3 After this operation, all ID3 data are irrevocably lost, so use it with caution. - A list of frame names can be given as an argument to +@xopindex{filter, used with --delete} + A list of frame names can be given either with the @option{--filter} +option, or (for compatibility with @command{idest} 1.x) as an argument to @option{--delete} (similarly to @option{--query}). For example, to delete only comment and genre tags: @example +$ idest --delete --filter=comment,genre *.mp3 +@end example + +@noindent +or + +@example $ idest --delete=comment,genre *.mp3 @end example @@ -404,7 +480,7 @@ all comment frames. To remove a particular one, use its qualified form: @example -$ idest --delete=comment::Bit_Rate track01.mp3 +$ idest --delete --filter=comment::Bit_Rate track01.mp3 @end example @node ID Versioning diff --git a/examples/echo.scm b/examples/echo.scm index 869b6b9..d6b07ab 100644 --- a/examples/echo.scm +++ b/examples/echo.scm @@ -1,24 +1,28 @@ -;; echo.scm - test a user script. +;; echo.scm - test another Idest script. ;; Copyright (C) 2011 Sergey Poznyakoff ;; License GPLv3+: GNU GPL version 3 or later ;; <http://gnu.org/licenses/gpl.html> ;; This is free software: you are free to change and redistribute it. ;; There is NO WARRANTY, to the extent permitted by law. -(let ((cmd (command-line))) +(let* ((cmd (command-line)) + (progname (car cmd))) + (set! %load-hook (lambda (filename) + (format #t "~A: loading ~a ...\n" + progname filename))) (cond ((< (length cmd) 3) - (error "usage: echo.scm SCRIPT FILE...") + (format (current-error-port) "usage: idest -S ~A SCRIPT FILE...~%" + progname) (exit 1)) (else (let ((file-name (list-ref cmd 1))) (set-program-arguments (cons (car cmd) (list-tail cmd 2))) - (load file-name) + (primitive-load-path file-name) (cond (idest-readonly (format (current-error-port) - "~A does not modify frames~%" file-name) - (exit 1)) + "~A: info: ~A does not modify frames~%" progname file-name)) ((catch #t (lambda () (procedure? idest-main)) @@ -32,6 +36,6 @@ (format #t "File ~A ~A~%" file result)))))) (else (format (current-error-port) - "idest-main is not defined in ~A~%" file-name) + "~A: idest-main is not defined in ~A~%" progname file-name) (exit 1))))))) @@ -125,6 +125,52 @@ output_list_free() } } +#define DESCR_COLUMN 24 +static int +describe_frame(const struct idest_frametab *ft, void *data) +{ + struct id3_frametype const *frametype; + int n; + + frametype = id3_frametype_lookup(ft->id, 4); + assert(frametype != NULL); + printf("%s", ft->id); + n = 0; + if (ft->qc) { + int i; + for (i = 0; i < ft->qc; i++) + n += printf(":%s", ft->qv[i]); + } + while (++n < DESCR_COLUMN) + putchar(' '); + + printf("%s\n", frametype->description); + return 0; +} + +void +list_supported_frames(void) +{ + if (filter_list) { + gl_list_iterator_t itr; + const void *p; + + itr = gl_list_iterator(filter_list); + + while (gl_list_iterator_next(&itr, &p, NULL)) { + const struct ed_item *item = p; + const struct idest_frametab *ft = + idest_frame_lookup(item->id); + if (!ft) + error(1, 0, "no such frame: %s", + item->id); + describe_frame(ft, NULL); + } + gl_list_iterator_free(&itr); + } else + frametab_enumerate(describe_frame, NULL, 1); +} + void safe_id3_file_update_and_close(struct id3_file *file) { @@ -98,36 +98,6 @@ qv_free(size_t qc, char **qv) free(qv); } -#define DESCR_COLUMN 24 -static int -describe_frame(const struct idest_frametab *ft, void *data) -{ - struct id3_frametype const *frametype; - int n; - - frametype = id3_frametype_lookup(ft->id, 4); - assert(frametype != NULL); - printf("%s", ft->id); - n = 0; - if (ft->qc) { - int i; - for (i = 0; i < ft->qc; i++) - n += printf(":%s", ft->qv[i]); - } - while (++n < DESCR_COLUMN) - putchar(' '); - - printf("%s\n", frametype->description); - return 0; -} - -static void -list_supported_frames(void) -{ - frametab_enumerate(describe_frame, NULL, 1); -} - - void verify_mp3(FILE *fp, const char *name) { |