Skip to content

redesign of updateStandaloneArtwork() - #1637

Draft
darrell-k wants to merge 7 commits into
LMS-Community:artwork-scan-dbfrom
darrell-k:image-scanning-new-approach
Draft

redesign of updateStandaloneArtwork()#1637
darrell-k wants to merge 7 commits into
LMS-Community:artwork-scan-dbfrom
darrell-k:image-scanning-new-approach

Conversation

@darrell-k

Copy link
Copy Markdown
Contributor

As discussed. I hope it all makes sense.

The diff generated by git for updateStandaloneArtwork() is a bit of a mess, probably best to view the new routine as a complete replacement for the old one.

This redesign enhances the new scanned_pics table so that it can drive updateStandaloneArtwork().

  • I've added acoverid column so that we can read it directly from the table (in the scanner process) when we need to update tracks or albums. In order for this to work, all external coverid generation will now use the image path, not the music file URL.
  • There is a new status column so we can differentiate new, existing and deleted images.
  • The url column is renamed to path as it will now hold the file system path of the image, not a file:// URL. This makes things much easier.
  • There is a new dir column as discussed.

In performance testing, this runs faster, even though we are now calling findStandaloneArtwork() for every track where an image change has been detected, rather than only once for each album/image group.

This change enables TitleFormatter to do its work correctly in cases when the user has specified a variable cover id which includes a "sub-album" field like discnumber or grouping. This means that disc or grouping-specific images can be applied to tracks using this existing mechanism when everything for the album is in the same directory.

I've added some comments to new/changed code in order to aid understanding.

I'm sure at this stage there is stuff I've missed.

Signed-off-by: darrell-k <darrell@darrell.org.uk>

@michaelherger michaelherger left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot! I hope to find time to actually test this later today. All my comments are just of theoretical nature. Haven't even pulled this change yet. Bear with me.

Comment thread SQL/mysql/schema_scanner.sql Outdated
Comment thread Slim/Utils/Scanner/Local.pm Outdated
# XXX how best to delete files in non-recursive mode?
# Delete the directory itself and all children
$dbh->do("DELETE FROM scanned_files WHERE url = '${file}' OR url LIKE '${file}/%'");
$dbh->do("DELETE FROM scanned_pics WHERE dir LIKE '${path}/%'");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need for this? I believe the pictures table has a different use than the files: the latter really is there to iterate over and process all records. The former (scaned_pics) is a helper to look up things for the tracks. In my plans/ideas this will be more than just cover artwork, but eg. artist pictures too. We shouldn't delete that data before we're really done. Wouldn't we potentially need it at a later stage to look up box set artwork, too?

BTW: I first wanted to complain about the use of variables in the SQL statement, instead of using prepared statements. That's a typical target for SQL injection. A folder name of drop table <table name>; -- or similar could potentially cause harm... something we should probably clean up at some point. But please try to avoid using variables potentially containing user data as much as possible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not. Also with the introduction of schema_scanner.sql you're also recreating scanned_files so the existing DELETE could be removed, too.

But this has prompted a thought: without the change which I assumed was temporary for debugging, to not run schema_scanner.sql unless we're in the scanner process, we'll also clear scanned_files when schema.pm is initialised in the main process. At the moment scanned_files remains populated until a full rescan. Might this affect things like autorescanning?

Comment thread SQL/mysql/schema_scanner.sql Outdated
Comment thread SQL/SQLite/schema_scanner.sql Outdated
Comment thread Slim/Utils/Scanner/Local.pm
Comment thread Slim/Utils/Scanner/Local/Async.pm Outdated
Comment thread Slim/Utils/Scanner/Local/Async.pm Outdated
Comment thread Slim/Music/Artwork.pm
Comment on lines +260 to +261
### I might have missed it, but I can't see where this might be called in main process async mode.
### If it is, we'll need more work to populate scanned_pics in the main process or just keep a version of the old subroutine for that use.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't remove this just yet... I'm a bit anxious we might be missing something. I want to double check this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please leave that code in, commented out if you want. Until we are certain.

Signed-off-by: darrell-k <darrell@darrell.org.uk>
Signed-off-by: darrell-k <darrell@darrell.org.uk>
CREATE INDEX scannedPicDirIndex ON scanned_pics (folder);
create index scannedPicStatusidx on scanned_pics(status);

CREATE INDEX IF NOT EXISTS trackscoveridx ON tracks(cover);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add this to one of the versioned files, too? If it's only used in the scanner (for now) we can probably get away adding it to the latest existing up files, avoiding another full wipe & rescan.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That reminds me, I changed the INSERT to check tracks using coverid rather than cover, in case the user has updated an image without changing the file name. So I don't think this index is required any more.

https://github.com/darrell-k/slimserver/blob/4da574ef3d256ac970f2baeb026895dd28f535b8/Slim/Utils/Scanner/Local/Async.pm#L50-L57

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You still might be using it around the update album artwork to first track coverid for remote and embedded images. query. But as that's also using album ID and coverid checks, I'm not sure it has much of an impact? Did you measure performance?

In any case: if you wanted to keep it, please name it, as it's not an index on the cover ID.

And for all index statements add a space or remove it everywhere between the table name and the index field 😉 .

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"idx" means index. I'll change it to "index"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah! Right! Keep the naming convention and upper/lower/camel casing consistent to help my aging and tired eyes and brain.

Signed-off-by: darrell-k <darrell@darrell.org.uk>
…de I used

Signed-off-by: darrell-k <darrell@darrell.org.uk>
Signed-off-by: darrell-k <darrell@darrell.org.uk>
Comment thread Slim/Music/Artwork.pm Outdated
Comment thread Slim/Schema.pm
Comment thread Slim/Music/Artwork.pm Outdated
Signed-off-by: darrell-k <darrell@darrell.org.uk>
@darrell-k

Copy link
Copy Markdown
Contributor Author

Some rework just pushed.

@michaelherger michaelherger left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, another sh..load of comments. I haven't even test run this yet, but I believe there's potential for performance optimisations on the DB level.

Comment thread Slim/Music/Artwork.pm
use Slim::Utils::OSDetect;

use constant MAX_RETRIES => 5;
use constant IS_SQLITE => (Slim::Utils::OSDetect->getOS()->sqlHelperClass() =~ /SQLite/ ? 1 : 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer used.

Comment thread Slim/Music/Artwork.pm
# Maybe a track instance was passed in, but no longer from updateStandaloneArtwork() which gives us
# the trackid instead, as we only need to instantiate a track if 'titleformatter' artwork naming is in use.
my $track = $trackAttributes && delete $trackAttributes->{_track};
$track ||= Slim::Schema->find('Track', $trackAttributes->{_trackid}) if $trackAttributes->{_trackid};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we delete the _trackid element here?

Comment thread Slim/Music/Artwork.pm
Comment on lines +260 to +261
### I might have missed it, but I can't see where this might be called in main process async mode.
### If it is, we'll need more work to populate scanned_pics in the main process or just keep a version of the old subroutine for that use.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please leave that code in, commented out if you want. Until we are certain.

Comment thread Slim/Music/Artwork.pm
Comment on lines +270 to +276
UPDATE albums
SET artwork = tracks.coverid
FROM tracks
WHERE tracks.album = albums.id
AND ( tracks.cover IS NULL OR CAST(CAST(tracks.cover AS INTEGER) AS TEXT) = tracks.cover OR tracks.cover LIKE 'https%' )
AND ( tracks.coverid <> albums.artwork OR albums.artwork IS NULL )
} );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting police alert: please move one out to align with the $dbh->do()

Comment thread Slim/Music/Artwork.pm
Comment on lines +267 to +268
### I considered adding rows to scanned_pics for these images so that they'd be processed in the loop below, but I think this is more efficient.
#there's a different syntax for MySql.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove outdated comment

Comment thread Slim/Music/Artwork.pm
Comment on lines +337 to +341
my $sth_update_tracks = $dbh->prepare( qq{
UPDATE tracks
SET cover = ?, coverid = ?, cover_cached = NULL
WHERE id = ?
} );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we optimise this? We would run an update for each track individually. But couldn't we update all tracks of an album in one update query? Hopefully (to be confirmed) the check in the loop would then no longer enter the conditional update, as the cover would already be updated on the remaining tracks of the album?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user is using TitleFormatter for variable names, then they could be different covers across the album (DISC, PERFORMANCE, GROUPING...).

Comment thread Slim/Music/Artwork.pm
Comment on lines +379 to +381
if ( $track->{cover} ne $newCover ) {
my ($newCoverid) = $dbh->selectrow_array($sth_scanned_pics, undef, $newCover);
$sth_update_tracks->execute( $newCover, $newCoverid, $track->{id} );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment where we define $sth_update_tracks: if we updated all the tracks of the album in a single update query, wouldn't this condition skip processing all the tracks individually?

# XXX how best to delete files in non-recursive mode?
# Delete the directory itself and all children
$dbh->do("DELETE FROM scanned_files WHERE url = '${file}' OR url LIKE '${file}/%'");
$dbh->do("DELETE FROM scanned_pics WHERE folder LIKE '${path}%'");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there really a need to delete this? I'd really like to use the same table for other purposes to avoid another scan for images for the contributor artwork.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not. But see my previous comment regarding scanned_files: #1637 (comment)

Comment thread Slim/Schema.pm
Slim::Utils::SQLHelper->executeSQLFile(
$driver, $class->storage->dbh, "schema_scanner.sql"
);
) if main::SCANNER; ### temporary, so we keep the contents from the last scan for debugging

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still proving useful for development. Also see #1637 (comment) (which I just mentioned elsewhere as well)

Comment thread Slim/Schema.pm
$columnValueHash{cover} = $cover;
}

# if ( $columnValueHash{cover} =~ /^https?/ || $columnValueHash{cover} =~ /^\d+$/ ) { ###combine the regex if this works!!!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obsolete idea, needs removing!

@darrell-k

Copy link
Copy Markdown
Contributor Author

Just to let you know, I'm currently testing the TitleFormatter variable artwork stuff and have found two problems with DISC:

  • In a full rescan when findStandaloneArtwork is called from Schema.pm, if total discs/disc count/discc is not in the tags therefore not in $deferredAttributes, TitleFormatter doesn't return anything for the DISC variable. It's OK in a n&c scan because we've got a database object by the time it's called via updateStandaloneArtwork and have written albums.discc regardless of whether it was tagged or not.
  • I've got some test files with a leading zero in the disc number tag, which also screws it up in a full rescan (no artwork returned again with either eg 1.jpg or 01.jpg in the album folder).

@darrell-k

Copy link
Copy Markdown
Contributor Author

I'm going to resolve some of the comments in this thread, it's getting hard to follow!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants