X-Git-Url: http://erislabs.net/gitweb/?p=id3fs.git;a=blobdiff_plain;f=bin%2Fid3fs-tag;h=9566d2c6c31531e2ebcb7795ec93d3a9cbbc5de5;hp=fbaf58db230d800f7b7ffba7a6ea25e982f7f634;hb=5083c8eb3103daaf471be64614df04ccf7bb55d7;hpb=3a873819780cb3f2d640eabb8776bdbbe75c72e4 diff --git a/bin/id3fs-tag b/bin/id3fs-tag old mode 100644 new mode 100755 index fbaf58d..9566d2c --- a/bin/id3fs-tag +++ b/bin/id3fs-tag @@ -19,88 +19,121 @@ 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 (%argv_tags, %dir_tags, %file_tags); +my $current_argv; 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 + $current_argv=$path; # ick, global nastiness + 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 @@ -113,26 +146,104 @@ 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; + my @argv_tags=(); + @argv_tags=@{$argv_tags{$current_argv}} if($argv_tags{$current_argv}); + $argv_tags{$current_argv}=[ ID3FS::AudioFile::uniq(@tags, @argv_tags) ]; +} + +sub summarize_tags +{ + my @all_tags=ID3FS::AudioFile::uniq(map { @$_; } values(%argv_tags)); + # find common tags + my @common_tags=(); +OUTER: for my $tag (@all_tags) + { + for my $taglist (values(%argv_tags)) + { + next OUTER unless(grep { $_ eq $tag; } @$taglist); + } + push(@common_tags, $tag); + } + print "ALL: ", join(', ', @all_tags), "\n"; + print "COMMON: ", join(', ', @common_tags), "\n"; + + use Data::Dumper; + # remove common tags from %argv_tags + for my $argv (keys(%argv_tags)) + { + next unless(@{$argv_tags{$argv}}); + $argv_tags{$argv}= [ ID3FS::AudioFile::list_remove(\@common_tags, $argv_tags{$argv}) ]; + } + + print "PER-DIR: \n"; + for my $argv (keys(%argv_tags)) + { + print "$argv: ", join(', ', @{$argv_tags{$argv}}), "\n"; + } } sub usage @@ -140,49 +251,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 @@ -238,24 +344,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