-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cover art support #278
Cover art support #278
Conversation
); | ||
query.bindValue(":id", trackId); | ||
|
||
if (query.exec()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a bit style nitpicking. Can you please change this to our default style of
if (!query.exec()) {
LOG_FAILED_QUERY(query);
return coverArtInfo();
}
// handle query results
from a first rough scanning I have 2 points that you still need to deal with
|
@rryan can we cut a 1.12 branch before we merge this? Currently this is still incomplete because there is no way for a user to change the cover (that would in the next PR) and concentrates on how covers are handled internally. Also this has likely still bugs and definitely is against our feature freeze. |
Done! 2be0d8b
It's already working, isn't it? |
@@ -27,6 +27,9 @@ DlgAnalysis::DlgAnalysis(QWidget* parent, | |||
connect(m_pAnalysisLibraryTableView, SIGNAL(loadTrackToPlayer(TrackPointer, QString)), | |||
this, SIGNAL(loadTrackToPlayer(TrackPointer, QString))); | |||
|
|||
connect(m_pAnalysisLibraryTableView, SIGNAL(loadCoverArt(QString, int)), | |||
this, SIGNAL(loadCoverArt(QString, int))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment why this is required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving to focusedTrackChanged() would clarify this as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parent class we use for library models is pretty stupid. This signal is needed so that we can inform wcoverart that a different track is being selected. 'focusedTrackChanged' is more descriptive so we could change it
Hi @cardinot Well done! It looks really nice. However, I have already commented some code with ideas to make it more streamline. Kind regards, Daniel |
return result; | ||
} | ||
|
||
CoverArtDAO::coverArtInfo coverInfo = m_pCoverArtDAO->getCoverArtInfo(trackId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not good. You are calling a functions across threads and pass data between them. This could end that you potentially do really a LOT of parallel queries. I don't know how sqlite deals with that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that's true... but we could do it outside this thread (into requestPixmap) and send coverInfo as a parameter of loadImage...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then you would to a query everytime you want to load a non cached picture. I thought the goal was to reduce db queries for normal loading
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use QFile::exists(coverLocation)
and just fill a CoverArtDAO::coverArtInfo
from DB if it not exists...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It means, something like:
CoverArtDAO::coverArtInfo coverInfo;
if (!QFile::exists(coverLocation)) {
coverInfo = m_pCoverArtDAO->getCoverArtInfo(trackId);
}
As all covers that were loaded correctly once would be stored in our disk-cache, it would return true most of times and we would be doing the db query just when it would be useful for loadImage()...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no Db queries outside of the UI-Thread! Especially with the highly parallel asynchronous Qt:Concurrent system. It is too easy for this to introduce race-condition bugs. I'd rather have that we avoid them when we can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no Db queries outside of the UI-Thread!
But my suggestion is move it to requestPixmap() to run in the main thread... so, the code that I suggested was to be placed to run in the main thread...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, just keep in mint that there is a pending pull request from last year, that moves all db quires into a dedicated thread.
I hope we can merge it in the 1.13 release, so there is no need to worry that much here to use the DB from the GUI thread.
And yes, db queries can involve HD access. I just wander if it is required to cache all our date in ram, rather than using sqlite cache facilities, but that is an other topic. (I am no sqlit expert)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah ok sorry I got you wrong. Yeah inside of the UI-Thread it is ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, just keep in mint that there is a pending pull request from last year, that moves all db quires into a dedicated thread.
But regardless of that the code will still be easier to understand if we don't do everything in 'loadImage' and we would benefit from it either way.
just added the cover arts to developer_skins/Shade_dev to be able to test on my netbook. |
The header for the cover location in "26". It seams a proper text is missing. |
sorry, but I didn't get your point... |
What I had in mind was more along these lines. For each function I can see from the name what it is supposed to do. The control flow in void requestPixmap(id, loc) {
// check if we are already looking for this tracks cover
if loc.isEmpty():
// get track information from coverArtDAO
QtConcurrent::run(searchForImage, coverInfo)
// set up future watcher and connect finished signal to 'imageFound'
else:
QtConcurrent::run(loadImage, loc, id)
// set up future watcher and connect finished signal to 'imageLoaded'
}
coverTuple loadImage(loc, id) {
img = QImage(loc)
return id, img
}
coverTuple searchForImage(coverInfo) {
// look in disk-cache
// look in id3tags
// look in track directory
// save image in disk-cache
return id, img, loc
}
void imageLoaded(id, img) {
// extract result from sender
// add to cache
// remove id from runningIDs
emit pixmapFound(id, pixmap)
}
void imageFound(id, img, loc) {
// extract result from sender
// add image to cover_art table
// update library table
// add to cache
// remove id from runningIDs
emit pixmapFound(id, pixmap)
} |
Hello @kain88-de, thanks for your approach. =D
What about files which are externally removed?
Actually we'd be doing it:
Note that we will be handling and loading QImages anyway during the search and other VERY important point is that the first step (look in disk cache) is exactly the same thing that you would be doing in the another thread (loadImage(loc, id))... So, IMHO there's no advantage in split it... I also noticed that the only difference between imageFound and imageLoaded is the db_updating stuff, but it's also very easy to know if we have or not to update it... Have you seen my last refactoring around this stuff? |
The header in the library table for the new cover location column- |
Oh that should be hidden from the user. @cardinot you can change this by adding the column to the |
@cardinot I have looked at your recent changes and I know that my approach has a little bit duplicate code. But I have to make just one decision where as in your code you have to make the same decision 3 times. Here is a little ascii-art to show you what I mean.
Maybe I'm a bit biased but 6 months from now I could understand my control-flow easier and faster then yours. Each function has one well defined task and the decision is happening just once. We've already been clever by using QtConcurrent, now we have to think what would be the easiest maintainable version of this code. @daschuer any comment on this? Another plus side to this approach is that it is easily testable. If you have a function that behaves differently on different input you have to test all possible input combinations, which is a combinatorial To check of we need to search for the cover you can also use |
Thanks @kain88-de and @daschuer |
Now only loads a cover once for rows that share the same hash.
Sorts by the hash. It works well except for the case where cover art is similar but not exactly the same. I think it's good enough to include, even if some users may be confused why visually similar cover art doesn't sort together.
Improves scrolling performance when scrolling via keyboard.
interaction timeout.
Note, there is discussion about this happening in: cardinot#22 (People subscribing to mixxxdj/mixxx won't see it since it's in @cardinot's fork) |
In DlgCoverArtFullSize, max across all top level windows to get the appropriate size for the cover art. Maybe this fixes the original issue?
* Adds CoverArtCache::guessCover(s) methods for calling CoverArtUtils::guessCoverArt in a background thread. * Removes processing of reset menu option from WCoverArtMenu and instead sends a signal that the owner of the menu should handle.
ControlObjectThread is deprecated all new CO's should be of type ControlObjectSlave
Github Issues migration
More information about the project:
http://mixxx.org/wiki/doku.php/cover_art_support
Skin for tests:
Supported features:
First Part
Second Part
cardinot#1
Third Part
cardinot#2