// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md #include #include "Common.hpp" #include #include #include #include #include #include #include #include #include namespace service::detail { InotifyHandler::~InotifyHandler() { for (const auto &path : monitoredPaths) { const auto err = mfsNotifier->rm_watch(path); if (err) { LOG_ERROR("Unable to remove watch errno: %i", err); } } } bool InotifyHandler::init(std::shared_ptr service) { svc = service; mfsNotifier = purefs::fs::inotify_create(svc); if (!mfsNotifier) { LOG_ERROR("Unable to create inotify object"); return false; } registerMessageHandlers(); return true; } void InotifyHandler::registerMessageHandlers() { if (!isParentServiceInitialized()) { return; } svc->connect(typeid(purefs::fs::message::inotify), [&](sys::Message *request) -> sys::MessagePointer { return handleInotifyMessage(static_cast(request)); }); } bool InotifyHandler::addWatch(std::string_view monitoredPath) { if (mfsNotifier == nullptr) { LOG_ERROR("Notifier not initialized"); return false; } // Wait for close, delete move to or from location const auto err = mfsNotifier->add_watch(monitoredPath, purefs::fs::inotify_flags::close_write | purefs::fs::inotify_flags::del | purefs::fs::inotify_flags::move_dst | purefs::fs::inotify_flags::move_src); if (err) { LOG_ERROR("Unable to create inotify watch errno: %i", err); return false; } monitoredPaths.emplace_back(monitoredPath); return true; } sys::MessagePointer InotifyHandler::handleInotifyMessage(purefs::fs::message::inotify *inotify) { if (inotify == nullptr) return sys::msgNotHandled(); if (inotify->flags && (purefs::fs::inotify_flags::close_write | purefs::fs::inotify_flags::move_dst)) { onUpdateOrCreate(inotify->name); } else if (inotify->flags && (purefs::fs::inotify_flags::del | purefs::fs::inotify_flags::move_src)) { onRemove(inotify->name); } return sys::msgHandled(); } namespace fs = std::filesystem; namespace { std::string getMimeType(const fs::path &path) { const auto extension = utils::stringToLowercase(path.extension()); if (extension == ".mp3") { return "audio/mpeg"; } if (extension == ".wav") { return "audio/wav"; } if (extension == ".flac") { return "audio/flac"; } return {}; } std::optional CreateMultimediaFilesRecord(const fs::path &path) { std::error_code errorCode; auto fileSize = fs::file_size(path, errorCode); if (errorCode) { LOG_WARN("Can't get file size"); return {}; } auto mimeType = getMimeType(path); auto tags = tags::fetcher::fetchTags(path); db::multimedia_files::MultimediaFilesRecord record{ Record(DB_ID_NONE), .fileInfo = {.path = std::string(path), .mediaType = mimeType, .size = static_cast(fileSize)}, .tags = { .title = tags.title, .album = { .artist = tags.artist, .title = tags.album, }, .comment = tags.comment, .genre = tags.genre, .year = tags.year, .track = tags.track, }, .audioProperties = {.songLength = tags.total_duration_s, .bitrate = tags.bitrate, .sampleRate = tags.sample_rate, .channels = tags.num_channel}}; return record; } } // namespace // On update or create content void InotifyHandler::onUpdateOrCreate(std::string_view path) { LOG_DEBUG("Processing file: %s", std::string(path).c_str()); if (!isParentServiceInitialized()) { return; } const auto extension = utils::stringToLowercase(fs::path(path).extension()); if (!isExtSupported(extension)) { LOG_WARN("Not supported extension: %s", extension.c_str()); return; } auto record = CreateMultimediaFilesRecord(path); if (record.has_value()) { auto query = std::make_unique(record.value()); DBServiceAPI::GetQuery(svc.get(), db::Interface::Name::MultimediaFiles, std::move(query)); } else { LOG_WARN("File corrupted, skipping."); } } // On remove content void InotifyHandler::onRemove(std::string_view path) { LOG_DEBUG("Processing file: %s", std::string(path).c_str()); if (!isParentServiceInitialized()) { return; } const auto extension = utils::stringToLowercase(fs::path(path).extension()); if (!isExtSupported(extension)) { LOG_WARN("Not supported extension: %s", extension.c_str()); return; } auto query = std::make_unique(std::string(path)); DBServiceAPI::GetQuery(svc.get(), db::Interface::Name::MultimediaFiles, std::move(query)); } bool InotifyHandler::isParentServiceInitialized() { if (svc == nullptr) { LOG_ERROR("Parent service is nullptr, cannot proceed"); return false; } return true; } } // namespace service::detail