X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=bin%2Fid3fs-tag;h=9ed8aaced7efe4b1f4b49da120232cb08ba97fc8;hb=a88e7ff8c0d20c928dbe6ea52c779454779d2366;hp=29cc4557b7bdb5abcc79d331b1cbd7a995611374;hpb=f2202e8bd7e2eb7f73d684950d65422e4d965634;p=id3fs.git
diff --git a/bin/id3fs-tag b/bin/id3fs-tag
old mode 100644
new mode 100755
index 29cc455..9ed8aac
--- a/bin/id3fs-tag
+++ b/bin/id3fs-tag
@@ -16,90 +16,122 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+use lib '/home/ianb/projects/id3fs/id3fs/lib'; # FIXME: remove
use strict;
use Getopt::Long qw(Configure);
+use File::Find;
use ID3FS::AudioFile;
use vars qw($me);
$me=($0=~/(?:.*\/)?(.*)/)[0];
+my @extensions=qw(mp3); # FIXME: flac ogg
+my (%file_tags);
my $verbose=0;
my $help=0;
my ($artist, $album, $track, $tracknum, $year, $v1genre, $comment,
$delete_artist, $delete_album, $delete_track, $delete_tracknum,
$delete_year, $delete_v1genre, $delete_comment, $delete_all,
- $delete_genre, $genre, $add_tags, $delete_tags, @replace_tags);
+ $delete_genre, $genre, $add_tags, $delete_tags, $overwrite_tagvals,
+ $delete_tagvals, $tag_summary);
Configure(qw(bundling no_ignore_case));
my $optret=GetOptions(
- "verbose|v" => \$verbose,
- "help|h" => \$help,
- "artist|a=s" => \$artist,
- "album|l=s" => \$album,
- "song|s=s" => \$track,
- "tracknum|n=s" => \$tracknum,
- "year|y=i" => \$year,
- "v1genre|1=s" => \$v1genre,
- "comment|c=s" => \$comment,
- "delete-artist|A" => \$delete_artist,
- "delete-album|L" => \$delete_album,
- "delete-song|S" => \$delete_track,
- "delete-tracknum|N" => \$delete_tracknum,
- "delete-year|Y" => \$delete_year,
- "delete-v1genre|0" => \$delete_v1genre,
- "delete-comment|C" => \$delete_comment,
- "delete|delete-all|D" => \$delete_all,
- "delete-genre|G|delete-tags" => \$delete_genre,
- "genre|g|replace-all-tags|R=s" => \$genre,
- "add-tags|tags|t=s" => \$add_tags,
- "delete-tags|T=s" => \$delete_tags,
- "replace-tags|r=s{2}" => \@replace_tags,
+ "verbose|v" => \$verbose,
+ "help|h" => \$help,
+ "artist|a=s" => \$artist,
+ "album|l=s" => \$album,
+ "song|s=s" => \$track,
+ "tracknum|n=s" => \$tracknum,
+ "year|y=i" => \$year,
+ "v1genre|1=s" => \$v1genre,
+ "comment|c=s" => \$comment,
+ "delete-artist|A" => \$delete_artist,
+ "delete-album|L" => \$delete_album,
+ "delete-song|S" => \$delete_track,
+ "delete-tracknum|N" => \$delete_tracknum,
+ "delete-year|Y" => \$delete_year,
+ "delete-v1genre|0" => \$delete_v1genre,
+ "delete-comment|C" => \$delete_comment,
+ "delete|delete-all|D" => \$delete_all,
+ "delete-genre|delete-all-tags|G" => \$delete_genre,
+ "genre|g|replace-all-tags|R=s" => \$genre,
+ "add-tags|tags|t=s" => \$add_tags,
+ "overwrite-tagvals|tagvals|o=s" => \$overwrite_tagvals,
+ "delete-tags|T=s" => \$delete_tags,
+ "delete-tags-with-values|O=s" => \$delete_tagvals,
+ "summary|V" => \$tag_summary,
);
usage() if(!@ARGV || !$optret || $help);
-while(my $filename=shift @ARGV)
+while(my $path=shift @ARGV)
{
- unless(-f $filename)
+ unless(-e $path)
{
- warn("$me: $filename: not found\n");
+ warn("$me: $path: not found\n");
next;
}
- my $file=ID3FS::AudioFile->new($filename);
- my $changes=0;
- $changes = do_deletes($file);
- $changes += do_adds($file);
- if($changes)
- {
- do_write($file);
- }
- else
+ File::Find::find( {wanted => \&wanted, follow => 1, no_chdir => 1}, $path);
+}
+
+summarize_tags() if($tag_summary);
+
+
+sub wanted
+{
+ my $ext='';
+ if(/.*\.(.*)/) { $ext=lc($1); }
+ if(-f && scalar(grep({ $ext eq lc($_);} @extensions)))
{
- do_display($file);
+ my $file=ID3FS::AudioFile->new($_);
+ return unless($file);
+ if($tag_summary)
+ {
+ gather_tags($_, $file);
+ }
+ else
+ {
+ my $changes=0;
+ $changes = do_deletes($file);
+ $changes += do_adds($file);
+ if($changes)
+ {
+ do_write($file);
+ }
+ else
+ {
+ do_display($file);
+ }
+ }
}
}
sub do_deletes
{
my($file)=@_;
- $file->delete_artist() if($delete_artist);
- $file->delete_album() if($delete_album);
- $file->delete_track() if($delete_track);
- $file->delete_tracknum() if($delete_tracknum);
- $file->delete_year() if($delete_year);
- $file->delete_v1genre() if($delete_v1genre);
- $file->delete_comment() if($delete_comment);
- $file->delete_all() if($delete_all);
- $file->delete_genre() if($delete_genre);
- $file->delete_tags($delete_tags) if($delete_tags);
- if(@replace_tags && $replace_tags[0])
+ if($delete_all)
{
- $file->delete_tags($replace_tags[0]);
+ $file->delete_all();
+ # we don't want to save the tag if we've deleted it
+ return 0;
}
-
- return($delete_artist || $delete_album || $delete_track ||
- $delete_tracknum || $delete_year || $delete_v1genre ||
- $delete_comment || $delete_all || $delete_genre ||
- $delete_tags || (@replace_tags && $replace_tags[0]));
+ $file->delete_artist() if($delete_artist);
+ $file->delete_album() if($delete_album);
+ $file->delete_track() if($delete_track);
+ $file->delete_tracknum() if($delete_tracknum);
+ $file->delete_year() if($delete_year);
+ $file->delete_v1genre() if($delete_v1genre);
+ $file->delete_comment() if($delete_comment);
+ $file->delete_genre() if($delete_genre || $genre);
+ $file->delete_tags($delete_tags, 0) if($delete_tags);
+ $file->delete_tags($delete_tagvals, 1) if($delete_tagvals);
+ $file->delete_tags($overwrite_tagvals, 1) if($overwrite_tagvals);
+
+ my $donesomething=($delete_artist || $delete_album || $delete_track ||
+ $delete_tracknum || $delete_year || $delete_v1genre ||
+ $delete_comment || $delete_genre || $delete_tags ||
+ $delete_tagvals || defined($genre) || $overwrite_tagvals);
+ return($donesomething ? 1 : 0);
}
sub do_adds
@@ -112,26 +144,148 @@ sub do_adds
$file->year($year) if($year);
$file->v1genre($v1genre) if($v1genre);
$file->comment($comment) if($comment);
- $file->genre($genre) if($genre);
$file->add_tags($add_tags) if($add_tags);
- if(@replace_tags && $replace_tags[0])
- {
- $file->add_tags($replace_tags[1]);
- }
-
- return($artist || $album || $track || $tracknum || $year || $v1genre ||
- $comment || $genre || $add_tags || (@replace_tags && $replace_tags[0]));
+ $file->add_tags($genre) if($genre);
+ $file->add_tags($overwrite_tagvals) if($overwrite_tagvals);
+
+ my $donesomething=(defined($artist) || defined($album) ||
+ defined($track) || defined($tracknum) ||
+ defined($year) || defined($v1genre) ||
+ defined($comment) || defined($genre) ||
+ defined($add_tags) || defined($overwrite_tagvals));
+ return( $donesomething ? 1 : 0 );
}
sub do_write
{
my($file)=@_;
- $file->write();
+ $file->write();
}
sub do_display
{
my($file)=@_;
+ my $artist=$file->artist();
+ my $album=$file->album();
+ my $track=$file->track();
+ my $tracknum=$file->tracknum();
+ my $year=$file->year();
+ my $comment=$file->comment();
+ my $v1genre=$file->v1genre();
+ my @tags=$file->tags();
+ @tags = map { (ref($_) eq "ARRAY") ? join('/', grep {defined;} @{$_}) : $_; } @tags;
+ if($verbose)
+ {
+ print $file->path(), ":\n";
+ print " tracknum: $tracknum\n" if($tracknum);
+ print " artist: $artist\n" if($artist);
+ print " album: $album\n" if($album);
+ print " song: $track\n" if($track);
+ print " year: $year\n" if($year);
+ print " v1genre: $v1genre\n" if($v1genre);
+ print " comment: $comment\n" if($comment);
+ }
+ else
+ {
+ my @fields=($file->path(), $tracknum, $artist, $album, $track,
+ $year, $v1genre, $comment);
+ @fields=map { defined($_) ? $_ : ""; } @fields;
+ print join(':', @fields), "\n";
+ }
+ if(@tags)
+ {
+ if($verbose) { print " tags: "; }
+# else { print $file->path() . ":tags:"; }
+ else { print "tags:"; }
+ print join(", ", @tags), "\n";
+ }
+}
+
+sub gather_tags
+{
+ my($path, $file)=@_;
+ my @tags=$file->tags();
+ @tags=map { join('/', grep { defined; } @$_); } @tags;
+ @tags=ID3FS::AudioFile::uniq(@tags);
+ $file_tags{$path}=\@tags;
+}
+
+sub summarize_tags
+{
+ my @all_tags=ID3FS::AudioFile::uniq(map { @$_; } values(%file_tags));
+
+ # group tags by directories
+ my %dir_tags=();
+ my @dirs=map { s/(.*)\/.*/$1/; $_; } keys %file_tags;
+ @dirs=ID3FS::AudioFile::uniq(@dirs);
+ for my $tag (@all_tags)
+ {
+ DIR: for my $dir (@dirs)
+ {
+ for my $file (keys %file_tags)
+ {
+ next unless(@{$file_tags{$file}});
+ next unless($file =~ /^$dir/);
+ next DIR unless(grep { $_ eq $tag; } @{$file_tags{$file}});
+ }
+ push(@{$dir_tags{$dir}}, $tag);
+ }
+ }
+
+ # remove dir tags from files
+ for my $path (keys %dir_tags)
+ {
+ for my $file (keys %file_tags)
+ {
+ next unless($file =~ /^$path/);
+ $file_tags{$file} = [ ID3FS::AudioFile::list_remove(
+ $dir_tags{$path},
+ $file_tags{$file}) ];
+ }
+ }
+
+ # find common tags
+ my @common_tags=();
+ OUTER: for my $tag (@all_tags)
+ {
+ for my $taglist (values(%dir_tags))
+ {
+ next OUTER unless(grep { $_ eq $tag; } @$taglist);
+ }
+ push(@common_tags, $tag);
+ }
+
+ # remove common tags from %file_tags
+ for my $filename (keys(%file_tags))
+ {
+ next unless(@{$file_tags{$filename}});
+ $file_tags{$filename}= [ ID3FS::AudioFile::list_remove(\@common_tags,
+ $file_tags{$filename}) ];
+ }
+
+ # remove common tags from %dir_tags
+ for my $path (keys(%dir_tags))
+ {
+ next unless(@{$dir_tags{$path}});
+ $dir_tags{$path}= [ ID3FS::AudioFile::list_remove(\@common_tags,
+ $dir_tags{$path}) ];
+ }
+
+# print "ALL: ", join(', ', @all_tags), "\n";
+ print "Common tags: ", join(', ', @common_tags), "\n";
+# print "PER-DIR:\n";
+ for my $path (sort keys %dir_tags)
+ {
+ next unless(@{$dir_tags{$path}});
+ print "$path: ", join(', ', @{$dir_tags{$path}}), "\n";
+ }
+
+# print "PER-FILE: \n";
+ for my $filename (keys(%file_tags))
+ {
+ next unless(@{$file_tags{$filename}});
+ print "$filename: ", join(', ', @{$file_tags{$filename}}), "\n";
+ }
}
sub usage
@@ -139,49 +293,44 @@ sub usage
die("Usage: $me [-vhALSNY0CDG] [-a ARTIST] [-l ALBUM] [-s SONG] [-n TRACKNUM] FILES...\n".
" $me [-y YEAR] [-g GENRE] [-1 V1GENRE] [-c COMMENT] [--] FILES...\n".
" $me [-t TAGS,TO,ADD] [-T TAGS,TO,DELETE] FILES...\n".
- " $me [-r TAGS,TO,DELETE, TAGS,TO,ADD] [-R TAGS,TO,OVERWRITE,WITH] FILES...\n".
-
- " verbose|v \n".
- " help|h \n".
- " artist|a=s \n".
- " album|l=s \n".
- " song|s=s \n".
- " tracknum|n=s \n".
- " year|y=i \n".
- " v1genre|1=s \n".
- " comment|c=s \n".
- " delete-artist|A \n".
- " delete-album|L \n".
- " delete-song|S \n".
- " delete-tracknum|N \n".
- " delete-year|Y \n".
- " delete-v1genre|0 \n".
- " delete-comment|C \n".
- " delete|delete-all|D \n".
- " delete-genre|G|delete-tags \n".
- " genre|g|replace-all-tags|R=s \n".
- " add-tags|tags|t=s \n".
- " delete-tags|T=s \n".
- " replace-tags|r=s{2} \n".
-
- " -d|--dir=PATH Base directory of source files (default: ARGV[0])\n".
- " -f|--database=FILE Path to database file (default: basedir/.id3fs)\n".
- " -e|--extensions=EXT1,EXT2 File extensions to index (default: mp3, ogg, flac)\n".
- " -l|list List tags in use\n" .
- " -v|--verbose Verbose\n".
- " -h|--help This help\n".
- " -- End of options\n");
+ " $me [-r TAGS,TO,DELETE TAGS,TO,ADD] [-R TAGS,TO,OVERWRITE,WITH] FILES...\n".
+ "With no options, displays current info in tag\n".
+ "Options:\n".
+ " -a|--artist=ARTIST Set artist\n".
+ " -l|--album=ALBUM Set album\n".
+ " -s|--song=SONG Set song\n".
+ " -n|--tracknum=NUM Set tracknum\n".
+ " -y|--year=NUM Set year\n".
+ " -1|--v1genre=GENRE Set ID3v1 genre\n".
+ " -c|--comment=COMMENT Set comment\n".
+ " -A|--delete-artist Delete artist\n".
+ " -L|--delete-album Delete album\n".
+ " -S|--delete-song Delete song\n".
+ " -N|--delete-tracknum Delete tracknum\n".
+ " -Y|--delete-year Delete year\n".
+ " -0|--delete-v1genre Delete ID3v1 genre\n".
+ " -C|--delete-comment Delete comment\n".
+ " -D|--delete|delete-all Delete entire ID3 tag\n".
+ " -G|--delete-genre|--delete-all-tags Delete all tags stored in genre\n".
+ " -g|-R|replace-all-tags|--genre=GENRE Replace all tags in genre tag\n".
+ " -t|--add-tags|tags=TAG1,TAG2 Add tags to genre tag, merging with existing ones\n".
+ " -T|--delete-tags=TAG1,TAG2 Delete tags from genre\n".
+ " -r|--replace-tags TAGS1 TAGS2 Replace TAGS1 in genre with TAGS2\n".
+ " -V|--summary Summarize tags by directory\n" .
+ " -v|--verbose Verbose display\n".
+ " -h|--help This help\n".
+ " -- End of options\n");
}
__END__
=head1 NAME
-id3fs-index - Add files to id3fs index
+id3fs-tag - Add files to id3fs index
=head1 SYNOPSIS
-B [B<-lvh>] S<[B<-d >I]> S<[B<-f >I]> S<[B<-e >I]> [B<-->] [I...]
+B [B<-lvh>] S<[B<-d >I]> S<[B<-f >I]> S<[B<-e >I]> [B<-->] [I...]
=head1 DESCRIPTION
@@ -237,24 +386,24 @@ End of options.
Index all files in the current directory:
- id3fs-index .
+ id3fs-tag .
Index current directory, printing each subdirectory as it recurses
into it:
- id3fs-index -v .
+ id3fs-tag -v .
Just index some sub-directories:
- id3fs-index -d . dir1 dir2
+ id3fs-tag -d . dir1 dir2
Store the database in a custom location:
- id3fs-index -f ~/.id3fs/index.sqlite .
+ id3fs-tag -f ~/.id3fs/index.sqlite .
Only index .mp3 and .flac files:
- id3fs-index -e mp3,flac .
+ id3fs-tag -e mp3,flac .
=head1 BUGS