# Archive.pm by Dan Aronson (danaronson@yahoo.com) # # 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. use strict; ########################################### ### Section 1. Change these as required ### ########################################### package Plugins::Archive; use Slim::Networking::SimpleAsyncHTTP; use Slim::Control::Command; use Slim::Utils::Strings qw (string); use Slim::Utils::Misc; use vars qw($VERSION); $VERSION = "0.93"; my @bands; my %band_index; my $UNKNOWN = 0; my $FETCH = 1; my $PARSE = 2; my $FETCH_SHOW = 3; my $PLAYING = 4; my $OK = 5; my $ERROR = 6; my %bands; my %which_concert; my %current_song; my %saved_song; my %song_count; my %band_url; my %current_artist; my %current_year; my %year_index; my %year_count; my %years; my @years; my %old_year; my %show_index_by_artist; my %old_show_index_by_artist; my %shows; my %show_count; my %show_index; my %retrieval_error; my %content; # used for http content (see subroutine 'get') my %error; # error during call to 'get' my %get_status; my %concert_by_id; sub getDisplayName() {return "PLUGIN_ARCHIVE_MODULE_NAME"; }; sub strings { return " PLUGIN_ARCHIVE_MODULE_NAME EN Live Music Archive ";} ################################################## ### Section 2. Your variables and code go here ### ################################################## sub setMode() { my $client = shift; if (%retrieval_error && (1 == $retrieval_error{$client})) { return; } $get_status{$client} = $UNKNOWN; $client->lines(\&lines); unless (%bands) { $band_index{$client} = 0; Slim::Buttons::Block::block($client, "Fetching Archive Band List", ""); get_band_listing($client); } else { *bands = $bands{$client} if defined $bands{$client}; $current_artist{$client} = $bands[$band_index{$client}] if defined $band_index{$client}; } } my %functions = ( 'up' => sub { my $client = shift; $band_index{$client} = Slim::Buttons::Common::scroll($client, -1, $#bands + 1, $band_index{$client} || 0); $current_artist{$client} = $bands[$band_index{$client}]; $client->update(); }, 'down' => sub { my $client = shift; $band_index{$client} = Slim::Buttons::Common::scroll($client, 1, $#bands + 1, $band_index{$client} || 0); $current_artist{$client} = $bands[$band_index{$client}]; $client->update(); }, 'left' => sub { my $client = shift; Slim::Buttons::Common::popModeRight($client); }, 'right' => sub { my $client = shift; if ($years{$current_artist{$client}}) { Slim::Buttons::Common::pushModeLeft($client, 'Archive::band_page'); } else { # if load_band_page is successful, it pushes Archive::band_page mode' load_band_page($client); } }, 'numberScroll' => sub { my $client = shift; my $button = shift; my $digit = shift; $band_index{$client} = Slim::Buttons::Common::numberScroll($client, $digit, \@bands, 1); $client->update(); } ); sub lines { my $client = shift; my ($line1, $line2); $line1 = "Live Music Archive"; if (-1 == $#bands) { $line2 = "couldn't find any bands"; } else { my $band_index = $band_index{$client}; $line1 .= " (" . ($band_index+1) . ' ' . string('OF') . ' ' . ($#bands + 1) . ")"; $line2 = $bands[$band_index]; } return ($line1, $line2, undef, Slim::Hardware::VFD::symbol('rightarrow')); } # real code sub URL_decode { my $the_URL = shift; $the_URL =~ tr/+/ /; $the_URL =~ s/%([a-fA-F0-9]{2,2})/chr(hex($1))/eg; $the_URL =~ s///g; return $the_URL; } my $tmp_file = "/tmp/osa.$$"; my $etree_listing = "http://www.archive.org/audio/etreelisting-browse.php?mode=mp3"; my $etree_details_db_url = "http://www.archive.org/audio/etree-details-db.php?id="; sub http_error_cb { my $http = shift; my $client = $http->params('client'); $retrieval_error{$client} = 1; Slim::Buttons::Block::unblock($client); $::d_plugins && Slim::Utils::Misc::msg("Archive: error while getting " . $http->url . "\n"); # abort and pop back to where we were before Slim::Buttons::Common::popModeRight($client); $retrieval_error{$client} = 0; } sub get_band_listing { my $client = shift; # start asynchronous get # we'll be called back when its done. my $http = Slim::Networking::SimpleAsyncHTTP->new(\&get_band_listing_cb, \&http_error_cb, {client=>$client}); $http->get($etree_listing); } # this method called when band listing http get returns. # here we do the real work sub get_band_listing_cb { my $http = shift; my $content = $http->content; my $client = $http->params('client'); # get the real data if ($content) { while ($content =~ s/collection=etree&mode=mp3&cat=([^"]*)"//) { my $band_name = URL_decode($1); $band_url{$band_name} = $1; push(@bands, $band_name); } } $bands{$client} = @bands; if (-1 == $#bands) { $::d_plugins && Slim::Utils::Misc::msg("Couldn't find any bands, take a look at\n"); $::d_plugins && Slim::Utils::Misc::msg("the result of ${etree_listing}\n"); } $current_artist{$client} = $bands[0]; Slim::Buttons::Block::unblock($client); $client->update(); } sub get_band_page_listing { my $client = shift; my $url = shift; # start asynchronous get # we'll be called back when its done. my $http = Slim::Networking::SimpleAsyncHTTP->new(\&get_band_page_listing_cb, \&http_error_cb, {client=>$client}); $http->get($url); } sub get_band_page_listing_cb { my $http = shift; my $content = $http->content; my $client = $http->params('client'); my $band_url = $band_url{$current_artist{$client}}; my $search_string = "collection=etree&mode=mp3&cat=${band_url}%3A%20(\\d\\d\\d\\d)"; my @years = (); while ($content =~ s/$search_string//) { push(@years, $1); } Slim::Buttons::Block::unblock($client); if (-1 == $#years) { $::d_plugins && Slim::Utils::Misc::msg("Couldn't find any years, take a look at\n");; #$::d_plugins && Slim::Utils::Misc::msg("the result of ${url}\n"); } else { $years{$current_artist{$client}} = join(':', reverse(@years)); $year_index{$current_artist{$client}} = 0; Slim::Buttons::Common::pushModeLeft($client, 'Archive::band_page'); } } sub load_band_page { my $client = shift; my $band_url = $band_url{$current_artist{$client}}; my $url = $etree_listing . "&cat=" . $band_url; Slim::Buttons::Block::block($client, "Fetching year list", $current_artist{$client}); get_band_page_listing($client, $url); } sub band_page_mode_sub { my $client = shift; $client->lines(\&band_page_lines); @years = split(':', $years{$current_artist{$client}}); $year_count{$client} = $#years; }; my $band_page_mode_sub = *band_page_mode_sub; sub band_page_lines() { my $client = shift; my ($line1, $line2); $line1 = $current_artist{$client}; if (-1 != $year_index{$current_artist{$client}}) { $line1 .= " (" . ($year_index{$current_artist{$client}}+1) . ' ' . string('OF') . ' ' . ($#years + 1) . ")"; $line2 = $years[$year_index{$current_artist{$client}}]; } else { $line2 = ""; } return ($line1, $line2, undef, Slim::Hardware::VFD::symbol('rightarrow')); }; my %band_page_functions = ( 'up' => sub { my $client = shift; $year_index{$current_artist{$client}} = Slim::Buttons::Common::scroll($client, -1, $year_count{$client} + 1, $year_index{$current_artist{$client}}); $client->update(); }, 'down' => sub { my $client = shift; $year_index{$current_artist{$client}} = Slim::Buttons::Common::scroll($client, 1, $year_count{$client} + 1, $year_index{$current_artist{$client}}); $client->update(); }, 'left' => sub { my $client = shift; Slim::Buttons::Common::popModeRight($client); }, 'right' => sub { my $client = shift; if (exists ($shows{$current_artist{$client}}{$year_index{$current_artist{$client}}})) { Slim::Buttons::Common::pushModeLeft($client, 'Archive::year_page'); } else { # if year_page_listing is successful than the mode is pushed year_page_listing($client); } } ); # Band listing mode Slim::Buttons::Common::addMode('Archive::band_page', \%band_page_functions, $band_page_mode_sub); my $ID_CONCERT_SEPARATOR = "ID_CONCERT_SEPARATOR"; my $CONCERT_LIST_SEPARATOR = "CONCERT_LIST_SEPARATOR"; my $SHOW_LIST_SEPARATOR = "SHOW_LIST_SEPARATOR"; sub trim_whitespace { my $string = shift; $string =~ s/\s*(\S.*?)\s*\z//; return($1); } sub year_page_mode_listing_aux { my $client = shift; my $url = shift; # start asynchronous get # we'll be called back when its done. my $http = Slim::Networking::SimpleAsyncHTTP->new(\&year_page_mode_listing_aux_cb, \&http_error_cb, {client=>$client}); $http->get($url); } sub year_page_mode_listing_aux_cb { my $http = shift; my $content = $http->content; my $client = $http->params('client'); my $search_string_details = "etree-details-db.php\\?id=(\\d+).*(.*)"; my $search_string_date = "Date:\\s*(.*?)[,<]"; my $search_string_venue = "Venue:\\s*(.*?)<"; my @concerts = (); while ($content =~ s/$search_string_details//) { my $id = $1; my $concert = $2; my $date = ""; my $venue = ""; my $concerts = (); if (defined($id) && defined($concert)) { # let's get the following date and venue location $content =~ s/$search_string_date//; $date = $1; $content =~ s/$search_string_venue//; $venue = $1; } push(@concerts, join($ID_CONCERT_SEPARATOR, trim_whitespace($id), trim_whitespace($concert), trim_whitespace($date), trim_whitespace($venue))); } Slim::Buttons::Block::unblock($client); if (-1 == $#concerts) { $::d_plugins && Slim::Utils::Misc::msg("Couldn't find conert list, take a look at\n");; $::d_plugins && Slim::Utils::Misc::msg("the result of ".$http->url."\n"); } else { $shows{$current_artist{$client}}{$year_index{$current_artist{$client}}} = join($CONCERT_LIST_SEPARATOR, @concerts); $show_index{$current_artist{$client}}{$year_index{$current_artist{$client}}} = 0; Slim::Buttons::Common::pushModeLeft($client, 'Archive::year_page'); } } sub year_page_listing { my $client = shift; my $year_index = $year_index{$current_artist{$client}}; my $year_name = (split(':', $years{$current_artist{$client}}))[$year_index]; my $year_url = $band_url{$current_artist{$client}} . "%3A%20" . $year_name; my $url = $etree_listing . "&cat=" . $year_url; # cache the index (so we don't have to keep looking this up) Slim::Buttons::Block::block($client, "Fetching Concert list from ${year_name}", $current_artist{$client}); year_page_mode_listing_aux($client, $url); }; sub year_page_mode_sub { my $client = shift; $client->lines(\&year_page_lines); my @concerts = split($CONCERT_LIST_SEPARATOR, $shows{$current_artist{$client}} {$year_index{$current_artist{$client}}}); $show_index{$client} = $show_index{$current_artist{$client}}{$year_index{$current_artist{$client}}}; $show_count{$client} = $#concerts; }; my $year_page_mode_sub = *year_page_mode_sub; sub year_page_lines() { my $client = shift; my ($line1, $line2); my @concerts = split($CONCERT_LIST_SEPARATOR, $shows{$current_artist{$client}} {$year_index{$current_artist{$client}}}); if (-1 != $show_index{$client}) { my ($id, $concert, $date, $venue) = split($ID_CONCERT_SEPARATOR, $concerts[$show_index{$client}]); $line1 = $current_artist{$client} . '/' . $years[$year_index{$current_artist{$client}}] . ' (' . ($show_index{$client} + 1) . ' ' . string('OF') . ' ' . ($#concerts + 1) . ")"; $line2 = $date . "/" . $venue; } else { $line1 = "Current Artist = " . $current_artist{$client} ."\n"; $line2 = ""; } return ($line1, $line2, undef, Slim::Hardware::VFD::symbol('notesymbol') . Slim::Hardware::VFD::symbol('rightarrow')); }; sub process_concert_button_press { my $client = shift; my $button = shift; my @concerts = split($CONCERT_LIST_SEPARATOR, $shows{$current_artist{$client}}{$year_index{$current_artist{$client}}}); my ($id, $concert) = split($ID_CONCERT_SEPARATOR, $concerts[$show_index{$client}]); if (exists $concert_by_id{$id}) { process_concert_button_press_aux($client, $id, $button); } else { my $details_url = $etree_details_db_url . $id; Slim::Buttons::Block::block($client, "Fetching playlist", $concert); retrieve_playlist($client, $id, $button); } } my $PLAYLISTITEM = "PLAYLISTITEM"; sub process_concert_button_press_aux { my $client = shift; my $id = shift; my $button = shift; if (!exists($concert_by_id{$id})) { return; } my @playlist = split($PLAYLISTITEM, $concert_by_id{$id}); $which_concert{$client} = $id; $song_count{$id} = $#playlist + 1; if (('play' eq $button) || ('add' eq $button)) { my $index = 0; my $song; # experimental: send some info, slimservice will use it # Slim::Utils::Misc::msg("Archive Concert: ". $concert_by_id{$id} . "\n"); my ($id, $concert, $date, $venue) = split($ID_CONCERT_SEPARATOR, $concert_by_id{$id}); my $playlistName = $date . "/" . $years[$year_index{$current_artist{$client}}] . "/" . $venue . "/" . $current_artist{$client}; my $playlistid = "Archive." . $id; Slim::Control::Command::execute($client, ['info', 'begin_playlist', $playlistid, $playlistName]); foreach $song (@playlist) { if ((0 == $index) && ('play' eq $button)) { Slim::Control::Command::execute($client, ['playlist', 'play', $song]); } else { Slim::Control::Command::execute($client, ['playlist', 'add', $song]); } $index++; } Slim::Control::Command::execute($client, ['info', 'end_playlist', $playlistid, $playlistName]); Slim::Control::Command::execute($client, ['play']); if ('add' eq $button) { Slim::Display::Animation::showBriefly($client, "Added to current playlist"); } } else { Slim::Buttons::Common::pushModeLeft($client, 'Archive::concert_page'); } } sub retrieve_playlist { my $client = shift; my $id = shift; my $button = shift; # start asynchronous get # we'll be called back when its done. my $http = Slim::Networking::SimpleAsyncHTTP->new(\&retrieve_playlist_cb, \&http_error_cb, {client=>$client, id=>$id, button=>$button}); $http->get($etree_details_db_url . $id); } sub retrieve_playlist_cb { my $http = shift; my $content = $http->content; my $client = $http->params('client'); my $id = $http->params('id'); my $button = $http->params('button'); Slim::Buttons::Block::unblock($client); if ($content) { my $song; my $index = 0; my @playlist = parse_playlist($content); if (-1 == $#playlist) { Slim::Display::Animation::showBriefly($client, "Error, getting show, try another."); $::d_plugins && Slim::Utils::Misc::msg("Couldn't find playlist, take a look at\n");; $::d_plugins && Slim::Utils::Misc::msg("the result of " . $etree_details_db_url . $id . "\n"); } else { $concert_by_id{$id} = join($PLAYLISTITEM, @playlist); } } else { Slim::Display::Animation::showBriefly($client, "Error, getting show, try another."); } process_concert_button_press_aux($client, $id, $button); } my %year_page_functions = ( 'up' => sub { my $client = shift; $show_index{$client} = Slim::Buttons::Common::scroll($client, -1, $show_count{$client} + 1, $show_index{$client}); $client->update(); }, 'down' => sub { my $client = shift; $show_index{$client} = Slim::Buttons::Common::scroll($client, 1, $show_count{$client} + 1, $show_index{$client}); $client->update(); }, 'left' => sub { my $client = shift; # reset cache $show_index{$current_artist{$client}}{$year_index{$current_artist{$client}}} = $show_index{$client}; Slim::Buttons::Common::popModeRight($client); }, 'right' => sub { my $client = shift; # reset cache $show_index{$current_artist{$client}}{$year_index{$current_artist{$client}}} = $show_index{$client}; process_concert_button_press($client, 'right'); }, 'add' => sub { my $client = shift; process_concert_button_press($client, 'add'); }, 'play' => sub { my $client = shift; process_concert_button_press($client, 'play'); } ); my $playlist_search_string = ".*