Commits

Carey Tilden committed 7b64bb6

Extracted track matching code into a module

  • Participants
  • Parent commits 9261fe4

Comments (0)

Files changed (2)

File bin/find-equals.pl

 
 use lib "$FindBin::RealBin/../lib";
 use Library::Analyze 'load_aliases', 'make_key', 'resolve_alias';
+use Library::Index;
 use Library::Read;
 
 usage() unless @ARGV >= 2;
 
 load_aliases('data/aliases.txt');
 
+my $index = Library::Index->new();
+
 for my $source (@sources) {
     my $lib = Library::Read->new(dbfile => $source);
     $lib->db_open();
-
-    while (my $item = $lib->fetch_item()) {
-        my ($path, %value_of) = unpack_item($item);
-
-        $info_for{$path} = $item;
-
-        for my $field (keys %value_of) {
-            next unless defined $value_of{$field};
-            push @{ $paths_with{$field}{$value_of{$field}} }, $path;
-        }
-    }
-
+    $index->load($lib);
     $lib->db_close();
 }
 
 my $lib = Library::Read->new(dbfile => $destination);
 $lib->db_open();
 
-while (my $item = $lib->fetch_item()) {
-    my ($path, %value_of) = unpack_item($item);
-
-    # Look for paths with matching values
-    my $max_matches = 3;
-    my %matches_for;
-
-    for my $field (keys %value_of) {
-        next unless defined $value_of{$field};
-
-        my $paths = $paths_with{$field}{$value_of{$field}};
-        next unless defined $paths;
-
-        for my $path (@$paths) {
-            # Remember the fields that matched
-            $matches_for{$path}{$field}++;
-
-            # And the highest number of matches
-            my $matches = keys %{ $matches_for{$path} };
-            $max_matches = $matches if $matches > $max_matches;
-        }
-    }
-
-    # Gather paths with enough matches
-    my @candidates;
-
-    for my $path (sort keys %matches_for) {
-        my @matched_fields = keys %{ $matches_for{$path} };
-        next unless @matched_fields == $max_matches;
-        next if "album;artist" eq join ";", sort @matched_fields;
-        next if "time_seconds;track_number" eq join ";", sort @matched_fields;
-        push @candidates, [ $path, @matched_fields ];
-    }
+while (my $dst_item = $lib->fetch_item()) {
+    my @candidates = $index->search($dst_item);
 
     next unless @candidates;
 
-    print summarize_item($item), "\n";
+    print summarize_item($dst_item), "\n";
 
-    for (@candidates) {
-        my ($path, @fields) = @$_;
+    for my $match (@candidates) {
+        my @fields = $match->matching_fields;
+        my $src_item = $match->item;
 
-        print +(join "\t", summarize_item($info_for{$path}), (scalar @fields), (join ";", sort @fields)), "\n";
+        print +(join "\t", summarize_item($src_item), (scalar @fields), (join ";", sort @fields)), "\n";
 
         # List values that don't match
         my %matched_on;
         @matched_on{@fields} = (1)x@fields;
 
-        for my $field (sort keys %value_of) {
+        for my $field (qw(artist album track_number name time_seconds)) {
             next if $matched_on{$field};
             no warnings 'uninitialized';
-            print "  Destination $field = $item->{$field}\n";
-            print "  Source      $field = $info_for{$path}{$field}\n";
+            print "  Destination $field = '$dst_item->{$field}'\n";
+            print "  Source      $field = '$src_item->{$field}'\n";
         }
     }
 
     exit 1;
 }
 
-sub unpack_item {
-    my ($item) = @_;
-
-    my %value_of;
-
-    for (qw(artist album name)) {
-        next unless defined $item->{$_};
-        $value_of{$_} = make_key(resolve_alias($_, $item->{$_}));
-    }
-
-    if (defined $item->track_number) {
-        $value_of{track_number} = sprintf "%02d", $item->track_number;
-    }
-
-    if (defined $item->time_seconds) {
-        $value_of{time_seconds} = int $item->time_seconds;
-    }
-
-    return $item->location, %value_of;
-}
-
 sub summarize_item {
     my ($item) = @_;
 

File lib/Library/Index.pm

+package Library::Index;
+use strict;
+use warnings;
+use Data::AsObject;
+use Moose;
+
+use Library::Analyze 'make_key', 'resolve_alias';
+
+has 'paths_with' => (is => 'ro', isa => 'HashRef', default => sub { {} });
+has 'item_at'    => (is => 'ro', isa => 'HashRef', default => sub { {} });
+
+sub load {
+    my ($self, $library) = @_;
+
+    while (my $item = $library->fetch_item()) {
+        my ($path, %value_of) = _unpack_item($item);
+
+        $self->item_at->{$path} = $item;
+
+        for my $field (keys %value_of) {
+            next unless defined $value_of{$field};
+            push @{ $self->paths_with->{$field}{$value_of{$field}} }, $path;
+        }
+    }
+}
+
+sub search {
+    my ($self, $item) = @_;
+
+    my ($path, %value_of) = _unpack_item($item);
+
+    # Look for paths with matching values
+    my $max_matches = 3;
+    my %matches_for;
+
+    for my $field (keys %value_of) {
+        next unless defined $value_of{$field};
+
+        my $paths = $self->paths_with->{$field}{$value_of{$field}};
+        next unless defined $paths;
+
+        for my $path (@$paths) {
+            # Remember the fields that matched
+            $matches_for{$path}{$field}++;
+
+            # And the highest number of matches
+            my $matches = keys %{ $matches_for{$path} };
+            $max_matches = $matches if $matches > $max_matches;
+        }
+    }
+
+    # Gather paths with enough matches
+    my @matches;
+
+    for my $path (sort keys %matches_for) {
+        my @matched_fields = keys %{ $matches_for{$path} };
+
+        next unless @matched_fields == $max_matches;
+
+        next if "album;artist" eq join ";", sort @matched_fields;
+        next if "time_seconds;track_number" eq join ";", sort @matched_fields;
+
+        push @matches, dao {
+            matching_fields => \@matched_fields,
+            item => $self->item_at->{$path},
+        };
+    }
+
+    return @matches;
+}
+
+sub _unpack_item {
+    my ($item) = @_;
+
+    my %value_of;
+
+    for (qw(artist album name)) {
+        next unless defined $item->{$_};
+        $value_of{$_} = make_key(resolve_alias($_, $item->{$_}));
+    }
+
+    if (defined $item->track_number) {
+        $value_of{track_number} = sprintf "%02d", $item->track_number;
+    }
+
+    if (defined $item->time_seconds) {
+        $value_of{time_seconds} = int $item->time_seconds;
+    }
+
+    return $item->location, %value_of;
+}
+
+1;