Skip to content

Commit

Permalink
Merge pull request #2899 from uklotzde/wtrackmenu_batchprocessing
Browse files Browse the repository at this point in the history
WTrackMenu: Display a modal progress dialog
  • Loading branch information
Be-ing authored Jul 10, 2020
2 parents 48f0d04 + 1a0b20d commit 80424d4
Show file tree
Hide file tree
Showing 16 changed files with 1,280 additions and 119 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -599,8 +599,11 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/library/starrating.cpp
src/library/tableitemdelegate.cpp
src/library/trackcollection.cpp
src/library/trackcollectioniterator.cpp
src/library/trackcollectionmanager.cpp
src/library/trackloader.cpp
src/library/trackmodeliterator.cpp
src/library/trackprocessing.cpp
src/library/traktor/traktorfeature.cpp
src/library/treeitem.cpp
src/library/treeitemmodel.cpp
Expand Down Expand Up @@ -779,6 +782,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/util/statsmanager.cpp
src/util/tapfilter.cpp
src/util/task.cpp
src/util/taskmonitor.cpp
src/util/threadcputimer.cpp
src/util/time.cpp
src/util/timer.cpp
Expand Down
4 changes: 4 additions & 0 deletions build/depends.py
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,9 @@ def sources(self, build):
"src/library/coverart.cpp",
"src/library/coverartcache.cpp",
"src/library/coverartutils.cpp",
"src/library/trackcollectioniterator.cpp",
"src/library/trackmodeliterator.cpp",
"src/library/trackprocessing.cpp",

"src/library/crate/cratestorage.cpp",
"src/library/crate/cratefeature.cpp",
Expand Down Expand Up @@ -1302,6 +1305,7 @@ def sources(self, build):
"src/util/file.cpp",
"src/util/mac.cpp",
"src/util/task.cpp",
"src/util/taskmonitor.cpp",
"src/util/experiment.cpp",
"src/util/xml.cpp",
"src/util/tapfilter.cpp",
Expand Down
14 changes: 0 additions & 14 deletions src/library/coverartutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,20 +223,6 @@ void guessTrackCoverInfoConcurrently(
}
}

void guessTrackCoverInfoConcurrently(
QList<TrackPointer> tracks) {
if (tracks.isEmpty()) {
return;
}
if (s_enableConcurrentGuessingOfTrackCoverInfo) {
QtConcurrent::run([tracks] {
CoverInfoGuesser().guessAndSetCoverInfoForTracks(tracks);
});
} else {
CoverInfoGuesser().guessAndSetCoverInfoForTracks(tracks);
}
}

void disableConcurrentGuessingOfTrackCoverInfoDuringTests() {
s_enableConcurrentGuessingOfTrackCoverInfo = false;
}
1 change: 0 additions & 1 deletion src/library/coverartutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ class CoverInfoGuesser {
// metadata and folders for image files. All I/O is done in a separate
// thread.
void guessTrackCoverInfoConcurrently(TrackPointer pTrack);
void guessTrackCoverInfoConcurrently(QList<TrackPointer> tracks);

// Concurrent guessing of track covers during short running
// tests may cause spurious test failures due to timing issues.
Expand Down
21 changes: 21 additions & 0 deletions src/library/trackcollectioniterator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "library/trackcollectioniterator.h"

#include "library/trackcollection.h"

namespace mixxx {

std::optional<TrackPointer> TrackByIdCollectionIterator::nextItem() {
const auto nextTrackId =
m_trackIdListIter.nextItem();
if (!nextTrackId) {
return std::nullopt;
}
const auto trackPtr =
m_pTrackCollection->getTrackById(*nextTrackId);
if (!trackPtr) {
return std::nullopt;
}
return std::make_optional(trackPtr);
}

} // namespace mixxx
43 changes: 43 additions & 0 deletions src/library/trackcollectioniterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/// Utilities for iterating through a selection or collection
/// of tracks.

#pragma once

#include <QModelIndex>

#include "track/trackiterator.h"

class TrackCollection;

namespace mixxx {

/// Iterate over selected and valid(!) track pointers in a TrackModel.
/// Invalid (= nullptr) track pointers are skipped silently.
class TrackByIdCollectionIterator final
: public virtual TrackPointerIterator {
public:
TrackByIdCollectionIterator(
const TrackCollection* pTrackCollection,
const TrackIdList& trackIds)
: m_pTrackCollection(pTrackCollection),
m_trackIdListIter(trackIds) {
DEBUG_ASSERT(m_pTrackCollection);
}
~TrackByIdCollectionIterator() override = default;

void reset() override {
m_trackIdListIter.reset();
}

std::optional<int> estimateItemsRemaining() override {
return m_trackIdListIter.estimateItemsRemaining();
}

std::optional<TrackPointer> nextItem() override;

private:
const TrackCollection* const m_pTrackCollection;
TrackIdListIterator m_trackIdListIter;
};

} // namespace mixxx
35 changes: 35 additions & 0 deletions src/library/trackmodeliterator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "library/trackmodeliterator.h"

#include "library/trackmodel.h"

namespace mixxx {

std::optional<TrackId> TrackIdModelIterator::nextItem() {
const auto nextModelIndex =
m_modelIndexListIter.nextItem();
if (!nextModelIndex) {
return std::nullopt;
}
const auto trackId =
m_pTrackModel->getTrackId(*nextModelIndex);
if (!trackId.isValid()) {
return std::nullopt;
}
return std::make_optional(trackId);
}

std::optional<TrackPointer> TrackPointerModelIterator::nextItem() {
const auto nextModelIndex =
m_modelIndexListIter.nextItem();
if (!nextModelIndex) {
return std::nullopt;
}
const auto trackPtr =
m_pTrackModel->getTrack(*nextModelIndex);
if (!trackPtr) {
return std::nullopt;
}
return std::make_optional(trackPtr);
}

} // namespace mixxx
72 changes: 72 additions & 0 deletions src/library/trackmodeliterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/// Utilities for iterating through a selection or collection
/// of tracks identified by QModelIndex.

#pragma once

#include <QModelIndex>

#include "track/trackiterator.h"

class TrackModel;

namespace mixxx {

/// Iterate over selected, valid track ids in a TrackModel.
/// Invalid track ids are skipped silently.
class TrackIdModelIterator final
: public virtual TrackIdIterator {
public:
TrackIdModelIterator(
const TrackModel* pTrackModel,
const QModelIndexList& indexList)
: m_pTrackModel(pTrackModel),
m_modelIndexListIter(indexList) {
DEBUG_ASSERT(m_pTrackModel);
}
~TrackIdModelIterator() override = default;

void reset() override {
m_modelIndexListIter.reset();
}

std::optional<int> estimateItemsRemaining() override {
return m_modelIndexListIter.estimateItemsRemaining();
}

std::optional<TrackId> nextItem() override;

private:
const TrackModel* const m_pTrackModel;
ListItemIterator<QModelIndex> m_modelIndexListIter;
};

/// Iterate over selected, valid track pointers in a TrackModel.
/// Invalid (= nullptr) track pointers are skipped silently.
class TrackPointerModelIterator final
: public virtual TrackPointerIterator {
public:
TrackPointerModelIterator(
const TrackModel* pTrackModel,
const QModelIndexList& indexList)
: m_pTrackModel(pTrackModel),
m_modelIndexListIter(indexList) {
DEBUG_ASSERT(m_pTrackModel);
}
~TrackPointerModelIterator() override = default;

void reset() override {
m_modelIndexListIter.reset();
}

std::optional<int> estimateItemsRemaining() override {
return m_modelIndexListIter.estimateItemsRemaining();
}

std::optional<TrackPointer> nextItem() override;

private:
const TrackModel* const m_pTrackModel;
ListItemIterator<QModelIndex> m_modelIndexListIter;
};

} // namespace mixxx
120 changes: 120 additions & 0 deletions src/library/trackprocessing.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#include "library/trackprocessing.h"

#include <QThread>

#include "library/trackcollection.h"
#include "library/trackcollectionmanager.h"
#include "util/logger.h"

namespace mixxx {

namespace {

const Logger kLogger("ModalTrackBatchProcessor");

} // anonymous namespace

int ModalTrackBatchProcessor::processTracks(
const QString& progressLabelText,
TrackCollectionManager* pTrackCollectionManager,
TrackPointerIterator* pTrackPointerIterator) {
DEBUG_ASSERT(pTrackCollectionManager);
DEBUG_ASSERT(pTrackPointerIterator);
DEBUG_ASSERT(QThread::currentThread() ==
pTrackCollectionManager->thread());
int finishedTrackCount = 0;
// The total count is initialized with the remaining count
// before starting the iteration. If this value is unknown
// we use 0 as the default until an estimation is available
// (see update below).
int estimatedTotalCount =
pTrackPointerIterator->estimateItemsRemaining().value_or(0);
m_bAborted = false;
TaskMonitor taskMonitor(
progressLabelText,
m_minimumProgressDuration,
this);
taskMonitor.registerTask(this);
while (auto nextTrackPointer = pTrackPointerIterator->nextItem()) {
const auto pTrack = *nextTrackPointer;
VERIFY_OR_DEBUG_ASSERT(pTrack) {
kLogger.warning()
<< progressLabelText
<< "failed to load next track for processing";
continue;
}
if (m_bAborted) {
kLogger.info()
<< "Aborting"
<< progressLabelText
<< "after processing"
<< finishedTrackCount
<< "of"
<< estimatedTotalCount
<< "track(s)";
return finishedTrackCount;
}
switch (doProcessNextTrack(pTrack)) {
case ProcessNextTrackResult::AbortProcessing:
kLogger.info()
<< progressLabelText
<< "aborted while processing"
<< finishedTrackCount + 1
<< "of"
<< estimatedTotalCount
<< "track(s)";
return finishedTrackCount;
case ProcessNextTrackResult::ContinueProcessing:
break;
case ProcessNextTrackResult::SaveTrackAndContinueProcessing:
pTrackCollectionManager->saveTrack(pTrack);
break;
}
++finishedTrackCount;
if (finishedTrackCount > estimatedTotalCount) {
// Update the total count which cannot be less than the
// number of already finished items plus the estimated number
// of remaining items.
auto estimatedRemainingCount =
pTrackPointerIterator->estimateItemsRemaining().value_or(0);
estimatedTotalCount = finishedTrackCount + estimatedRemainingCount;
}
DEBUG_ASSERT(finishedTrackCount <= estimatedTotalCount);
taskMonitor.reportTaskProgress(
this,
kPercentageOfCompletionMin +
(kPercentageOfCompletionMax -
kPercentageOfCompletionMin) *
finishedTrackCount /
static_cast<PercentageOfCompletion>(
estimatedTotalCount));
}
return finishedTrackCount;
}

ModalTrackBatchOperationProcessor::ModalTrackBatchOperationProcessor(
const TrackPointerOperation* pTrackPointerOperation,
Mode mode,
Duration progressGracePeriod,
QObject* parent)
: ModalTrackBatchProcessor(progressGracePeriod, parent),
m_pTrackPointerOperation(pTrackPointerOperation),
m_mode(mode) {
DEBUG_ASSERT(m_pTrackPointerOperation);
}

ModalTrackBatchProcessor::ProcessNextTrackResult
ModalTrackBatchOperationProcessor::doProcessNextTrack(
const TrackPointer& pTrack) {
m_pTrackPointerOperation->apply(pTrack);
switch (m_mode) {
case Mode::Apply:
return ProcessNextTrackResult::ContinueProcessing;
case Mode::ApplyAndSave:
return ProcessNextTrackResult::SaveTrackAndContinueProcessing;
}
DEBUG_ASSERT(!"unreachable");
return ProcessNextTrackResult::AbortProcessing;
}

} // namespace mixxx
Loading

0 comments on commit 80424d4

Please sign in to comment.