Alternative artwork/box set approach - #1536
Conversation
Signed-off-by: darrell-k <darrell@darrell.org.uk>
|
Copilot's thoughts (Concern #4 is what made me raise an eyebrow): Concerns & Observations:
Testing Recommendations:
Minor Issues:
Overall, this is a thoughtful approach to a complex problem, but it would benefit from the noted refinements before merging. |
|
On the concerns:
On testing: Except for performance testing, I've tested the other stuff to the point of boredom, but I intend to do some more testing and it's a good checklist. Given the almost infinite combinations of adding/removing/changing artwork both at the disc/track level and album parent level, we can only do our best. Once we've verified performance is OK and it passes regression testing (ie current behaviour for the simple cases of changing artwork in a single album folder and embedded artwork) I'd suggest merging it to 9.2 for user feedback. On minor issues:
|
|
@darrell-k - happy to help testing when you are ready for that. I have some test cases set up from before. I was testing with embedded artwork where I had one with artwork embedded that varied by disc and another with embedded artwork varied by Grouping all with the “box set” artwork as cover.jpg. |
Signed-off-by: darrell-k <darrell@darrell.org.uk>
Thanks for the offer.
with the versions from my branch https://github.com/darrell-k/slimserver/tree/artwork Start with a full rescan to ensure a good starting point. Then put the new & changed scan through its paces. |
|
@mikeysas I should have reminded you that discsubtitle matching and file name heuristics are not implemented yet. I'm working on it now. |
Signed-off-by: darrell-k <darrell@darrell.org.uk>
|
Initial performance tests show quite a hit (40 seconds combined for updateStandaloneArtwork and precacheArtwork combined for the 40,000 track test library, compared to 1 second for current 9.2! I'll work on this tomorrow. |
|
@mikeysas Yes, this can be resolved by Material sending an extra tag (J) in the tracks command and then using the returned album image id (which is confusingly called There's a related issue: the artist shown in the album heading is the lead artist of the track, rather than the lead artist of the album. This will require an LMS change, and possibly another Material change, so I'll hold off creating a PR for Material until I've decided how to approach the LMS query amendment, which is probably going to need an extra join to the contributors table for the name. |
I've got it down to 5 seconds for this test case. I conditioned a couple of SQL UPDATE statements to stop them being issued unnecessarily, but the big improvements are:
I also tried replacing the dirname SQL grouping with Perl code, but it added a lot of complication and only saved 1 second. Turns out Sqlite is quite good at such things! I'm going to look at whether similar efficiencies are useful in @mikeysas please check I haven't broken anything with the latest changes if you get the chance. You only need to refresh your Slim/Music/Artwork.pm from my branch to pick up this change. I also need to make sure I haven't broken online albums, and also test the async call from the main process. |
Signed-off-by: darrell-k <darrell@darrell.org.uk>
|
How did you profile? I've found Which should give a web page where you can drill down to hot spots etc.
|
|
Timings from the scan messages. Experimented by commenting out different bits of the code. As you say, the DBIx stuff was a prime suspect. Thanks for the tool recommendation, I'll try it out. I'll look at further changes to ensure that DBIx is never used in |
I've been struggling a bit with this, trying to create a compatible hash with raw SQL, but I just found that the variable artwork name stuff (I'm testing with an artwork format of
Or am I missing something? |
|
The failure: is this with stock 9.2? And 9.1 would be correct? There aren't too many changes in 9.2 yet. |
|
I've examined the code and the source history. I can't see how the hash created in I dumped the hash received by |
|
Are you saying that if I define |
|
Oh, is the answer in the code already? slimserver/Slim/Music/Artwork.pm Line 86 in 9508fbe So this would have been broken for >16 years, for anyone trying to use more than just track title. The artist would not even be referenced in that data structure... |
|
Hmm... and it's different whether you run a full wipe & rescan, or just a rescan. In the former case we'd get a flattened |
|
Yes, I can fix it now we've confirmed it is broken. |
Signed-off-by: darrell-k <darrell@darrell.org.uk>
|
See latest commit. I've coded direct SQL to build the necessary metadata for TitleFormatter and tried to introduce consistency in response to the findings above. Not massively tested, and probably not the last word on the issue. |
On the second issue, this has been resolved by recent LMS/Material changes. This needs more testing, including now the title format fixes which do not apply when using Material. |
|
Wow... had forgotten about this one, too. Is the performance hit still there? In order to prevent memory exhaustion caching DB queries in memory we use I'll hopefully find some time over the weekend to look into this, as I want to give it some testing, too. |
michaelherger
left a comment
There was a problem hiding this comment.
I'm sorry, I'm slow these days. Some more questions...
I'll try to run tests locally to see the performance impact. Just to make sure I get this right: this change mostly impacts the rescan, but not wipe & scan?
There was a problem hiding this comment.
What is the reason for all these changes in what seems unrelated to artwork? Are they to fix the custom artwork file name parsing or something?
There was a problem hiding this comment.
Because of removing track object instantiation and replacing with direct SQL, I tested titleFormatter, which is used in the artwork custom name processing.
As mentioned earlier in this thread I found it was broken so tried to fix and make consistent.
The multiple value/array question you raised below did cross my mind, too.
| my $rtn = $_[0]->{ lc($attr) } || $_[0]->{ 'tracks.' . lc($attr) } || ''; | ||
| utf8::decode($rtn); | ||
| return $rtn; |
There was a problem hiding this comment.
Would Slim::Utils::Unicode::utf8on(...) do the trick to avoid the same repeated three lines?
return Slim::Utils::Unicode::utf8on($_[0]->{ lc($attr) } || $_[0]->{ 'tracks.' . lc($attr) } || '');In order to avoid the repeated lengthy call you could define a local alias at the top of the file:
*utf8on = \&Slim::Utils::Unicode::utf8on;| my $discc = $_[0]->{discc} || $_[0]->{'albums.discc'} || ''; | ||
| # suppress disc counts of 1 or less | ||
| return ($discc && $discc > 1 ? $discc : ''); |
There was a problem hiding this comment.
That's an opinionated change - which I'm ok with. But we need to keep in mind that some people might be surprised and not like it.
Applies to all the following disc related changes.
| if ( ref $_[0] eq 'HASH' ) { | ||
| return $_[0]->{artist} || $_[0]->{albumartist} || $_[0]->{trackartist} || $_[0]->{'contributors.name'} || ''; | ||
| my $artists = $_[0]->{artist} || $_[0]->{albumartist} || $_[0]->{trackartist} || $_[0]->{'contributors.name'} || ''; | ||
| my $rtn = ref $artists && scalar @$artists ? join(' & ', @$artists) : $artists ? $artists : ''; |
There was a problem hiding this comment.
Hmm... this could potentially break things, right? We'd previously only return a single element, now we'd concatenate names? And concatenate them with a hard coded separator - when we have a pref for "split characters". Should we be using one of them instead? That's another opinionated change we'll have to defend.
| $parsedFormats{'VOLUME'} = sub { | ||
|
|
||
| my $url; | ||
| my $output = ''; | ||
|
|
||
| if ( ref $_[0] eq 'HASH' ) { | ||
| return $_[0]->{volume} || ''; | ||
| $url = $_[0]->{url} || $_[0]->{'tracks.url'}; | ||
| } | ||
| else { | ||
| $url = $_[0]->get('url'); | ||
| } |
There was a problem hiding this comment.
Oh... that got me confused: what has the audio volume to do with a URL? 😉
More seriously: what is the newly introduced version getting from? What are you assuming is $_[0] there?
|
|
||
| $output = (splitpath($url))[2]; | ||
| $output =~ s/\.[^\.]*?$//; | ||
| $output = Slim::Utils::Misc::unescape($output); |
There was a problem hiding this comment.
Wouldn't this be done by pathFromFileURL already?
| # Generate coverid value based on artwork, mtime, filesize: | ||
| # if cover is embedded in the file (ie it's a number, the size of the embedded image) use the track URL, | ||
| # if cover is external use the URL of the image. | ||
| # This avoids generating multiple cover ids for the same image. I'm not sure if they would ever get cached | ||
| # because Slim::Music::Artwork would consolidate them, but this seems cleaner. | ||
| $columnValueHash{coverid} = Slim::Schema::Track->generateCoverId( { | ||
| cover => $columnValueHash{cover}, | ||
| url => $url, | ||
| url => $columnValueHash{cover} =~ /^\d+$/ ? $url : Slim::Utils::Misc::fileURLFromPath($columnValueHash{cover}), | ||
| mtime => $columnValueHash{timestamp}, | ||
| size => $columnValueHash{filesize}, | ||
| } ); |
There was a problem hiding this comment.
Isn't this the same as in Track.pm? If so: can we consolidate in to one helper? Or use Slim::Schema::Track::coverid here?
There was a problem hiding this comment.
No, but we could call Slim::Music::Artwork->generateImageId() with almost the same parameters. And then have generateImageId() sort out the URL value? There actually already is a code branch dealing with the all numerical cover ID.
| url => Slim::Utils::Misc::fileURLFromPath($parentArtwork), | ||
| }) || ''; | ||
| $sth_update_albums->execute( $parentArtworkId, $albumid ); | ||
| Slim::Utils::ImageResizer->resize($parentArtwork, "music/$parentArtworkId/cover_", join(',', @specs), undef); |
There was a problem hiding this comment.
Slim::Utils::ImageResizer is not loaded nor needed unless pre-caching is enabled.
$isEnabled && Slim::Utils::ImageResizer->resize($parentArtwork, "music/$parentArtworkId/cover_", join(',', @specs), undef);There was a problem hiding this comment.
Oh, did you add that line to pre-cache the album artwork, so we don't need to change the schema to add the artwork file URL? Would we still be able to get the original size file (or any resized version of it)?
There was a problem hiding this comment.
The titles query doesn't return a url for local content. albums.artwork is currently set to (one of if multiples) tracks.coverid.
So I just push the album image through the same process as for track images.
There may be holes in this, for example if there's code I haven't identified that looks up the associated file url from the tracks table using tracks.coverid = albums.artwork.
There was a problem hiding this comment.
And sure enough there is a problem:
Clicking the image on the album info page in default skin opens a url for example http://localhost:9000/Default/music/50628676/cover which results in no image shown in the resulting new tab in the case of an album-level only image.
I guess we'd need to cache the original image (pass an extra spec to ::ImageResizer->resize?)
There was a problem hiding this comment.
That might be the result of having no reference to the original file.
I'll have something to play with tonight. It would create helper entries in the tracks table (content type dir - for the album level folder). Using that seems to address the resizing challenges nicely.
| types => 'audio', | ||
| recursive => 0, | ||
| } ) if scalar @paths; | ||
| Slim::Music::Artwork->precacheAllArtwork if scalar @paths; |
There was a problem hiding this comment.
Is this really needed? The scanner would run in its separate process, but calling precacheAllArtwork() here would do the job in-process. And why only if the scanner was run for a specific folder?
There was a problem hiding this comment.
I think because in the single album scan it otherwise is not called.
| $meta->{"albumid"} = delete $meta->{"tracks.album"}; | ||
| $meta->{"workid"} = delete $meta->{"tracks.work"}; | ||
|
|
||
| if ( $1 =~ /ARTIST|COMPOSER|CONDUCTOR|BAND/ ) { |
There was a problem hiding this comment.
Store $1 in a named variable. This can easily break if we add some regex to the previous few lines.
|
Unfortunately the performance hit by this PR is unacceptable in my (limited) testing: Mac M2Pro, 16GB, SSD for the data, 20k tracks on NAS (1Gb), another 5k tracks from local SSD. The artwork processing takes 30x the time of 9.2. 30s vs. 1s. I'll continue the investigation. |
|
Ouch! That's much worse than I was seeing. I'm not at my computer right now but later I'll check that I pushed everything. |
This was a response to your performance observation just now. I didn't reply in the right place. |
| @albumDirs = Slim::Utils::Misc::uniq(@albumDirs); | ||
| my $commonParent = _findCommonParent(\@albumDirs); | ||
|
|
||
| if ( my $parentArtwork = Slim::Music::Artwork->findStandaloneArtwork({}, {}, Slim::Utils::Misc::fileURLFromPath($commonParent), 1) ) { |
There was a problem hiding this comment.
This check is costing about 24s in my test case.
|
@darrell-k first let me thank you once again for the energy and time you put into these changes. And I definitely don't want to sound ungrateful. I've recently been too busy to give quicker feedback. Unfortunately I think this time we've lost track of what the PR tries to fix/add/improve. It mentions another approach to box set handling, but then admits that problem wasn't solved. It then tries to fix a bunch of other artwork related issues instead. And title formatting (which then isn't used). The more time I spend trying to understand everything in this PR, the more I believe we should step back a bit. Let's try to come up with a well defined list of issues, and try to address them. Then create smaller PRs, each one solving one problem, or a well defined group of related problems. Maybe it's time to re-think the artwork scan? Should the scanner not only index audio files, but collect a list of artwork files while it's crawling the media folders? This could avoid later repeated reading of the same folders again. Finding a set of artwork files in a folder always requires reading the full file list, which then would be filtered. If we already knew the candidate file name from the initial scan, we wouldn't have to do this again (that's what's costing most of the time in my tests). This might be a major task, but at least we'd know what we're targeting. Would you remember a list of bugs you tried to fix in this PR? One might actually be the one addressed (slightly differently) in #1607? |
Extracted and tweaked from #1536 Signed-off-by: Michael Herger <michael@herger.net>
|
|
||
| if ( keys %$meta == 0 && $trackid ) { | ||
|
|
||
| my @cols = map { "tracks.$_" } keys %{Slim::Schema::Track->attributes}; |
There was a problem hiding this comment.
Could all of this be replaced with a call to the titles query?
Yes, it's been a long time since we discussed this! This PR is my reworking of the AI-assisted #1513 The primary objective is to support album box-set artwork. For the title formatting issue, read from #1536 (comment) onwards. It is used when the user sets the wildcard formatting option for artwork. As you will see from that, I changed the code to avoid instantiating track objects, as you suggested, and that was when I discovered the existing wildcard artwork format processing was broken in a N&C scan. So yes there are two issues here now and perhaps they could be split into 2 PRs. the second, being a fix, should come first if we do this.
#1607 I hadn't looked at, it's a different issue (but might be affected by the changes here, I'll have a look) Rethinking the artwork scan might be worth it - building a temporary table containing the paths to new/changed/deleted artwork in the library during the initial scan, perhaps? I'll have a think. But maybe addressing the specific points in your review today might be good enough? I'll work through those now. |
Signed-off-by: darrell-k <darrell@darrell.org.uk>
Signed-off-by: darrell-k <darrell@darrell.org.uk>
Signed-off-by: darrell-k <darrell@darrell.org.uk>
|
I've just pushed a change which saves artwork paths in a new database table during the file discovery phase and then uses this table rather than rescanning the disk in It's not complete yet (I need to look at the variable artwork names because that's still doing an existence check on the disk, and maybe other stuff) but you should be able to run the performance test for a n&c scan without an artwork template in advanced/formatting. If this turns out to be a useful change, there may be a better way of finding the image files than what I'm doing here. |
|
@darrell-k - please, pretty really please open an issue describing the artwork/box set problem. I've spent hours trying to understand and testing what you've been suggesting here. But it's so convoluted, with mixed in other solutions, I don't even know any more what we're trying to solve. Let's have a conscious description of the problem and a potential solution before we create and/or review more changes. |
|
@michaelherger the functional requirements haven't changed from #1513 . I raised this PR because you were unhappy with the LLM-generated code there and hinted that you would like me to take a look. However, I've had a look around and there doesn't seem to be a concise description of the requirements for that, its the usual long forum thread(s). Perhaps @mikeysas / @audiomuze could help by creating an issue describing the objectives. I could then add the extra things that came to light during development/testing. In the meantime, I'd be grateful if you could tell me if yesterday's changes have mitigated the performance problems with a NAS scan that you identified. |
|
#1513 is a draft pull request without description, other than a link to some random forum post. I can't spend clicking around and reading endless discussion just to figure out what users consider a "box set". So please. https://github.com/LMS-Community/slimserver/issues/new?template=BLANK_ISSUE Define what a box set is. Describe what does work in LMS, and what doesn't. Explain what needs to be fixed. Feel free to suggest an approach to fixing it. Once we agree on this, we can go and implement things. And that PR will point to that issue it fixes, and everybody knows what we're looking for in the PR. Let's just have a little structure. Not too much, just a little. I haven't run the tests yet, sorry. I got distracted trying to understand this PR... |
Yes, I said the same above, but asked if the users who were discussing / testing it before could do it. I think it's better to get this from the users requesting the feature. If I were to do it myself, the issue would end up describing my understanding of the requirements based on the coding I have done here, which might not be complete. Of course I will then comment on the issue based on my findings during analysis etc. |
|
I captured the original requirements for this a while back with #1430 . The core concept there was to support varying artwork for tracks and separate album cover artwork. There are three use cases that we have identified:
When I originally submitted that issue, I was thinking the track artwork would have to be embedded in the files and then the album cover would come from cover.jpg / folder.jpg logic. @audiomuze added the requirements with PR #1513 to support scanning track for level artwork from .jpg file names that matched disc#, disc subtitles and grouping tags so users don’t have to embed their artwork in files. That is the history here as I recall this. Does this cover what you need @michaelherger or I can update #1430 or create a new issue if you prefer? |
|
Updated Requirements compiled in #1430 (comment) |
|
Can we use #1430 to discuss the feature before continuing the code work? I actually believe a lot of this can already be handled with custom file name formats. We "only" need a mechanism to detach the album artwork from the track artwork. |






Here is an alternative approach to #1513
It fixes a couple of existing new&changed artwork scanning problems including embedded artwork not being removed after being removed from the music file and issues I found with albums in multiple directories.
I've tried to implement the functionality of #1513 but I have not yet coded the "file/folder name to disc number" heuristics or the discsubtitle processing. But these should be relatively straightforward additions to the code structure in this PR.
I've also ensured that for box sets, if a particular disc doesn't have specific artwork, we fall back to the overall album artwork.
Needs much more testing, not just by me!
Comments please.