diff --git a/SQL/SQLite/schema_23_up.sql b/SQL/SQLite/schema_23_up.sql index 1379caf6038..32a7f552cdc 100644 --- a/SQL/SQLite/schema_23_up.sql +++ b/SQL/SQLite/schema_23_up.sql @@ -3,6 +3,7 @@ ALTER TABLE tracks ADD subtitle blob; ALTER TABLE tracks ADD grouping blob; ALTER TABLE albums ADD subtitle blob; ALTER TABLE albums ADD label blob; +CREATE INDEX albumsLabelIndex ON albums (label); CREATE INDEX tracksWorkIndex ON tracks (work); DROP TABLE IF EXISTS works; diff --git a/SQL/mysql/schema_23_up.sql b/SQL/mysql/schema_23_up.sql index 5aa52e77e5a..a57933bd5b7 100644 --- a/SQL/mysql/schema_23_up.sql +++ b/SQL/mysql/schema_23_up.sql @@ -3,6 +3,7 @@ ALTER TABLE tracks ADD subtitle blob; ALTER TABLE tracks ADD grouping blob; ALTER TABLE albums ADD subtitle blob; ALTER TABLE albums ADD label blob; +CREATE INDEX albumsLabelIndex ON albums (label); CREATE INDEX tracksWorkIndex ON tracks (work); CREATE TABLE works ( id integer PRIMARY KEY AUTO_INCREMENT, diff --git a/Slim/Control/Queries.pm b/Slim/Control/Queries.pm index 7e320f241e7..8db4733cbe8 100644 --- a/Slim/Control/Queries.pm +++ b/Slim/Control/Queries.pm @@ -195,7 +195,7 @@ sub alarmsQuery { my $client = $request->client(); my $index = $request->getParam('_index'); my $quantity = $request->getParam('_quantity'); - my $filter = $request->getParam('filter'); + my $filter = $request->getParam('filter'); my $alarmDOW = $request->getParam('dow'); # being nice: we'll still be accepting 'defined' though this doesn't make sense any longer @@ -293,9 +293,10 @@ sub albumsQuery { my $year = $request->getParam('year'); my $sort = $request->getParam('sort') || ($roleID ? 'artistalbum' : 'album'); # a work_id of -1 would mean "all works" - my $work = $request->getParam('work_id'); + my $work = $request->getParam('work_id'); my $composerID = $request->getParam('composer_id'); my $fromSearch = $request->getParam('from_search'); + my $label = $request->getParam('record_label'); my $ignoreNewAlbumsCache = $search || $compilation || $contributorID || $genreID || $trackID || $albumID || $year || Slim::Music::Import->stillScanning(); @@ -514,6 +515,11 @@ sub albumsQuery { push @{$p}, $year; } + if (defined $label) { + push @{$w}, 'albums.label = ?'; + push @{$p}, $label; + } + if (defined $fromSearch && !defined $search) { # If we've got here from a search, we don't want to show the album unless it matches all the user's search criteria. # This matters for a Works search: we've shown the user a Work because it matches their criteria, but it is possible @@ -1937,7 +1943,7 @@ sub infoTotalQuery { my $request = shift; # check this is the correct query. - if ($request->isNotQuery([['info'], ['total'], ['genres', 'artists', 'albums', 'songs', 'duration']])) { + if ($request->isNotQuery([['info'], ['total'], ['genres', 'artists', 'albums', 'songs', 'duration', 'playlists', 'works', 'labels']])) { $request->setStatusBadDispatch(); return; } @@ -1964,6 +1970,15 @@ sub infoTotalQuery { elsif ($entity eq 'songs') { $request->addResult("_$entity", $totals->{track}); } + elsif ($entity eq 'playlists') { + $request->addResult("_$entity", $totals->{playlist}); + } + elsif ($entity eq 'works') { + $request->addResult("_$entity", $totals->{work}); + } + elsif ($entity eq 'labels') { + $request->addResult("_$entity", $totals->{label}); + } elsif ($entity eq 'duration') { $request->addResult("_$entity", Slim::Schema->totalTime($request->client)); } @@ -1989,6 +2004,147 @@ sub irenableQuery { $request->setStatusDone(); } +sub labelsQuery { + my $request = shift; + + # check this is the correct query. + if ($request->isNotQuery([['labels']])) { + $request->setStatusBadDispatch(); + return; + } + + if (!Slim::Schema::hasLibrary()) { + $request->setStatusNotDispatchable(); + return; + } + + my $sqllog = main::DEBUGLOG && logger('database.sql'); + + # get our parameters + my $client = $request->client(); + my $index = $request->getParam('_index'); + my $quantity = $request->getParam('_quantity'); + my $year = $request->getParam('year'); + my $contributorID = $request->getParam('artist_id'); + my $workID = $request->getParam('work_id'); + my $libraryID = Slim::Music::VirtualLibraries->getRealId($request->getParam('library_id')); + my $tags = $request->getParam('tags') || ''; + + my $sql = 'SELECT distinct label FROM albums '; + my $w = []; + my $p = []; + push @{$w}, 'albums.label IS NOT NULL'; + + if (defined $contributorID) { + + $sql .= 'JOIN contributor_album ON albums.id = contributor_album.contributor '; + # handle the case where we're asked for the VA id => return compilations + if ($contributorID == Slim::Schema->variousArtistsObject->id) { + push @{$w}, 'albums.compilation = ?'; + push @{$p}, 1; + } + else { + push @{$w}, 'contributor_album.contributor = ?'; + push @{$p}, $contributorID; + } + } + + if ( $libraryID ) { + $sql .= 'JOIN library_album ON library_album.album = albums.id '; + push @{$w}, 'library_album.library = ?'; + push @{$p}, $libraryID; + } + + if (defined $year) { + push @{$w}, 'albums.year = ?'; + push @{$p}, $year; + } + if (defined $workID) { + $sql .= "JOIN tracks on tracks.album = albums.id "; + if ( $workID eq "-1" ) { + push @{$w}, 'tracks.work IS NOT NULL'; + } else { + push @{$w}, 'tracks.work = ?'; + push @{$p}, $workID; + } + } + + if ( @{$w} ) { + $sql .= 'WHERE '; + my $s = join( ' AND ', @{$w} ); + $s =~ s/\%/\%\%/g; + $sql .= $s . ' '; + } + + my $dbh = Slim::Schema->dbh; + + my $stillScanning = Slim::Music::Import->stillScanning(); + + # Get count of all results, the count is cached until the next rescan done event + my $cacheKey = md5_hex($sql . join( '', @{$p} ) . Slim::Music::VirtualLibraries->getLibraryIdForClient($client)); + + my $count = $cache->{$cacheKey}; + if ( !$count ) { + my $total_sth = $dbh->prepare_cached( qq{ + SELECT COUNT(1) FROM ( $sql ) AS t1 + } ); + + $total_sth->execute( @{$p} ); + ($count) = $total_sth->fetchrow_array(); + $total_sth->finish; + + if ( !$stillScanning ) { + $cache->{$cacheKey} = $count; + } + } + + # now build the result + + if ($stillScanning) { + $request->addResult('rescan', 1); + } + + $count += 0; + + my ($valid, $start, $end) = $request->normalize(scalar($index), scalar($quantity), $count); + + if ($valid && $tags ne 'CC') { + + $sql .= 'ORDER BY albums.label '; + + my $loopname = 'labels_loop'; + my $chunkCount = 0; + + # Limit the real query + if ( $index =~ /^\d+$/ && $quantity =~ /^\d+$/ ) { + $sql .= "LIMIT $index, $quantity "; + } + + if ( main::DEBUGLOG && $sqllog->is_debug ) { + $sqllog->debug( "Labels query: $sql / " . Data::Dump::dump($p) ); + } + + my $sth = $dbh->prepare_cached($sql); + $sth->execute( @{$p} ); + + my ($label); + $sth->bind_columns( \$label ); + + while ( $sth->fetch ) { + + $request->addResultLoop($loopname, $chunkCount, 'record_label', $label); + + $chunkCount++; + + main::idleStreams() if !($chunkCount % 5); + } + } + + $request->addResult('count', $count); + + $request->setStatusDone(); +} + sub librariesQuery { my $request = shift; @@ -3643,6 +3799,9 @@ sub serverstatusQuery { $request->addResult("info total artists", $totals->{contributor}); $request->addResult("info total genres", $totals->{genre}); $request->addResult("info total songs", $totals->{track}); + $request->addResult("info total playlists", $totals->{playlist}); + $request->addResult("info total works", $totals->{work}); + $request->addResult("info total labels", $totals->{label}); $request->addResult("info total duration", Slim::Schema->totalTime()); } diff --git a/Slim/Control/Request.pm b/Slim/Control/Request.pm index 0144f0024ad..8fce29f3dbf 100644 --- a/Slim/Control/Request.pm +++ b/Slim/Control/Request.pm @@ -501,7 +501,10 @@ sub init { addDispatch(['info', 'total', 'albums', '?'], [0, 1, 0, \&Slim::Control::Queries::infoTotalQuery]); addDispatch(['info', 'total', 'artists', '?'], [0, 1, 0, \&Slim::Control::Queries::infoTotalQuery]); addDispatch(['info', 'total', 'genres', '?'], [0, 1, 0, \&Slim::Control::Queries::infoTotalQuery]); + addDispatch(['info', 'total', 'labels', '?'], [0, 1, 0, \&Slim::Control::Queries::infoTotalQuery]); + addDispatch(['info', 'total', 'playlists', '?'], [0, 1, 0, \&Slim::Control::Queries::infoTotalQuery]); addDispatch(['info', 'total', 'songs', '?'], [0, 1, 0, \&Slim::Control::Queries::infoTotalQuery]); + addDispatch(['info', 'total', 'works', '?'], [0, 1, 0, \&Slim::Control::Queries::infoTotalQuery]); addDispatch(['info', 'total', 'duration', '?'], [0, 1, 0, \&Slim::Control::Queries::infoTotalQuery]); addDispatch(['ir', '_ircode', '_time'], [1, 0, 0, \&Slim::Control::Commands::irCommand]); addDispatch(['irenable', '?'], [1, 1, 0, \&Slim::Control::Queries::irenableQuery]); @@ -509,6 +512,7 @@ sub init { addDispatch(['libraries'], [0, 1, 0, \&Slim::Control::Queries::librariesQuery]); addDispatch(['libraries', 'getid'], [1, 1, 0, \&Slim::Control::Queries::librariesQuery]); addDispatch(['linesperscreen', '?'], [1, 1, 0, \&Slim::Control::Queries::linesperscreenQuery]); + addDispatch(['labels', '_index', '_quantity'], [0, 1, 1, \&Slim::Control::Queries::labelsQuery]); addDispatch(['logging'], [0, 0, 1, \&Slim::Control::Commands::loggingCommand]); addDispatch(['mixer', 'bass', '?'], [1, 1, 0, \&Slim::Control::Queries::mixerQuery]); addDispatch(['mixer', 'bass', '_newvalue'], [1, 0, 1, \&Slim::Control::Commands::mixerCommand]); diff --git a/Slim/Menu/BrowseLibrary.pm b/Slim/Menu/BrowseLibrary.pm index 645737c8e22..822a78cbceb 100644 --- a/Slim/Menu/BrowseLibrary.pm +++ b/Slim/Menu/BrowseLibrary.pm @@ -155,6 +155,7 @@ use JSON::XS::VersionOneAndTwo; use Slim::Menu::BrowseLibrary::Releases; use Slim::Menu::BrowseLibrary::Works; +use Slim::Menu::BrowseLibrary::Labels; use Slim::Music::VirtualLibraries; use Slim::Utils::Cache; use Slim::Utils::Log; @@ -596,6 +597,23 @@ sub _registerBaseNodes { weight => 40, cache => 1, }, + { + type => 'link', + name => 'BROWSE_BY_LABEL', + params => {mode => 'labels'}, + feed => \&_labels, + icon => 'html/images/albums.png', + jiveIcon => 'html/images/albums.png', + homeMenuText => 'BROWSE_LABELS', + condition => sub { + return unless isEnabledNode(@_); + my $totals = Slim::Schema->totals($_[0]); + return $totals->{label} if $totals; + }, + id => 'myMusicLabels', + weight => 45, + cache => 1, + }, { type => 'link', name => 'BROWSE_NEW_MUSIC', @@ -735,7 +753,7 @@ sub setMode { $client->modeParam( handledTransition => 1 ); } -our @topLevelArgs = qw(track_id artist_id genre_id album_id playlist_id year only_album_years folder_id role_id library_id remote_library release_type work_id composer_id from_search subtitle grouping performance); +our @topLevelArgs = qw(track_id artist_id genre_id album_id playlist_id year only_album_years folder_id role_id library_id remote_library release_type work_id composer_id from_search subtitle grouping performance record_label); sub _topLevel { my ($client, $callback, $args, $pt) = @_; diff --git a/Slim/Menu/BrowseLibrary/Labels.pm b/Slim/Menu/BrowseLibrary/Labels.pm new file mode 100644 index 00000000000..d848bb3429b --- /dev/null +++ b/Slim/Menu/BrowseLibrary/Labels.pm @@ -0,0 +1,71 @@ +package Slim::Menu::BrowseLibrary; + +use strict; + +use Slim::Utils::Log; +use Slim::Utils::Strings qw(cstring); + +use constant BROWSELIBRARY => 'browselibrary'; + +my $log = logger('database.info'); + +sub _labels { + my ($client, $callback, $args, $pt) = @_; + my @searchTags = $pt->{'searchTags'} ? @{$pt->{'searchTags'}} : (); + my $library_id = $args->{'library_id'} || $pt->{'library_id'}; + my $search = $args->{'search'} || $pt->{'search'}; + my $remote_library = $args->{'remote_library'} ||= $pt->{'remote_library'}; + + if ($library_id && !grep /library_id/, @searchTags) { + push @searchTags, 'library_id:' . $library_id; + } + + Slim::Menu::BrowseLibrary::_generic($client, $callback, $args, 'labels', + [ @searchTags, ($search ? 'search:' . $search : undef) ], + sub { + my $results = shift; + my $items = $results->{'labels_loop'}; + $remote_library ||= $args->{'remote_library'}; + + foreach (@$items) { + $_->{'name'} = $_->{'record_label'}; + $_->{'image'} = 'html/images/albums.png'; + $_->{'type'} = 'playlist'; + $_->{'playlist'} = \&_tracks; + $_->{'url'} = \&_albums; + $_->{'passthrough'} = [ { searchTags => [@searchTags, "record_label:" . $_->{'record_label'}], remote_library => $remote_library } ]; + }; + + my $params = _tagsToParams(\@searchTags); + my %actions = $remote_library ? ( + commonVariables => [record_label => 'record_label'] + ) : ( + allAvailableActionsDefined => 1, + commonVariables => [record_label => 'record_label'], + items => { + command => [BROWSELIBRARY, 'items'], + fixedParams => { + mode => 'albums', + %$params + }, + }, + play => { + command => ['playlistcontrol'], + fixedParams => {cmd => 'load', %$params}, + }, + add => { + command => ['playlistcontrol'], + fixedParams => {cmd => 'add', %$params}, + }, + insert => { + command => ['playlistcontrol'], + fixedParams => {cmd => 'insert', %$params}, + }, + ); + $actions{'playall'} = $actions{'play'}; + $actions{'addall'} = $actions{'add'}; + + return {items => $items, actions => \%actions, sorted => 1}, undef; + }, undef, 1, + ); +} diff --git a/Slim/Schema.pm b/Slim/Schema.pm index 6b471647bbb..5266db9865f 100644 --- a/Slim/Schema.pm +++ b/Slim/Schema.pm @@ -1248,6 +1248,8 @@ sub _createOrUpdateAlbum { $albumHash->{release_type} = Slim::Utils::Text::ignoreCase( $releaseType || 'album' ); Slim::Schema::Album->addReleaseTypeMap($releaseType, $albumHash->{release_type}); + $albumHash->{label} = $attributes->{LABEL}; + # Bug 3255 - add album contributor which is either VA or the primary artist, used for sort by artist $vaObjId ||= $self->variousArtistsObject->id; @@ -2814,6 +2816,8 @@ sub _preCheckAttributes { # We also need these in _postCheckAttributes, but they should be set during create() $deferredAttributes->{'DISC'} = $attributes->{'DISC'} if $attributes->{'DISC'}; + $deferredAttributes->{'LABEL'} = $attributes->{'LABEL'} || undef; + # thumb has gone away, since we have GD resizing. delete $attributes->{'THUMB'}; @@ -3266,6 +3270,7 @@ sub totals { track => ['titles', 0, 1, 'tags:CC'], playlist => ['playlists', 0, 1, 'tags:CC'], work => ['works', 0, 1, 'tags:CC'], + label => ['labels', 0, 1, 'tags:CC'], ); while (my ($key, $query) = each %categories) { diff --git a/Slim/Schema/Album.pm b/Slim/Schema/Album.pm index 2ee7f52ba53..bed9edabcbf 100644 --- a/Slim/Schema/Album.pm +++ b/Slim/Schema/Album.pm @@ -36,6 +36,7 @@ my $log = logger('database.info'); musicbrainz_id release_type extid + label ), title => { accessor => undef() }); $class->set_primary_key('id'); diff --git a/strings.txt b/strings.txt index c556c535983..9c518879dc0 100644 --- a/strings.txt +++ b/strings.txt @@ -14628,6 +14628,17 @@ BROWSE_GENRES RU Просмотр жанров SV Bläddra efter genre +BROWSE_LABELS + CS Procházet nahrávací společnosti + DA Gennemse pladeselskaber + DE Plattenlabels durchsuchen + EN Browse Labels + ES Explorar sellos discográficos + FR Parcourir les labels d'enregistrement + HU Lemezkiadók böngészése + NL Bladeren door platenlabels + PT Procurar etiquetas de discos + BROWSE_WORKS CS Procházet díla DA Gennemsøg værker @@ -14789,6 +14800,14 @@ BROWSE_BY_SONG_DBL FR Morceaux PL Utwory +BROWSE_BY_LABEL + DA Pladeselskab + DE Plattenlabel + EN Record Label + FR Label d'enregistrement + HU Lemezkiadó + NL Platenmaatschappij + BROWSE_BY_WORK DA Værker DE Werke