# vim: foldmethod=marker
# Live365 tuner plugin for SlimServer
# Copyright (C) 2004  Jim Knepley
#
# This program 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.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#

package Plugins::Live365::Plugin;

use strict;
use vars qw( $VERSION );
$VERSION = 1.20;

use Slim::Player::ProtocolHandlers;
use Slim::Utils::Strings qw( string );
use Slim::Utils::Misc qw( msg );
use Slim::Control::Request;

# Need this to include the other modules now that we split up Live365.pm
use Plugins::Live365::ProtocolHandler;
use Plugins::Live365::Live365API;
use Plugins::Live365::Web;

use constant ROWS_TO_RETRIEVE => 50;

# {{{ Initialize
our $live365 = {};
our %channelModeFunctions;
our %mainModeFunctions;
our %genreModeFunctions;
our %searchModeFunctions;
our %infoModeFunctions = ();

our @searchModeItems;
our @mainModeItems;

our $mainModeIdx = 0;
our $searchModeIdx = 0;

our %searchString;
our @statusText;

my $cli_next;

sub addMenu {
	return "RADIO";
}

sub getDisplayName {
	return 'PLUGIN_LIVE365_MODULE_NAME';
}

sub setupGroup {
	my %Group = (
		PrefOrder => [
			'plugin_live365_username',
			'plugin_live365_password',
			'plugin_live365_sort_order',
			'plugin_live365_web_show_details'
		],
		GroupHead => string( 'SETUP_GROUP_PLUGIN_LIVE365' ),
		GroupDesc => string( 'SETUP_GROUP_PLUGIN_LIVE365_DESC' ),
		GroupLine => 1,
		GroupSub  => 1,
		Suppress_PrefSub  => 1,
		Suppress_PrefLine => 1
	);

	my %sort_options = (
		'T:A' => string( 'SETUP_PLUGIN_LIVE365_SORT_TITLE' ),
		'B:D' => string( 'SETUP_PLUGIN_LIVE365_SORT_BPS' ),
		'R:D' => string( 'SETUP_PLUGIN_LIVE365_SORT_RATING' ),
		'L:D' => string( 'SETUP_PLUGIN_LIVE365_SORT_LISTENERS' ),
	);

	my %Prefs = (
		plugin_live365_username => {
		},
		plugin_live365_password => { 
			'onChange' => sub {
				my $encoded = pack( 'u', $_[1]->{plugin_live365_password}->{new} );
				chomp $encoded;
				Slim::Utils::Prefs::set( 'plugin_live365_password', $encoded );
			},
			'inputTemplate' => 'setup_input_passwd.html',
			'changeMsg' => string('SETUP_PLUGIN_LIVE365_PASSWORD_CHANGED'),
		},
		plugin_live365_sort_order => {
			options => \%sort_options
		},
		plugin_live365_web_show_details => {
				validate => \&Slim::Utils::Validate::trueFalse,
				options  => {
						1 => string('ON'),
						0 => string('OFF')
				},
				'PrefChoose' => string('SETUP_PLUGIN_LIVE365_WEB_SHOW_DETAILS')
		}
	);

	return( \%Group, \%Prefs );
}

sub playOrAddCurrentStation {
	my $client = shift;
	my $play = shift;

	my $stationURL = $live365->{$client}->getCurrentChannelURL();
	$::d_plugins && msg( "Live365.ChannelMode URL: $stationURL\n" );

	my $line1;
	if ($play) {
		$line1 = $client->string('CONNECTING_FOR');
	}
	else {
		$line1 = $client->string('ADDING_TO_PLAYLIST');
	}

	my $title = $live365->{$client}->getCurrentStation()->{STATION_TITLE};
	$client->showBriefly({
		'line1'    => $line1,
		'line2'    => $title,
		'overlay2' => $client->symbols('notesymbol'),
	});

	Slim::Music::Info::setContentType($stationURL, 'mp3');
	Slim::Music::Info::setTitle($stationURL, $title);

	if ( $play ) {
		$client->execute([ 'playlist', 'clear' ] );
		$client->execute([ 'playlist', 'play', $stationURL ] );
	}
	else {
		$client->execute([ 'playlist', 'add', $stationURL ] );
	}
}

# }}}

#############################
# Main mode {{{
# 
MAINMODE: {
	sub setMode {
		my $client = shift;
		my $entryType = shift;
	
		if ($entryType eq 'pop') {
			Slim::Buttons::Common::popMode($client);
			return;
		}
	
		unless( defined $live365->{$client} ) {
			$live365->{$client} = new Plugins::Live365::Live365API();
		}
	
		my ( $loginModePtr ) = ( grep { $_->[0] eq 'loginMode' } @mainModeItems )[0];
		if( $entryType eq 'push' ) {
			loginMode($client, {'silent' => 1}) unless( $live365->{$client}->isLoggedIn() );
		}
	
		if( $live365->{$client}->isLoggedIn() ) {
			$loginModePtr->[1] = 'PLUGIN_LIVE365_LOGOUT';
		} else {
			$loginModePtr->[1] = 'PLUGIN_LIVE365_LOGIN';
		}
		
		Slim::Buttons::Common::pushModeLeft($client, 'INPUT.List',
			{
			'listRef'        => \@mainModeItems,
			'externRef'      => sub { 
								if ($_[1][0] eq 'loginMode') {
									return $_[0]->string($live365->{$_[0]}->isLoggedIn() ? 'PLUGIN_LIVE365_LOGOUT' : 'PLUGIN_LIVE365_LOGIN');
								} else {
									return $_[0]->string($_[1][1]);
								}
							},
			'externRefArgs'  => 'CV',
			'header'         => \&mainHeader,
			'headerArgs'     => 'CI',
			'stringHeader'   => 1,
			'headerAddCount' => 1,
			'stringHeader'   => 1,
			'headerAddCount' => 1,
			'callback'       => \&mainExitHandler,
			'overlayRef'     => sub {
									return (undef, Slim::Display::Display::symbol('rightarrow'));
								},
			}
		);
	}
	
	sub mainExitHandler {
		my ($client,$exitType) = @_;
	
		$exitType = uc($exitType);
		my $selection = ${$client->param('valueRef')};
	
		if ($exitType eq 'LEFT') {
			Slim::Buttons::Common::popModeRight($client);
		} else {
			my $success = 0;
			my $stationParams = {};
			SWITCH: {
				$selection->[1] eq 'PLUGIN_LIVE365_PRESETS' && do {
					if( !$live365->{$client}->getSessionID() ) {
						$client->showBriefly({'line1' => $client->string( 'PLUGIN_LIVE365_NOT_LOGGED_IN' )});
					} else {
						$success = 1;
					}
					last SWITCH;
				};
	
				$selection->[1] eq 'PLUGIN_LIVE365_BROWSEALL' && do {
					$stationParams = {
						'sort'			=> Slim::Utils::Prefs::get( 'plugin_live365_sort_order' ),
						'searchfields'	=> Slim::Utils::Prefs::get( 'plugin_live365_search_fields' )
					};
					$success = 1;
					last SWITCH;
				};
	
				$selection->[1] eq 'PLUGIN_LIVE365_BROWSEPICKS' && do {
					$stationParams = {
						'sort'			=> Slim::Utils::Prefs::get( 'plugin_live365_sort_order' ),
						'searchfields'	=> Slim::Utils::Prefs::get( 'plugin_live365_search_fields' ),
						'genre' 		=> 'ESP'
					};
					$success = 1;
					last SWITCH;
				};
	
				$selection->[1] eq 'PLUGIN_LIVE365_BROWSEPROS' && do {
					$stationParams = {
						'sort'			=> Slim::Utils::Prefs::get( 'plugin_live365_sort_order' ),
						'searchfields'	=> Slim::Utils::Prefs::get( 'plugin_live365_search_fields' ),
						'genre'			=> 'Pro'
					};
					$success = 1;
					last SWITCH;
				};
	
				$success = 1;
			}
	
			if( $success ) {
				if ($selection->[0] eq 'loginMode') {
					loginMode($client);
				}
				else {
					Slim::Buttons::Common::pushModeLeft( $client, $selection->[0], { source => $selection->[1], stationParams => $stationParams } );
				}
			}
		}
	}
	
	sub mainHeader {
		my $client = shift;
		my $index = shift;
		
		if ( my $APImessage = $live365->{$client}->status() ) {
			return $client->string( $APImessage);
		} else {
			return $client->string('PLUGIN_LIVE365_MODULE_NAME');
		}
	}
	
	sub getFunctions {
		return \%mainModeFunctions;
	}

} # end main mode

# }}}

#############################
# Login mode {{{
#
LOGINMODE: {
	sub loginMode {
		my $client = shift;
		my $args = shift;
		
		my $silent = $args->{'silent'};
	
		my $userID   = Slim::Utils::Prefs::get( 'plugin_live365_username' );
		my $password = Slim::Utils::Prefs::get( 'plugin_live365_password' );
		my $loggedIn = $live365->{$client}->isLoggedIn();
	
		if (defined $password) {
			$password = unpack('u', $password);
		}
	
		if( $loggedIn ) {
			$::d_plugins && msg( "Logging out $userID\n" );
			my $logoutStatus = $live365->{$client}->logout($client, \&logoutDone, $silent);
		
		} else {
		
			if( $userID and $password ) {
				$::d_plugins && msg( "Logging in $userID\n" );
				my $loginStatus = $live365->{$client}->login( $userID, $password,
															  $client, \&loginDone, $silent);
			} else {
				$::d_plugins && msg( "Live365.login: no credentials set\n" );
				unless ($silent) {
					$client->showBriefly({'line1' => $client->string( 'PLUGIN_LIVE365_NO_CREDENTIALS' )});
				}
			}
		}
	};
	
	sub loginDone {
		my $client = shift;
		my $args = shift;
		
		my $loginStatus = $args->{'status'};
		my $webOnly = $args->{'webOnly'};
		my $silent = $args->{'silent'};
	
		if( $loginStatus == 0 ) {
	
			Slim::Utils::Prefs::set( 'plugin_live365_sessionid', $live365->{$client}->getSessionID() );
			Slim::Utils::Prefs::set( 'plugin_live365_memberstatus', $live365->{$client}->getMemberStatus() );
	
			unless ($silent) {
				$client->showBriefly({'line1' => $client->string( 'PLUGIN_LIVE365_LOGIN_SUCCESS' )});
			}
	
			$live365->{$client}->setLoggedIn( 1 );
			$::d_plugins && msg( "Live365 logged in: " . $live365->{$client}->getSessionID() . "\n" );
	
		} else {
			Slim::Utils::Prefs::set( 'plugin_live365_sessionid', undef );
			Slim::Utils::Prefs::set( 'plugin_live365_memberstatus', undef );
			$client->showBriefly({'line1' => $client->string( $statusText[ $loginStatus ] )});
			$live365->{$client}->setLoggedIn( 0 );
			$::d_plugins && msg( "Live365 login failure: " . $statusText[ $loginStatus ] . "\n" );
		}
	}
	
	sub logoutDone {
		my $client = shift;
		my $args = shift;
		
		my $logoutStatus = $args->{'status'};
		my $silent = $args->{'silent'};
	
		if( $logoutStatus == 0 ) {
			$client->showBriefly({'line1' => $client->string( $statusText[ $logoutStatus ])});
			  $::d_plugins && msg( "Live365 logged out.\n" );
	
		} else {
			$client->showBriefly({'line1' => $client->string( $statusText[ $logoutStatus ])});
			$::d_plugins && msg( "Live365 logout error: $statusText[ $logoutStatus ]\n" );
		}
	
		$live365->{$client}->setLoggedIn( 0 );
		Slim::Utils::Prefs::set( 'plugin_live365_sessionid', '' );
	}
	
	sub noLoginMode {
		my $client = shift;
	};

} # end login mode

# }}}

#############################
# Genre mode {{{
#
my @genreList = ();

GENREMODE: {
	sub setGenreMode {
		my $client = shift;
		my $entryType = shift;
	
		if ($entryType eq 'pop') {
			Slim::Buttons::Common::popMode($client);
			return;
		}
	
		$client->lines( \&loadingLines );
	
		if (!scalar(@genreList)) {
			$live365->{$client}->setBlockingStatus( 'PLUGIN_LIVE365_LOADING_GENRES' );
			$live365->{$client}->loadGenreList($client, \&genreModeLoad, \&genreModeError);
		} else {
			genreModeLoad($client);
		}
	
	};
	
	sub genreModeLoad {
		my $client = shift;
		my $list = shift;
	
		@genreList = @$list if $list;
	
		$live365->{$client}->clearBlockingStatus();

		Slim::Buttons::Common::pushModeLeft($client, 'INPUT.List',
			{
			'listRef'        => \@genreList,
			'externRef'      => sub { 
										return $_[1][0];
								},
			'externRefArgs'  => 'CV',
			'header'         => sub {
									if( my $APImessage = $live365->{$client}->status() ) {
										return $_[0]->string( $APImessage );
									} else {
										return $_[0]->string('PLUGIN_LIVE365_GENRES');
									}
								},
			'headerArgs'     => 'C',
			'stringHeader'   => 1,
			'headerAddCount' => 1,
			'stringHeader'   => 1,
			'headerAddCount' => 1,
			'callback'       => \&genreExitHandler,
			'overlayRef'     => sub {
									return (undef, Slim::Display::Display::symbol('rightarrow'));
								},
			'isSorted'       => 'L',
			'lookupRef'      => sub { 
									my $index = shift;
									return $genreList[$index][0];
								},
			'lookupRefArgs'  => 'I',
			}
		);
	}
	
	sub genreModeError {
		my $client = shift;
	
		$live365->{$client}->clearBlockingStatus();
	
		$client->showBriefly({'line1' => $client->string( 'PLUGIN_LIVE365_LOGIN_ERROR_HTTP' )});
		#Slim::Buttons::Common::popModeRight( $client );
	}
	
	sub genreExitHandler {
		my ($client,$exitType) = @_;
	
		$exitType = uc($exitType);
		my $selection = ${$client->param('valueRef')};
	
		if ($exitType eq 'LEFT') {
				$live365->{$client}->stopLoading();
				Slim::Buttons::Common::popModeRight( $client );
		} else {
				if (!scalar(@genreList)) {
					$client->bumpRight();
					return;
				}
		
				my $genrePointer = ${$client->param('valueRef')};
				
				my $stationParams = {
					'genre'			=> $genrePointer->[1],
					'sort'			=> Slim::Utils::Prefs::get( 'plugin_live365_sort_order' ),
					'searchfields'	=> Slim::Utils::Prefs::get( 'plugin_live365_search_fields' )
				};
				Slim::Buttons::Common::pushModeLeft( $client, 'Live365Channels', 
									{ source => $genrePointer->[0], 
									  stationParams => $stationParams  } );
		}
	}
	
	sub noGenreMode {
		my $client = shift;
	};

} # end genre mode

# }}}

#############################
# Channel mode {{{
#
CHANNELMODE: {
	sub setChannelMode {
		my $client = shift;
		my $entryType = shift;
	
		if ($entryType eq 'pop') {
			Slim::Buttons::Common::popMode($client);
			return;
		}
		
		$client->lines( \&loadingLines );
		
		$live365->{$client}->setBlockingStatus( 'PLUGIN_LIVE365_LOADING_DIRECTORY' );

		my $source = $client->param('source');
		if (defined($source) && $source eq 'PLUGIN_LIVE365_PRESETS') {
			$live365->{$client}->clearStationDirectory();
			$live365->{$client}->loadMemberPresets($source,
												   $client, 
												   \&channelModeLoad,
												   \&channelModeError);
			return;
		}
		elsif (!defined($source) ||
			   $source ne $live365->{$client}->getStationSource()) {

			my $pointer = defined($source) ? $live365->{$client}->getChannelModePointer($source) || 0 : 0;

			my $stationParams = $client->param('stationParams');

			# If the last position within the station list is greater than
			# the default number of rows to retrieve, get enough so that
			# we have a non-sparse station array.
			if ($pointer > ROWS_TO_RETRIEVE) {
				$stationParams->{'rows'} = $pointer + ROWS_TO_RETRIEVE;
			}
			else {
				$stationParams->{'rows'} = ROWS_TO_RETRIEVE;
			}

			$live365->{$client}->clearStationDirectory();
			$live365->{$client}->loadStationDirectory(
													  $source,
													  $client, 
													  \&channelModeLoad,
													  \&channelModeError,
													  0,
													  %$stationParams);
			return;
		}
		channelModeLoad($client);
	};
	
	sub channelModeLoad {
		my $client = shift || return;
	
		my $source = $client->param('source');
		
		# Check if the current pointer for the source mode is greater than
		# the number of stations currently loaded. If so, clip it.
		my $pointer = defined($source) ? $live365->{$client}->getChannelModePointer($source) || 0 : 0;
		my $listlength = $live365->{$client}->getStationListLength();
		if ($listlength) {
			if ($pointer >= $listlength) {
				$pointer = $listlength - 1;
			}
			
			$live365->{$client}->setStationListPointer($pointer);
		}
	
		$live365->{$client}->clearBlockingStatus();
		
		Slim::Buttons::Common::pushModeLeft($client, 'INPUT.List',
			{
			'listRef'        => [1..$live365->{$client}->getStationListLength()],
			'externRef'      => sub { 
										if (my $station = $live365->{$_[0]}->getCurrentStation()) {
											return $station->{STATION_TITLE};
										} elsif ( my $APImessage = $live365->{$client}->status() ) {
											return $_[0]->string( $APImessage );
										}
								},
			'externRefArgs'  => 'C',
			'header'         => sub {
									if( my $APImessage = $live365->{$client}->status() ) {
										return $_[0]->string( $APImessage );
									} else {
										return $_[0]->string('PLUGIN_LIVE365_STATIONS');
									}
								},
			'headerArgs'     => 'C',
			'stringHeader'   => 1,
			'headerAddCount' => 1,
			'stringHeader'   => 1,
			'headerAddCount' => 1,
			'onChange' => sub {
							if( $live365->{$client}->willRequireLoad( $_[1] ) ) {
								$live365->{$client}->setBlockingStatus( 'PLUGIN_LIVE365_LOADING_DIRECTORY' );
								$client->update();
								$live365->{$_[0]}->setStationListPointer($_[1], 
												   $_[0],
												   \&channelAdditionalLoad,
												   \&channelAdditionalError);
							} else {
								$live365->{$client}->setStationListPointer( $_[1] );
							};
						},
			'onChangeArgs' => 'CV',
			'callback'       => \&channelExitHandler,
			'overlayRef'     => sub {
									return (undef, Slim::Display::Display::symbol('notesymbol'));
								},
			}
		);
	}
	
	sub channelExitHandler {
		my ($client,$exitType) = @_;
	
		$exitType = uc($exitType);
		my $selection = ${$client->param('valueRef')};
	
		if ($exitType eq 'LEFT') {
				$live365->{$client}->stopLoading();
				Slim::Buttons::Common::popModeRight( $client );
		} else {
				if (!$live365->{$client}->getStationListLength()) {
					$client->bumpRight();
					return;
				}
		
				my $source = $client->param( 'source');
	
				if (defined($source)) {
					$live365->{$client}->setChannelModePointer($source, 
						$live365->{$client}->getStationListPointer());
				}
			
				Slim::Buttons::Common::pushModeLeft( $client, 'ChannelInfo' );
		}
	}
	
	sub channelModeError {
		my $client = shift;
	
		my $source = $client->param( 'source');
	
		$live365->{$client}->clearBlockingStatus();
	
		if ($live365->{$client}->isLoggedIn()) {
			$::d_plugins && defined($source) && msg( "No stations for source: $source\n");
			$client->showBriefly({'line1' => $client->string( 'PLUGIN_LIVE365_NOSTATIONS' )});
	
		} else {
			$client->showBriefly({'line1' => $client->string('PLUGIN_LIVE365_NOT_LOGGED_IN' )});
		}
	
		Slim::Buttons::Common::popModeRight( $client );
	}
	
	sub noChannelMode {
		my $client = shift;
	};
	
	sub channelAdditionalLoad {
		my $client = shift;
	
		# if the numberscroll has been used, we may have to keep loading additional blocks until 
		# it is in range of the new stationListPointer
		if( $live365->{$client}->willRequireLoad( $live365->{$client}->getStationListPointer ) ) {
			$live365->{$client}->setBlockingStatus( 'PLUGIN_LIVE365_LOADING_DIRECTORY' );
			$client->update();
			$live365->{$client}->setStationListPointer($live365->{$client}->getStationListPointer, 
						   $client,
						   \&channelAdditionalLoad,
						   \&channelAdditionalError);
		} else {
	
			$live365->{$client}->clearBlockingStatus();
			$client->update();
		}
	}
	
	sub channelAdditionalError {
		my $client = shift;
	
		$live365->{$client}->clearBlockingStatus();
		$client->update();
	}
	
	sub loadingLines {
		my $client = shift;
	
		if( my $APImessage = $live365->{$client}->status() ) {
			return { 'line1' => $client->string( $APImessage ) };
		}
	}

} # end channel mode

# }}}

#############################
# Information mode {{{
#
INFOMODE: {

	sub setInfoMode {
		my $client = shift;
		my $method = shift;
		
		if ($method eq 'pop') {
			Slim::Buttons::Common::popMode($client);
			return;
		}
	
		$live365->{$client}->setBlockingStatus( 'PLUGIN_LIVE365_LOADING_INFORMATION' );
		$client->update();
	
		$live365->{$client}->loadInfoForStation( $live365->{$client}->getCurrentStation()->{STATION_ID}, $client, \&infoModeLoad, \&infoModeError );;
	};
	
	sub infoModeLoad {
		my $client = shift;
	
		my @infoItems = $live365->{$client}->getStationInfo();
		my @items = map {"{PLUGIN_LIVE365_". $_->[0] . "}: " . $_->[1]} @infoItems;
	
		infoModeCommon($client, @items);
	}
	
	sub infoModeError {
		my $client = shift;
	
		infoModeCommon($client, "{PLUGIN_LIVE365_NO_INFO}");
	}
	
	sub infoModeCommon {
		my $client = shift;
	
		my $url = $live365->{$client}->getCurrentChannelURL();
		my $title = $live365->{$client}->getCurrentStation()->{STATION_TITLE};
	
		# use remotetrackinfo mode to show all details
		my %params  = (
			url     => $url,
			title   => $title,
			details => \@_,
		);
	
		$live365->{$client}->clearBlockingStatus();
		Slim::Buttons::Common::pushMode($client, 'remotetrackinfo', \%params);  
		$client->update();
	}
	
	sub noInfoMode {
		my $client = shift;
	};

} # end info mode

# }}}

#############################
# Search mode {{{
#
SEARCHMODE: {
	
	@searchModeItems = (
		[ 'PLUGIN_LIVE365_SEARCH_TAC', 'T:A:C' ],
		[ 'PLUGIN_LIVE365_SEARCH_A'  , 'A' ],
		[ 'PLUGIN_LIVE365_SEARCH_T'  , 'T' ],
		[ 'PLUGIN_LIVE365_SEARCH_C'  , 'C' ],
		[ 'PLUGIN_LIVE365_SEARCH_E'  , 'E' ],
		[ 'PLUGIN_LIVE365_SEARCH_L'  , 'L' ],
		[ 'PLUGIN_LIVE365_SEARCH_H'  , 'H' ]
	);
	
	sub setSearchMode {
		my $client = shift;
		my $entryType = shift;

		if ($entryType eq 'pop') {
			Slim::Buttons::Common::popMode($client);
			return;
		}

		$searchString{$client} = '';
		Slim::Buttons::Common::pushModeLeft($client, 'INPUT.List',
				{
				'listRef'        => \@searchModeItems,
				'externRef'      => sub { 
										if( my $APImessage = $live365->{$client}->status() ) {
											return $_[0]->string( $APImessage )
										} else {
											return $_[0]->string($_[1][0]);
										}
									},
				'externRefArgs'  => 'CV',
				'header'         => 'PLUGIN_LIVE365_SEARCH',
				'stringHeader'   => 1,
				'headerAddCount' => 1,
				'callback'       => \&searchExitHandler,
				'overlayRef'     => sub { return (undef, Slim::Display::Display::symbol('rightarrow')) },
				'overlayRefArgs' => '',
				}
			);
	}
	
	sub searchExitHandler {
		my ($client,$exitType) = @_;
	
		$exitType = uc($exitType);
		
		if ($exitType eq 'LEFT') {
			Slim::Buttons::Common::popModeRight($client);
		} else {
			my $type = ${$client->param('valueRef')}->[1];

			Slim::Buttons::Common::pushModeLeft( $client, 'INPUT.Text',
				{
					'callback'	   => \&doSearch,
					'valueRef'	   => \$searchString{$client},
					'header'	   => $client->string( 'PLUGIN_LIVE365_SEARCHPROMPT' ),
					'cursorPos'    => 0,
					'searchfields' => $type,
				}
			);
		}
	}
	
	sub noSearchMode {
		my $client = shift;
	};
	
	sub doSearch {
		my $client = shift;
		my $exitType = shift;
	
		my $arrow = Slim::Display::Display::symbol('rightarrow');
		$searchString{$client} =~ s/$arrow//;
		my $searchfields = $client->param('searchfields');
	
		ExitEventType: {
			$::d_plugins && msg( "Live365.doSearch exit input mode: '$exitType'\n" );
	
			$exitType =~ /(backspace|cursor_left|delete|scroll_left)/ && do {
				Slim::Buttons::Common::popModeRight( $client );
				return;
				last ExitEventType;
			};
	
			$exitType =~ /(cursor_right|nextChar|scroll_right)/ && do {
				if( $searchString{$client} eq '' ) {
					$::d_plugins && msg( "Live365.doSearch string empty, returning\n" );
					return;
				}
				$::d_plugins && msg( "Live365.doSearch string: '$searchString{$client}'\n" );
				last ExitEventType;
			};
	
			$::d_plugins && msg( "Live365.doSearch: unsupported exit '$exitType'\n" );
			return;
		}
	
		my $stationParams = {
			'searchdesc'   => $searchString{$client},
			'sort'		   => Slim::Utils::Prefs::get( 'plugin_live365_sort_order' ),
			'searchfields' => $searchfields
		};
	
		Slim::Buttons::Common::pushModeLeft( $client, 'Live365Channels',
											 { stationParams => $stationParams } );
	}
} # end search mode

#
# Add web pages and handlers.  See Plugins::Live365::Web for handlers.
#
sub webPages {
	$::d_plugins && msg("Live365: webPages()\n");
	
	my %pages = (
		"browse\.(?:htm|xml)"   => \&Plugins::Live365::Web::handleBrowse,
		"search\.(?:htm|xml)"   => \&Plugins::Live365::Web::handleSearch,
		"action\.(?:htm|xml)"   => \&Plugins::Live365::Web::handleAction,
		"index\.(?:htm|xml)"    => \&Plugins::Live365::Web::handleIndex,
		"loginout\.(?:htm|xml)" => \&Plugins::Live365::Web::handleLogin
	);

	if (grep {$_ eq 'Live365::Plugin'} Slim::Utils::Prefs::getArray('disabledplugins')) {
		Slim::Web::Pages->addPageLinks("radio", { 'PLUGIN_LIVE365_MODULE_NAME' => undef } );
	} else {
		Slim::Web::Pages->addPageLinks("radio", { 'PLUGIN_LIVE365_MODULE_NAME' => "plugins/Live365/index.html?autologin=1" } );
	}
	
	return (\%pages, undef);
}

sub getLive365 {
	my $client = shift;
	
	if ( defined $live365->{$client} ) {
		return $live365->{$client};
	} else {
		return undef;
	}
}

sub initPlugin {
	$::d_plugins && msg("Live365: initPlugin()\n");

	Slim::Player::ProtocolHandlers->registerHandler("live365", "Plugins::Live365::ProtocolHandler");
	Slim::Buttons::Common::addMode( 'searchMode',      \%searchModeFunctions,  \&setSearchMode,  \&noSearchMode );
	Slim::Buttons::Common::addMode( 'genreMode',       \%genreModeFunctions,   \&setGenreMode,   \&noGenreMode );
	Slim::Buttons::Common::addMode( 'ChannelInfo',     \%infoModeFunctions,    \&setInfoMode,    \&noInfoMode );
	Slim::Buttons::Common::addMode( 'Live365Channels', \%channelModeFunctions, \&setChannelMode, \&noChannelMode );
	
	@mainModeItems = (
		[ 'genreMode',			'PLUGIN_LIVE365_BROWSEGENRES' ],
		[ 'Live365Channels',	'PLUGIN_LIVE365_BROWSEPICKS' ],
		[ 'Live365Channels',	'PLUGIN_LIVE365_BROWSEPROS' ],
		[ 'Live365Channels',	'PLUGIN_LIVE365_BROWSEALL' ],
		[ 'searchMode',			'PLUGIN_LIVE365_SEARCH' ],
		[ 'Live365Channels',	'PLUGIN_LIVE365_PRESETS' ],
		[ 'loginMode',			'PLUGIN_LIVE365_LOGIN' ]
	);

	@statusText = qw(
		PLUGIN_LIVE365_LOGIN_SUCCESS
		PLUGIN_LIVE365_LOGIN_ERROR_NAME
		PLUGIN_LIVE365_LOGIN_ERROR_LOGIN
		PLUGIN_LIVE365_LOGIN_ERROR_ACTION
		PLUGIN_LIVE365_LOGIN_ERROR_ORGANIZATION
		PLUGIN_LIVE365_LOGIN_ERROR_SESSION
		PLUGIN_LIVE365_LOGIN_ERROR_HTTP
	);
	
	# register our functions
	
#		  |requires Client
#		  |  |is a Query
#		  |  |  |has Tags
#		  |  |  |  |Function to call
#		  C  Q  T  F
	Slim::Control::Request::addDispatch(['live365', 'genres', '_index', '_quantity'],  
		 [0, 1, 1, \&Plugins::Live365::Web::cli_genresQuery]);
	Slim::Control::Request::addDispatch(['live365', 'stations', '_index', '_quantity'],  
		 [0, 1, 1, \&Plugins::Live365::Web::cli_stationsQuery]);
	Slim::Control::Request::addDispatch(['live365', 'playlist', '_mode'],  
		 [1, 0, 1, \&Plugins::Live365::Web::cli_playlistCommand]);
	$cli_next=Slim::Control::Request::addDispatch(['radios', '_index', '_quantity' ],
		 [0, 1, 1, \&cli_radiosQuery]);

	%channelModeFunctions = (
		'play' => sub {
			my $client = shift;
	
			playOrAddCurrentStation($client, 1);
		},
	
		'add' => sub {
			my $client = shift;
	
			playOrAddCurrentStation($client, 0);
		}
	);
}

sub cli_radiosQuery {
	my $request = shift;
	
	$::d_plugins && msg("Live365: cli_radiosQuery()\n");
	
	# what we want the query to report about ourself
	my $data = {
		'cmd' => 'live365',                    # cmd label
		'name' => Slim::Utils::Strings::string(getDisplayName()),  # nice name
		'type' => 'live365',              # type
	};
	
	# let our super duper function do all the hard work
	Slim::Control::Queries::dynamicAutoQuery($request, 'radios', $cli_next, $data);
}

# }}}

sub strings {
	return q^
PLUGIN_LIVE365_MODULE_NAME
	DE	Internetradio Live365
	EN	Live365 Internet Radio
	ES	Radio por Internet Live365
	FR	Radio Internet Live365
	HE	רדיו אינטרנט Live365
	IT	Radio Internet Live365
	NL	Live365-internetradio

PLUGIN_LIVE365_LOGOUT
	CS	Odhlásit
	DE	Abmelden
	EN	Log out
	ES	Cerrar sesión
	FR	Déconnexion
	HE	יציאה מהמערכת
	IT	Disconnessione
	NL	Afmelden

PLUGIN_LIVE365_LOGIN
	CS	Přihlásit
	DE	Anmelden
	EN	Log in
	ES	Iniciar sesión
	FR	Connexion
	HE	כניסה למערכת
	IT	Accesso
	NL	Aanmelden

PLUGIN_LIVE365_NOT_LOGGED_IN
	DE	Nicht bei Live365 angemeldet
	EN	Not logged in to Live365
	ES	No se ha iniciado sesión en Live365
	FR	Non connecté à Live365
	HE	לא התבצעה כניסה למערכת Live365
	IT	Non connesso a Live365
	NL	Niet aangemeld bij Live365

PLUGIN_LIVE365_NO_CREDENTIALS
	DE	Keine Live365-Anmeldeinformationen
	EN	No Live365 account information
	ES	No existe información de cuenta para Live365
	FR	Aucune information de compte Live365
	HE	אין פרטי חשבון עבור Live365
	IT	Nessuna informazione sull'account Live365
	NL	Geen Live365-accountinformatie

PLUGIN_LIVE365_LOGIN_SUCCESS
	DE	Erfolgreich
	EN	Successful
	ES	Correcto
	FR	Réussi
	HE	הצלחה
	IT	Riuscito
	NL	Geslaagd

PLUGIN_LIVE365_LOGIN_ERROR_NAME
	DE	Problem mit Anmeldenamen
	EN	Member name problem
	ES	Problema con el nombre de miembro
	FR	Problème de nom de membre
	HE	בעיה עם שם החבר
	IT	Problema con nome utente registrato.
	NL	Probleem met lidnaam

PLUGIN_LIVE365_LOGIN_ERROR_LOGIN
	CS	Problém s přihlášením
	DE	Problem beim Anmelden
	EN	Login problem
	ES	Problema de inicio de sesión
	FR	Problème de connexion
	HE	בעיה בכניסה למערכת
	IT	Problema di accesso
	NL	Aanmeldprobleem

PLUGIN_LIVE365_LOGIN_ERROR_ACTION
	DE	Unbekannter Vorgang
	EN	Unknown action
	ES	Acción desconocida
	FR	Action inconnue
	HE	פעולה לא ידועה
	IT	Azione sconosciuta
	NL	Onbekende actie

PLUGIN_LIVE365_LOGIN_ERROR_ORGANIZATION
	DE	Unbekanntes Unternehmen
	EN	Unknown organization
	ES	Organización desconocida
	FR	Organisation inconnue
	HE	ארגון לא מוכר
	IT	Organizzazione sconosciuta
	NL	Onbekende organisatie

PLUGIN_LIVE365_LOGIN_ERROR_SESSION
	DE	Sitzung nicht mehr gültig. Melden Sie sich erneut an.
	EN	Session no longer valid. Log in again.
	ES	La sesión ya no es válida. Vuelva a iniciar sesión.
	FR	La durée de validité de la session a expiré. Veuillez vous reconnecter.
	HE	ההפעלה אינה חוקית עוד. היכנס למערכת מחדש.
	IT	Sessione non più valida. Accedere di nuovo.
	NL	Sessie niet langer geldig. Opnieuw aanmelden.

PLUGIN_LIVE365_LOGIN_ERROR_HTTP
	DE	Problem mit Live365 Website. Versuchen Sie es noch einmal.
	EN	Live365 website error, try again
	ES	Error del sitio Web de Live365, vuelva a intentarlo
	FR	Erreur du site Live365, veuillez réessayer.
	HE	שגיאה באתר האינטרנט של Live365, נסה שוב
	IT	Errore sito Live365. Riprovare.
	NL	Live365-websitefout; probeer het opnieuw

PLUGIN_LIVE365_LOADING_GENRES
	CS	Stahuji žánry z Live365...
	DE	Stilrichtungsliste wird von Live365 geladen ...
	EN	Loading genre list from Live365...
	ES	Cargando la lista de géneros de Live365...
	FR	Chargement de la liste de genres à partir de Live365…
	HE	טוען רשימת סגנונות מ-Live365...
	IT	Caricamento elenco generi musicali di Live365...
	NL	Genrelijst van Live365 wordt geladen...

PLUGIN_LIVE365_LOADING_GENRES_ERROR
	CS	Chyba při stahování stylů. Zkuste to prosím později.
	DE	Fehler beim Laden der Stilrichtungen. Wiederholen Sie den Vorgang.
	EN	Error loading genres, try again
	ES	Error al cargar géneros, vuelva a intentarlo
	FR	Erreur lors du chargement des genres, veuillez réessayer.
	HE	שגיאה בטעינת הסגנונות, נסה שוב
	IT	Errore durante il caricamento dei generi. Riprovare.
	NL	Fout bij laden genres; probeer het opnieuw

PLUGIN_LIVE365_PRESETS
	DE	Eigene Voreinstellungen
	EN	My presets
	ES	Mis memorias
	FR	Mes présélections
	HE	ההגדרות הקבועות מראש שלי
	IT	Stazioni predefinite
	NL	Mijn voorkeuren

PLUGIN_LIVE365_BROWSEGENRES
	CS	Procházeet žánry
	DE	Stilrichtungen durchsuchen
	EN	Browse genres
	ES	Examinar géneros
	FI	Selaa tyylilajeittain
	FR	Parcourir les genres
	HE	עיון בסגנונות
	IT	Sfoglia generi musicali
	NL	Genres bekijken

PLUGIN_LIVE365_BROWSEALL
	CS	Procházet všechny stanice (mnoho)
	DE	Alle Sender durchsuchen (viele)
	EN	Browse all stations (many)
	ES	Examinar todas las emisoras (muchas)
	FI	Selaa kaikki asemat (paljon)
	FR	Parcourir toutes les stations (plusieurs)
	HE	עיון בכל התחנות (רבות)
	IT	Sfoglia tutte le stazioni (molte)
	NL	Alle stations bekijken (veel)

PLUGIN_LIVE365_BROWSEPICKS
	DE	Editor Picks durchsuchen
	EN	Browse editor picks
	ES	Examinar las selecciones del editor
	FR	Parcourir les sélections des éditeurs
	HE	עיון בבחירות העורך
	IT	Sfoglia brani scelti dal curatore
	NL	Redactievoorkeuren bekijken

PLUGIN_LIVE365_BROWSEPROS
	CS	Procházet profesionální stanice
	DE	Professionelle Sender durchsuchen
	EN	Browse professional stations
	ES	Examinar emisoras profesionales
	FI	Selaa ammattimaiset asemat
	FR	Parcourir les stations professionnelles
	HE	עיון בתחנות ייעודיות
	IT	Sfoglia le stazioni professionali
	NL	Professionele stations bekijken

PLUGIN_LIVE365_SEARCH
	DE	Live365 durchsuchen
	EN	Search Live365
	ES	Buscar en Live365
	FR	Rechercher Live365
	HE	חיפוש ב-Live365
	IT	Cerca in Live365
	NL	Live365 zoeken

PLUGIN_LIVE365_SEARCHPROMPT
	DE	Live365 durchsuchen nach:
	EN	Search Live365:
	ES	Buscar en Live365:
	FR	Rechercher Live365
	HE	חיפוש ב-Live365:
	IT	Cerca in Live365:
	NL	Live365 zoeken:

PLUGIN_LIVE365_LOADING_DIRECTORY
	CS	Nahrávám...
	DE	Ladevorgang läuft ...
	EN	Loading...
	ES	Cargando...
	FR	Chargement…
	HE	טוען...
	IT	Caricamento...
	NL	Bezig met laden...

PLUGIN_LIVE365_NOSTATIONS
	DE	Keine Sender gefunden
	EN	No stations found
	ES	No se encontraron emisoras
	FR	Aucune station trouvée
	HE	לא נמצאו תחנות
	IT	Nessuna stazione trovata.
	NL	Geen stations gevonden

PLUGIN_LIVE365_LOADING_INFORMATION
	DE	Senderinformationen werden geladen ...
	EN	Loading channel information...
	ES	Cargando información de canales...
	FR	Chargement des informations sur le canal…
	HE	טוען מידע ערוצים...
	IT	Caricamento informazioni sul canale...
	NL	Kanaalinformatie wordt geladen...

PLUGIN_LIVE365_DESCRIPTION
	CS	Popis stanice
	DE	Senderbeschreibung
	EN	Station Description
	ES	Descripción de emisora
	FR	Description de la station
	HE	תיאור התחנה
	IT	Descrizione stazione
	NL	Beschrijving van station

PLUGIN_LIVE365_STATION_LISTENERS_ACTIVE
	CS	Aktivních posluchačů
	DE	Aktive Zuhörer
	EN	Active listeners
	ES	Oyentes activos
	FR	Auditeurs actifs
	HE	מאזינים פעילים
	IT	Ascoltatori attivi
	NL	Actieve luisteraars

PLUGIN_LIVE365_STATION_LISTENERS_MAX
	CS	Maximum posluchačů
	DE	Maximale Anzahl Hörer
	EN	Maximum listeners
	ES	Cantidad máxima de oyentes
	FR	Nombre maximal d'auditeurs
	HE	מספר מאזינים מרבי
	IT	Numero massimo di ascoltatori
	NL	Maximumaantal luisteraars

PLUGIN_LIVE365_LISTENER_ACCESS
	DE	Hörerzugriff
	EN	Listener access
	ES	Acceso para oyentes
	FR	Accès auditeur
	HE	גישה למאזינים
	IT	Accesso ascoltatore
	NL	Luisteraartoegang

PLUGIN_LIVE365_STATION_QUALITY_LEVEL
	CS	Kvalita vysílání stanice
	DE	Senderqualität
	EN	Station quality
	ES	Calidad de la emisora
	FR	Qualité de la station
	HE	איכות תחנה
	IT	Qualità stazione
	NL	Zenderkwaliteit

PLUGIN_LIVE365_STATION_CONNECTION
	DE	Bandbreite
	EN	Bandwidth
	ES	Ancho de banda
	FR	Bande passante
	HE	רוחב פס
	IT	Larghezza di banda
	NL	Bandbreedte

PLUGIN_LIVE365_STATION_CODEC
	DE	Codec
	EN	Codec
	ES	Códec
	FR	Codec
	HE	רכיב Codec
	IT	Codec
	NL	Codec

PLUGIN_LIVE365_ERROR
	DE	Live365 - Fehler
	EN	Live365 ERROR
	ES	ERROR de Live365
	FR	Erreur Live365
	HE	שגיאה ב-Live365
	IT	ERRORE Live365
	NL	Live365-fout

PLUGIN_LIVE365_SEARCH_TAC
	DE	Interpret/Titel/Album suchen
	EN	Search Artists/Tracks/CDs
	ES	Buscar artistas/pistas/CDs
	FR	Rechercher des artistes/pistes/CD
	HE	איתור מבצעים/רצועות/תקליטורים
	IT	Ricerca artisti/brani/CD
	NL	Artiesten/tracks/cd's zoeken

PLUGIN_LIVE365_SEARCH_A
	DE	Interpret suchen
	EN	Search Artists
	ES	Buscar artistas
	FR	Rechercher des artistes
	HE	איתור מבצעים
	IT	Ricerca artisti
	NL	Artiesten zoeken

PLUGIN_LIVE365_SEARCH_T
	DE	Titel suchen
	EN	Search Tracks
	ES	Buscar pistas
	FR	Rechercher des pistes
	HE	איתור רצועות
	IT	Ricerca brani
	NL	Tracks zoeken

PLUGIN_LIVE365_SEARCH_C
	DE	Album suchen
	EN	Search Albums
	ES	Buscar álbumes
	FR	Rechercher des albums
	HE	איתור אלבומים
	IT	Ricerca album
	NL	Albums zoeken

PLUGIN_LIVE365_SEARCH_E
	DE	Sender suchen
	EN	Search Stations
	ES	Buscar emisoras
	FR	Rechercher des stations
	HE	איתור תחנות
	IT	Ricerca stazioni
	NL	Stations zoeken

PLUGIN_LIVE365_SEARCH_L
	DE	Region suchen
	EN	Search Locations
	ES	Buscar ubicaciones
	FR	Rechercher des emplacements
	HE	איתור מיקומים
	IT	Ricerca posizioni
	NL	Locaties zoeken

PLUGIN_LIVE365_SEARCH_H
	DE	Broadcaster suchen
	EN	Search Broadcasters
	ES	Buscar locutores
	FR	Rechercher des diffuseurs
	HE	איתור שדרנים
	IT	Ricerca emittenti
	NL	Omroepen zoeken

SETUP_GROUP_PLUGIN_LIVE365
	DE	Internetradio Live365
	EN	Live365 Internet Radio
	ES	Radio por Internet Live365
	FR	Radio Internet Live365
	HE	רדיו אינטרנט Live365
	IT	Radio Internet Live365
	NL	Live365-internetradio

SETUP_GROUP_PLUGIN_LIVE365_DESC
	CS	Prohledávat, procházet a ladit stanice Live365
	DE	Durchsuchen und Hören von Radiosendern auf Live365
	EN	Search, browse, and tune Live365 stations
	ES	Buscar, examinar y sintonizar emisoras Live365
	FR	Recherchez, parcourez et écoutez les stations Live365
	HE	איתור, עיון והתכווננות לתחנות Live365
	IT	Cerca, sfoglia e sintonizza stazioni Live365
	NL	Live365-stations zoeken, bekijken en instellen

SETUP_PLUGIN_LIVE365_USERNAME
	DE	Live365-Benutzername
	EN	Live365 Username
	ES	Nombre de usuario de Live365
	FR	Nom d'utilisateur Live365
	HE	שם משתמש עבור Live365
	IT	Nome utente Live365
	NL	Live365-gebruikersnaam

SETUP_PLUGIN_LIVE365_USERNAME_DESC
	DE	Ihr Live365-Benutzername. Melden Sie sich bei live365.com an.
	EN	Your Live365 username, visit live365.com to sign up
	ES	Su nombre de usuario de Live365, visite live365.com para registrarse
	FR	Votre nom d'utilisateur Live365 (visitez live365.com pour vous inscrire):
	HE	שם המשתמש שלך עבור Live365, עבור לכתובת live365.com כדי להירשם
	IT	Nome utente su Live365. Per eseguire la registrazione accedere a live365.com.
	NL	Je Live365-gebruikersnaam. Ga naar live365.com om je aan te melden.

SETUP_PLUGIN_LIVE365_PASSWORD
	DE	Live365-Kennwort
	EN	Live365 Password
	ES	Contraseña para Live365
	FR	Mot de passe Live365
	HE	סיסמה עבור Live365
	IT	Password per Live365
	NL	Live365-wachtwoord

SETUP_PLUGIN_LIVE365_PASSWORD_DESC
	DE	Ihr Live365-Kennwort
	EN	Your Live365 password
	ES	Su contraseña para Live365
	FR	Votre mot de passe Live365:
	HE	הסיסמה שלך עבור Live365
	IT	Password per Live365
	NL	Je Live365-wachtwoord

SETUP_PLUGIN_LIVE365_PASSWORD_CHANGED
	DE	Das Live365-Kennwort wurde geändert
	EN	Your Live365 password has been changed
	ES	Se ha cambiado la contraseña para Live365
	FR	Votre mot de passe Live365 a été modifié
	HE	הסיסמה עבור Live365 השתנתה
	IT	La password Live365 è stata modificata
	NL	Je Live365-wachtwoord is gewijzigd

SETUP_PLUGIN_LIVE365_SORT_ORDER
	DE	Spalten sortieren
	EN	Sort columns
	ES	Ordenar columnas
	FR	Ordre de tri
	HE	מיון עמודות
	IT	Ordina colonne
	NL	Kolommen sorteren

SETUP_PLUGIN_LIVE365_SORT_ORDER_DESC
	DE	Sortierreihenfolge der Sender definieren
	EN	Define the sort order of stations
	ES	Definir la secuencia de ordenamiento para emisoras
	FR	Spécifiez l'ordre de tri des stations
	HE	הגדרת סדר המיון של תחנות
	IT	Definisci l'ordinamento delle stazioni
	NL	De sorteervolgorde van stations definiëren

SETUP_PLUGIN_LIVE365_SORT_TITLE
	CS	Název stanice
	DE	Sendersortierung
	EN	Station title
	ES	Título de la emisora
	FR	Nom de la station
	HE	שם התחנה
	IT	Nome stazione
	NL	Stationstitel

SETUP_PLUGIN_LIVE365_SORT_BPS
	DE	Sender-Bitrate
	EN	Station bitrate
	ES	Velocidad de bits de emisora
	FR	Taux binaire de la station
	HE	קצב הסיביות של התחנה
	IT	Velocità in bit stazione
	NL	Bitsnelheid van station

SETUP_PLUGIN_LIVE365_SORT_RATING
	DE	Senderbeurteilung
	EN	Station rating
	ES	Calificación de la emisora
	FR	Classement de la station
	HE	דירוג התחנה
	IT	Valutazione stazione
	NL	Stationsbeoordeling

SETUP_PLUGIN_LIVE365_SORT_LISTENERS
	DE	Anzahl Hörer
	EN	Number of listeners
	ES	Número de oyentes
	FR	Nombre de connectés
	HE	מספר המאזינים
	IT	Numero di ascoltatori
	NL	Aantal luisteraars

SETUP_PLUGIN_LIVE365_WEB_SHOW_DETAILS
	DE	Ausführliche Live365-Suchergebnisse
	EN	Detailed Live365 search results
	ES	Resultados detallados de búsqueda en Live365
	FR	Résultats de recherche détaillés Live365
	HE	תוצאות חיפוש מפורטות ב-live365
	IT	Risultati di ricerca dettagliati su Live365
	NL	Gedetailleerde Live365-zoekresultaten

SETUP_PLUGIN_LIVE365_WEB_SHOW_DETAILS_DESC
	DE	Wenn Sie Live365 über die Web-Benutzeroberfläche durchsuchen, werden ausführliche Ergebnisse angezeigt. Dabei werden Informationen über Intepret, Titel und Album angezeigt. Das Durchsuchen der Suchergebnisse kann dann jedoch länger dauern und schwieriger sein.
	EN	Show detailed results when searching Live365 through the web interface.  Includes artist, track, and album info when searching for one of these.  May make your search results longer and harder to browse.
	ES	Mostrar resultados detallados al buscar en Live365 a través de la interfaz Web. Incluye información de artista, pista y álbum al buscar alguno de ellos. Puede que los resultados de la búsqueda sean más largos y más difíciles de examinar.
	FR	Affiche des résultats détaillés lors d'une recherche sur Live365 par l'interface Web (artiste, piste, album, etc.) La taille et la lisibilité des résultats peuvent s'en trouver affectées.
	HE	הצג תוצאות מפורטות בעת חיפוש ב-live365 באמצעות ממשק האינטרנט. התוצאות כוללות מידע מבצעים, רצועות ואלבומים בעת חיפוש אחד מרכיבים אלה. הדבר עשוי להאריך את תוצאות החיפוש ולהקשות על העיון בהן.
	IT	Consente di visualizzare risultati dettagliati di ricerche eseguite in Live365 tramite l'interfaccia Web. Vengono indicate informazioni relative ad artista, brano e album quando si esegue la ricerca di uno di questi elementi. La ricerca potrebbe richiedere tempi più lunghi e la consultazione dei risultati potrebbe essere più complessa.
	NL	Laat gedetailleerde resultaten zien bij het doorzoeken van Live365 via de webinterface. Toont ook artiest-, track- en albumgegevens wanneer hierop gezocht wordt. De resultatenlijst kan dan wel langer en lastiger te lezen zijn.

PLUGIN_LIVE365_GENRES
	CS	Styly Live365
	DE	Live365 - Stilrichtungen
	EN	Live365 genres
	ES	Géneros de Live365
	FR	Genres Live365
	HE	סגנונות Live365
	IT	Generi musicali Live365
	NL	Live365-genres

PLUGIN_LIVE365_STATIONS
	DE	Live365-Sender
	EN	Live365 stations
	ES	Emisoras de Live365
	FR	Stations Live365
	HE	תחנות Live365
	IT	Stazioni Live365
	NL	Live365-zenders

PLUGIN_LIVE365_POPULAR
	DE	Populär
	EN	Popular
	ES	Popular
	FR	Populaire
	HE	פופולרי
	IT	Di successo
	NL	Populair

PLUGIN_LIVE365_RECENT
	DE	Kürzlich gehört
	EN	Recent
	ES	Reciente
	FR	Récent
	HE	פריטים אחרונים שהופעלו
	IT	Recenti
	NL	Recent

PLUGIN_LIVE365_NO_INFO
	DE	Fehler beim Laden der Informationen
	EN	Error loading info
	ES	Error al cargar información
	FR	Erreur de chargement des informations
	HE	שגיאה בטעינת מידע
	IT	Errore durante il caricamento delle informazioni
	NL	Fout bij laden info

TACMATCHING
	DE	Passende Interpreten/Titel/Alben
	EN	Tracks/Artists/CDs matching
	ES	Pistas/Artistas/CDs coincidentes con
	FR	Pistes/Artistes/CD trouvés
	HE	התאמת רצועות/מבצעים/תקליטורים
	IT	Tracce/Artisti/CD corrispondenti
	NL	Zoekresultaten voor liedjes/artiesten/cd's 

STATIONSMATCHING
	DE	Passende Sender
	EN	Stations matching
	ES	Emisoras coincidentes con
	FR	Stations trouvées
	HE	התאמת תחנות
	IT	Stazioni corrispondenti
	NL	Zoekresultaten voor stations

LOCATIONSMATCHING
	DE	Passende Standorte
	EN	Locations matching
	ES	Ubicaciones coincidentes con
	FI	Täsmäävät sijainnit
	FR	Emplacement trouvé
	HE	התאמת מיקומים
	IT	Posizioni corrispondenti
	NL	Zoekresultaten voor locaties

BROADCASTERSMATCHING
	DE	Passende Sender
	EN	Broadcasters matching
	ES	Emisoras coincidentes con
	FI	Lähettäjät jotka täsmäävät
	FR	Diffuseurs trouvés
	HE	התאמת שדרנים
	IT	Ricerca emittenti corrispondenti
	NL	Zoekresultaten voor omroepen

PLUGIN_LIVE365_NO_INFO
	DE	Fehler beim Laden der Informationen
	EN	Error loading info
	ES	Error al cargar información
^;
}

1;

# }}}

