3 # id3fs - a FUSE-based filesystem for browsing audio metadata
4 # Copyright (C) 2010 Ian Beckwith <ianb@erislabs.net>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 use lib '/home/ianb/projects/id3fs/id3fs/lib'; # FIXME: remove
21 use Getopt::Long qw(Configure);
24 $me=($0=~/(?:.*\/)?(.*)/)[0];
28 my ($artist, $album, $track, $tracknum, $year, $v1genre, $comment,
29 $delete_artist, $delete_album, $delete_track, $delete_tracknum,
30 $delete_year, $delete_v1genre, $delete_comment, $delete_all,
31 $delete_genre, $genre, $add_tags, $delete_tags, @replace_tags);
33 Configure(qw(no_ignore_case));
34 my $optret=GetOptions(
35 "verbose|v" => \$verbose,
37 "artist|a=s" => \$artist,
38 "album|l=s" => \$album,
39 "song|s=s" => \$track,
40 "tracknum|n=s" => \$tracknum,
42 "v1genre|1=s" => \$v1genre,
43 "comment|c=s" => \$comment,
44 "delete-artist|A" => \$delete_artist,
45 "delete-album|L" => \$delete_album,
46 "delete-song|S" => \$delete_track,
47 "delete-tracknum|N" => \$delete_tracknum,
48 "delete-year|Y" => \$delete_year,
49 "delete-v1genre|0" => \$delete_v1genre,
50 "delete-comment|C" => \$delete_comment,
51 "delete|delete-all|D" => \$delete_all,
52 "delete-genre|delete-all-tags|G" => \$delete_genre,
53 "genre|g|replace-all-tags|R=s" => \$genre,
54 "add-tags|tags|t=s" => \$add_tags,
55 "delete-tags|T=s" => \$delete_tags,
56 "replace-tags|r=s{2}" => \@replace_tags,
59 usage() if(!@ARGV || !$optret || $help);
61 while(my $filename=shift @ARGV)
65 warn("$me: $filename: not found\n");
68 my $file=ID3FS::AudioFile->new($filename);
70 $changes = do_deletes($file);
71 $changes += do_adds($file);
85 $file->delete_artist() if($delete_artist);
86 $file->delete_album() if($delete_album);
87 $file->delete_track() if($delete_track);
88 $file->delete_tracknum() if($delete_tracknum);
89 $file->delete_year() if($delete_year);
90 $file->delete_v1genre() if($delete_v1genre);
91 $file->delete_comment() if($delete_comment);
92 $file->delete_genre() if($delete_genre);
93 $file->delete_tags($delete_tags) if($delete_tags);
94 if(@replace_tags && $replace_tags[0])
96 $file->delete_tags($replace_tags[0]);
98 $file->delete_all() if($delete_all);
100 # we don't want to save the tag if we've deleted it
101 return 0 if($delete_all);
103 my $donesomething=($delete_artist || $delete_album || $delete_track ||
104 $delete_tracknum || $delete_year || $delete_v1genre ||
105 $delete_comment || $delete_genre || $delete_tags ||
106 (@replace_tags && $replace_tags[0]));
107 return($donesomething ? 1 : 0);
113 $file->artist($artist) if($artist);
114 $file->album($album) if($album);
115 $file->track($track) if($track);
116 $file->tracknum($tracknum) if($tracknum);
117 $file->year($year) if($year);
118 $file->v1genre($v1genre) if($v1genre);
119 $file->comment($comment) if($comment);
120 $file->genre($genre) if($genre);
121 $file->add_tags($add_tags) if($add_tags);
122 if(@replace_tags && $replace_tags[0])
124 $file->add_tags($replace_tags[1]);
127 my $donesomething=(defined($artist) || defined($album) ||
128 defined($track) || defined($tracknum) ||
129 defined($year) || defined($v1genre) ||
130 defined($comment) || defined($genre) ||
131 defined($add_tags) ||
132 (@replace_tags && defined($replace_tags[1])));
133 return( $donesomething ? 1 : 0 );
145 my $artist=$file->artist();
146 my $album=$file->album();
147 my $track=$file->track();
148 my $tracknum=$file->tracknum();
149 my $year=$file->year();
150 my $comment=$file->comment();
151 my $v1genre=$file->v1genre();
152 my @tags=$file->tags();
153 @tags = map { (ref($_) eq "ARRAY") ? join('/', grep {defined;} @{$_}) : $_; } @tags;
156 print $file->path(), ":\n";
157 print " tracknum: $tracknum\n" if($tracknum);
158 print " artist: $artist\n" if($artist);
159 print " album: $album\n" if($album);
160 print " song: $track\n" if($track);
161 print " year: $year\n" if($year);
162 print " v1genre: $v1genre\n" if($v1genre);
163 print " comment: $comment\n" if($comment);
167 my @fields=($file->path(), $tracknum, $artist, $album, $track,
168 $year, $v1genre, $comment);
169 @fields=map { defined($_) ? $_ : ""; } @fields;
170 print join(':', @fields), "\n";
174 if($verbose) { print " tags: "; }
175 else { print $file->path() . ":tags:"; }
176 print join(", ", @tags), "\n";
182 die("Usage: $me [-vhALSNY0CDG] [-a ARTIST] [-l ALBUM] [-s SONG] [-n TRACKNUM] FILES...\n".
183 " $me [-y YEAR] [-g GENRE] [-1 V1GENRE] [-c COMMENT] [--] FILES...\n".
184 " $me [-t TAGS,TO,ADD] [-T TAGS,TO,DELETE] FILES...\n".
185 " $me [-r TAGS,TO,DELETE TAGS,TO,ADD] [-R TAGS,TO,OVERWRITE,WITH] FILES...\n".
186 "With no options, displays current info in tag\n".
188 " -a|--artist=ARTIST Set artist\n".
189 " -l|--album=ALBUM Set album\n".
190 " -s|--song=SONG Set song\n".
191 " -n|--tracknum=NUM Set tracknum\n".
192 " -y|--year=NUM Set year\n".
193 " -1|--v1genre=GENRE Set ID3v1 genre\n".
194 " -c|--comment=COMMENT Set comment\n".
195 " -A|--delete-artist Delete artist\n".
196 " -L|--delete-album Delete album\n".
197 " -S|--delete-song Delete song\n".
198 " -N|--delete-tracknum Delete tracknum\n".
199 " -Y|--delete-year Delete year\n".
200 " -0|--delete-v1genre Delete ID3v1 genre\n".
201 " -C|--delete-comment Delete comment\n".
202 " -D|--delete|delete-all Delete entire ID3 tag\n".
203 " -G|--delete-genre|--delete-all-tags Delete all tags stored in genre\n".
204 " -g|-R|replace-all-tags|--genre=GENRE Replace all tags in genre tag\n".
205 " -t|--add-tags|tags=TAG1,TAG2 Add tags to genre tag, merging with existing ones\n".
206 " -T|--delete-tags=TAG1,TAG2 Delete tags from genre\n".
207 " -r|--replace-tags TAGS1 TAGS2 Replace TAGS1 in genre with TAGS2\n".
208 " -v|--verbose Verbose display\n".
209 " -h|--help This help\n".
210 " -- End of options\n");
217 id3fs-tag - Add files to id3fs index
221 B<id3fs-tag> [B<-lvh>] S<[B<-d >I<basedir>]> S<[B<-f >I<dbpath>]> S<[B<-e >I<mp3,ogg,flac>]> [B<-->] [I<DIR>...]
225 Extracts id3 tags from mp3 files (and comment tags from ogg and flac
226 files) and adds them to a sqlite database, ready for mounting
233 =item B<-l> | B<--list>
235 List tags in use in specified database.
237 =item S<B<-d >I<PATH>> | S<B<--dir=>I<PATH>>
239 Specify base directory of source files. All files will be indexed
240 relative to this point.
242 If not specified, defaults to the first non-option argument on the
243 command line. Note that to avoid ambiguities, if more than one
244 directory is specified on the command line, the base directory must
245 be specified explicitly.
247 All files indexed must be under the base directory.
249 =item S<B<-f >I<FILE>> | S<B<--database=>I<FILE>>
251 Database file to use. If not specified, defaults to
252 a hidden file called B<".id3fs"> under the base directory.
254 =item S<B<-e >I<EXT1,EXT2>> | S<B<--extensions=>I<EXT1,EXT2>>
256 File extensions to consider when indexing.
257 Defaults to B<.mp3>, B<.ogg> and B<.flac>.
261 Enable verbose operation.
265 Show a short help message.
275 Index all files in the current directory:
279 Index current directory, printing each subdirectory as it recurses
284 Just index some sub-directories:
286 id3fs-tag -d . dir1 dir2
288 Store the database in a custom location:
290 id3fs-tag -f ~/.id3fs/index.sqlite .
292 Only index .mp3 and .flac files:
294 id3fs-tag -e mp3,flac .
298 Please report any found to ianb@erislabs.net
302 L<id3fsd(8)>, L<MP3::Tag>, L<Audio::Flac::Header>, L<Ogg::Vorbis::Header>
306 Ian Beckwith <ianb@erislabs.net>
308 Many thanks to Aubrey Stark-Toller for help wrangling SQL.
312 The latest version can be found at:
314 L<http://erislabs.net/ianb/projects/id3fs/>
318 Copyright (C) 2010 Ian Beckwith <ianb@erislabs.net>
320 This program is free software: you can redistribute it and/or modify
321 it under the terms of the GNU General Public License as published by
322 the Free Software Foundation, either version 3 of the License, or
323 (at your option) any later version.
325 This program is distributed in the hope that it will be useful,
326 but WITHOUT ANY WARRANTY; without even the implied warranty of
327 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
328 GNU General Public License for more details.
330 You should have received a copy of the GNU General Public License
331 along with this program. If not, see <http://www.gnu.org/licenses/>.