/** @file * @author Edouard DUPIN * @copyright 2014, Edouard DUPIN, all right reserved * @license MPL v2.0 (see license file) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static std::mutex g_mutex; static std::string g_basePath; static std::string g_basePathCover; static std::string g_basePathCoverGroup; static std::string g_baseDBName = std::string(SERVICE_NAME) + "-database.json"; static std::vector> m_listFile; static uint64_t m_lastMaxId = 0; static uint64_t m_lastMaxImportId = 0; static bool g_needToStore = false; static uint64_t createUniqueID() { m_lastMaxId++; return m_lastMaxId; } static uint64_t createUniqueImportID() { m_lastMaxImportId++; return m_lastMaxImportId; } static std::string removeSpaceOutQuote(const std::string& _in) { std::string out; bool insideQuote = false; for (auto &it : _in) { if (it == '\'') { if (insideQuote == false) { insideQuote = true; } else { insideQuote = false; } out += it; } else if ( it == ' ' && insideQuote == false) { // nothing to add ... } else { out += it; } } return out; } static std::vector splitAction(const std::string& _in) { std::vector out; bool insideQuote = false; std::string value; for (auto &it : _in) { if (it == '\'') { if (insideQuote == false) { insideQuote = true; } else { insideQuote = false; } if (value != "") { out.push_back(value); value.clear(); } } else { value += it; } } if (value != "") { out.push_back(value); } return out; } static void metadataChange(zeus::MediaImpl* _element, const std::string& _key) { g_needToStore = true; // meta_data have chage ==> we need to upfdate the path of the file where the data is stored ... if (_element == nullptr) { return; } _element->forceUpdateDecoratedName(); std::string current = _element->getFileName(); std::string next = _element->getDecoratedName() + "_" + _element->getSha512(); if (next == current) { return; } _element->move(next); } namespace appl { class VideoService : public zeus::service::Video { private: //ememory::SharedPtr& m_client; zeus::ProxyClientProperty m_client; std::string m_userName; public: /* VideoService(ememory::SharedPtr& _client, const std::string& _userName) : m_client(_client), m_userName(_userName) { APPL_WARNING("New VideoService ... for user: "); } */ VideoService(uint16_t _clientId) { APPL_VERBOSE("New VideoService ... for user: " << _clientId); } ~VideoService() { APPL_VERBOSE("delete VideoService ..."); } public: uint32_t count() override { std::unique_lock lock(g_mutex); // TODO : Check right ... return m_listFile.size(); } std::vector getIds(uint32_t _start, uint32_t _stop) override { std::unique_lock lock(g_mutex); // TODO : Check right ... std::vector out; for (size_t iii=_start; iiigetUniqueId()); } return out; } uint32_t getId(std::string _sha512) override { std::unique_lock lock(g_mutex); // TODO : Check right ... uint32_t out; for (size_t iii=0; iiigetSha512() == _sha512) { return m_listFile[iii]->getUniqueId(); } } throw std::invalid_argument("sha512 not find..."); } // Return a File Data (might be a video .tiff/.png/.jpg) ememory::SharedPtr get(uint32_t _mediaId) override { std::unique_lock lock(g_mutex); // TODO : Check right ... ememory::SharedPtr property; for (auto &it : m_listFile) { if (it == nullptr) { continue; } if (it->getUniqueId() == _mediaId) { return it; } } throw std::invalid_argument("Wrong file ID ..."); // } uint32_t add(zeus::ActionNotification& _notifs, zeus::ProxyFile _dataFile) override { //_action.setProgress("{\"pourcent\":" + etk::to_string(23.54) + ", \"comment\":\"Start loading file\""); //_action.setProgress("{\"pourcent\":" + etk::to_string(23.54) + ", \"comment\":\"transfering file\"");; //_action.setProgress("{\"pourcent\":" + etk::to_string(23.54) + ", \"comment\":\"synchronize meta-data\""); uint64_t id = 0; uint64_t importId = 0; { std::unique_lock lock(g_mutex); // TODO : Check right ... id = createUniqueID(); importId = createUniqueImportID(); } auto futRemoteSha512 = _dataFile.getSha512(); auto futType = _dataFile.getMineType(); auto futName = _dataFile.getName(); // wait the sha1 to check his existance: futRemoteSha512.wait(); std::string sha512StringRemote = futRemoteSha512.get(); { std::unique_lock lock(g_mutex); for (auto &it : m_listFile) { if (it == nullptr) { continue; } if (it->getSha512() == sha512StringRemote) { APPL_INFO("File already registered at ");// << it.m_creationData); // simply send the Id of the file // TODO : Check right of this file ... return it->getUniqueId(); } } } std::string tmpFileName = g_basePath + "tmpImport_" + etk::to_string(importId); std::string sha512String = zeus::storeInFile(_dataFile, tmpFileName); futType.wait(); futName.wait(); // move the file at the good position: APPL_DEBUG("move temporay file in : " << g_basePath << sha512String); if (etk::FSNodeGetSize(tmpFileName) == 0) { APPL_ERROR("try to store an empty file"); throw std::runtime_error("file size == 0"); } if (zeus::getExtention(futType.get()) != "") { std::unique_lock lock(g_mutex); etk::FSNodeMove(tmpFileName, g_basePath + sha512String + "." + zeus::getExtention(futType.get())); ememory::SharedPtr property = ememory::makeShared(id, sha512String + "." + zeus::getExtention(futType.get()), g_basePath); property->setMetadata("sha512", sha512String); property->setMetadata("mime-type", futType.get()); property->setCallbackMetadataChange(&metadataChange); m_listFile.push_back(property); g_needToStore = true; } else { std::unique_lock lock(g_mutex); etk::FSNodeMove(tmpFileName, g_basePath + sha512String); ememory::SharedPtr property = ememory::makeShared(id, sha512String, g_basePath); property->setMetadata("sha512", sha512String); property->setCallbackMetadataChange(&metadataChange); m_listFile.push_back(property); g_needToStore = true; } APPL_DEBUG(" filename : " << sha512String); return id; } void remove(uint32_t _mediaId) override { std::unique_lock lock(g_mutex); // TODO : Check right ... //Check if the file exist: bool find = false; ememory::SharedPtr property; for (auto it = m_listFile.begin(); it != m_listFile.end(); /* No increment */) { if (*it == nullptr) { it = m_listFile.erase(it); continue; } if ((*it)->getUniqueId() == _mediaId) { property = *it; it = m_listFile.erase(it); find = true; } else { ++it; } g_needToStore = true; } if (find == false) { throw std::invalid_argument("Wrong file name ..."); } // Real Remove definitly the file // TODO : Set it in a trash ... For a while ... if (property->erase() == false) { throw std::runtime_error("Can not remove file ..."); } } std::vector> interpreteSQLRequest(const std::string& _sqlLikeRequest) { std::vector> out; if (_sqlLikeRequest != "*") { std::vector listAnd = etk::split(_sqlLikeRequest, "AND"); APPL_INFO("Find list AND : "); for (auto &it : listAnd) { it = removeSpaceOutQuote(it); std::vector elements = splitAction(it); if (elements.size() != 3) { APPL_ERROR("element : '" + it + "' have wrong spliting " << elements); throw std::invalid_argument("element : \"" + it + "\" have wrong spliting"); } if ( elements[1] != "==" && elements[1] != "!=" && elements[1] != ">=" && elements[1] != "<=" && elements[1] != ">" && elements[1] != "<") { throw std::invalid_argument("action invalid : '" + elements[1] + "' only availlable : [==,!=,<=,>=,<,>]"); } APPL_INFO(" - '" << elements[0] << "' action='" << elements[1] << "' with='" << elements[2] << "'"); out.push_back(elements); } } return out; } bool isValid(const std::vector>& _listElement, const std::map& _metadata) { for (auto &itCheck : _listElement) { // find matadataValue: auto itM = _metadata.find(itCheck[0]); if (itM == _metadata.end()) { // not find key ==> no check to do ... return false; } if (itCheck[1] == "==") { if (itM->second != itCheck[2]) { return false; } } else if (itCheck[1] == "!=") { if (itM->second == itCheck[2]) { return false; } } else if (itCheck[1] == "<=") { if (itM->second < itCheck[2]) { return false; } } else if (itCheck[1] == ">=") { if (itM->second > itCheck[2]) { return false; } } else if (itCheck[1] == "<") { if (itM->second <= itCheck[2]) { return false; } } else if (itCheck[1] == ">") { if (itM->second >= itCheck[2]) { return false; } } } return true; } std::string mapToString(const std::map& _metadata) { std::string out = "{"; for (auto &it : _metadata) { out += it.first + ":" + it.second + ","; } out += "}"; return out; } std::vector getSQL(std::string _sqlLikeRequest) override { std::vector out; if (_sqlLikeRequest == "") { throw std::invalid_argument("empty request"); } APPL_DEBUG("check : " << _sqlLikeRequest); std::vector> listAndParsed = interpreteSQLRequest(_sqlLikeRequest); std::unique_lock lock(g_mutex); for (auto &it : m_listFile) { if (it == nullptr) { continue; } APPL_DEBUG(" [" << it->getUniqueId() << " list=" << mapToString(it->getMetadataDirect())); bool isCorrectElement = isValid(listAndParsed, it->getMetadataDirect()); if (isCorrectElement == true) { APPL_DEBUG(" select"); out.push_back(it->getUniqueId()); } } return out; } std::vector getMetadataValuesWhere(std::string _keyName, std::string _sqlLikeRequest) override { std::vector out; if (_sqlLikeRequest == "") { throw std::invalid_argument("empty request"); } std::vector> listAndParsed = interpreteSQLRequest(_sqlLikeRequest); std::unique_lock lock(g_mutex); for (auto &it : m_listFile) { if (it == nullptr) { continue; } bool isCorrectElement = isValid(listAndParsed, it->getMetadataDirect()); if (isCorrectElement == false) { continue; } auto it2 = it->getMetadataDirect().find(_keyName); if (it2 == it->getMetadataDirect().end()) { continue; } std::string value = it2->second; isCorrectElement = false; for (auto &it2: out) { if (it2 == value) { isCorrectElement = true; break; } } if (isCorrectElement == false) { out.push_back(value); } } return out; } ememory::SharedPtr internalGetCover(const std::string& _baseName, const std::string& _mediaString, uint32_t _maxSize) { if (etk::FSNodeExist(_baseName + _mediaString + ".jpg") == true) { return zeus::File::create(_baseName + _mediaString + ".jpg", "", ""); } if (etk::FSNodeExist(_baseName + _mediaString + ".png") == true) { return zeus::File::create(_baseName + _mediaString + ".jpg", "", ""); } throw std::runtime_error("No cover availlable"); } void internalSetCover(const std::string& _baseName, zeus::ActionNotification& _notifs, zeus::ProxyFile _cover, std::string _mediaString) { uint64_t importId = 0; { std::unique_lock lock(g_mutex); importId = createUniqueImportID(); } auto futType = _cover.getMineType(); std::string tmpFileName = g_basePath + "tmpImport_" + etk::to_string(importId); std::string sha512String = zeus::storeInFile(_cover, tmpFileName); futType.wait(); if (etk::FSNodeGetSize(tmpFileName) == 0) { APPL_ERROR("try to store an empty file"); throw std::runtime_error("file size == 0"); } if (etk::FSNodeGetSize(tmpFileName) > 1024*1024) { APPL_ERROR("try to store a Bigger file"); throw std::runtime_error("file size > 1Mo"); } if (futType.get() == "image/png") { std::unique_lock lock(g_mutex); etk::FSNodeRemove(_baseName + _mediaString + ".jpg"); etk::FSNodeMove(tmpFileName, _baseName + _mediaString + ".png"); } else if (futType.get() == "image/jpeg") { std::unique_lock lock(g_mutex); etk::FSNodeRemove(_baseName + _mediaString + ".png"); etk::FSNodeMove(tmpFileName, _baseName + _mediaString + ".jpg"); } else { APPL_ERROR("try to store a file with the wrong format"); throw std::runtime_error("wrong foramt :" + futType.get() + " support only image/jpeg, image/png"); } } ememory::SharedPtr getCover(uint32_t _mediaId, uint32_t _maxSize) override { return internalGetCover(g_basePathCover, etk::to_string(_mediaId), _maxSize); } void setCover(zeus::ActionNotification& _notifs, zeus::ProxyFile _cover, uint32_t _mediaId) override { return internalSetCover(g_basePathCover, _notifs, _cover, etk::to_string(_mediaId)); } ememory::SharedPtr getGroupCover(std::string _groupName, uint32_t _maxSize) override { return internalGetCover(g_basePathCoverGroup, _groupName, _maxSize); } void setGroupCover(zeus::ActionNotification& _notifs, zeus::ProxyFile _cover, std::string _groupName) override { return internalSetCover(g_basePathCoverGroup, _notifs, _cover, _groupName); } }; } static void store_db() { APPL_ERROR("Store database [START]"); ejson::Document database; ejson::Array listFilesArray; database.add("list-files", listFilesArray); for (auto &it : m_listFile) { if (it != nullptr) { listFilesArray.add(it->getJson()); } } bool retGenerate = database.storeSafe(g_basePath + g_baseDBName); APPL_ERROR("Store database [STOP] : " << (g_basePath + g_baseDBName) << " ret = " << retGenerate); g_needToStore = false; } static void load_db() { ejson::Document database; bool ret = database.load(g_basePath + g_baseDBName); if (ret == false) { APPL_WARNING(" ==> LOAD error"); } ejson::Array listFilesArray = database["list-files"].toArray(); for (const auto itArray: listFilesArray) { auto property = ememory::makeShared(g_basePath, itArray.toObject()); if (property == nullptr) { APPL_ERROR("can not allocate element ..."); continue; } if (m_lastMaxId < property->getUniqueId()) { m_lastMaxId = property->getUniqueId()+1; } if (property->getFileName() == "") { APPL_ERROR("Can not access on the file : ... No name "); } else { property->setCallbackMetadataChange(&metadataChange); m_listFile.push_back(property); } } g_needToStore = false; } ETK_EXPORT_API bool SERVICE_IO_init(int _argc, const char *_argv[], std::string _basePath) { g_basePath = _basePath; g_basePathCover = _basePath + "/AAAASDGDFGQN4352SCVdfgBSXDFGFCVQDSGFQSfd_cover/"; g_basePathCoverGroup = _basePath + "/AAAASDGDFGQN4352SCVdfgBSXDFGFCVQDSGFQSfd_cover_group/"; std::unique_lock lock(g_mutex); APPL_WARNING("Load USER: " << g_basePath); load_db(); APPL_WARNING("new USER: [STOP]"); return true; } ETK_EXPORT_API bool SERVICE_IO_uninit() { std::unique_lock lock(g_mutex); store_db(); APPL_WARNING("delete USER [STOP]"); return true; } ETK_EXPORT_API void SERVICE_IO_peridic_call() { if (g_needToStore == false) { return; } // try lock mutex: if (g_mutex.try_lock() == false) { return; } store_db(); g_mutex.unlock(); } ZEUS_SERVICE_VIDEO_DECLARE(appl::VideoService);