Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions HTML/EN/settings/server/formatting.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
<input type="text" class="stdedit selectFolder" name="pref_artfolder" id="artfolder" value="[% prefs.pref_artfolder | html %]" size="40">
[% END %]

[% WRAPPER setting title="SETUP_BOXSET_ARTWORK" desc="SETUP_BOXSET_ARTWORK_DESC" %]
<select class="stdedit" name="pref_updateBoxsetArtwork" id="updateBoxsetArtwork">
<option [% IF !prefs.pref_updateBoxsetArtwork %]selected [% END %]value="0">[% "SETUP_BOXSET_ARTWORK_OFF" | string %]</option>
<option [% IF prefs.pref_updateBoxsetArtwork %]selected [% END %]value="1">[% "SETUP_BOXSET_ARTWORK_ON" | string %]</option>
</select>
[% END %]

[% WRAPPER setting title="SETUP_NO_PORTRAITS" desc="" %]
<select class="stdedit" name="pref_noContributorPictures" id="noContributorPictures">
<option [% IF !prefs.noContributorPictures %]selected [% END %]value="0">[% "SETUP_NO_PORTRAITS_OFF" | string %]</option>
Expand Down
184 changes: 168 additions & 16 deletions Slim/Music/Artwork.pm
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use Slim::Utils::Unicode;
use Slim::Utils::OSDetect;

use constant MAX_RETRIES => 5;
use constant MAX_LEVELS_FOR_BOX_ARTWORK => 3;

# Global caches:
my $artworkDir = '';
Expand All @@ -59,7 +60,7 @@ my $imageTypesRegex;

# Public class methods
sub findStandaloneArtwork {
my ( $class, $trackAttributes, $deferredAttributes, $dirurl ) = @_;
my ( $class, $trackAttributes, $deferredAttributes, $dirurl, $args ) = @_;

return wantarray ? () : 0 if !Slim::Music::Info::isFileURL($dirurl);

Expand All @@ -68,7 +69,7 @@ sub findStandaloneArtwork {
my $art = $findArtCache{$dirurl};

# Files to look for
my @files = qw(cover album folder thumb);
my @files = @{$args->{coverFiles} || []} || qw(cover album folder thumb);

# User-defined artwork format
my $coverFormat = $prefs->get('coverArt');
Expand Down Expand Up @@ -176,7 +177,7 @@ sub _findStandaloneArtwork {

$imageTypesRegex ||= Slim::Music::Info::validTypeExtensions('image');

my @candidates = $filenameTemplates ? map {
my @candidates = $filenameTemplates ? Slim::Utils::Misc::uniq(map {
my $name = $_;
my @variations;

Expand All @@ -191,7 +192,7 @@ sub _findStandaloneArtwork {
}

@variations;
} @$filenameTemplates : ();
} @$filenameTemplates) : ();

my @images;

Expand All @@ -202,9 +203,9 @@ sub _findStandaloneArtwork {
}
else {
# doing a range search helps us avoid a LIKE query, which would result in a scan
$sql .= '>= ? AND url < ?';
$sql .= '>= ? AND url < ? AND instr(substr(url, length(?)+2), "/") < 1';
my $pathUrl = Slim::Utils::Misc::fileURLFromPath($parentDir);
push @candidates, $pathUrl, $pathUrl . chr(0xff);
push @candidates, $pathUrl, $pathUrl . chr(0xff), $pathUrl;
}

my $sth = Slim::Schema->dbh->prepare_cached($sql);
Expand Down Expand Up @@ -316,14 +317,14 @@ sub updateStandaloneArtwork {
SELECT COUNT(*) FROM ( $sql ) AS t1
} );

$log->error("Starting updateStandaloneArtwork for $count albums");

if ( !$count ) {
$cb && $cb->();
main::SCANNER && Slim::Music::Import->endImporter('updateStandaloneArtwork');
return;
}

$log->error("Starting updateStandaloneArtwork for $count albums");

my $progress = Slim::Utils::Progress->new( {
type => 'importer',
name => 'updateStandaloneArtwork',
Expand Down Expand Up @@ -445,6 +446,156 @@ sub updateStandaloneArtwork {
}
}

sub updateBoxsetArtwork {
my $class = shift;
my $cb = shift; # optional callback when done (main process async mode)

my $dbh = Slim::Schema->dbh;

# get singledir parameter from the scanner if available
# shortcut for online library scan only - we don't have the necessary information
my $singledir = main::SCANNER ? $ARGV[-1] : undef;
my $skipUpdate = $singledir && $singledir eq 'onlinelibrary';

my $boxsetSql = qq{
SELECT album
FROM tracks
WHERE album IS NOT NULL AND substr(url, 0, 8) = 'file://'
GROUP BY album
HAVING COUNT(DISTINCT coverid) > 1 OR COUNT(coverid) = 0
};

my ($count) = $dbh->selectrow_array( qq{
SELECT COUNT(*) FROM ( $boxsetSql ) AS t1
} ) unless $skipUpdate;

if ( !$count ) {
$cb && $cb->();
main::SCANNER && Slim::Music::Import->endImporter('updateBoxsetArtwork');
return;
}

my ($isPrecachingEnabled, $specs) = _initPrecacheArtworkIfEnabled();

$log->error("Starting updateBoxsetArtwork for $count albums");

my $progress = Slim::Utils::Progress->new( {
type => 'importer',
name => 'updateBoxsetArtwork',
total => $count,
bar => 1,
} );

my $sth_boxset = $dbh->prepare_cached($boxsetSql);
$sth_boxset->execute;

my $sth_album_tracks = $dbh->prepare_cached( qq{
SELECT url FROM tracks WHERE album = ?
} );

my $sth_update_albums = $dbh->prepare( qq{
UPDATE albums
SET artwork = ?
WHERE id = ?
} );

my $albumId;
$sth_boxset->bind_columns(\$albumId);

my $t = 0;

my $work = sub {
if ( $sth_boxset->fetch ) {
$sth_album_tracks->execute($albumId);

# get unique folder names for this album, as we may have multiple folders for a boxset
my @paths = Slim::Utils::Misc::uniq(
map {
dirname(Slim::Utils::Misc::pathFromFileURL($_->[0]))
} @{$sth_album_tracks->fetchall_arrayref()}
);

# put parent folder first in list if we have multiple folders for this album
unshift @paths, Slim::Utils::Misc::commonParentPath(\@paths, MAX_LEVELS_FOR_BOX_ARTWORK) if scalar @paths > 1;

# don't look for artwork in the root folder or on a Windows drive letter, as that is likely to be a false positive
@paths = grep { $_ && $_ ne '/' && !Slim::Utils::Misc::isWinDrive(substr($_, 0, 2)) } @paths;

if (main::DEBUGLOG && $log->is_debug) {
$log->debug("Track folders and common parent for album:\n" . Data::Dump::dump(@paths));
}

$progress->update( $paths[0] || '' );

if ( $t < time ) {
Slim::Schema->forceCommit;
$t = time + 5;
}

my ($newCover, $folder);
foreach (@paths) {
$newCover = Slim::Music::Artwork->findStandaloneArtwork({}, {}, Slim::Utils::Misc::fileURLFromPath($_), {
coverFiles => [qw(boxset album cover folder)],
});

if ($newCover) {
$folder = $_;
last;
}
}

my $newCoverId = $class->generateImageId({
image => $newCover,
url => Slim::Utils::Misc::fileURLFromPath($newCover),
}) if $newCover;

if ($newCoverId) {
my ($coverTrackExists) = $dbh->selectrow_array( qq{
SELECT 1 FROM tracks WHERE coverid = ?
}, undef, $newCoverId );

# In order to avoid the need for another schema change in albums, we create a track object
# for the album folder and store the coverid there. This is a bit of a hack, but it works.
my $trackObjForAlbum = Slim::Schema->objectForUrl({
url => Slim::Utils::Misc::fileURLFromPath($folder),
create => 1,
readTags => 0,
playlist => 0,
checkMTime => 0,
});

$trackObjForAlbum->content_type('dir'); # directories should not show up anywhere (audio, lists, images...)
$trackObjForAlbum->coverid($newCoverId);
$trackObjForAlbum->cover($newCover);
$trackObjForAlbum->update;

$sth_update_albums->execute( $newCoverId, $albumId );

Slim::Utils::ImageResizer->resize($newCover, "music/$newCoverId/cover_", $specs) if $isPrecachingEnabled;
}

return 1;
}

$progress->final;

$cb && $cb->();

return 0;
};

if ( main::SCANNER ) {
# Non-async mode in scanner
while ( $work->() ) { }

Slim::Music::Import->endImporter('updateBoxsetArtwork');
}
else {
# Run async in main process
Slim::Utils::Scheduler::add_ordered_task($work);
}
}

sub getImageContentAndType {
my $class = shift;
my $path = shift;
Expand Down Expand Up @@ -627,7 +778,7 @@ sub precacheAllArtwork {

my $isDebug = main::DEBUGLOG && $importlog->is_debug;

my $isEnabled = $prefs->get('precacheArtwork');
my ($isEnabled, $specs) = _initPrecacheArtworkIfEnabled();

my $dbh = Slim::Schema->dbh;

Expand Down Expand Up @@ -697,12 +848,6 @@ sub precacheAllArtwork {
# 3+ SqueezePlay/Jive size artwork
my @specs;

if ($isEnabled) {
@specs = getResizeSpecs();

require Slim::Utils::ImageResizer;
}

my $sth = $dbh->prepare($sql);
$sth->execute;

Expand Down Expand Up @@ -785,7 +930,7 @@ sub precacheAllArtwork {
# have scheduler wait for the finished callback
Slim::Utils::Scheduler::pause() if !main::SCANNER;

Slim::Utils::ImageResizer->resize($path, "music/$coverid/cover_", join(',', @specs), $finished);
Slim::Utils::ImageResizer->resize($path, "music/$coverid/cover_", $specs, $finished);
}
else {
$finished->();
Expand Down Expand Up @@ -888,4 +1033,11 @@ sub getResizeSpecs {
return @specs;
}

sub _initPrecacheArtworkIfEnabled {
return (0, undef) unless $prefs->get('precacheArtwork');

require Slim::Utils::ImageResizer;
return (1, join(',', getResizeSpecs()));
}

1;
5 changes: 5 additions & 0 deletions Slim/Music/Import.pm
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,11 @@ sub runScanPostProcessing {
$importsRunning{'precacheArtwork'} = Time::HiRes::time();
Slim::Music::Artwork->precacheAllArtwork;

if ($prefs->get('updateBoxsetArtwork')) {
$importsRunning{'updateBoxsetArtwork'} = Time::HiRes::time();
Slim::Music::Artwork->updateBoxsetArtwork();
}

# Always run an optimization pass at the end of our scan.
$log->error("Starting Database optimization.");

Expand Down
25 changes: 25 additions & 0 deletions Slim/Utils/Misc.pm
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,31 @@ sub fileURLFromPath {
return $file;
}

sub commonParentPath {
my ($pathsOrUrls, $maxLevels) = @_;
$maxLevels = 999 unless defined $maxLevels;

my @paths = uniq(map { Slim::Music::Info::isFileURL($_) ? pathFromFileURL($_) : $_ } @{$pathsOrUrls || []});
return '' unless @paths;

my @common = splitdir(shift @paths);
my $popCount = 0;

for my $path (@paths) {
my @parts = splitdir($path);

while (@common && (
@parts < @common ||
grep { $common[$_] ne $parts[$_] } 0 .. $#common
)) {
pop @common;
return '' if ++$popCount > $maxLevels;
}
}

return catdir(@common);
}

########

# other people call us externally.
Expand Down
2 changes: 1 addition & 1 deletion Slim/Web/Settings/Server/TextFormatting.pm
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ sub page {
}

sub prefs {
return ($prefs, qw(coverArt artfolder noContributorPictures));
return ($prefs, qw(coverArt artfolder updateBoxsetArtwork noContributorPictures));
}

sub handler {
Expand Down
37 changes: 36 additions & 1 deletion strings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6765,6 +6765,34 @@ SETUP_ARTFOLDER_DESC
SV Du kan välja att spara alla albumomslag i en och samma mapp med hjälp av alternativet för variabla filnamn ovan. Ange sökvägen till omslagsfilerna här. Oavsett sökvägen du anger här kommer Lyrion Music Server att söka efter omslagsbilder i samma mapp som var och en av ljudfilerna om inget omslag hittas i mappen för omslagsfiler.
ZH_CN 您可以利用以上的文件名变量选项,把所有的图象集中存放在一个文件夹内。请在这里输入图象文件所在地点。如果服务器未能在所指定的封面图象文件夹中找到匹配图象,便会在各个音像文件所属的文件夹中寻找匹配的图象。

SETUP_BOXSET_ARTWORK
DE Boxset-Artwork
EN Box Set Artwork
FR Illustration du coffret
NL Boxset-artwork
SV Boxset-omslag

SETUP_BOXSET_ARTWORK_DESC
DE Die meisten Alben haben ein einzelnes Cover, manche Veröffentlichungen enthalten jedoch mehrere Bilder. So kann ein Boxset für jede Disc ein anderes Artwork haben, oder eine Playlist bzw. ein klassisches Album kann Artwork für einzelne Titel oder Werke enthalten. Lyrion Music Server kann versuchen, solches Artwork in übergeordneten Ordnern oder unter bestimmten Namen (z. B. "boxset.jpg") zu finden.
EN Most albums have a single cover, but some releases include multiple images. For example, a box set may have different artwork for each disc, or a playlist or classical release may have artwork for individual tracks or works. Lyrion Music Server can try to find such artwork in parent folders, or given specific names (eg. "boxset.jpg").
FR La plupart des albums ont une seule pochette, mais certaines parutions incluent plusieurs images. Par exemple, un coffret peut avoir une illustration différente pour chaque disque, ou une playlist ou une publication classique peut avoir des illustrations pour des pistes ou des œuvres individuelles. Lyrion Music Server peut essayer de trouver de telles illustrations dans les dossiers parents, ou à partir de noms spécifiques (par ex. "boxset.jpg").
NL De meeste albums hebben één hoes, maar sommige releases bevatten meerdere afbeeldingen. Zo kan een boxset voor elke schijf andere artwork hebben, of kan een afspeellijst of klassieke release artwork hebben voor afzonderlijke tracks of werken. Lyrion Music Server kan proberen zulke artwork te vinden in bovenliggende mappen, of met specifieke namen (bijv. "boxset.jpg").
SV De flesta album har ett enda omslag, men vissa utgåvor innehåller flera bilder. Ett boxset kan till exempel ha olika omslag för varje skiva, eller så kan en spellista eller en klassisk utgåva ha bilder för enskilda spår eller verk. Lyrion Music Server kan försöka hitta sådan bildkonst i överordnade mappar eller med angivna namn (t.ex. "boxset.jpg").

SETUP_BOXSET_ARTWORK_ON
DE Nach gemeinsamen Covern in übergeordneten Ordnern suchen
EN Search for common artwork in parent folders
FR Rechercher des illustrations communes dans les dossiers parents
NL Zoek naar gemeenschappelijke hoesafbeeldingen in bovenliggende mappen
SV Sök efter gemensamma omslag i överordnade mappar

SETUP_BOXSET_ARTWORK_OFF
DE Nicht nach gemeinsamen Covern suchen
EN Don't search for common artwork
FR Ne pas rechercher d'illustrations communes
NL Niet zoeken naar gemeenschappelijke hoesafbeeldingen
SV Sök inte efter gemensamma omslag

SETUP_NO_PORTRAITS
CS Portréty umělců
DE Künstlerbilder
Expand Down Expand Up @@ -10366,7 +10394,7 @@ SETUP_IGNORE_RELEASE_TYPES_1
NL Releasetypes voor albums uitschakelen
PT Ignore o tipo de lançamento dos albuns
PT Ignore o tipo de lançamento dos albuns
SV Ignorera Utgåvetyper för album
SV Ignorera Utgåvetyper för album
ZH_CN 忽略专辑发行类型

SETUP_CLEANUP_RELEASE_TYPES
Expand Down Expand Up @@ -21886,6 +21914,13 @@ UPDATESTANDALONEARTWORK_PROGRESS
SV Hitta uppdaterade skivomslag
ZH_CN 查找更新的封面艺术文件

UPDATEBOXSETARTWORK_PROGRESS
DE Suche übergeordnete Cover für Boxsets, digitale Sammlungen usw.
EN Find top level artwork for box sets, digital compilations etc.
FR Rechercher les illustrations de niveau supérieur pour les coffrets, les compilations numériques, etc.
NL Zoek bovenliggende hoesafbeeldingen voor boxsets, digitale compilaties enz.
SV Sök överordnade omslagsbilder för boxset, digitala samlingar osv.

PRECACHEARTWORK_PROGRESS
CS Příprava ukládání obalů alb do mezipaměti
DA Gem coverbilleder i cachelager
Expand Down
Loading
Loading