~aleteoryx/muditaos

13af1f4cde458a9124bd315732be1e689a311bf0 — Lucjan Bryndza 4 years ago 9c0dde9
[EGD-7334] Add initial startup indexer

Add initial startup indexer which index all music
files when the phone is run first time.

Signed-off-by: Lucjan Bryndza <lucjan.bryndza@mudita.com>
M module-services/service-fileindexer/ServiceFileIndexer.cpp => module-services/service-fileindexer/ServiceFileIndexer.cpp +2 -5
@@ 30,7 30,6 @@ namespace service
        auto inotify = dynamic_cast<purefs::fs::message::inotify *>(msg);
        if (inotify) {
            int err;
            LOG_ERROR("Inotify event %s %08x", inotify->name.c_str(), int(inotify->flags));
            if (inotify->flags && (purefs::fs::inotify_flags::close_write | purefs::fs::inotify_flags::move_dst)) {
                err = onUpdateOrCreate(inotify->name);
                if (err) {


@@ 62,10 61,6 @@ namespace service
    // Initialize data notification handler
    sys::ReturnCodes ServiceFileIndexer::InitHandler()
    {
        /*
        mStartupIndexer.start(shared_from_this(), service::name::file_indexer);
        */

        mfsNotifier = purefs::fs::inotify_create(shared_from_this());
        if (!mfsNotifier) {
            LOG_ERROR("Unable to create inotify object");


@@ 80,6 75,8 @@ namespace service
            LOG_ERROR("Unable to create inotify watch errno: %i", err);
            return sys::ReturnCodes::Failure;
        }
        // Start the initial indexer
        mStartupIndexer.start(shared_from_this(), service::name::file_indexer);
        return sys::ReturnCodes::Success;
    }


M module-services/service-fileindexer/StartupIndexer.cpp => module-services/service-fileindexer/StartupIndexer.cpp +78 -40
@@ 3,9 3,11 @@

#include "StartupIndexer.hpp"
#include <Timers/TimerFactory.hpp>
#include <filesystem>
#include <purefs/filesystem_paths.hpp>
#include <purefs/fs/inotify_message.hpp>
#include "Constants.hpp"
#include <filesystem>
#include <fstream>

namespace service::detail
{


@@ 15,60 17,96 @@ namespace service::detail
    {
        using namespace std::string_literals;
        // File extensions indexing allow list
        const std::vector<std::pair<std::string_view, mimeType>> allowed_exts{
            {".txt", mimeType::text}, {".wav", mimeType::audio}, {".mp3", mimeType::audio}, {".flac", mimeType::audio}};
        static constexpr const char *allowed_exts[]{".wav", ".mp3", ".flac"};

        // List of initial dirs for scan
        const std::vector<std::string> start_dirs{purefs::dir::getUserDiskPath(), purefs::dir::getCurrentOSPath()};
        const std::vector<std::string> start_dirs{purefs::dir::getUserDiskPath() / "music"};
        // Lock file name
        const auto lock_file_name = purefs::dir::getUserDiskPath() / ".directory_is_indexed";
        // Time for indexing first unit
        constexpr auto timer_indexing_delay = 400;
        // Time for initial delay after start
        constexpr auto timer_run_delay = 10000;
    } // namespace

    auto StartupIndexer::getFileType(std::string_view path) -> mimeType
    // Process single entry
    auto StartupIndexer::processEntry(std::shared_ptr<sys::Service> svc,
                                      const std::filesystem::recursive_directory_iterator::value_type &entry) -> void
    {
        for (const auto &ext : allowed_exts) {
            if (fs::path(path).extension() == ext.first) {
                return ext.second;
        using namespace std::string_view_literals;
        if (fs::is_regular_file(entry)) {
            for (const auto &ext : allowed_exts) {
                if (fs::path(entry).extension() == ext) {
                    const auto abspath    = fs::absolute(entry).string();
                    const auto inotifyMsg = std::make_shared<purefs::fs::message::inotify>(
                        purefs::fs::inotify_flags::close_write, abspath, ""sv);
                    svc->bus.sendUnicast(inotifyMsg, std::string(service::name::file_indexer));
                }
            }
        }
        return mimeType::_none_;
    }

    // Collect startup files when service starts
    auto StartupIndexer::collectStartupFiles() -> void
    // On timer timeout
    auto StartupIndexer::onTimerTimeout(std::shared_ptr<sys::Service> svc) -> void
    {
        /*
        using namespace std::string_literals;
        auto searcher_cb = [](void *ctx, const char *path, bool isDir) {
            auto _this = reinterpret_cast<StartupIndexer *>(ctx);
            if (!isDir) {
                for (const auto &ext : allowed_exts) {
                    if (fs::path(path).extension() == ext.first) {
                        _this->mMsgs.emplace_back(std::make_shared<msg::FileChangeMessage>(
                            path, msg::FileChangeMessage::evt_t::modified, ""s));
                        LOG_DEBUG("Initial indexing file added");
                    }
                }
        if (!mStarted) {
            mIdxTimer.restart(std::chrono::milliseconds{timer_indexing_delay});
            mStarted = true;
        }
        if (mSubDirIterator == std::filesystem::recursive_directory_iterator()) {
            if (mTopDirIterator == std::cend(start_dirs)) {
                createLockFile();
                LOG_INFO("Initial startup indexer - Finished ...");
                mIdxTimer.stop();
            }
            else {
                mSubDirIterator = fs::recursive_directory_iterator(*mTopDirIterator);
                mTopDirIterator++;
            }
        };
        for (const auto &path : start_dirs) {
            ff_stdio_listdir_recursive(path.c_str(), searcher_cb, this);
        }
        */
        else {
            processEntry(svc, *mSubDirIterator);
            mSubDirIterator++;
        }
    }

    // Setup timers for notification
    auto StartupIndexer::setupTimers(std::shared_ptr<sys::Service> svc, std::string_view svc_name) -> void
    {
        if (!mIdxTimer.isValid()) {
            mIdxTimer = sys::TimerFactory::createPeriodicTimer(
                svc.get(), "file_indexing", std::chrono::milliseconds{timer_indexing_time}, [this, svc](sys::Timer &) {
                    if (!mMsgs.empty()) {
                        // svc->bus.sendUnicast(mMsgs.front(), std::string(service::name::file_indexer));
                        mMsgs.pop_front();
                    }
                    else {
                        mIdxTimer.stop();
                    }
                });
            mIdxTimer.start();
        mIdxTimer = sys::TimerFactory::createPeriodicTimer(svc.get(),
                                                           "file_indexing",
                                                           std::chrono::milliseconds{timer_run_delay},
                                                           [this, svc](sys::Timer &) { onTimerTimeout(svc); });
        mIdxTimer.start();
    }

    // Start the initial file indexing
    auto StartupIndexer::start(std::shared_ptr<sys::Service> svc, std::string_view svc_name) -> void
    {
        if (!hasLockFile()) {
            LOG_INFO("Initial startup indexer - Started...");
            mTopDirIterator = std::begin(start_dirs);
            setupTimers(svc, svc_name);
        }
        else {
            LOG_INFO("Initial startup indexer - Not needed...");
        }
    }

    // Create lock file
    auto StartupIndexer::createLockFile() -> bool
    {
        std::ofstream ofs(lock_file_name);
        ofs << time(nullptr);
        return ofs.good();
    }
    //  Check if lock file exists
    auto StartupIndexer::hasLockFile() -> bool
    {
#if 0
        std::error_code ec;
        return fs::is_regular_file(lock_file_name, ec);
#else
        return false;
#endif
    }
} // namespace service::detail

M module-services/service-fileindexer/StartupIndexer.hpp => module-services/service-fileindexer/StartupIndexer.hpp +14 -28
@@ 5,51 5,37 @@

#include <Service/Service.hpp>
#include <module-sys/Timers/TimerHandle.hpp>
#include <filesystem>

#include <list>

namespace service::msg
{
    class FileChangeMessage;
}
namespace service::detail
{
    // RFC6838 IANA MIME types
    enum class mimeType
    {
        _none_,
        application,
        audio,
        example,
        font,
        image,
        text,
        video
    };
    class StartupIndexer
    {
        static constexpr auto timer_indexing_time = 100U;

      public:
        StartupIndexer()                       = default;
        ~StartupIndexer()                      = default;
        StartupIndexer(const StartupIndexer &) = delete;
        StartupIndexer &operator=(StartupIndexer) = delete;
        auto start(std::shared_ptr<sys::Service> svc, std::string_view svc_name) -> void
        {
            collectStartupFiles();
            setupTimers(svc, svc_name);
        }
        static auto getFileType(std::string_view path) -> mimeType;
        auto start(std::shared_ptr<sys::Service> svc, std::string_view svc_name) -> void;

      private:
        // Collect startup files when service starts
        auto collectStartupFiles() -> void;
        // Process single entry
        static auto processEntry(std::shared_ptr<sys::Service> svc,
                                 const std::filesystem::recursive_directory_iterator::value_type &entry) -> void;
        // Setup timers for notification
        auto setupTimers(std::shared_ptr<sys::Service> svc, std::string_view svc_name) -> void;
        // On timer timeout
        auto onTimerTimeout(std::shared_ptr<sys::Service> svc) -> void;
        // Create lock file
        static auto createLockFile() -> bool;
        //  Check if lock file exists
        static auto hasLockFile() -> bool;

      private:
        std::list<std::shared_ptr<msg::FileChangeMessage>> mMsgs;
        std::vector<std::string>::const_iterator mTopDirIterator;
        std::filesystem::recursive_directory_iterator mSubDirIterator;
        sys::TimerHandle mIdxTimer;
        bool mStarted{};
    };
} // namespace service::detail