summaryrefslogtreecommitdiff
path: root/lib/gcstar/GCExtract/GCExtractMusics.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gcstar/GCExtract/GCExtractMusics.pm')
-rw-r--r--lib/gcstar/GCExtract/GCExtractMusics.pm370
1 files changed, 370 insertions, 0 deletions
diff --git a/lib/gcstar/GCExtract/GCExtractMusics.pm b/lib/gcstar/GCExtract/GCExtractMusics.pm
new file mode 100644
index 0000000..1a56b68
--- /dev/null
+++ b/lib/gcstar/GCExtract/GCExtractMusics.pm
@@ -0,0 +1,370 @@
+package GCExtract::GCExtractMusics;
+
+###################################################
+#
+# Copyright 2005-2010 Christian Jodar
+#
+# This file is part of GCstar.
+#
+# GCstar is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GCstar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCstar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+#
+###################################################
+
+use strict;
+use GCExtract;
+
+use GCDialogs;
+{
+ package GCExtractMusicsResultsDialog;
+ use base 'GCModalDialog';
+
+ sub show
+ {
+ my $self = shift;
+
+ $self->SUPER::show();
+ $self->show_all;
+ my $response = $self->run;
+ my $idx = ($self->{results}->get_selected_indices)[0];
+ $self->hide;
+ return -1 if $response ne 'ok';
+ return $idx;
+ }
+
+ sub setData
+ {
+ my ($self, @cddbData) = @_;
+ my @listData;
+ foreach(@cddbData)
+ {
+ push @listData, [$_->{genre}, $_->title, $_->artist, $_->year];
+ }
+ @{$self->{results}->{data}} = @listData;
+ $self->{results}->select(0);
+ $self->{results}->columns_autosize;
+ }
+
+ sub new
+ {
+ my ($proto, $parent, $model) = @_;
+ my $class = ref($proto) || $proto;
+
+ my $self = $class->SUPER::new($parent,
+ $model->getDisplayedText('ResultsDialog'));
+ $self->{parent} = $parent;
+
+ my $hbox = new Gtk2::HBox(0,0);
+
+ $self->{results} = new Gtk2::SimpleList(
+ $model->getDisplayedText('Genre') => 'text',
+ $model->getDisplayedText('Title') => 'text',
+ $model->getDisplayedText('Artist') => 'text',
+ $model->getDisplayedText('Release') => 'text',
+ );
+
+ $self->{results}->set_rules_hint(1);
+ $self->{results}->set_headers_clickable(1);
+ for my $i (0..3)
+ {
+ my $column = $self->{results}->get_column($i);
+ $column->set_resizable(1);
+ $column->set_sort_column_id($i);
+ }
+ $self->{results}->signal_connect(row_activated => sub {
+ $self->response('ok');
+ });
+
+ my $scrollPanelList = new Gtk2::ScrolledWindow;
+ $scrollPanelList->set_policy ('never', 'automatic');
+ $scrollPanelList->set_shadow_type('etched-in');
+ $scrollPanelList->set_border_width($GCUtils::margin);
+ $scrollPanelList->add($self->{results});
+
+ $self->vbox->pack_start($scrollPanelList,1,1,0);
+
+ $self->set_default_size(-1,300);
+ return $self;
+ }
+}
+
+{
+ package GCExtract::GCmusicsExtracter;
+ use base 'GCItemExtracter';
+
+ sub new
+ {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = $class->SUPER::new(@_);
+ bless ($self, $class);
+
+ $self->{hasMP3Info} = $self->checkModule('MP3::Info');
+ $self->{hasMP3Tag} = $self->checkModule('MP3::Tag');
+ $self->{hasOggVorbisHeader} = $self->checkModule('Ogg::Vorbis::Header::PurePerl');
+ $self->{hasNetFreeDB} = $self->checkModule('Net::FreeDB');
+ # Even if previous check fails, we want to use it for tracks feature
+ $self->{errors} = 0;
+
+ $self->{fields} = ['title', 'artist', 'release', 'genre', 'running', 'tracks'];
+ return $self;
+ }
+
+ sub resetTracks
+ {
+ my $self = shift;
+ $self->{tracks} = [];
+ $self->{totalTime} = 0;
+ $self->{currentTrack} = 0;
+ $self->{firstTrack} = '';
+ }
+
+ sub addTrack
+ {
+ my ($self, $title, $time, $number) = @_;
+ $self->{currentTrack}++;
+ $self->{totalTime} += $time;
+ $number = $self->{currentTrack} if !defined $number;
+ push @{$self->{tracks}},
+ [$number, $title, $self->secondsToString($time)];
+ }
+
+ sub getTracks
+ {
+ my $self = shift;
+ return $self->{tracks};
+ }
+
+ sub secondsToString
+ {
+ my ($self, $time) = @_;
+ return int($time / 60) .':'. sprintf '%02d', ($time %60);
+ }
+
+ sub getTotalTime
+ {
+ my $self = shift;
+ return $self->secondsToString($self->{totalTime});
+ }
+
+ sub getM3UInfo
+ {
+ my ($self) = @_;
+ my $file = $self->{file};
+ my $info = {};
+ while (<$file>)
+ {
+ chomp;
+ s/\r//;
+ if (/^#/)
+ {
+ next if ! /^#EXTINF:(.*)/;
+ my @values = split /,/, $1;
+ $self->addTrack($values[1], $values[0]);
+ }
+ else
+ {
+ $self->{firstTrack} = $_
+ if !$self->{firstTrack};
+ }
+ }
+ $info->{tracks} = $self->getTracks;
+ $info->{running} = $self->getTotalTime;
+ return $info;
+ }
+
+ sub getPLSInfo
+ {
+ my ($self) = @_;
+ my $file = $self->{file};
+ my $info = {};
+ my @tracks;
+ while (<$file>)
+ {
+ chomp;
+ s/\r//;
+ next if ! /(File|Title|Length)(\d+)=(.*)$/;
+ $tracks[$2]->{$1} = $3;
+ $tracks[$2]->{Number} = $2;
+ }
+ foreach (@tracks)
+ {
+ next if !$_->{Title};
+ $self->addTrack($_->{Title}, $_->{Length}, $_->{Number});
+ }
+
+ $info->{tracks} = $self->getTracks;
+ $info->{running} = $self->getTotalTime;
+ $self->{firstTrack} = $tracks[1]->{File};
+ return $info;
+ }
+
+ sub getFreeDB
+ {
+ my @genres = qw(blues classical country data folk jazz newage reggae rock soundtrack misc);
+ my ($self) = @_;
+ my $file = $self->{fileName};
+ my $info = {};
+ return $info if ! -e $file;
+ return $info if ! $self->{hasNetFreeDB};
+
+ my $freedb = Net::FreeDB->new;
+ my $discdata = $freedb->getdiscdata($file);
+ return if !$discdata;
+ my $cddb_file_object;
+ my @results;
+
+ foreach (@genres)
+ {
+ my $tmpCddb = $freedb->read($_, $discdata->{ID});
+ if ($tmpCddb)
+ {
+ $tmpCddb->{genre} = $tmpCddb->genre || $_;
+ push @results, $tmpCddb;
+ }
+ }
+
+ if ($#results == -1)
+ {
+ return;
+ }
+ elsif ($#results == 0)
+ {
+ $cddb_file_object = $results[0];
+ }
+ else
+ {
+ my $dialog = new GCExtractMusicsResultsDialog(
+ $self->{parent},
+ $self->{model}
+ );
+ $dialog->setData(@results);
+ my $selected = $dialog->show;
+ $dialog->destroy;
+ return if $selected == -1;
+ $cddb_file_object = $results[$selected];
+ }
+
+ foreach my $track ($cddb_file_object->tracks)
+ {
+ $self ->addTrack($track->title,$track->length,$track->number);
+ }
+
+ $info->{tracks} = $self->getTracks;
+ $info->{running} = $self->getTotalTime;
+ $info->{title} = $cddb_file_object->title;
+ $info->{artist} = $cddb_file_object->artist;
+ $info->{release} = $cddb_file_object->year;
+ $info->{genre} = $cddb_file_object->{genre};
+
+ return $info;
+ }
+
+ sub addFirstTrackInfo
+ {
+ my ($self, $info) = @_;
+
+ if ($^O =~ /win32/i)
+ {
+ $self->{firstTrack} =~ s|\\|/|g;
+ $self->{fileName} =~ /^(.{2})/;
+ my $drive = $1;
+ $self->{firstTrack} = $drive.$self->{firstTrack}
+ if $self->{firstTrack} =~ m|^/|;
+ }
+
+ if ($self->{firstTrack} =~ /mp3$/i)
+ {
+ if ($self->{hasMP3Info})
+ {
+ MP3::Info::use_mp3_utf8(1);
+ my $song = MP3::Info::get_mp3tag($self->{firstTrack});
+ $info->{title} = $song->{ALBUM};
+ $info->{artist} = $song->{ARTIST};
+ $info->{release} = $song->{YEAR};
+ $info->{genre} = $song->{GENRE};
+ }
+ elsif ($self->{hasMP3Tag})
+ {
+ my $song = MP3::Tag->new($self->{firstTrack});
+ (undef, undef, $info->{artist}, $info->{title}) = $song->autoinfo;
+ }
+ }
+ elsif ($self->{firstTrack} =~ /ogg$/i)
+ {
+ if ($self->{hasOggVorbisHeader})
+ {
+ my $song = Ogg::Vorbis::Header::PurePerl->new($self->{firstTrack});
+ $info->{title} = ($song->comment('album'))[0];
+ $info->{artist} .= $_.', ' foreach $song->comment('artist');
+ $info->{artist} =~ s/, $//;
+ ($info->{release} = ($song->comment('date'))[0]) =~ s|^(\d{4})-(\d{2})-(\d{2}).*$|$3/$2/$1|;
+ $info->{genre} .= $_.', ' foreach $song->comment('genre');
+ $info->{genre} =~ s/, $//;
+ }
+ }
+ }
+
+ sub getInfo
+ {
+ my $self = shift;
+ my $info = {};
+ $self->resetTracks;
+
+ if ((!$self->{fileName}) || ($self->{fileName} =~ /\/dev\//))
+ {
+ if (!$self->{fileName})
+ {
+ $self->{fileName} = $self->{parent}->{options}->cdDevice;
+ }
+ $info = $self->getFreeDB;
+ }
+ else
+ {
+
+ open FILE, '<'.$self->{fileName};
+ binmode FILE;
+
+ $self->{file} = \*FILE;
+ my $header = <FILE>;
+
+ $info = $self->getM3UInfo
+ if ($self->{fileName} =~ /m3u$/) || ($header =~ /^#EXTM3U/);
+ $info = $self->getPLSInfo
+ if ($self->{fileName} =~ /pls$/) || ($header =~ /^\[playlist\]/);
+ close FILE;
+ }
+
+ $self->addFirstTrackInfo($info);
+
+ return if !defined $info;
+ my $result;
+ my $firstTrackName = $info->{tracks}->[0]->[1];
+ $result->{tracks} = {displayed => $firstTrackName, value => $info->{tracks}};
+ foreach (@{$self->{fields}})
+ {
+ next if /^tracks$/;
+ $result->{$_} = {displayed => $info->{$_}, value => $info->{$_}};
+ }
+ return $result;
+ }
+
+ sub getFields
+ {
+ my $self = shift;
+ return $self->{fields};
+ }
+}
+
+1;