id3fs-tag -V: per-dir summaries
[id3fs.git] / bin / id3fs-tag
index 0b1f20e..9ed8aac 100755 (executable)
@@ -25,13 +25,14 @@ 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, $overwrite_tagvals,
-    $delete_tagvals);
+    $delete_tagvals, $tag_summary);
 
 Configure(qw(bundling no_ignore_case));
 my $optret=GetOptions(
@@ -58,6 +59,7 @@ my $optret=GetOptions(
     "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);
@@ -72,6 +74,9 @@ while(my $path=shift @ARGV)
     File::Find::find( {wanted => \&wanted, follow => 1, no_chdir => 1}, $path);
 }
 
+summarize_tags() if($tag_summary);
+
+
 sub wanted
 {
     my $ext='';
@@ -80,16 +85,23 @@ sub wanted
     {
        my $file=ID3FS::AudioFile->new($_);
        return unless($file);
-       my $changes=0;
-       $changes =  do_deletes($file);
-       $changes += do_adds($file);
-       if($changes)
+       if($tag_summary)
        {
-           do_write($file);
+           gather_tags($_, $file);
        }
        else
        {
-           do_display($file);
+           my $changes=0;
+           $changes =  do_deletes($file);
+           $changes += do_adds($file);
+           if($changes)
+           {
+               do_write($file);
+           }
+           else
+           {
+               do_display($file);
+           }
        }
     }
 }
@@ -189,6 +201,93 @@ sub do_display
     }
 }
 
+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
 {
     die("Usage: $me [-vhALSNY0CDG] [-a ARTIST] [-l ALBUM] [-s SONG] [-n TRACKNUM] FILES...\n".
@@ -217,6 +316,7 @@ sub usage
        " -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");