Anyone knows of an easy fix for this? Or should we just wait and see?

Could you please give this version a try? Rename it to Plugin.pm and move it to the Plugin/Biography folder. Then restart your server.

I can't test thoroughly right now as I'm on my laptop without my music collection at hand. If you could give me a thumbs up I'd upload a new release. Thanks for the feedback!

--

Michael

-----------------------------------------------------------
Help translate SlimServer by using the
StringEditor Plugin (http://www.herger.net/slim/)
# Biography::Plugin.pm by mh 2005
#
# This code is derived from code with the following copyright message:
#
# SliMP3 Server Copyright (C) 2001 Sean Adams, Slim Devices Inc.
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License,
# version 2.
#
# Changelog
# 1.1 - improved ignore list behaviour for artists like "Trio" or "The The"
#     - added first level cache (memory) to reduce disk acces
# 1.0 - fixed name guess with online streams
# 0.9 - Dan adapted the plugin to using non blocking web requests - thanks a 
lot!
#     - first release for SlimServer v6
#     - added user configurable $ignoreList: words like 'the band group quartet 
trio' 
#       that are not to be respected in the artist's name
#     - fixed caching duration
# 0.2 - added support for proxy server (thanks to Jaimes Craig for pointing 
this out)
#     - added _blank target to non relative external links
#     - added configuration options for slideshow mode, web interface refresh, 
cache purge
#     - added regular cache purge 
#     - fixed crash when there's no player available
#     - added some more informational texts (nothing found, gathering 
information etc.)
# 0.1 - first release

package Plugins::Biography::Plugin;

use strict;

use vars qw($VERSION);
$VERSION = substr(q$Revision: 1.0 $,10);

use File::Spec;
use File::Spec;
use FindBin qw($Bin);
use lib(File::Spec->catdir($Bin, 'Plugins','MHCPAN'));
use HTML::FormatText;

use Slim::Buttons::Common;
use Slim::Display::Animation;
use Slim::Music::Info;
use Slim::Player::Playlist;
use Slim::Player::Source;
use Slim::Utils::Prefs;
use Slim::Utils::Strings qw(string);

my $baseURL    = 'http://www.allmusic.com';
my $searchURL  = "$baseURL/cg/amg.dll";

# TODO mh: back to "my" after debugging...
my ($cacheFolder, %status);

sub initPlugin {

        $cacheFolder = initCacheFolder();

}

sub getDisplayName {
        return 'PLUGIN_BIOGRAPHY';
}

sub setMode {
        my $client = shift;
        $client->lines( \&lines );

        # Please wait...
        $status{$client}{status} ||= -1;
        $client->update();
        checkArtistBio($client);
        $client->update();
}

sub lines {
        my $client = shift;

        my $line1 = '';
        my $line2 = '';
        
        if ($status{$client}{status} eq "0") {
                $line1 = $client->string('PLUGIN_BIOGRAPHY_COLLECTINGINFO');
        } 
        elsif ($status{$client}{status} eq "-1") {
                $line1 = $client->string('PLUGIN_BIOGRAPHY');
                $line2 = $client->string('PLUGIN_BIOGRAPHY_NOTHINGFOUND');
        } 
        else {
                $line1 = $client->string('PLUGIN_BIOGRAPHY') . ' - ' . 
$status{$client}{status};
                $line2 = $status{$client}{playerBio}[$status{$client}{line}];
        }

        return ($line1, $line2);
}

sub getFunctions {
        my %functions = (
                'up' => sub  {
                        my $client = shift;
                        $status{$client}{line} = 
Slim::Buttons::Common::scroll($client, -1, $#{ $status{$client}{playerBio} } + 
1, $status{$client}{line} || 0);
                        $client->update();
                },
        
                        'down' => sub  {
                        my $client = shift;
                        $status{$client}{line} = 
Slim::Buttons::Common::scroll($client, 1, $#{ $status{$client}{playerBio} } + 
1, $status{$client}{line} || 0);
                        $client->update();
                },
                
                'left' => sub  {
                        my $client = shift;
                        Slim::Buttons::Common::popModeRight($client);
                },
        
                        'right' => sub  {
                        my $client = shift;
                        Slim::Display::Animation::bumpRight($client);
                }
        );

        return \%functions;
}

sub setupGroup {

        my %setupGroup = (
                PrefOrder => ['plugin_biography_slideshow', 
'plugin_biography_refresh', 'plugin_biography_cache', 
'plugin_biography_purge_cache', 'plugin_biography_ignorelist'],
                GroupHead => string('PLUGIN_BIOGRAPHY'),
                GroupLine => 1,
                GroupSub => 0,
                Suppress_PrefSub => 0,
                Suppress_PrefLine => 1
        );

        my %setupPrefs = (

                'plugin_biography_slideshow' => {
                        'validate' => \&Slim::Web::Setup::validateInt,
                        'validateArgs' => [0,undef,1]
                },

                'plugin_biography_refresh' => {
                        'validate' => \&Slim::Web::Setup::validateInt,
                        'validateArgs' => [0,undef,1]
                },

                'plugin_biography_cache' => {
                        'validate' => \&Slim::Web::Setup::validateInt,
                        'validateArgs' => [1,undef,1],
                        'onChange' => sub {
                                my ($client,$changeref,$paramref,$pageref) = @_;
                                
Slim::Utils::Prefs::set('plugin_biography_cache', 
$changeref->{'plugin_biography_cache'}->{'new'});
                                initCacheFolder();
                        }
                },

                'plugin_biography_ignorelist' => {
                                'PrefSize' => 'large'
                }

        );

        return (\%setupGroup, \%setupPrefs);
}

sub webPages {
        my %pages = ("index\.htm" => \&handleWebIndex);
        return (\%pages, "index.html");
}

sub handleWebIndex {
        my ($client, $params, $callback, $httpClient, $response) = @_;

        if (!$client) {
                $params->{'artist'}    = string('NO_PLAYER_FOUND');
                $params->{'artistbio'} = string('NO_PLAYER_DETAILS');
        } 
        elsif (checkArtistBio($client)) {
                if (Slim::Utils::Prefs::get('plugin_biography_refresh')) {
                        $params->{'refresh'} = 
Slim::Utils::Prefs::get('plugin_biography_refresh') * 1000;
                }
        
                # Try memory cache first
                $params->{'artist'}       = $status{$client}{status};
                $params->{'artistbio'}    = $status{$client}{htmlBio};
                $params->{'artistimages'} = $status{$client}{images};
                $params->{'artistlinks'}  = $status{$client}{links};
        } else {
                $params->{'artist'} = string('PLUGIN_BIOGRAPHY_NOTHINGFOUND');
        }

        return 
Slim::Web::HTTP::filltemplatefile('plugins/Biography/index.html', $params);
}

sub initStatus {
        my $client = shift;
        $status{$client}{status} = shift;
        $status{$client}{playerBio} = shift;
        $status{$client}{line} = 0;
        $status{$client}{htmlBio} = shift;
        $status{$client}{images} = shift;
        $status{$client}{links} = shift;
}

sub checkArtistBio {
        my $client = shift;

        # check whether there's actually a song!
        if (my $url = Slim::Player::Playlist::song($client)) {
                my $artist = getArtistFromSongURL($url);

                # only read bio if artist changed
                if ($artist ne $status{$client}{status}) {
                        my $artistHTML   = getArtistInfo($client, $artist);

                        # initialize status hash
                        $status{$client}{status} = $artist;
                        $status{$client}{playerBio} = ();
                        $status{$client}{line} = 0;
                        $status{$client}{htmlBio} = '';
                        $status{$client}{images} = '';
                        $status{$client}{links} = '';

                        # Some error when downloading data
                        if (!$artistHTML) {
                                $status{$client}{htmlBio} = 
string('PLUGIN_BIOGRAPHY_NOTHINGFOUND');
                                $status{$client}{playerBio} = 
[$client->string('PLUGIN_BIOGRAPHY_NOTHINGFOUND')];
                        } 
                        # nothing (unique) found on allmusic -> display 
selection
                        elsif ($artistHTML =~ /^<!-- NOT FOUND -->/) {
                                $status{$client}{htmlBio} = $artistHTML;
                                $status{$client}{playerBio} = 
[$client->string('PLUGIN_BIOGRAPHY_NOTHINGFOUND')];
                        } 
                        # we're fine!
                        else {
                                # cache values for web interface
                                $status{$client}{htmlBio} = 
getArtistBio($artistHTML, $artist);
                                $status{$client}{images} = 
getArtistImages($artistHTML);
                                $status{$client}{links}  = 
getArtistLinks($artistHTML);
                                
                                # create player's content
                                my $bioFormatter = 
HTML::FormatText->new(leftmargin => 0, rightmargin => 39);
                                my $formattedBio = $status{$client}{htmlBio};
                                $formattedBio =~ s/Read More\.\.\./(Read more 
on the web)/i;
                                $formattedBio = 
$bioFormatter->format_string($formattedBio);
                                $formattedBio =~ s/\n+/\n/g;

                                @{$status{$client}{playerBio}} = split(/\n/, 
$formattedBio);

                                if (@{$status{$client}{playerBio}} == 0) {
                                        push(@{$status{$client}{playerBio}}, 
$client->string('PLUGIN_BIOGRAPHY_NOTEXTFOUND'));
                                } 
                                else {
                                        push(@{$status{$client}{playerBio}}, 
'----------------------------------------');
                                }
                        }
                }
                else {
                        $::d_plugins && Slim::Utils::Misc::msg("Biography: 
$artist was in memory cache\n");     
                }
        } 
        else {
                # initialize status hash
                $status{$client}{status} = '-1';
                $status{$client}{playerBio} = 
[string('PLUGIN_BIOGRAPHY_NOTHINGFOUND')];
                $status{$client}{line} = 0;
                $status{$client}{htmlBio} = '';
                $status{$client}{images} = '';
                $status{$client}{links} = '';
                return 0;
        }
        return 1;
}

sub getArtistFromSongURL {
        my ($url, $params) = @_;

        my $ds    = Slim::Music::Info::getCurrentDataStore();
        my $track = $ds->objectForUrl($url);

        $params->{'artist'} = $track->artist();

        $::d_plugins && Slim::Utils::Misc::msg("Biography: Let's guess the 
artists name: '$params->{artist}'\n");
        # We didn't get an artist - try to extract one from the title.
        unless ($params->{'artist'} && ($params->{'artist'} ne 
string('NO_ARTIST'))) {
                $::d_plugins && Slim::Utils::Misc::msg("Biography: artist's 
name is either empty or 'No artist'\n");

                $params->{'artist'} = Slim::Music::Info::getCurrentTitle('', 
$url);
                $::d_plugins && Slim::Utils::Misc::msg("Biography: give the 
track's title a try (for streams): '$params->{artist}'\n");
                $params->{'artist'} =~ /([^\-]+?) -/;

                if ($1) {
                        $params->{'artist'} = $1;
                        $::d_plugins && Slim::Utils::Misc::msg("Biography: 
tried to match 'artist - song': '$params->{artist}'\n");
                }
        }

        return $params->{'artist'};
}

sub getArtistInfo {
        my $client = shift;
        my $searchArtist = shift;
        $::d_plugins && Slim::Utils::Misc::msg("Biography: Looking up 
information for artist '$searchArtist'\n");

        if (my $ignoreList = 
Slim::Utils::Prefs::get('plugin_biography_ignorelist')) {
                my $originalArtist = $searchArtist;
                for (split(/ /, $ignoreList)) {
                        $searchArtist =~ s/\b$_\b//sig;
                }
                # remove leading/trailing spaces
                $searchArtist =~ s/^\s*//g;
                $searchArtist =~ s/\s*$//g;

                # if the artist's name is empty (as for eg. "Trio" and "The 
The"), use the original instead
                $searchArtist = $originalArtist if not $searchArtist =~ /\w/;
        }

        # Try and use the locally cached copy first
        my $cachedFile   = "$cacheFolder/$searchArtist.html";
        my $cacheAge = Slim::Utils::Prefs::get('plugin_biography_cache') || 30;
        if (-e $cachedFile && -M $cachedFile < $cacheAge) {
                $::d_plugins && Slim::Utils::Misc::msg("Biography: 
$searchArtist.html was in disk cache\n");
                local $/ = undef;

                open CACHE, $cachedFile;
                my $cachedContent = <CACHE>;
                close CACHE;
                
                # change timestamp to keep it a little longer...
                utime time(), time(), $cachedFile;
                
                return $cachedContent;
        }

        # ...ok, no cached copy, we'll have to go through this...
        $::d_plugins && Slim::Utils::Misc::msg("Biography: artist after 
ignore-cleanup '$searchArtist'\n");
        
        # This makes a non-blocking request to the remote server.
        my $http = Slim::Player::Protocols::HTTP->new({
                'url'     => $searchURL,
                'post'   => "SQL=$searchArtist&OPT1=1&submit=Go&P=amg",
                'create'  => 0,
                'timeout' => 10
    });
    
    my $html;
    if (defined $http) {
                $html = $http->content();
                $http->close();
    }
    
    if (not $html) {
        $::d_plugins && Slim::Utils::Misc::msg("Biography: timeout problem?\n");
        return 'timeout?';
    }
        
        # direct hit?
        if (($html =~ /Overview/si) && ($html =~ /Biography/si) && ($html =~ 
/Discography/si)) {

                $::d_plugins && Slim::Utils::Misc::msg("Biography: Match!\n");
                storeCachedFile($cachedFile, $html);
                
                return $html;

        } else {
                $html =~ /<a 
href="(\/cg\/amg.dll\?p=amg\&sql=\d{1,2}:\w+)">(.*?)<\/a>/si;
                
                my $artistURL = $1;
                my $artistFound = $2;
                my $ok = 1;
                
                if (defined $artistURL && defined $artistFound) {
                        $::d_plugins && Slim::Utils::Misc::msg("Biography: URL 
found ($artistURL, $artistFound)\n");
        
                        # verify if we really match the search expression
                        for (split(/[\W]/, $searchArtist))      {
                                $::d_plugins && 
Slim::Utils::Misc::msg("Biography: verify name part '$_'\n");
        
                                if ($_ && $artistFound !~ /$_/i) {
                                        $ok = 0;
                                        last;
                                }
                        }
                }

                # give the first entry a try
                if ($ok || ($searchArtist =~ $artistFound)) {

                        my $http = 
Slim::Player::Source::openRemoteStream($baseURL . $artistURL);
                        my $html = $http->content();
                        $http->close();

                        storeCachedFile($cachedFile, $html);
                        return $html;

                } else {
                        # show list of possible matches
                        my $htmlList = '';

                        while ($html =~ /<a 
(href="\/cg\/amg.dll\?p=amg\&sql=\d{1,2}:\w+")>(.*?)<\/a>(<\/strong>|)<\/td><td.*?>(.*?)<\/td>/sig)
 {
                                $htmlList .= "<li><a " . fixBaseURL($1) . ">$2 
($4)</a></li>\n";
                        }

                        return "<!-- NOT FOUND --><ul>$htmlList</ul>\n";
                }
        }
}

sub storeCachedFile {
        my $cachedFile    = shift;
        my $cachedContent = shift;
        
        open(CACHE, ">$cachedFile") || Slim::Utils::Misc::msg("Could not open 
$cachedFile for writing: $!\n");
        print CACHE $cachedContent;
        close(CACHE);
}

sub getArtistLinks {
        my $artistHTML = shift;

#       if ($artistHTML =~ 
/<hr[^>]*?><table.*?(<table.*?)<\/td><\/tr><\/table><\/td><!--End Center 
Content-->/si) {
        if ($artistHTML =~ /Begin Center 
Content-->.*?<\/table><hr.*?>(<table.*?)<\/td><\/tr><\/table><\/td><!--End 
Center Content-->/si) {
                my $html = $1;
                $html =~ s/<[\/]?(span|div)[^>]*?>//sig;

                return fixBaseURL($html);

        } else {
                return '';
        }
}


sub getArtistBio {
        my $artistHTML   = shift;
        my $searchArtist = shift;

        if ($artistHTML =~ /<div id="artistminibio">.*?<p.*?>(.*?)<\/p.*?>/si) {
                return fixBaseURL($1);
        }

        return string('PLUGIN_BIOGRAPHY_NOTHINGFOUND');
}

sub getArtistImages {

        my $artistHTML = shift;
        my $html = '<script type="text/javascript" language="javascript" src="' 
. $baseURL . '/js/pictures.js"></script>';
        
        if ($artistHTML !~ /Begin Page Photo-->(.*?)<!--End Page Photo/si) {
                return '';
        }

        $1 =~ /<table[^>]*?>(.*?)<\/table[^>]*?>/i;
        $html .= "\n\n$1\n\n";
        $html = "<table cellpadding=\"0\" cellspacing=\"0\">\n$html</table>";
        
        # ...the picture list...
        if ($artistHTML =~ /^(Pictures.*?\n*^Credits.*?)$/msi) {

                my $imgList = $1;
                   $imgList =~ /Pictures = new Array\(([^\)]*?)\);/si;

                my @images   = split /,/, $1;
                my $startImg = int rand @images;

                $imgList .= "\nvar i = $startImg;\n";

                if (Slim::Utils::Prefs::get('plugin_biography_slideshow')) {
                        $imgList .= 
"nextPicture();\nsetInterval(\"nextPicture()\", " . 
                                
Slim::Utils::Prefs::get('plugin_biography_slideshow') * 1000 . ");\n";
                }

                $html .= "<script type=\"text/javascript\" 
language=\"javascript\">\n<!--\n$imgList\n-->\n</script>\n";
        }

        return $html;
}

sub fixBaseURL {
        my $html = shift;

        $html =~ s/href="(.*?allmovie.com.*?)"/href="$1" target="_blank"/sig;
        $html =~ s/href="\/(.*?)"/href="$baseURL\/$1" target="_blank"/sig;

        return $html;
}

sub initCacheFolder {
        my $cacheFolder = Slim::Utils::Prefs::get('cachedir');

        mkdir($cacheFolder) unless (-d $cacheFolder);

        $cacheFolder .= "/.slimserver-biography-cache";

        mkdir($cacheFolder) unless (-d $cacheFolder);
        
        my $cacheAge = Slim::Utils::Prefs::get('plugin_biography_cache') || 30;
        
        # purge the cache
        if (opendir(DIR, $cacheFolder)) {
                while (defined(my $cachedFile = readdir(DIR))) {
        
                        $cachedFile = "$cacheFolder/$cachedFile";
                        if (-M $cachedFile > $cacheAge) {
                                unlink $cachedFile;
                        }
                }
        }
        else {
                $::d_plugins && Slim::Utils::Misc::msg("can't opendir 
$cacheFolder: $!\n");
        }

        closedir(DIR);
        
        # set timer to purge cache once a day
        Slim::Utils::Timers::setTimer(0, Time::HiRes::time() + 60*60*24, 
\&initCacheFolder);
        
        return $cacheFolder;
}

sub strings {
        return q^PLUGIN_BIOGRAPHY
        DE      Biographie
        EN      Biography

BIOGRAPHY
        DE      Biographie
        EN      Biography

PLUGIN_BIOGRAPHY_RELOAD
        DE      Aktualisieren
        EN      Refresh

PLUGIN_BIOGRAPHY_NOTHINGFOUND
        DE      Konnte keine Informationen finden!
        EN      Couldn't find any information!

PLUGIN_BIOGRAPHY_NOTEXTFOUND
        DE      Konnte keinen Biographie-Text finden!
        EN      Couldn't find any biography text!

PLUGIN_BIOGRAPHY_COLLECTINGINFO
        DE      Suche Informationen...
        EN      Gathering information...

SETUP_PLUGIN_BIOGRAPHY_SLIDESHOW
        DE      Dia-Show
        EN      Slide show mode
        ES      Modo Show de Diapositivas

SETUP_PLUGIN_BIOGRAPHY_SLIDESHOW_CHOOSE
        DE      Bildwechsel-Intervall in Sekunden:
        EN      Image change interval in seconds:
        ES      Intervalo para cambio de imágenes en segundos:

SETUP_PLUGIN_BIOGRAPHY_SLIDESHOW_DESC
        DE      Falls mehrere Bilder gefunden werden, so kann mit einem 
Mausklick auf das Bild zwischen diesen gewechselt werden. Sie können diesen 
Wechsel aber auch wie bei einer Diashow automatisieren. Geben Sie ein 
Zeitinterval an, in welchem die Bilder gewechselt werden sollen. "0" 
deaktiviert den automatischen Wechsel. (Vorgabe: 0)
        EN      There may be more than one picture for an interpret. You can 
change between them by clicking on the image or automatically by defining a 
change interval. Put 0 if you don't want the pictures to flick automatically or 
any other value for the change interval in seconds. (Default: 0)

SETUP_PLUGIN_BIOGRAPHY_REFRESH
        DE      Aktualisierungs Intervall
        EN      Refresh interval
        ES      Intervalo de recarga

SETUP_PLUGIN_BIOGRAPHY_REFRESH_CHOOSE
        DE      Aktualisierungs-Intervall in Sekunden:
        EN      Refresh interval in seconds:
        ES      Intervalo de recarga en segundos:

SETUP_PLUGIN_BIOGRAPHY_REFRESH_DESC
        DE      Sie können die Biographie im Web-Interface automatisch 
aktualisieren lassen, um sie mit der aktuell gespielten Musik zu 
synchronisieren. Geben Sie ein Zeitinterval an, in welchem die Biographie 
aktualisiert werden soll. "0" deaktiviert die automatische Aktualisierung. 
(Vorgabe: 0)
        EN      You can have the biography in your web interface update 
automatically to stay in sync with the currently playing song. Define the 
refresh interval in seconds or 0 if you don't want it to be updated 
automatically. (Default: 0)

SETUP_PLUGIN_BIOGRAPHY_CACHE
        DE      Biographie Cache
        EN      Biography cache
        ES      Cache de Biografías

SETUP_PLUGIN_BIOGRAPHY_CACHE_CHOOSE
        DE      Anzahl Tage, die die Biographien lokal gespeichert bleiben 
sollen:
        EN      Days to keep local copies of biographies:

SETUP_PLUGIN_BIOGRAPHY_CACHE_DESC
        DE      Die Biographie-Daten werden im SlimServer Cache-Verzeichnis 
zwischengespeichert, um den Zugriff zu beschleunigen. Definieren Sie hier, 
wieviele Tage die Daten lokal gespeichert werden sollen.
        EN      The artist's biographies are cached locally in SlimServer's 
cache folder to speed up the display. Define for how many days you want to keep 
the local copies. (Default: 30)

SETUP_PLUGIN_BIOGRAPHY_IGNORELIST
        DE      Liste zu ignorierender Ausdrücke
        EN      List of strings to be ignored in artist search

SETUP_PLUGIN_BIOGRAPHY_IGNORELIST_CHOOSE
        DE      Ausdrücke, die bei der Artistensuche ignoriert werden sollen:
        EN      Strings to be ignored during artist search:

SETUP_PLUGIN_BIOGRAPHY_IGNORELIST_DESC
        DE      Namen von Musikgruppen sind oft etwas unterschiedlich erfasst 
("The Beatles", "Beatles") oder aber nach einem der Musiker benannt. Dies kann 
dazu führen, dass die Gruppe nicht gefunden wird, obwohl der Artist sehr wohl 
bekannt ist (z.B. "Tony Levin Band"). Definieren Sie hier eine Liste von 
Ausdrücken, die bei der Biographie-Suche ignoriert werden sollen, um die Suche 
zu vereinfachen (z.B. "Band Trio Quartet The"). Trennen Sie die Ausdrücke mit 
einem Leerzeichen.
        EN      Some names of music groups are often edited in several slightly 
different ways ("The Beatles", "Beatles"), or contain the main artist's name. 
This can hamper the search for eg. little known or short-lived groups but who's 
main artist is well known (eg. "Tony Levin Band"). You can define a list of 
strings which are to be ignored when looking for biographies. Please define 
those strings space separated (eg. "Band Trio Quartet The").^;
}
1;
_______________________________________________
plugins mailing list
[email protected]
http://lists.slimdevices.com/lists/listinfo/plugins

Reply via email to