~aleteoryx/muditaos

061fd401593af77a41ebdb623dfa5ad71f37900a — Maciej Gibowicz 1 year, 11 months ago 895d9d3
[BH-1893] Create SoundsRepository global class

Added a new class called "SoundsRepository" for
all applications that use music files.
26 files changed, 359 insertions(+), 289 deletions(-)

M module-db/Interface/MultimediaFilesRecord.cpp
M module-db/Tables/MultimediaFilesTable.cpp
M module-db/Tables/MultimediaFilesTable.hpp
M module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.cpp
M module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp
M products/BellHybrid/alarms/src/actions/PlayAudioActions.hpp
M products/BellHybrid/apps/application-bell-powernap/ApplicationBellPowerNap.cpp
M products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.cpp
M products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.hpp
M products/BellHybrid/apps/application-bell-relaxation/ApplicationBellRelaxation.cpp
M products/BellHybrid/apps/application-bell-relaxation/CMakeLists.txt
M products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsModel.cpp
M products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsModel.hpp
D products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsRepository.cpp
D products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsRepository.hpp
M products/BellHybrid/apps/application-bell-settings/ApplicationBellSettings.cpp
M products/BellHybrid/apps/application-bell-settings/presenter/BedtimeSettingsPresenter.cpp
M products/BellHybrid/apps/application-bell-settings/presenter/BedtimeSettingsPresenter.hpp
M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/AlarmSettingsPresenter.cpp
M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/AlarmSettingsPresenter.hpp
M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/PrewakeUpPresenter.cpp
M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/PrewakeUpPresenter.hpp
M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/SnoozePresenter.cpp
M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/SnoozePresenter.hpp
M products/BellHybrid/apps/common/include/common/SoundsRepository.hpp
M products/BellHybrid/apps/common/src/SoundsRepository.cpp
M module-db/Interface/MultimediaFilesRecord.cpp => module-db/Interface/MultimediaFilesRecord.cpp +3 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "MultimediaFilesRecord.hpp"


@@ 257,7 257,8 @@ namespace db::multimedia_files
    std::unique_ptr<db::multimedia_files::query::GetLimitedResult> MultimediaFilesRecordInterface::
        runQueryImplGetLimited(const std::shared_ptr<db::multimedia_files::query::GetLimitedByPaths> &query)
    {
        const auto records = database->files.getLimitOffsetByPaths(query->paths, query->offset, query->limit);
        const auto records =
            database->files.getLimitOffsetByPaths(query->paths, query->offset, query->limit, query->sorting);
        auto response      = std::make_unique<query::GetLimitedResult>(records, database->files.count(query->paths));
        response->setRequestQuery(query);


M module-db/Tables/MultimediaFilesTable.cpp => module-db/Tables/MultimediaFilesTable.cpp +20 -5
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "MultimediaFilesTable.hpp"


@@ 8,6 8,20 @@
#include <magic_enum.hpp>
#include <inttypes.h>

namespace
{
    std::string getSorting(db::multimedia_files::SortingBy sorting)
    {
        switch (sorting) {
        case db::multimedia_files::SortingBy::IdAscending:
            return "_id ASC";
        case db::multimedia_files::SortingBy::TitleAscending:
        default:
            return "title ASC";
        }
    }
} // namespace

namespace db::multimedia_files
{
    TableRow CreateTableRow(const QueryResult &result)


@@ 350,11 364,12 @@ namespace db::multimedia_files
    }

    auto MultimediaFilesTable::getLimitOffsetByPaths(const std::vector<std::string> &paths,
                                                     uint32_t offset,
                                                     uint32_t limit) -> std::vector<TableRow>
                                                     std::uint32_t offset,
                                                     std::uint32_t limit,
                                                     SortingBy sorting) -> std::vector<TableRow>
    {
        const std::string query = "SELECT * FROM files WHERE " + constructMatchPattern(paths) +
                                  " ORDER BY title ASC LIMIT " + std::to_string(limit) + " OFFSET " +
        const std::string query = "SELECT * FROM files WHERE " + constructMatchPattern(paths) + " ORDER BY " +
                                  getSorting(sorting) + " LIMIT " + std::to_string(limit) + " OFFSET " +
                                  std::to_string(offset) + ";";
        std::unique_ptr<QueryResult> retQuery = db->query(query.c_str());
        return retQueryUnpack(std::move(retQuery));

M module-db/Tables/MultimediaFilesTable.hpp => module-db/Tables/MultimediaFilesTable.hpp +11 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 70,6 70,12 @@ namespace db::multimedia_files
        channels
    };

    enum class SortingBy
    {
        TitleAscending,
        IdAscending
    };

    class MultimediaFilesTable : public Table<TableRow, TableFields>
    {
      public:


@@ 101,8 107,10 @@ namespace db::multimedia_files
        auto getLimitOffset(const Album &album, uint32_t offset, uint32_t limit) -> std::vector<TableRow>;
        auto count(const Album &album) -> uint32_t;

        auto getLimitOffsetByPaths(const std::vector<std::string> &paths, uint32_t offset, uint32_t limit)
            -> std::vector<TableRow>;
        auto getLimitOffsetByPaths(const std::vector<std::string> &paths,
                                   std::uint32_t offset,
                                   std::uint32_t limit,
                                   SortingBy sorting = SortingBy::TitleAscending) -> std::vector<TableRow>;
        auto count(const std::vector<std::string> &paths) -> uint32_t;
        TableRow getByPath(std::string path);


M module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.cpp => module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.cpp +6 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryMultimediaFilesGetLimited.hpp"


@@ 106,8 106,11 @@ namespace db::multimedia_files::query
        return std::string{"GetAlbumsLimitedResult"};
    }

    GetLimitedByPaths::GetLimitedByPaths(const std::vector<std::string> &paths, uint32_t offset, uint32_t limit)
        : Query(Query::Type::Read), paths{paths}, offset(offset), limit(limit)
    GetLimitedByPaths::GetLimitedByPaths(const std::vector<std::string> &paths,
                                         std::uint32_t offset,
                                         std::uint32_t limit,
                                         SortingBy sorting)
        : Query(Query::Type::Read), paths{paths}, offset{offset}, limit{limit}, sorting{sorting}
    {}

    auto GetLimitedByPaths::debugInfo() const -> std::string

M module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp => module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp +6 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 104,11 104,15 @@ namespace db::multimedia_files::query
    class GetLimitedByPaths : public Query
    {
      public:
        GetLimitedByPaths(const std::vector<std::string> &paths, uint32_t offset, uint32_t limit);
        GetLimitedByPaths(const std::vector<std::string> &paths,
                          std::uint32_t offset,
                          std::uint32_t limit,
                          SortingBy sorting = SortingBy::TitleAscending);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        const std::vector<std::string> paths;
        const uint32_t offset = 0;
        const uint32_t limit  = 0;
        const SortingBy sorting = SortingBy::TitleAscending;
    };
} // namespace db::multimedia_files::query

M products/BellHybrid/alarms/src/actions/PlayAudioActions.hpp => products/BellHybrid/alarms/src/actions/PlayAudioActions.hpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 33,7 33,7 @@ namespace alarms

        sys::Service &service;
        sys::TimerHandle timer;
        SoundsRepository soundsRepository;
        SimpleSoundsRepository soundsRepository;
        const std::string toneSetting;
        const std::optional<std::string> durationSetting;
        const audio::PlaybackType playbackType;

M products/BellHybrid/apps/application-bell-powernap/ApplicationBellPowerNap.cpp => products/BellHybrid/apps/application-bell-powernap/ApplicationBellPowerNap.cpp +1 -1
@@ 62,7 62,7 @@ namespace app
                auto timeModel       = std::make_unique<app::TimeModel>();
                auto frontlightModel = std::make_unique<powernap::PowerNapFrontlightModel>(this, powerNapAlarmDuration);
                auto soundsRepository =
                    std::make_unique<SoundsRepository>(paths::audio::proprietary() / paths::audio::alarm());
                    std::make_unique<SimpleSoundsRepository>(paths::audio::proprietary() / paths::audio::alarm());
                auto presenter = std::make_unique<powernap::PowerNapProgressPresenter>(app,
                                                                                       settings.get(),
                                                                                       std::move(soundsRepository),

M products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.cpp => products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.cpp +9 -8
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "PowerNapProgressPresenter.hpp"


@@ 18,13 18,14 @@ namespace

namespace app::powernap
{
    PowerNapProgressPresenter::PowerNapProgressPresenter(app::ApplicationCommon *app,
                                                         settings::Settings *settings,
                                                         std::unique_ptr<AbstractSoundsRepository> soundsRepository,
                                                         AbstractAudioModel &audioModel,
                                                         std::unique_ptr<AbstractTimeModel> timeModel,
                                                         std::unique_ptr<PowerNapFrontlightModel> frontlightModel,
                                                         const std::chrono::seconds &powerNapAlarmDuration)
    PowerNapProgressPresenter::PowerNapProgressPresenter(
        app::ApplicationCommon *app,
        settings::Settings *settings,
        std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository,
        AbstractAudioModel &audioModel,
        std::unique_ptr<AbstractTimeModel> timeModel,
        std::unique_ptr<PowerNapFrontlightModel> frontlightModel,
        const std::chrono::seconds &powerNapAlarmDuration)
        : app{app}, settings{settings}, soundsRepository{std::move(soundsRepository)},
          audioModel{audioModel}, timeModel{std::move(timeModel)}, frontlightModel{std::move(frontlightModel)},
          napAlarmTimer{sys::TimerFactory::createSingleShotTimer(

M products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.hpp => products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.hpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 66,7 66,7 @@ namespace app::powernap
    {
        app::ApplicationCommon *app{};
        settings::Settings *settings{};
        std::unique_ptr<AbstractSoundsRepository> soundsRepository;
        std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository;
        AbstractAudioModel &audioModel;
        std::unique_ptr<app::TimerWithCallbacks> timer;
        std::unique_ptr<AbstractTimeModel> timeModel;


@@ 79,7 79,7 @@ namespace app::powernap
      public:
        PowerNapProgressPresenter(app::ApplicationCommon *app,
                                  settings::Settings *settings,
                                  std::unique_ptr<AbstractSoundsRepository> soundsRepository,
                                  std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository,
                                  AbstractAudioModel &audioModel,
                                  std::unique_ptr<AbstractTimeModel> timeModel,
                                  std::unique_ptr<PowerNapFrontlightModel> frontlightModel,

M products/BellHybrid/apps/application-bell-relaxation/ApplicationBellRelaxation.cpp => products/BellHybrid/apps/application-bell-relaxation/ApplicationBellRelaxation.cpp +11 -4
@@ 22,6 22,7 @@
#include <Paths.hpp>
#include <apps-common/messages/AppMessage.hpp>
#include <apps-common/models/SongsRepository.hpp>
#include <common/SoundsRepository.hpp>
#include <common/models/TimeModel.hpp>
#include <common/models/BatteryModel.hpp>
#include <common/models/AudioModel.hpp>


@@ 85,13 86,19 @@ namespace app
    void ApplicationBellRelaxation::createUserInterface()
    {
        windowsFactory.attach(gui::name::window::main_window, [](ApplicationCommon *app, const std::string &name) {
            const auto paths = std::map<relaxation::MusicType, std::string>{
            const auto pathsTypeMap = std::map<relaxation::MusicType, std::string>{
                {relaxation::MusicType::Relaxation, paths::audio::proprietary() / paths::audio::relaxation()},
                {relaxation::MusicType::ColorsOfNoise, paths::audio::proprietary() / paths::audio::colorOfNoises()},
                {relaxation::MusicType::User, paths::audio::userApp() / paths::audio::relaxation()}};

            auto soundsRepository = std::make_unique<relaxation::RelaxationSongsRepository>(app, paths);
            auto songsModel = std::make_unique<relaxation::RelaxationSongsModel>(app, std::move(soundsRepository));
            const auto pathsSortingVector = std::vector<SoundsRepository::PathSorting>{
                {paths::audio::proprietary() / paths::audio::relaxation(), SoundsRepository::SortingBy::TitleAscending},
                {paths::audio::proprietary() / paths::audio::colorOfNoises(),
                 SoundsRepository::SortingBy::TitleAscending},
                {paths::audio::userApp() / paths::audio::relaxation(), SoundsRepository::SortingBy::TitleAscending}};

            auto soundsRepository = std::make_unique<SoundsRepository>(app, pathsSortingVector);
            auto songsModel =
                std::make_unique<relaxation::RelaxationSongsModel>(app, std::move(soundsRepository), pathsTypeMap);
            auto presenter  = std::make_unique<relaxation::RelaxationMainWindowPresenter>(std::move(songsModel));
            return std::make_unique<gui::RelaxationMainWindow>(app, std::move(presenter));
        });

M products/BellHybrid/apps/application-bell-relaxation/CMakeLists.txt => products/BellHybrid/apps/application-bell-relaxation/CMakeLists.txt +0 -2
@@ 62,8 62,6 @@ target_sources(application-bell-relaxation
        windows/RelaxationErrorWindow.hpp
        model/RelaxationSongsModel.hpp
        model/RelaxationSongsModel.cpp
        model/RelaxationSongsRepository.hpp
        model/RelaxationSongsRepository.cpp

        PUBLIC
        include/application-bell-relaxation/ApplicationBellRelaxation.hpp

M products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsModel.cpp => products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsModel.cpp +24 -27
@@ 1,31 1,20 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RelaxationSongsModel.hpp"
#include "common/options/OptionBellMenu.hpp"
#include "widgets/RelaxationOption.hpp"

namespace
{
    app::relaxation::MusicType getTypeFromPath(const std::string &path,
                                               const std::map<app::relaxation::MusicType, std::string> &pathPrefixes)
    {
        for (const auto &[type, pathPrefix] : pathPrefixes) {
            if (path.find(pathPrefix) != std::string::npos) {
                return type;
            }
        }
        return app::relaxation::MusicType::Relaxation;
    }
} // namespace
namespace app::relaxation
{
    RelaxationSongsProvider::RelaxationSongsProvider(ApplicationCommon *app) : DatabaseModel(app)
    {}

    RelaxationSongsModel::RelaxationSongsModel(
        ApplicationCommon *application, std::unique_ptr<app::relaxation::RelaxationSongsRepository> soundsRepository)
        : RelaxationSongsProvider(application), application(application), songsRepository{std::move(soundsRepository)}
    RelaxationSongsModel::RelaxationSongsModel(ApplicationCommon *application,
                                               std::unique_ptr<AbstractSoundsRepository> soundsRepository,
                                               const std::map<MusicType, std::string> &pathPrefixes)
        : RelaxationSongsProvider(application),
          application(application), songsRepository{std::move(soundsRepository)}, pathPrefixes{pathPrefixes}
    {}

    void RelaxationSongsModel::createData(OnActivateCallback callback)


@@ 42,7 31,7 @@ namespace app::relaxation

    unsigned int RelaxationSongsModel::requestRecordsCount()
    {
        return songsRepository->getRecordsCount();
        return songsRepository->getFilesCount();
    }
    unsigned int RelaxationSongsModel::getMinimalItemSpaceRequired() const
    {


@@ 64,15 53,14 @@ namespace app::relaxation
        if (!sound) {
            return nullptr;
        }
        auto item =
            gui::option::RelaxationOption{getTypeFromPath(sound->fileInfo.path, songsRepository->getPathPrefixes()),
                                          sound->tags.title,
                                          [=]([[maybe_unused]] gui::Item &item) {
                                              activateCallback(*sound);
                                              return true;
                                          },
                                          [=]([[maybe_unused]] gui::Item &item) { return true; },
                                          nullptr};
        auto item = gui::option::RelaxationOption{getTypeFromPath(sound->fileInfo.path),
                                                  sound->tags.title,
                                                  [=]([[maybe_unused]] gui::Item &item) {
                                                      activateCallback(*sound);
                                                      return true;
                                                  },
                                                  []([[maybe_unused]] gui::Item &item) { return true; },
                                                  nullptr};

        return item.build();
    }


@@ 100,4 88,13 @@ namespace app::relaxation
        list->onProviderDataUpdate();
        return true;
    }
    MusicType RelaxationSongsModel::getTypeFromPath(const std::string &path)
    {
        for (const auto &[type, pathPrefix] : pathPrefixes) {
            if (path.find(pathPrefix) != std::string::npos) {
                return type;
            }
        }
        return MusicType::Relaxation;
    }
} // namespace app::relaxation

M products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsModel.hpp => products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsModel.hpp +9 -5
@@ 1,9 1,10 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "RelaxationSongsRepository.hpp"
#include "data/RelaxationCommon.hpp"
#include <common/SoundsRepository.hpp>
#include <apps-common/models/SongsModelInterface.hpp>
#include <gui/widgets/ListItemProvider.hpp>



@@ 25,18 26,21 @@ namespace app::relaxation
    {
      private:
        ApplicationCommon *application;
        std::unique_ptr<RelaxationSongsRepository> songsRepository;
        std::unique_ptr<AbstractSoundsRepository> songsRepository;
        std::map<MusicType, std::string> pathPrefixes;
        OnActivateCallback activateCallback{nullptr};

        bool onMusicListRetrieved(const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                                  unsigned int repoRecordsCount);
        [[nodiscard]] bool updateRecords(std::vector<db::multimedia_files::MultimediaFilesRecord> records) override;
        MusicType getTypeFromPath(const std::string &path);

      public:
        virtual ~RelaxationSongsModel() = default;

        explicit RelaxationSongsModel(ApplicationCommon *application,
                                      std::unique_ptr<RelaxationSongsRepository> soundsRepository);
        RelaxationSongsModel(ApplicationCommon *application,
                             std::unique_ptr<AbstractSoundsRepository> soundsRepository,
                             const std::map<MusicType, std::string> &pathPrefixes);

        unsigned int requestRecordsCount() override;


D products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsRepository.cpp => products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsRepository.cpp +0 -145
@@ 1,145 0,0 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "RelaxationSongsRepository.hpp"
#include <module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp>
#include <module-db/queries/multimedia_files/QueryMultimediaFilesCount.hpp>
#include <AsyncTask.hpp>
#include <vector>

namespace
{
    std::uint32_t calculateOffset(std::uint32_t offset, std::uint32_t filesCount)
    {
        if (offset >= filesCount) {
            return offset - filesCount;
        }
        return offset;
    }
    std::pair<std::uint32_t, std::uint32_t> calculateNewOffsetAndLimit(std::uint32_t offset,
                                                                       std::uint32_t limit,
                                                                       std::uint32_t loadedFiles)
    {
        const auto newLimit  = limit - loadedFiles;
        const auto newOffset = offset + loadedFiles;
        return {newOffset, newLimit};
    }
} // namespace
namespace app::relaxation
{

    RelaxationSongsRepository::RelaxationSongsRepository(ApplicationCommon *application,
                                                         const std::map<MusicType, std::string> &pathPrefixes)
        : app::AsyncCallbackReceiver{application}, application{application}, pathPrefixes{pathPrefixes}
    {}

    void RelaxationSongsRepository::init()
    {
        updateFilesCount();
    }

    void RelaxationSongsRepository::updateFilesCount()
    {
        for (const auto &[musicType, musicPath] : pathPrefixes) {
            updateFilesCount(musicType, musicPath);
        }
    }

    void RelaxationSongsRepository::updateFilesCount(const MusicType &type, const std::string &path)
    {
        auto query = std::make_unique<db::multimedia_files::query::GetCountForPath>(path);
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);
        task->setCallback([this, &type](auto response) {
            auto result = dynamic_cast<db::multimedia_files::query::GetCountResult *>(response);
            if (result == nullptr) {
                return false;
            }
            return updateCountCallback(type, result->getCount());
        });
        task->execute(application, this);
    }

    bool RelaxationSongsRepository::updateCountCallback(const MusicType &type, std::uint32_t count)
    {
        filesPerType[type] = count;
        return true;
    }

    void RelaxationSongsRepository::getMusicFiles(
        const MusicType &musicType,
        std::uint32_t offset,
        std::uint32_t limit,
        const RelaxationSongsRepository::OnGetMusicFilesListCallback &viewUpdateCallback)
    {
        auto taskCallback = [this, viewUpdateCallback, offset](auto response) {
            auto result = dynamic_cast<db::multimedia_files::query::GetLimitedResult *>(response);
            if (result == nullptr) {
                return false;
            }
            for (auto &record : result->getResult()) {
                musicFilesViewCache.records.push_back(record);
            }
            musicFilesViewCache.recordsOffset = offset;
            musicFilesViewCache.recordsCount  = getRecordsCount();

            if (viewUpdateCallback) {
                viewUpdateCallback(musicFilesViewCache.records, musicFilesViewCache.recordsCount);
            }
            return true;
        };

        auto query = std::make_unique<db::multimedia_files::query::GetLimitedByPaths>(
            std::vector<std::string>{pathPrefixes[musicType]}, offset, limit);
        auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);
        task->setCallback(taskCallback);
        task->execute(application, this);
    }

    std::uint32_t RelaxationSongsRepository::getRecordsCount()
    {
        return std::accumulate(filesPerType.begin(),
                               filesPerType.end(),
                               0,
                               [](const std::uint32_t sum, const auto &record) { return sum + record.second; });
    }

    void RelaxationSongsRepository::getMusicFiles(
        std::uint32_t offset,
        std::uint32_t limit,
        const RelaxationSongsRepository::OnGetMusicFilesListCallback &callback)
    {
        const auto skipViewUpdate = []([[maybe_unused]] const auto &sound, [[maybe_unused]] auto count) {
            return true;
        };
        musicFilesViewCache.records.clear();
        std::uint32_t totalFilesCount{0};
        const auto lastFileIndex = offset + limit;
        if (filesPerType.empty()) {
            getMusicFiles(MusicType::Relaxation, offset, limit, callback);
        }

        for (const auto &[type, filesCount] : filesPerType) {
            if (filesCount == 0) {
                continue;
            }
            totalFilesCount += filesCount;
            if (const auto newOffset = calculateOffset(offset, filesCount); newOffset != offset) {
                offset = newOffset;
            }
            else if (lastFileIndex <= totalFilesCount) {
                getMusicFiles(type, offset, limit, callback);
                break;
            }
            else {
                const auto filesToLoad = filesCount - offset;
                getMusicFiles(type, offset, filesToLoad, skipViewUpdate);
                std::tie(offset, limit) = calculateNewOffsetAndLimit(offset, limit, filesToLoad);
                offset                  = calculateOffset(offset, filesCount);
            }
        }
    }
    std::map<MusicType, std::string> &RelaxationSongsRepository::getPathPrefixes()
    {
        return pathPrefixes;
    }

} // namespace app::relaxation

D products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsRepository.hpp => products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsRepository.hpp +0 -41
@@ 1,41 0,0 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "data/RelaxationCommon.hpp"
#include <apps-common/models/SongsRepository.hpp>
#include <memory>
namespace app::relaxation
{

    class RelaxationSongsRepository : public app::AsyncCallbackReceiver
    {
      public:
        using OnGetMusicFilesListCallback =
            std::function<bool(const std::vector<db::multimedia_files::MultimediaFilesRecord> &, std::uint32_t)>;

        explicit RelaxationSongsRepository(ApplicationCommon *application,
                                           const std::map<MusicType, std::string> &pathPrefixes);
        void init();
        void getMusicFiles(std::uint32_t offset,
                           std::uint32_t limit,
                           const OnGetMusicFilesListCallback &viewUpdateCallback);

        std::uint32_t getRecordsCount();
        void updateFilesCount();
        std::map<MusicType, std::string> &getPathPrefixes();

      private:
        ApplicationCommon *application;
        std::map<MusicType, std::string> pathPrefixes;
        std::map<MusicType, std::uint32_t> filesPerType;
        music::FilesCache musicFilesViewCache;
        void updateFilesCount(const MusicType &type, const std::string &path);
        bool updateCountCallback(const MusicType &type, std::uint32_t count);
        void getMusicFiles(const MusicType &musicType,
                           std::uint32_t offset,
                           std::uint32_t limit,
                           const OnGetMusicFilesListCallback &viewUpdateCallback);
    };
} // namespace app::relaxation

M products/BellHybrid/apps/application-bell-settings/ApplicationBellSettings.cpp => products/BellHybrid/apps/application-bell-settings/ApplicationBellSettings.cpp +7 -5
@@ 127,8 127,8 @@ namespace app
        windowsFactory.attach(
            gui::window::name::bellSettingsBedtimeTone, [this](ApplicationCommon *app, const std::string &name) {
                auto bedtimeModel = std::make_shared<bell_bedtime::BedtimeModel>(app, *audioModel);
                auto soundsRepository =
                    std::make_unique<SoundsRepository>(paths::audio::proprietary() / paths::audio::bedtimeReminder());
                auto soundsRepository = std::make_unique<SimpleSoundsRepository>(paths::audio::proprietary() /
                                                                                 paths::audio::bedtimeReminder());
                auto provider = std::make_shared<bell_settings::BedtimeSettingsListItemProvider>(
                    bedtimeModel, soundsRepository->getSongTitles());
                auto presenter = std::make_unique<bell_settings::SettingsPresenter>(


@@ 174,7 174,8 @@ namespace app
                                                                            std::move(prewakeUpChimeVolumeModel),
                                                                            std::move(prewakeUpLightDurationModel),
                                                                            std::move(prewakeUpFrontlightModel));
                auto soundsRepository = std::make_unique<SoundsRepository>(paths::audio::proprietary() / paths::audio::preWakeup());
                auto soundsRepository =
                    std::make_unique<SimpleSoundsRepository>(paths::audio::proprietary() / paths::audio::preWakeup());
                auto provider         = std::make_unique<bell_settings::PrewakeUpListItemProvider>(
                    *prewakeUpSettingsModel, soundsRepository->getSongTitles());
                auto frontlightModel = std::make_unique<bell_settings::FrontlightModel>(app);


@@ 206,7 207,7 @@ namespace app
                                                                         std::move(snoozeChimeToneModel),
                                                                         std::move(snoozeChimeVolumeModel));
                auto soundsRepository =
                    std::make_unique<SoundsRepository>(paths::audio::proprietary() / paths::audio::snooze());
                    std::make_unique<SimpleSoundsRepository>(paths::audio::proprietary() / paths::audio::snooze());
                auto provider = std::make_shared<bell_settings::SnoozeListItemProvider>(
                    *snoozeSettingsModel, soundsRepository->getSongTitles());
                auto presenter = std::make_unique<bell_settings::SnoozePresenter>(


@@ 226,7 227,8 @@ namespace app
                                                                        std::move(alarmFadeOnOffModel),
                                                                        std::move(alarmLightOnOffModel),
                                                                        std::move(alarmFrontlightModel));
                auto soundsRepository = std::make_unique<SoundsRepository>(paths::audio::proprietary() / paths::audio::alarm());
                auto soundsRepository =
                    std::make_unique<SimpleSoundsRepository>(paths::audio::proprietary() / paths::audio::alarm());
                auto frontlightModel  = std::make_unique<bell_settings::FrontlightModel>(app);
                auto provider         = std::make_unique<bell_settings::AlarmSettingsListItemProvider>(
                    *alarmSettingsModel, soundsRepository->getSongTitles());

M products/BellHybrid/apps/application-bell-settings/presenter/BedtimeSettingsPresenter.cpp => products/BellHybrid/apps/application-bell-settings/presenter/BedtimeSettingsPresenter.cpp +1 -1
@@ 8,7 8,7 @@ namespace app::bell_settings
    SettingsPresenter::SettingsPresenter(std::shared_ptr<BedtimeSettingsListItemProvider> provider,
                                         std::shared_ptr<AbstractBedtimeModel> model,
                                         AbstractAudioModel &audioModel,
                                         std::unique_ptr<AbstractSoundsRepository> soundsRepository)
                                         std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository)
        : provider(std::move(provider)),
          model(std::move(model)), audioModel{audioModel}, soundsRepository{std::move(soundsRepository)}
    {

M products/BellHybrid/apps/application-bell-settings/presenter/BedtimeSettingsPresenter.hpp => products/BellHybrid/apps/application-bell-settings/presenter/BedtimeSettingsPresenter.hpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 45,7 45,7 @@ namespace app::bell_settings
        SettingsPresenter(std::shared_ptr<BedtimeSettingsListItemProvider> provider,
                          std::shared_ptr<AbstractBedtimeModel> model,
                          AbstractAudioModel &audioModel,
                          std::unique_ptr<AbstractSoundsRepository> soundsRepository);
                          std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository);

        auto getPagesProvider() const -> std::shared_ptr<gui::ListItemProvider> override;
        auto saveData() -> void override;


@@ 59,7 59,7 @@ namespace app::bell_settings
        std::shared_ptr<BedtimeSettingsListItemProvider> provider;
        std::shared_ptr<AbstractBedtimeModel> model;
        AbstractAudioModel &audioModel;
        std::unique_ptr<AbstractSoundsRepository> soundsRepository;
        std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository;
        UTF8 currentSoundPath;
    };
} // namespace app::bell_settings

M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/AlarmSettingsPresenter.cpp => products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/AlarmSettingsPresenter.cpp +1 -1
@@ 9,7 9,7 @@ namespace app::bell_settings
    AlarmSettingsPresenter::AlarmSettingsPresenter(std::unique_ptr<AlarmSettingsListItemProvider> &&provider,
                                                   std::unique_ptr<AbstractAlarmSettingsModel> &&settingsModel,
                                                   AbstractAudioModel &audioModel,
                                                   std::unique_ptr<AbstractSoundsRepository> &&soundsRepository,
                                                   std::unique_ptr<AbstractSimpleSoundsRepository> &&soundsRepository,
                                                   std::unique_ptr<AbstractFrontlightModel> &&frontlight)
        : provider{std::move(provider)}, settingsModel{std::move(settingsModel)}, audioModel{audioModel},
          soundsRepository{std::move(soundsRepository)}, frontlight{std::move(frontlight)}

M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/AlarmSettingsPresenter.hpp => products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/AlarmSettingsPresenter.hpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 49,7 49,7 @@ namespace app::bell_settings
        AlarmSettingsPresenter(std::unique_ptr<AlarmSettingsListItemProvider> &&provider,
                               std::unique_ptr<AbstractAlarmSettingsModel> &&settingsModel,
                               AbstractAudioModel &audioModel,
                               std::unique_ptr<AbstractSoundsRepository> &&soundsRepository,
                               std::unique_ptr<AbstractSimpleSoundsRepository> &&soundsRepository,
                               std::unique_ptr<AbstractFrontlightModel> &&frontlight);

        auto getPagesProvider() const -> std::shared_ptr<gui::ListItemProvider> override;


@@ 65,7 65,7 @@ namespace app::bell_settings
        std::shared_ptr<AlarmSettingsListItemProvider> provider;
        std::unique_ptr<AbstractAlarmSettingsModel> settingsModel;
        AbstractAudioModel &audioModel;
        std::unique_ptr<AbstractSoundsRepository> soundsRepository;
        std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository;
        std::unique_ptr<AbstractFrontlightModel> frontlight;
    };
} // namespace app::bell_settings

M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/PrewakeUpPresenter.cpp => products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/PrewakeUpPresenter.cpp +6 -5
@@ 6,11 6,12 @@

namespace app::bell_settings
{
    PrewakeUpWindowPresenter::PrewakeUpWindowPresenter(std::unique_ptr<PrewakeUpListItemProvider> &&provider,
                                                       std::unique_ptr<AbstractPrewakeUpSettingsModel> &&model,
                                                       AbstractAudioModel &audioModel,
                                                       std::unique_ptr<AbstractSoundsRepository> &&soundsRepository,
                                                       std::unique_ptr<AbstractFrontlightModel> &&frontlight)
    PrewakeUpWindowPresenter::PrewakeUpWindowPresenter(
        std::unique_ptr<PrewakeUpListItemProvider> &&provider,
        std::unique_ptr<AbstractPrewakeUpSettingsModel> &&model,
        AbstractAudioModel &audioModel,
        std::unique_ptr<AbstractSimpleSoundsRepository> &&soundsRepository,
        std::unique_ptr<AbstractFrontlightModel> &&frontlight)
        : provider{std::move(provider)}, model{std::move(model)}, audioModel{audioModel},
          soundsRepository{std::move(soundsRepository)}, frontlight{std::move(frontlight)}
    {

M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/PrewakeUpPresenter.hpp => products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/PrewakeUpPresenter.hpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 48,7 48,7 @@ namespace app::bell_settings
        PrewakeUpWindowPresenter(std::unique_ptr<PrewakeUpListItemProvider> &&provider,
                                 std::unique_ptr<AbstractPrewakeUpSettingsModel> &&model,
                                 AbstractAudioModel &audioModel,
                                 std::unique_ptr<AbstractSoundsRepository> &&soundsRepository,
                                 std::unique_ptr<AbstractSimpleSoundsRepository> &&soundsRepository,
                                 std::unique_ptr<AbstractFrontlightModel> &&frontlight);

        auto getPagesProvider() const -> std::shared_ptr<gui::ListItemProvider> override;


@@ 63,7 63,7 @@ namespace app::bell_settings
        std::shared_ptr<PrewakeUpListItemProvider> provider;
        std::unique_ptr<AbstractPrewakeUpSettingsModel> model;
        AbstractAudioModel &audioModel;
        std::unique_ptr<AbstractSoundsRepository> soundsRepository;
        std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository;
        std::unique_ptr<AbstractFrontlightModel> frontlight;
        UTF8 currentSoundPath;
    };

M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/SnoozePresenter.cpp => products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/SnoozePresenter.cpp +1 -1
@@ 9,7 9,7 @@ namespace app::bell_settings
    SnoozePresenter::SnoozePresenter(std::shared_ptr<SnoozeListItemProvider> provider,
                                     std::unique_ptr<AbstractSnoozeSettingsModel> snoozeSettingsModel,
                                     AbstractAudioModel &audioModel,
                                     std::unique_ptr<AbstractSoundsRepository> soundsRepository)
                                     std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository)
        : provider{provider}, snoozeSettingsModel{std::move(snoozeSettingsModel)}, audioModel{audioModel},
          soundsRepository{std::move(soundsRepository)}
    {

M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/SnoozePresenter.hpp => products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/SnoozePresenter.hpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 42,7 42,7 @@ namespace app::bell_settings
        SnoozePresenter(std::shared_ptr<SnoozeListItemProvider> provider,
                        std::unique_ptr<AbstractSnoozeSettingsModel> snoozeSettingsModel,
                        AbstractAudioModel &audioModel,
                        std::unique_ptr<AbstractSoundsRepository> soundsRepository);
                        std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository);
        auto getPagesProvider() const -> std::shared_ptr<gui::ListItemProvider> override;
        void saveData() override;
        void loadData() override;


@@ 55,7 55,7 @@ namespace app::bell_settings
        std::shared_ptr<SnoozeListItemProvider> provider;
        std::unique_ptr<AbstractSnoozeSettingsModel> snoozeSettingsModel;
        AbstractAudioModel &audioModel;
        std::unique_ptr<AbstractSoundsRepository> soundsRepository;
        std::unique_ptr<AbstractSimpleSoundsRepository> soundsRepository;
        UTF8 currentSoundPath;
    };
} // namespace app::bell_settings

M products/BellHybrid/apps/common/include/common/SoundsRepository.hpp => products/BellHybrid/apps/common/include/common/SoundsRepository.hpp +69 -5
@@ 1,28 1,29 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <module-audio/tags_fetcher/TagsFetcher.hpp>
#include <apps-common/models/SongsRepository.hpp>
#include <utf8/UTF8.hpp>

#include <filesystem>
#include <optional>
#include <vector>

class AbstractSoundsRepository
class AbstractSimpleSoundsRepository
{
  public:
    virtual ~AbstractSoundsRepository()                                               = default;
    virtual ~AbstractSimpleSoundsRepository()                                         = default;
    virtual std::optional<std::filesystem::path> titleToPath(const UTF8 &title) const = 0;
    virtual std::optional<UTF8> pathToTitle(std::filesystem::path) const              = 0;
    virtual std::vector<UTF8> getSongTitles()                                         = 0;
};

class SoundsRepository : public AbstractSoundsRepository
class SimpleSoundsRepository : public AbstractSimpleSoundsRepository
{
  public:
    explicit SoundsRepository(std::filesystem::path dirToScan);
    explicit SimpleSoundsRepository(std::filesystem::path dirToScan);

    std::optional<std::filesystem::path> titleToPath(const UTF8 &title) const override;
    std::optional<UTF8> pathToTitle(std::filesystem::path path) const override;


@@ 33,3 34,66 @@ class SoundsRepository : public AbstractSoundsRepository

    std::vector<tags::fetcher::Tags> samples;
};

class AbstractSoundsRepository
{
  public:
    using OnGetMusicFilesListCallback =
        std::function<bool(const std::vector<db::multimedia_files::MultimediaFilesRecord> &, std::uint32_t)>;

    virtual ~AbstractSoundsRepository() noexcept = default;

    virtual void init()                                                     = 0;
    virtual void getMusicFiles(std::uint32_t offset,
                               std::uint32_t limit,
                               const OnGetMusicFilesListCallback &callback) = 0;

    virtual std::uint32_t getFilesCount() = 0;
    virtual void updateFilesCount()       = 0;
};

class SoundsRepository : public AbstractSoundsRepository, public app::AsyncCallbackReceiver
{
  public:
    enum class SortingBy
    {
        IdAscending,
        TitleAscending
    };
    struct PathSorting
    {
        std::string prefix;
        SortingBy sorting;
    };

    SoundsRepository(app::ApplicationCommon *application, const PathSorting &path);
    SoundsRepository(app::ApplicationCommon *application, const std::vector<PathSorting> &pathsVector);
    void init() override;
    void getMusicFiles(std::uint32_t offset,
                       std::uint32_t limit,
                       const OnGetMusicFilesListCallback &viewUpdateCallback) override;

    std::uint32_t getFilesCount() override;
    void updateFilesCount() override;

  private:
    struct PathDetails
    {
        std::uint32_t id;
        std::uint32_t count;
        SortingBy sorting;
        std::string prefix;
    };

    app::ApplicationCommon *application;
    std::vector<PathDetails> paths;
    app::music::FilesCache musicFilesViewCache;

    void updateFilesCount(const std::uint32_t id, const std::string &path);
    bool updateCountCallback(const std::uint32_t id, const std::uint32_t count);
    void getMusicFiles(const std::string &path,
                       const SortingBy sorting,
                       const std::uint32_t offset,
                       const std::uint32_t limit,
                       const OnGetMusicFilesListCallback &viewUpdateCallback);
};

M products/BellHybrid/apps/common/src/SoundsRepository.cpp => products/BellHybrid/apps/common/src/SoundsRepository.cpp +157 -6
@@ 1,8 1,11 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SoundsRepository.hpp"

#include <module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp>
#include <module-db/queries/multimedia_files/QueryMultimediaFilesCount.hpp>
#include <AsyncTask.hpp>
#include <algorithm>

namespace fs = std::filesystem;


@@ 10,9 13,35 @@ namespace fs = std::filesystem;
namespace
{
    constexpr auto allowedExtensions = {".wav", ".mp3", ".flac"};

    constexpr std::uint32_t calculateOffset(std::uint32_t offset, std::uint32_t filesCount)
    {
        if (offset >= filesCount) {
            return offset - filesCount;
        }
        return offset;
    }
    constexpr std::pair<std::uint32_t, std::uint32_t> calculateNewOffsetAndLimit(std::uint32_t offset,
                                                                                 std::uint32_t limit,
                                                                                 std::uint32_t loadedFiles)
    {
        const auto newLimit  = limit - loadedFiles;
        const auto newOffset = offset + loadedFiles;
        return {newOffset, newLimit};
    }
    db::multimedia_files::SortingBy transformSorting(SoundsRepository::SortingBy sorting)
    {
        switch (sorting) {
        case SoundsRepository::SortingBy::IdAscending:
            return db::multimedia_files::SortingBy::IdAscending;
        case SoundsRepository::SortingBy::TitleAscending:
        default:
            return db::multimedia_files::SortingBy::TitleAscending;
        }
    }
} // namespace

SoundsRepository::SoundsRepository(std::filesystem::path dirToScan)
SimpleSoundsRepository::SimpleSoundsRepository(std::filesystem::path dirToScan)
{
    for (auto const &entry : std::filesystem::directory_iterator(dirToScan)) {
        processEntry(entry);


@@ 21,7 50,7 @@ SoundsRepository::SoundsRepository(std::filesystem::path dirToScan)
    /// Sort entries by track ID
    std::sort(samples.begin(), samples.end(), [](const auto &a, const auto &b) { return a.track < b.track; });
}
std::optional<std::filesystem::path> SoundsRepository::titleToPath(const UTF8 &title) const
std::optional<std::filesystem::path> SimpleSoundsRepository::titleToPath(const UTF8 &title) const
{
    const auto res =
        std::find_if(samples.begin(), samples.end(), [title](const auto &e) { return e.title == title.c_str(); });


@@ 30,7 59,7 @@ std::optional<std::filesystem::path> SoundsRepository::titleToPath(const UTF8 &t
    }
    return {};
}
std::optional<UTF8> SoundsRepository::pathToTitle(std::filesystem::path path) const
std::optional<UTF8> SimpleSoundsRepository::pathToTitle(std::filesystem::path path) const
{
    const auto res =
        std::find_if(samples.begin(), samples.end(), [&path](const auto &e) { return e.filePath == path; });


@@ 40,14 69,14 @@ std::optional<UTF8> SoundsRepository::pathToTitle(std::filesystem::path path) co
    return {};
}

std::vector<UTF8> SoundsRepository::getSongTitles()
std::vector<UTF8> SimpleSoundsRepository::getSongTitles()
{
    std::vector<UTF8> ret;
    std::transform(samples.begin(), samples.end(), std::back_inserter(ret), [](const auto &e) { return e.title; });
    return ret;
}

void SoundsRepository::processEntry(const std::filesystem::recursive_directory_iterator::value_type &entry)
void SimpleSoundsRepository::processEntry(const std::filesystem::recursive_directory_iterator::value_type &entry)
{
    if (fs::is_regular_file(entry)) {
        for (const auto &ext : allowedExtensions) {


@@ 57,3 86,125 @@ void SoundsRepository::processEntry(const std::filesystem::recursive_directory_i
        }
    }
}

SoundsRepository::SoundsRepository(app::ApplicationCommon *application, const PathSorting &path)
    : app::AsyncCallbackReceiver{application}, application{application}
{
    paths.push_back({0, 0, path.sorting, path.prefix});
}

SoundsRepository::SoundsRepository(app::ApplicationCommon *application, const std::vector<PathSorting> &pathsVector)
    : app::AsyncCallbackReceiver{application}, application{application}
{
    std::uint32_t id = 0;
    for (const auto &[path, sorting] : pathsVector) {
        paths.push_back({id, 0, sorting, path});
        id++;
    }
}

void SoundsRepository::init()
{
    updateFilesCount();
}

void SoundsRepository::getMusicFiles(std::uint32_t offset,
                                     std::uint32_t limit,
                                     const OnGetMusicFilesListCallback &viewUpdateCallback)
{
    const auto skipViewUpdate = []([[maybe_unused]] const auto &sound, [[maybe_unused]] auto count) { return true; };
    musicFilesViewCache.records.clear();
    std::uint32_t totalFilesCount{0};
    const auto lastFileIndex = offset + limit;
    if (getFilesCount() == 0 && !paths.empty()) {
        getMusicFiles(paths[0].prefix, paths[0].sorting, offset, limit, viewUpdateCallback);
    }

    for (const auto &[id, filesCount, sorting, path] : paths) {
        if (filesCount == 0) {
            continue;
        }
        totalFilesCount += filesCount;
        if (const auto newOffset = calculateOffset(offset, filesCount); newOffset != offset) {
            offset = newOffset;
        }
        else if (lastFileIndex <= totalFilesCount) {
            getMusicFiles(path, sorting, offset, limit, viewUpdateCallback);
            break;
        }
        else {
            const auto filesToLoad = filesCount - offset;
            getMusicFiles(path, sorting, offset, filesToLoad, skipViewUpdate);
            std::tie(offset, limit) = calculateNewOffsetAndLimit(offset, limit, filesToLoad);
            offset                  = calculateOffset(offset, filesCount);
        }
    }
}

void SoundsRepository::getMusicFiles(const std::string &path,
                                     const SortingBy sorting,
                                     const std::uint32_t offset,
                                     const std::uint32_t limit,
                                     const OnGetMusicFilesListCallback &viewUpdateCallback)
{
    auto taskCallback = [this, viewUpdateCallback, offset](auto response) {
        auto result = dynamic_cast<db::multimedia_files::query::GetLimitedResult *>(response);
        if (result == nullptr) {
            return false;
        }
        for (auto &record : result->getResult()) {
            musicFilesViewCache.records.push_back(record);
        }
        musicFilesViewCache.recordsOffset = offset;
        musicFilesViewCache.recordsCount  = getFilesCount();

        if (viewUpdateCallback) {
            viewUpdateCallback(musicFilesViewCache.records, musicFilesViewCache.recordsCount);
        }
        return true;
    };

    const auto sortBy = transformSorting(sorting);
    auto query        = std::make_unique<db::multimedia_files::query::GetLimitedByPaths>(
        std::vector<std::string>{path}, offset, limit, sortBy);
    auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);
    task->setCallback(taskCallback);
    task->execute(application, this);
}

std::uint32_t SoundsRepository::getFilesCount()
{
    return std::accumulate(
        paths.begin(), paths.end(), 0, [](const std::uint32_t sum, const auto &record) { return sum + record.count; });
}

void SoundsRepository::updateFilesCount()
{
    for (const auto &path : paths) {
        updateFilesCount(path.id, path.prefix);
    }
}

void SoundsRepository::updateFilesCount(const std::uint32_t id, const std::string &path)
{
    auto query = std::make_unique<db::multimedia_files::query::GetCountForPath>(path);
    auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);
    task->setCallback([this, id](auto response) {
        auto result = dynamic_cast<db::multimedia_files::query::GetCountResult *>(response);
        if (result == nullptr) {
            return false;
        }
        return updateCountCallback(id, result->getCount());
    });
    task->execute(application, this);
}

bool SoundsRepository::updateCountCallback(const std::uint32_t id, const std::uint32_t count)
{
    const auto it = std::find_if(paths.begin(), paths.end(), [id](auto path) { return path.id == id; });
    if (it != paths.end()) {
        (*it).count = count;
        return true;
    }
    return false;
}