-
-
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
WTrackMenu: add Remove From Disk action #3212
Conversation
aa34f46
to
69e340b
Compare
69e340b
to
5f9c7be
Compare
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.
I suggest to delete and purge tracks in lockstep one after another while displaying a progress dialog. Implemented like the other batch operations for tracks.
It might take longer but it reduces the temporary inconsistencies between the file system and the database if anything goes wrong.
So you suggest to only delete the files in removeTracksFromDisk(), then simply pass the refs to purgeTracks()? |
didn't encounter a progress bar so far, we have it only for track export, right? |
so I'll fork the mechanics from src/library/export/ |
All batch operations that affect individual track objects are using mixxx::TrackPointerOperation. I suggest to apply this pattern here, because it is not a single batch database operation. You need to obtain the file locations from the track objects anyway. Applying the batch database and filesystem operations separately is inconsistent. Your code already reveals this mismatch. |
No, not necessary. Just take a look at WTrackMenu, everything is already there. It's ok to execute those actions in the UI thread if the user is able to abort the long running operation at any point, i.e. after each processed track. |
Excellent! btw, ignore the current implementation, I'm only tweaking it to get a feeling for the UX, fromatting etc. |
Purge a single track immediately after the corresponding file has been deleted successfully, i.e. pass a list with a single element. |
5f9c7be
to
532c130
Compare
@uklotzde I need some more guidance here: Also it would be helpful to store all failed deletions and show that list to the user when the batch process has finished. |
solved. |
532c130
to
5a5d2ab
Compare
I tried to understand the existing batch process structure in WTrackMenu and adapt it to what's needed for file deletion. deletion and purging seems to work okay but I don't get a progress bar and it always crashes after like 4-8 tracks. I do get a progress bar when I experimented with the disabled hide() command. But has a few other drawbacks, like the "hide from playlists?" dialog covering the delete progress dialog. Also hiding doesn't seem to succed for all track pointers. Any help is appreciated. |
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.
debug [Main] GlobalTrackCache - Deleting Track(0x8aa8180)
critical [Main] Invalid database identifier value: QVariant(Invalid)
critical [Main] DEBUG ASSERT: "m_taskInfos.isEmpty()" in function virtual mixxx::TaskMonitor::~TaskMonitor() at ../src/util/taskmonitor.cpp:22
Purging tracks one by one doesn't seem to be possible, because we are not able to suppress the intermediate signals that invalidate the track model during the iteration.
Probably TrackDAO::forceModelUpdate()
is the signal that causes the problematic behavior. I don't see any quick workaround other than waiting for a rewrite of the UI. The chaotic dependencies and runtime side-effects between different classes are unmaintainable.
Workaround: You already collect the TrackRefs upfront. Use these directly instead of invoking applyTrackPointerOperation(). You can reuse the existing framework if you implement a custom TrackPointerIterator that does not access m_pTrackModel during the iteration. |
Thanks for your feedback, appreciated. |
5a5d2ab
to
0ec310b
Compare
wooo, it works! (had to test in with a slow SD card.. just didn't even get a progressbar when purging from nvme) |
0ec310b
to
947b40e
Compare
I need to move the de-duplication to the trackpointer operation, now only the Delete warning list is de-duplicated. re: workflow & protecting users from mistakes |
I'll rewind to 394e338 and fix the dialog setup according to Uwe's review. Then this is ready. |
b4204a1
to
d84c8a3
Compare
I think I'm done here. Let's merge this. |
src/widget/wtrackmenu.cpp
Outdated
|
||
class RemoveTrackFilesFromDiskTrackPointerOperation : public mixxx::TrackPointerOperation { | ||
public: | ||
QList<TrackRef> getTracksToPurge() const { |
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.
Return by const-ref, even if implicitly shared.
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.
Are you sure? I'm confused why one would return a value by const reference. Wouldn't that reference have a high chance of becoming dangling in the future?
As a C++ novice, I'm confused by this and I'd highly appreciate an explanation if you wouldn't mind.
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, common practice for getters. Return by value is only used for cheap copies and move (RVO).
Of course, the caller is responsible for taking lifetimes into account. Not checked as in Rust.
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, common practice for getters. Return by value is only used for cheap copies and move (RVO).
So should we always return by const ref if we return an existing list/vector/map (i.e. not constructed on-demand)? I vaguely remember someone requested to change the const ref into a by-value return value on one of my PR some time ago, and that confused me but I assumed it was due to some c++ weirdness that I didn't know about.
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.
Am I getting it right: const QList<TrackRef>& getTracksToPurge() const {
?
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.
References to members are returned by const-ref, local variables by value.
Exception: Members of objects that are used by multiple threads where the reference would otherwise outlive the lock guard! And even if you don't need a guard (smart pointers & implicitly shared object) you should return by value for a taking a snapshot of the state.
If you are unsure about lifetimes then please stay away from C/C++ and better use Rust ;)
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.
Thanks for the explanation. I was not aware that this was a getter function that just returns a member (my fault for not actually looking at the code), returning by const-ref makes sense then.
If you are unsure about lifetimes then please stay away from C/C++ and better use Rust ;)
True, but that doesn't mean I don't want to learn. I'd say I'm pretty comfortable with lifetimes (because I learned rust) but this tripped me up so I figured I'd ask so I can improve my understanding. Thanks for the explanation again.
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.
@Swiftb0y You hopefully noticed that my sarcastic comment was not directed at you, Niko!
In C/C++ you have to do the work of the borrow checker in your mind, thereby reducing your mental capacity. Or you simply ignore it and your code will work by chance or 99.9999999% of the time. Managed, imperative languages have their own pitfalls.
Thank you for picking this up and finishing it! LGTM |
This deserves a CHANGELOG entry. Forgot to ask. |
Woohoo, finally! |
Woohoo! thank you for picking this up, and getting it done!
…On 24/11/2021 12:35 pm, ronso0 wrote:
Woohoo, finally!
Thanks for your guidance, and your patience @uklotzde
<https://github.com/uklotzde>
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#3212 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AEHF465PKWYHVTYXHZHIUJ3UNQQMDANCNFSM4S55MTXA>.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
supersedes #1737 by @WaylonR
Remove From Disk track menu action in
Browse & Rec don't automatically refresh after deletion, the view has to be activated again (click).
This is mentioned in the Delete Summary dialog.
Nice to have:
Current simple implementation:
Which workflows do we want to cover?
Delete file and ...
¹ delete waveform and replayGain only
² delete all cue types, rating, track color? etc.
ToDo
Delete?
confirmation dialog with a scrollable file listintroduced in Better file export #3572 to minimize accidentsDelete Track Files
>Delete Files From Disk
Nice to have
automatically refresh Browse/Rec folder view after deletionNot worth the effort