Index: Slim/Control/Queries.pm
===================================================================
--- Slim/Control/Queries.pm	(revision 21643)
+++ Slim/Control/Queries.pm	(working copy)
@@ -43,6 +43,7 @@
 use Slim::Utils::Log;
 use Slim::Utils::Unicode;
 use Slim::Utils::Prefs;
+use Slim::Schema::ResultSet::Contributor;
 
 {
 	if ($^O =~ /Win32/) {
@@ -162,7 +163,9 @@
 	my $menu          = $request->getParam('menu');
 	my $insert        = $request->getParam('menu_all');
 	my $to_cache      = $request->getParam('cache');
-	
+	my $includeYear   = 1; # should be defined somehow
+	my $textkeyByYear = 0;
+
 	if ($request->paramNotOneOfIfDefined($sort, ['new', 'album', 'artflow', 'artistalbum', 'yearalbum', 'yearartistalbum' ])) {
 		$request->setStatusBadParams();
 		return;
@@ -234,9 +237,31 @@
 				$where->{'contributorAlbums.contributor'} = $contributorID;
 				push @{$attr->{'join'}}, 'contributorAlbums';
 				$attr->{'distinct'} = 1;
-			}			
+			
+				# If no other specific sort has been specified, order the albums
+				# by year according to the player settings.
+				if (!$sort)
+				{
+					my $sortByYear = $Slim::Schema::ResultSet::Contributor::prefs->get('sortAlbumsByYear') || 0;
+					my $sort = $Slim::Schema::ResultSet::Contributor::albumSortRules[$sortByYear] ||
+					           $Slim::Schema::ResultSet::Contributor::albumSortRules[0];
+					$attr->{'order_by'} = $sort;
+
+					# Fix up the sort key to use the 'me' name, rather than 'album'
+					# table name which was used by the contributor album sort code.
+					$attr->{'order_by'} =~ s/album\./me./g;
+					
+					# If we are using the year, then we
+					# need to ensure that the key that's
+					# used (the background display on Jive)
+					# will be based on the year.
+					if ($attr->{'order_by'} =~ /year/) {
+						$textkeyByYear = 1;
+					}
+				}
+			}
 		}
-	
+
 		if (defined $genreID){
 			$where->{'genreTracks.genre'} = $genreID;
 			push @{$attr->{'join'}}, {'tracks' => 'genreTracks'};
@@ -271,7 +296,11 @@
 		push @{ $attr->{'join'} }, { contributorAlbums => 'contributor' };
 		$attr->{'distinct'} = 1;
 		
-		$attr->{'cols'} = [ qw(id artwork title contributor.name contributor.namesort titlesort musicmagic_mixable ) ];
+		$attr->{'cols'} = [ qw(id artwork title contributor.id contributor.name titlesort musicmagic_mixable ) ];
+		if ($includeYear)
+		{
+		    push @{$attr->{'cols'}}, 'year';
+		}
 	}
 	
 	# Flatten request for lookup in cache, only for Jive menu queries
@@ -420,12 +449,32 @@
 				# we want the text to be album\nartist
 				$artist = $eachitem->contributor->name;
 				my $text   = $eachitem->title;
+
+				# If we're looking at <artist>->'No album'
+				# then the description on Jive should say
+				# that's what we're looking at, not 'Various
+				# artists'
+				if ($eachitem->contributor->id == Slim::Schema->variousArtistsObject->id && 
+				    defined $contributorID &&
+				    $contributorID != Slim::Schema->variousArtistsObject->id) {
+					my $obj = Slim::Schema->single('Contributor', { 'id' => $contributorID });
+					if (defined $obj) {
+						$artist = $obj->name;
+					}
+				}
 				if (defined $artist) {
 					$text = $text . "\n" . $artist;
 				}
 
 				my $favorites_title = $text;
 				$favorites_title =~ s/\n/ - /g;
+				
+				if (defined $includeYear &&
+				    defined $eachitem->year &&
+				    $eachitem->year != 0)
+				{
+					$text .= "\n(" . $eachitem->year . ")";
+				}
 
 				$request->addResultLoop($loopname, $chunkCount, 'text', $text);
 				
@@ -437,6 +486,31 @@
 				# title is a really stupid thing to use, since there's no assurance it's unique
 				my $url = 'db:album.titlesearch=' . $eachitem->title;
 
+				# If they specified a contributor, we need to restrict to JUST that contributor
+				# otherwise 'Madonna' -> 'No album' will show everything that doesn't have an album,
+				# rather than everything that's by Madonna but which isn't in an album.
+				if (defined $contributorID)
+				{
+					my $obj = Slim::Schema->single('Contributor', { 'id' => $contributorID });
+					if (defined $obj)
+					{
+						$url .= "&contributor.namesearch=" . $obj->name;
+					}
+				}
+				if (defined $genreID)
+				{
+					my $obj = Slim::Schema->single('Genre', { 'id' => $genreID });
+					if (defined $obj)
+					{
+						$url .= "&genre.namesearch=" . $obj->name;
+					}
+				}
+				if (defined $year)
+				{
+					# The year id is the year
+					$url .= "&year.id=$year";
+				}
+
 				my $params = {
 					'album_id'        => $id,
 					'favorites_url'   => $url,
@@ -446,12 +520,24 @@
 				if (defined $contributorID) {
 					$params->{artist_id} = $contributorID;
 				}
+				if (defined $year) {
+					$params->{year} = $year;
+				}
 
 				#FIXME: see if multiple char textkey is doable for year/genre sort
 				if ($sort && ($sort eq 'artflow' || $sort eq 'artistalbum') ) {
 					$params->{textkey} = substr($eachitem->contributor->namesort, 0, 1);
 				} elsif ($sort && $sort ne 'new') {
-					$params->{textkey} = substr($eachitem->titlesort, 0, 1);
+					if ($textkeyByYear)
+					{
+						if ($eachitem->year != 0) {
+							$params->{textkey} = substr($eachitem->year, 2, 2);
+						}
+					}
+					else
+					{
+						$params->{textkey} = substr($eachitem->titlesort, 0, 1),
+					}
 				}
 
 				$request->addResultLoop($loopname, $chunkCount, 'params', $params);
@@ -4229,6 +4315,7 @@
 	$request->setStatusDone();
 }
 
+# might also be known as tracksQuery
 sub titlesQuery {
 	my $request = shift;
 
@@ -4455,7 +4542,13 @@
 				my $text = $item->title;
 				my $album;
 				my $albumObj = $item->album();
+				my $trackNum = $item->tracknum;
 				
+				if (defined $trackNum)
+				{
+				    $text = "$trackNum. " . $text;
+				}
+				
 				# Bug 7443, check for a track cover before using the album cover
 				my $iconId = $item->coverArtExists ? $id : 0;
 				
@@ -4702,6 +4795,11 @@
 					'favorites_title' => $id,
 				};
 
+				if ($id != 0)
+				{
+					$params->{textkey} = substr($id, 2, 2);
+				}
+
 				$request->addResultLoop($loopname, $chunkCount, 'params', $params);
 			}
 			else {
Index: Slim/Formats.pm
===================================================================
--- Slim/Formats.pm	(revision 21643)
+++ Slim/Formats.pm	(working copy)
@@ -222,6 +222,49 @@
 
 			$tags->{$key} ||= 0;
 		}
+		
+		# If there is no lyric content present in the file,
+		# see if there is a local lyric file we can use
+		# instead.
+		if (!defined($tags->{'LYRICS'}) ||
+		    $tags->{'LYRICS'} eq "")
+		{
+			my $lyricfilepath = $filepath;
+			if ($lyricfilepath =~ s/\..{2,4}$/.txt/)
+			{
+				if (open(IN, "< $lyricfilepath"))
+				{
+					my $lyrics = "";
+					while (<IN>)
+					{
+						$lyrics .= $_;
+					}
+					close(IN);
+					
+					# Try and guess the encoding, otherwise just use latin1
+					my $dec = Encode::Guess->guess($lyrics);
+
+					if (ref $dec) {
+						$lyrics = $dec->decode($lyrics);
+					} else {
+						# Best try
+						$lyrics = Encode::decode('iso-8859-1', $lyrics);
+					}
+
+					# Normalise to LF terminations
+					$lyrics =~ s/\r\n/\n/g;
+					
+					# Trim leading newlines
+					$lyrics =~ s/^\n+//;
+					
+					# Expand tabs
+					$lyrics =~ s/^(.*?)\t/$1 . (' ' x (8-(length($1) % 8)))/meg;
+
+					# Finally, assign
+					$tags->{'LYRICS'} = $lyrics;
+				}
+			}
+		}
 	}
 
 	# Last resort
Index: Slim/Schema/ResultSet/Contributor.pm
===================================================================
--- Slim/Schema/ResultSet/Contributor.pm	(revision 21643)
+++ Slim/Schema/ResultSet/Contributor.pm	(working copy)
@@ -7,6 +7,31 @@
 
 use Slim::Utils::Prefs;
 
+our $prefs = preferences('server');
+
+our @albumSortRules = (
+	# 0: No special sorting
+	"concat('0', album.titlesort), album.disc",
+	
+	# 1: Sort by year ascending, unset years last
+	"CASE album.year " .
+	" WHEN 0 THEN 9999" .
+	" ELSE album.year " .
+	"END, concat('0', album.titlesort), album.disc",
+	
+	# 2: Sort by year ascending, unset years first
+	"album.year, concat('0', album.titlesort), album.disc",
+	
+	# 3: Sort by year descending, unset years last
+	"album.year DESC, concat('0', album.titlesort), album.disc",
+	
+	# 4: Sort by year ascending, unset years first
+	"CASE album.year " .
+	" WHEN 0 THEN 9999" .
+	" ELSE album.year " .
+	"END DESC, concat('0', album.titlesort), album.disc"
+);
+
 sub pageBarResults {
 	my $self = shift;
 
@@ -113,8 +138,9 @@
 		$sort = $rs->fixupSortKeys($sort);
 
 	} else {
-
-		$sort = "concat('0', album.titlesort), album.disc";
+	  
+		my $sortByYear = $prefs->get('sortAlbumsByYear') || 0;
+		$sort = $albumSortRules[$sortByYear] || $albumSortRules[0];
 	}
 
 	my $attr = {
Index: Slim/Utils/Prefs.pm
===================================================================
--- Slim/Utils/Prefs.pm	(revision 21643)
+++ Slim/Utils/Prefs.pm	(working copy)
@@ -222,6 +222,7 @@
 		'timeFormat'            => q(|%I:%M %p),
 		'showArtist'            => 0,
 		'showYear'              => 0,
+		'sortAlbumsByYear'      => 0,
 		'guessFileFormats'	    => [
 									'(ARTIST - ALBUM) TRACKNUM - TITLE',
 									'/ARTIST/ALBUM/TRACKNUM - TITLE',
Index: Slim/Web/Settings/Server/UserInterface.pm
===================================================================
--- Slim/Web/Settings/Server/UserInterface.pm	(revision 21643)
+++ Slim/Web/Settings/Server/UserInterface.pm	(working copy)
@@ -26,7 +26,7 @@
 
 sub prefs {
 	return ($prefs, qw(displaytexttimeout skin itemsPerPage refreshRate thumbSize
-					   longdateFormat shortdateFormat timeFormat showArtist showYear titleFormatWeb));
+					   longdateFormat shortdateFormat timeFormat showArtist showYear sortAlbumsByYear titleFormatWeb));
 }
 
 sub handler {
Index: HTML/EN/settings/server/interface.html
===================================================================
--- HTML/EN/settings/server/interface.html	(revision 21643)
+++ HTML/EN/settings/server/interface.html	(working copy)
@@ -30,6 +30,18 @@
 			
 			</select>
 		[% END %]
+
+		[% WRAPPER settingGroup title="SETUP_SORTALBUMSBYYEAR" desc="SETUP_SORTALBUMSBYYEAR_DESC" %]
+			<select class="stdedit" name="sortAlbumsByYear" id="sortAlbumsByYear">
+ 
+				<option [% IF prefs.sortAlbumsByYear == 0 %]selected [% END %]value="0">[% 'DISABLED' | getstring %]</option>
+				<option [% IF prefs.sortAlbumsByYear == 1 %]selected [% END %]value="1">[% 'SETUP_SORTALBUMSBYYEAR_1' | getstring %]</option>
+				<option [% IF prefs.sortAlbumsByYear == 2 %]selected [% END %]value="2">[% 'SETUP_SORTALBUMSBYYEAR_2' | getstring %]</option>
+				<option [% IF prefs.sortAlbumsByYear == 3 %]selected [% END %]value="3">[% 'SETUP_SORTALBUMSBYYEAR_3' | getstring %]</option>
+				<option [% IF prefs.sortAlbumsByYear == 4 %]selected [% END %]value="4">[% 'SETUP_SORTALBUMSBYYEAR_4' | getstring %]</option>
+			
+			</select>
+		[% END %]
 	[% END %]
 
 	[% WRAPPER setting title="SETUP_TITLEFORMAT" desc="SETUP_GROUP_TITLEFORMATS_DESC" %]
Index: strings.txt
===================================================================
--- strings.txt	(revision 21643)
+++ strings.txt	(working copy)
@@ -4444,6 +4444,21 @@
 	NO	Du kan velge å vise årstallet et album ble gitt ut når du viser albumlisten eller albumomslag. Når valget er påskrudd vil årstallet vises sammen med tittelen til albumet.
 	ZH_CN	您可以选择通过浏览专辑或浏览专辑图象时是否一同显示专辑的年份。当您选择一同显示时，年份信息将显示在专辑标题旁边。
 
+SETUP_SORTALBUMSBYYEAR
+	EN	Sort Albums in Artist lists by year
+
+SETUP_SORTALBUMSBYYEAR_DESC
+	EN	You may choose to sort the albums by year when displayed within the artists list. Artist menu will be sorted by the year associated with that album. The order of sorting can be configured to be ascending or descended, with albums without years first or last. When disabled, the Albums listed from an Artist menu will be sorted alphabetic order.
+
+SETUP_SORTALBUMSBYYEAR_1
+	EN	Sort by year ascending, unset years last
+SETUP_SORTALBUMSBYYEAR_2
+	EN	Sort by year ascending, unset years first
+SETUP_SORTALBUMSBYYEAR_3
+	EN	Sort by year descending, unset years last
+SETUP_SORTALBUMSBYYEAR_4
+	EN	Sort by year descending, unset years first
+
 SETUP_SHOWARTIST
 	DA	Vis kunstner ved album
 	DE	Interpreten mit Alben anzeigen
