--- /dev/null
+package ID3FS::File;
+
+use strict;
+use warnings;
+use ID3FS::File::Mp3;
+use ID3FS::File::Ogg;
+use ID3FS::File::Flac;
+
+# omg a factory class, I feel vaguely dirty
+sub new
+{
+ my (undef,$path)=@_;
+ my $ext=($path=~/.*\.(.*)/)[0];
+ return undef unless($ext);
+ $ext=lc($ext);
+ if($ext eq "mp3")
+ {
+ return ID3FS::File::Mp3->new($path);
+ }
+ elsif($ext eq "ogg")
+ {
+ return ID3FS::File::Ogg->new($path);
+ }
+ elsif($ext eq "flac")
+ {
+ return ID3FS::File::Flac->new($path);
+ }
+ else
+ {
+ print("Unknown extension: $ext\n");
+ return undef;
+ }
+}
+
+1;
--- /dev/null
+package ID3FS::File::Flac;
+
+use strict;
+use warnings;
+use Audio::FLAC::Header;
+
+sub new
+{
+ my $proto=shift;
+ my $class=ref($proto) || $proto;
+ my $self={};
+ bless($self,$class);
+
+ $self->{path}=shift;
+ my $flac=Audio::FLAC::Header->new($self->{path});
+ $self->{tags}=$flac->tags();
+ return $self;
+}
+
+sub get
+{
+ my ($self, $tag, $complain)=@_;
+ for my $key (keys %{$self->{tags}})
+ {
+ if($key =~ /$tag/i &&
+ defined($self->{tags}->{$key}) &&
+ length($self->{tags}->{$key}) &&
+ $self->{tags}->{$key} =~ /\S+/)
+ {
+ my $val=$self->{tags}->{$key};
+ $val =~ s/\//-/g; # drop slashes
+ return $val;
+ }
+ }
+ warn("$self->{path}: no $tag defined in FLAC comments\n") if($complain);
+ return undef;
+}
+
+sub artist { shift->get("ARTIST", 1); }
+sub album { shift->get("ALBUM", 1); }
+sub audiotype { return "flac"; }
+sub haspic { return undef; } # FIXME
+sub v1genre { return undef; } # ID3 only
+
+# We don't care if year is not set
+sub year
+{
+ my ($self)=@_;
+ my $date=shift->get("DATE", 0);
+ return undef unless($date);
+ if($date =~/(\d\d\d\d)/)
+ {
+ $date=$1;
+ }
+ return $date;
+}
+
+sub tags
+{
+ my $self=shift;
+ my @tags=();
+ my $tags={};
+ for my $key (keys %{$self->{tags}})
+ {
+ if($key =~ /genre/i &&
+ defined($self->{tags}->{$key}) &&
+ length($self->{tags}->{$key}) &&
+ $self->{tags}->{$key} =~ /\S+/)
+ {
+ push(@tags, $self->{tags}->{$key});
+ }
+ }
+ # combine then split on commas
+ # so multiple comma-delimited tags will work
+ @tags=split(/\s*,\s*/, join(', ', @tags));
+ for my $tag (@tags)
+ {
+ if($tag=~/([^\/]+)\/(.*)/)
+ {
+ my $tagname=$1;
+ my $tagval=$2;
+ $tagval=~s/\//-/g;
+ $self->{tags}->{$tagname}=$tagval;
+ }
+ else
+ {
+ $self->{tags}->{$tag}=undef;
+ }
+ }
+ return $self->{tags};
+}
+
+1;
+
--- /dev/null
+package ID3FS::File::Mp3;
+
+use strict;
+use warnings;
+use MP3::Tag;
+
+sub new
+{
+ my $proto=shift;
+ my $class=ref($proto) || $proto;
+ my $self={};
+ bless($self,$class);
+
+ $self->{path}=shift;
+ $self->{mp3}=MP3::Tag->new($self->{path});
+ $self->get_tags();
+ $self->{v1}=undef;
+ $self->{v1}=$self->{mp3}->{ID3v1} if(exists($self->{mp3}->{ID3v1}));
+ $self->{v2}=undef;
+ $self->{v2}=$self->{mp3}->{ID3v2} if(exists($self->{mp3}->{ID3v2}));
+
+ $self->{tags}={};
+
+ return $self;
+}
+
+sub choose
+{
+ my ($self, $func, $verbose)=@_;
+ my $thing=undef;
+ if(defined($self->{v2}))
+ {
+ $thing=$self->{v2}->$func();
+ }
+ if(defined($self->{v1}) && (!defined($thing) || !length($thing)))
+ {
+ $thing=$self->{v1}->$func();
+ }
+ if(!defined($thing) || !length($thing))
+ {
+ warn("$self->{path}: no $func defined in ID3 tags\n") if($verbose);
+ return undef;
+ }
+ $thing=~s/\//-/g; # drop slashes
+ return $thing;
+}
+
+sub artist { shift->choose("artist", 1); }
+sub album { shift->choose("album", 1); }
+# We don't care if year is not set
+sub year { shift->choose("year", 0); }
+sub audiotype { return "mp3"; }
+sub haspic { return undef; } # FIXME
+sub v1genre
+{
+ my($self)=@_;
+ my $genre=undef;
+ if(defined($self->{v1}))
+ {
+ $genre=$self->{v1}->genre();
+ $genre =~ s/\//-/g if(defined($genre));
+ }
+ return $genre;
+}
+
+sub tags
+{
+ my $self=shift;
+ return({}) unless(exists($self->{mp3}->{ID3v2}) && defined($self->{mp3}->{ID3v2}));
+ my $genre=$self->{mp3}->{ID3v2}->genre();
+ return({}) unless(defined($genre) && length($genre));
+ my @tags=split(/\s*,\s*/, $genre);
+ for my $tag (@tags)
+ {
+ if($tag=~/([^\/]+)\/(.*)/)
+ {
+ my $tagname=$1;
+ my $tagval=$2;
+ $tagval=~s/\//-/g;
+ $self->{tags}->{$tagname}=$tagval;
+ }
+ else
+ {
+ $self->{tags}->{$tag}=undef;
+ }
+ }
+ return $self->{tags};
+}
+
+sub get_tags
+{
+ my ($self)=@_;
+ # MP3::Tag->get_tags shows cryptic debug info via print when it finds
+ # an unhandled id3v2 version, in addition to the warning, so use
+ # select to send prints to /dev/null
+ my $oldout=undef;
+ if(open(NULL,">/dev/null"))
+ {
+ $oldout=select(NULL);
+ }
+ eval { $self->{mp3}->get_tags; };
+ warn("$self->{path}: $@\n") if($@);
+ if(defined($oldout))
+ {
+ select($oldout);
+ close(NULL);
+ }
+}
+
+1;
+
--- /dev/null
+package ID3FS::File::Ogg;
+
+use strict;
+use warnings;
+use Ogg::Vorbis::Header;
+
+sub new
+{
+ my $proto=shift;
+ my $class=ref($proto) || $proto;
+ my $self={};
+ bless($self,$class);
+
+ $self->{path}=shift;
+ $self->{ogg}=Ogg::Vorbis::Header->new($self->{path});
+ $self->{comments}=[ $self->{ogg}->comment_tags() ];
+ return $self;
+}
+
+sub get
+{
+ my ($self, $tag, $complain)=@_;
+ for my $commenttype (@{$self->{comments}})
+ {
+ if($commenttype =~ /$tag/i)
+ {
+ my @comments=$self->{ogg}->comment($commenttype);
+ if(@comments)
+ {
+ # take first comment with actual contents
+ while(my $comment=shift @comments)
+ {
+ if(defined($comment) &&
+ length($comment) &&
+ $comment =~ /\S+/)
+ {
+ $comment =~ s/\//-/g; # drop slashes
+ return $comment;
+ }
+ }
+ }
+ }
+ }
+ warn("$self->{path}: no $tag defined in Ogg comments\n") if($complain);
+ return undef;
+}
+
+sub artist { shift->get("Artist", 1); }
+sub album { shift->get("Album", 1); }
+sub audiotype { return "ogg"; }
+sub haspic { return undef; } # FIXME
+sub v1genre { return undef; } # ID3 only
+
+# We don't care if year is not set
+sub year
+{
+ my ($self)=@_;
+ my $date=shift->get("Date", 0);
+ return undef unless($date);
+ if($date =~/(\d\d\d\d)/)
+ {
+ $date=$1;
+ }
+ return $date;
+}
+
+sub tags
+{
+ my $self=shift;
+ my @comments;
+ for my $commenttype (@{$self->{comments}})
+ {
+ if($commenttype =~ /genre/i)
+ {
+ push(@comments,$self->{ogg}->comment($commenttype));
+ }
+ }
+ my $tags={};
+ if(@comments)
+ {
+ # filter for useful comments
+ @comments= grep { defined($_); } @comments;
+ @comments= grep { length($_); } @comments;
+ @comments= grep { /\S+/; } @comments;
+ # combine then split on commas
+ # so multiple comma-delimited tags will work
+ @comments=split(/\s*,\s*/, join(', ', @comments));
+ for my $comment (@comments)
+ {
+ if($comment=~/([^\/]+)\/(.*)/)
+ {
+ my $tagname=$1;
+ my $tagval=$2;
+ $tagval=~s/\//-/g;
+ $tags->{$tagname}=$tagval;
+ }
+ else
+ {
+ $tags->{$comment}=undef;
+ }
+ }
+ }
+ return $tags;
+}
+
+1;
+
+++ /dev/null
-package ID3FS::File;
-
-use strict;
-use warnings;
-use ID3FS::File::Mp3;
-use ID3FS::File::Ogg;
-use ID3FS::File::Flac;
-
-# omg a factory class, I feel vaguely dirty
-sub new
-{
- my (undef,$path)=@_;
- my $ext=($path=~/.*\.(.*)/)[0];
- return undef unless($ext);
- $ext=lc($ext);
- if($ext eq "mp3")
- {
- return ID3FS::File::Mp3->new($path);
- }
- elsif($ext eq "ogg")
- {
- return ID3FS::File::Ogg->new($path);
- }
- elsif($ext eq "flac")
- {
- return ID3FS::File::Flac->new($path);
- }
- else
- {
- print("Unknown extension: $ext\n");
- return undef;
- }
-}
-
-1;
+++ /dev/null
-package ID3FS::File::Flac;
-
-use strict;
-use warnings;
-use Audio::FLAC::Header;
-
-sub new
-{
- my $proto=shift;
- my $class=ref($proto) || $proto;
- my $self={};
- bless($self,$class);
-
- $self->{path}=shift;
- my $flac=Audio::FLAC::Header->new($self->{path});
- $self->{tags}=$flac->tags();
- return $self;
-}
-
-sub get
-{
- my ($self, $tag, $complain)=@_;
- for my $key (keys %{$self->{tags}})
- {
- if($key =~ /$tag/i &&
- defined($self->{tags}->{$key}) &&
- length($self->{tags}->{$key}) &&
- $self->{tags}->{$key} =~ /\S+/)
- {
- my $val=$self->{tags}->{$key};
- $val =~ s/\//-/g; # drop slashes
- return $val;
- }
- }
- warn("$self->{path}: no $tag defined in FLAC comments\n") if($complain);
- return undef;
-}
-
-sub artist { shift->get("ARTIST", 1); }
-sub album { shift->get("ALBUM", 1); }
-sub audiotype { return "flac"; }
-sub haspic { return undef; } # FIXME
-sub v1genre { return undef; } # ID3 only
-
-# We don't care if year is not set
-sub year
-{
- my ($self)=@_;
- my $date=shift->get("DATE", 0);
- return undef unless($date);
- if($date =~/(\d\d\d\d)/)
- {
- $date=$1;
- }
- return $date;
-}
-
-sub tags
-{
- my $self=shift;
- my @tags=();
- my $tags={};
- for my $key (keys %{$self->{tags}})
- {
- if($key =~ /genre/i &&
- defined($self->{tags}->{$key}) &&
- length($self->{tags}->{$key}) &&
- $self->{tags}->{$key} =~ /\S+/)
- {
- push(@tags, $self->{tags}->{$key});
- }
- }
- # combine then split on commas
- # so multiple comma-delimited tags will work
- @tags=split(/\s*,\s*/, join(', ', @tags));
- for my $tag (@tags)
- {
- if($tag=~/([^\/]+)\/(.*)/)
- {
- my $tagname=$1;
- my $tagval=$2;
- $tagval=~s/\//-/g;
- $self->{tags}->{$tagname}=$tagval;
- }
- else
- {
- $self->{tags}->{$tag}=undef;
- }
- }
- return $self->{tags};
-}
-
-1;
-
+++ /dev/null
-package ID3FS::File::Mp3;
-
-use strict;
-use warnings;
-use MP3::Tag;
-
-sub new
-{
- my $proto=shift;
- my $class=ref($proto) || $proto;
- my $self={};
- bless($self,$class);
-
- $self->{path}=shift;
- $self->{mp3}=MP3::Tag->new($self->{path});
- $self->get_tags();
- $self->{v1}=undef;
- $self->{v1}=$self->{mp3}->{ID3v1} if(exists($self->{mp3}->{ID3v1}));
- $self->{v2}=undef;
- $self->{v2}=$self->{mp3}->{ID3v2} if(exists($self->{mp3}->{ID3v2}));
-
- $self->{tags}={};
-
- return $self;
-}
-
-sub choose
-{
- my ($self, $func, $verbose)=@_;
- my $thing=undef;
- if(defined($self->{v2}))
- {
- $thing=$self->{v2}->$func();
- }
- if(defined($self->{v1}) && (!defined($thing) || !length($thing)))
- {
- $thing=$self->{v1}->$func();
- }
- if(!defined($thing) || !length($thing))
- {
- warn("$self->{path}: no $func defined in ID3 tags\n") if($verbose);
- return undef;
- }
- $thing=~s/\//-/g; # drop slashes
- return $thing;
-}
-
-sub artist { shift->choose("artist", 1); }
-sub album { shift->choose("album", 1); }
-# We don't care if year is not set
-sub year { shift->choose("year", 0); }
-sub audiotype { return "mp3"; }
-sub haspic { return undef; } # FIXME
-sub v1genre
-{
- my($self)=@_;
- my $genre=undef;
- if(defined($self->{v1}))
- {
- $genre=$self->{v1}->genre();
- $genre =~ s/\//-/g if(defined($genre));
- }
- return $genre;
-}
-
-sub tags
-{
- my $self=shift;
- return({}) unless(exists($self->{mp3}->{ID3v2}) && defined($self->{mp3}->{ID3v2}));
- my $genre=$self->{mp3}->{ID3v2}->genre();
- return({}) unless(defined($genre) && length($genre));
- my @tags=split(/\s*,\s*/, $genre);
- for my $tag (@tags)
- {
- if($tag=~/([^\/]+)\/(.*)/)
- {
- my $tagname=$1;
- my $tagval=$2;
- $tagval=~s/\//-/g;
- $self->{tags}->{$tagname}=$tagval;
- }
- else
- {
- $self->{tags}->{$tag}=undef;
- }
- }
- return $self->{tags};
-}
-
-sub get_tags
-{
- my ($self)=@_;
- # MP3::Tag->get_tags shows cryptic debug info via print when it finds
- # an unhandled id3v2 version, in addition to the warning, so use
- # select to send prints to /dev/null
- my $oldout=undef;
- if(open(NULL,">/dev/null"))
- {
- $oldout=select(NULL);
- }
- eval { $self->{mp3}->get_tags; };
- warn("$self->{path}: $@\n") if($@);
- if(defined($oldout))
- {
- select($oldout);
- close(NULL);
- }
-}
-
-1;
-
+++ /dev/null
-package ID3FS::File::Ogg;
-
-use strict;
-use warnings;
-use Ogg::Vorbis::Header;
-
-sub new
-{
- my $proto=shift;
- my $class=ref($proto) || $proto;
- my $self={};
- bless($self,$class);
-
- $self->{path}=shift;
- $self->{ogg}=Ogg::Vorbis::Header->new($self->{path});
- $self->{comments}=[ $self->{ogg}->comment_tags() ];
- return $self;
-}
-
-sub get
-{
- my ($self, $tag, $complain)=@_;
- for my $commenttype (@{$self->{comments}})
- {
- if($commenttype =~ /$tag/i)
- {
- my @comments=$self->{ogg}->comment($commenttype);
- if(@comments)
- {
- # take first comment with actual contents
- while(my $comment=shift @comments)
- {
- if(defined($comment) &&
- length($comment) &&
- $comment =~ /\S+/)
- {
- $comment =~ s/\//-/g; # drop slashes
- return $comment;
- }
- }
- }
- }
- }
- warn("$self->{path}: no $tag defined in Ogg comments\n") if($complain);
- return undef;
-}
-
-sub artist { shift->get("Artist", 1); }
-sub album { shift->get("Album", 1); }
-sub audiotype { return "ogg"; }
-sub haspic { return undef; } # FIXME
-sub v1genre { return undef; } # ID3 only
-
-# We don't care if year is not set
-sub year
-{
- my ($self)=@_;
- my $date=shift->get("Date", 0);
- return undef unless($date);
- if($date =~/(\d\d\d\d)/)
- {
- $date=$1;
- }
- return $date;
-}
-
-sub tags
-{
- my $self=shift;
- my @comments;
- for my $commenttype (@{$self->{comments}})
- {
- if($commenttype =~ /genre/i)
- {
- push(@comments,$self->{ogg}->comment($commenttype));
- }
- }
- my $tags={};
- if(@comments)
- {
- # filter for useful comments
- @comments= grep { defined($_); } @comments;
- @comments= grep { length($_); } @comments;
- @comments= grep { /\S+/; } @comments;
- # combine then split on commas
- # so multiple comma-delimited tags will work
- @comments=split(/\s*,\s*/, join(', ', @comments));
- for my $comment (@comments)
- {
- if($comment=~/([^\/]+)\/(.*)/)
- {
- my $tagname=$1;
- my $tagval=$2;
- $tagval=~s/\//-/g;
- $tags->{$tagname}=$tagval;
- }
- else
- {
- $tags->{$comment}=undef;
- }
- }
- }
- return $tags;
-}
-
-1;
-