From 90d05e6c5432cd14856461a811fe7e0b53568b5d Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Tue, 23 Feb 2021 20:38:54 +0100 Subject: [PATCH] Custom Effects - Clean-ups and Enhancements (#1163) * Cleanup EffectFileHandler * Support Custom Effect Schemas and align EffectFileHandler * Change back to colon prefix for system effects * WebSockets - Fix error in handling fragmented frames * Correct missing colon updates * Update json with image file location for custom gif effects * Image effect deletion - considere full filename is stored in JSON * Correct selection lists indentions --- assets/webconfig/i18n/en.json | 6 +- .../js/content_effectsconfigurator.js | 408 ++++++++++-------- include/effectengine/EffectFileHandler.h | 4 +- libsrc/api/JsonAPI.cpp | 243 +++++------ libsrc/effectengine/EffectFileHandler.cpp | 204 +++++---- libsrc/webserver/WebSocketClient.cpp | 25 +- libsrc/webserver/WebSocketClient.h | 3 + 7 files changed, 486 insertions(+), 407 deletions(-) diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 5e46251e..5a0e7d98 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -821,7 +821,9 @@ "remote_maptype_label": "Mapping type", "remote_maptype_label_multicolor_mean": "Multicolor", "remote_maptype_label_unicolor_mean": "Unicolor", - "remote_optgroup_syseffets": "Provided Effects", + "remote_optgroup_syseffets": "System Effects", + "remote_optgroup_templates_custom": "User Templates", + "remote_optgroup_templates_system": "System Templates", "remote_optgroup_usreffets": "User Effects", "remote_videoMode_2D": "2D", "remote_videoMode_3DSBS": "3DSBS", @@ -935,4 +937,4 @@ "wiz_yeelight_intro1": "This wizards configures Hyperion for the Yeelight system. Features are the Yeelighs' auto detection, setting each light to a specific position on your picture or disable it and tune the Hyperion settings automatically! So in short: All you need are some clicks and you are done!", "wiz_yeelight_title": "Yeelight Wizard", "wiz_yeelight_unsupported": "Unsupported" -} \ No newline at end of file +} diff --git a/assets/webconfig/js/content_effectsconfigurator.js b/assets/webconfig/js/content_effectsconfigurator.js index feea705c..674882b5 100644 --- a/assets/webconfig/js/content_effectsconfigurator.js +++ b/assets/webconfig/js/content_effectsconfigurator.js @@ -1,204 +1,246 @@ -$(document).ready( function() { - performTranslation(); - var oldDelList = []; - var effectName = ""; - var imageData = ""; - var effects_editor = null; - var effectPy = ""; - var testrun; +$(document).ready(function () { + performTranslation(); + var oldDelList = []; + var effectName = ""; + var imageData = ""; + var effects_editor = null; + var effectPyScript = ""; + var testrun; - if(window.showOptHelp) - createHintH("intro", $.i18n('effectsconfigurator_label_intro'), "intro_effc"); + if (window.showOptHelp) + createHintH("intro", $.i18n('effectsconfigurator_label_intro'), "intro_effc"); - function updateDelEffectlist(){ - var newDelList = window.serverInfo.effects; - if(newDelList.length != oldDelList.length) - { - $('#effectsdellist').html(""); - var usrEffArr = []; - var sysEffArr = []; - for(var idx=0; idx 0) { + var usrEffGrp = createSelGroup($.i18n('remote_optgroup_usreffets')); + for (var idx = 0; idx < usrEffArr.length; idx++) + { + usrEffGrp.appendChild(createSelOpt('ext_' + newDelList[usrEffArr[idx]].name, newDelList[usrEffArr[idx]].name)); + } + $('#effectsdellist').append(usrEffGrp); + } + + var sysEffGrp = createSelGroup($.i18n('remote_optgroup_syseffets')); + for (var idx = 0; idx < sysEffArr.length; idx++) + { + sysEffGrp.appendChild(createSelOpt('int_' + newDelList[sysEffArr[idx]].name, newDelList[sysEffArr[idx]].name)); + } + $('#effectsdellist').append(sysEffGrp); + + $("#effectsdellist").trigger("change"); + + oldDelList = newDelList; + } + } + + function triggerTestEffect() { + testrun = true; + + var args = effects_editor.getEditor('root.args'); + requestTestEffect(effectName, effectPyScript, JSON.stringify(args.getValue()), imageData); + }; + + // Specify upload handler for image files + JSONEditor.defaults.options.upload = function (type, file, cbs) { + var fileReader = new FileReader(); + + //check file + if (!file.type.startsWith('image')) { + imageData = ""; + cbs.failure('File upload error'); + // TODO clear file dialog. + showInfoDialog('error', "", $.i18n('infoDialog_writeimage_error_text', file.name)); + return; + } + + fileReader.onload = function () { + imageData = this.result.split(',')[1]; + cbs.success(file.name); + }; + + fileReader.readAsDataURL(file); + }; + + $("#effectslist").off().on("change", function (event) { + if (effects_editor != null) + effects_editor.destroy(); + + for (var idx = 0; idx < effects.length; idx++) { + if (effects[idx].script == this.value) { + effects_editor = createJsonEditor('editor_container', { + args: effects[idx].schemaContent, + }, false, true, false); + + effectPyScript = effects[idx].script; + + imageData = ""; + $("#name-input").trigger("change"); + + var desc = $.i18n(effects[idx].schemaContent.title + '_desc'); + if (desc === effects[idx].schemaContent.title + '_desc') { + desc = "" + } + + $("#eff_desc").html(createEffHint($.i18n(effects[idx].schemaContent.title), desc)); + break; + } + } + + effects_editor.on('change', function () { + if ($("#btn_cont_test").hasClass("btn-success") && effects_editor.validate().length == 0 && effectName != "") { + triggerTestEffect(); + } + if (effects_editor.validate().length == 0 && effectName != "") { + $('#btn_start_test').attr('disabled', false); + !window.readOnlyMode ? $('#btn_write').attr('disabled', false) : $('#btn_write').attr('disabled', true); + } + else { + $('#btn_start_test, #btn_write').attr('disabled', true); + } + }); + }); + + // disable or enable control elements + $("#name-input").on('change keyup', function (event) { + effectName = $(this).val(); + if ($(this).val() == '') { + effects_editor.disable(); + $("#eff_footer").children().attr('disabled', true); + } else { + effects_editor.enable(); + $("#eff_footer").children().attr('disabled', false); + !window.readOnlyMode ? $('#btn_write').attr('disabled', false) : $('#btn_write').attr('disabled', true); + } + }); + + // Save Effect + $('#btn_write').off().on('click', function () { + requestWriteEffect(effectName, effectPyScript, JSON.stringify(effects_editor.getValue()), imageData); + $(window.hyperion).one("cmd-create-effect", function (event) { + if (event.response.success) + showInfoDialog('success', "", $.i18n('infoDialog_effconf_created_text', effectName)); }); - // Save Effect - $('#btn_write').off().on('click',function() { - requestWriteEffect(effectName,effectPy,JSON.stringify(effects_editor.getValue()),imageData); - $(window.hyperion).one("cmd-create-effect", function(event) { - if (event.response.success) - showInfoDialog('success', "", $.i18n('infoDialog_effconf_created_text', effectName)); - }); + if (testrun) + setTimeout(requestPriorityClear, 100); + }); - if (testrun) - setTimeout(requestPriorityClear,100); + // Start test + $('#btn_start_test').off().on('click', function () { + triggerTestEffect(); + }); - }); + // Stop test + $('#btn_stop_test').off().on('click', function () { + requestPriorityClear(); + testrun = false; + }); - // Start test - $('#btn_start_test').off().on('click',function() { - triggerTestEffect(); - }); + // Continuous test + $('#btn_cont_test').off().on('click', function () { + toggleClass('#btn_cont_test', "btn-success", "btn-danger"); + }); - // Stop test - $('#btn_stop_test').off().on('click',function() { - requestPriorityClear(); - testrun = false; - }); + // Delete Effect + $('#btn_delete').off().on('click', function () { + var name = $("#effectsdellist").val().split("_")[1]; + requestDeleteEffect(name); + $(window.hyperion).one("cmd-delete-effect", function (event) { + if (event.response.success) + showInfoDialog('success', "", $.i18n('infoDialog_effconf_deleted_text', name)); + }); + }); - // Continuous test - $('#btn_cont_test').off().on('click',function() { - toggleClass('#btn_cont_test', "btn-success", "btn-danger"); - }); + // Disable or enable Delete Effect Button + $('#effectsdellist').off().on('change', function () { + $(this).val() == null ? $('#btn_edit, #btn_delete').prop('disabled', true) : ""; + $(this).val().startsWith("int_") ? $('#btn_delete').prop('disabled', true) : $('#btn_delete').prop('disabled', false); + }); - // Delete Effect - $('#btn_delete').off().on('click',function() { - var name = $("#effectsdellist").val().split("_")[1]; - requestDeleteEffect(name); - $(window.hyperion).one("cmd-delete-effect", function(event) { - if (event.response.success) - showInfoDialog('success', "", $.i18n('infoDialog_effconf_deleted_text', name)); - }); - }); + // Load Effect + $('#btn_edit').off().on('click', function () { - // disable or enable Delete Effect Button - $('#effectsdellist').off().on('change', function(){ - $(this).val() == null ? $('#btn_edit, #btn_delete').prop('disabled',true) : ""; - $(this).val().startsWith("int_") ? $('#btn_delete').prop('disabled',true) : $('#btn_delete').prop('disabled',false); - }); + var name = $("#effectsdellist").val().replace("ext_", ""); + if (name.startsWith("int_")) { + name = name.split("_").pop(); + $("#name-input").val("My Modded Effect"); + } + else { + name = name.split("_").pop(); + $("#name-input").val(name); + } - // Load Effect - $('#btn_edit').off().on('click', function(){ - var name = $("#effectsdellist").val().replace("ext_",""); + var efx = window.serverInfo.effects; + for (var i = 0; i < efx.length; i++) { + if (efx[i].name == name) { + var py = efx[i].script; + $("#effectslist").val(py).trigger("change"); - if(name.startsWith("int_")) - { name = name.split("_").pop(); - $("#name-input").val("My Modded Effect"); - } - else - { - name = name.split("_").pop(); - $("#name-input").val(name); - } + for (var key in efx[i].args) { + var ed = effects_editor.getEditor('root.args.' + [key]); + if (ed) + ed.setValue(efx[i].args[key]); + } + break; + } + } + }); - var efx = window.serverInfo.effects; - for(var i = 0; i 0) { + var custTmplGrp = createSelGroup($.i18n('remote_optgroup_templates_custom')); + for (var idx = 0; idx < custTemplatesIDs.length; idx++) + { + custTmplGrp.appendChild(createSelOpt(effects[custTemplatesIDs[idx]].script, $.i18n(effects[custTemplatesIDs[idx]].schemaContent.title))); + } + $('#effectslist').append(custTmplGrp); + } - //interval update - $(window.hyperion).on("cmd-effects-update", function(event){ - window.serverInfo.effects = event.response.data.effects - updateDelEffectlist(); - }); + var sysTmplGrp = createSelGroup($.i18n('remote_optgroup_templates_system')); + for (var idx = 0; idx < sysTemplatesIDs.length; idx++) + { + sysTmplGrp.appendChild(createSelOpt(effects[sysTemplatesIDs[idx]].script, $.i18n(effects[sysTemplatesIDs[idx]].schemaContent.title))); + } + $('#effectslist').append(sysTmplGrp); - removeOverlay(); + $("#effectslist").trigger("change"); + + updateDelEffectlist(); + + //interval update + $(window.hyperion).on("cmd-effects-update", function (event) { + window.serverInfo.effects = event.response.data.effects + updateDelEffectlist(); + }); + + removeOverlay(); }); + diff --git a/include/effectengine/EffectFileHandler.h b/include/effectengine/EffectFileHandler.h index ea998e29..103a3504 100644 --- a/include/effectengine/EffectFileHandler.h +++ b/include/effectengine/EffectFileHandler.h @@ -64,12 +64,12 @@ private: /// /// @brief Load the effect definition, called by updateEffects() /// - bool loadEffectDefinition(const QString & path, const QString & effectConfigFile, EffectDefinition &effectDefinition); + bool loadEffectDefinition(const QString& path, const QString& effectConfigFile, EffectDefinition& effectDefinition); /// /// @brief load effect schemas, called by updateEffects() /// - bool loadEffectSchema(const QString & path, const QString & effectSchemaFile, EffectSchema &effectSchema); + bool loadEffectSchema(const QString& path, const QString& effectSchemaFile, EffectSchema& effectSchema); private: QJsonObject _effectConfig; diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 49abfd5b..e75fa71b 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -41,7 +41,7 @@ using namespace hyperion; -JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject *parent, bool noListener) +JsonAPI::JsonAPI(QString peerAddress, Logger* log, bool localConnection, QObject* parent, bool noListener) : API(log, localConnection, parent) { _noListener = noListener; @@ -86,7 +86,7 @@ bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced) return false; } -void JsonAPI::handleMessage(const QString &messageString, const QString &httpAuthHeader) +void JsonAPI::handleMessage(const QString& messageString, const QString& httpAuthHeader) { const QString ident = "JsonRpc@" + _peerAddress; QJsonObject message; @@ -187,17 +187,17 @@ proceed: handleNotImplemented(command, tan); } -void JsonAPI::handleColorCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleColorCommand(const QJsonObject& message, const QString& command, int tan) { emit forwardJsonMessage(message); int priority = message["priority"].toInt(); int duration = message["duration"].toInt(-1); const QString origin = message["origin"].toString("JsonRpc") + "@" + _peerAddress; - const QJsonArray &jsonColor = message["color"].toArray(); + const QJsonArray& jsonColor = message["color"].toArray(); std::vector colors; // TODO faster copy - for (const auto &entry : jsonColor) + for (const auto& entry : jsonColor) { colors.emplace_back(uint8_t(entry.toInt())); } @@ -206,7 +206,7 @@ void JsonAPI::handleColorCommand(const QJsonObject &message, const QString &comm sendSuccessReply(command, tan); } -void JsonAPI::handleImageCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleImageCommand(const QJsonObject& message, const QString& command, int tan) { emit forwardJsonMessage(message); @@ -230,7 +230,7 @@ void JsonAPI::handleImageCommand(const QJsonObject &message, const QString &comm sendSuccessReply(command, tan); } -void JsonAPI::handleEffectCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& command, int tan) { emit forwardJsonMessage(message); @@ -249,19 +249,19 @@ void JsonAPI::handleEffectCommand(const QJsonObject &message, const QString &com sendErrorReply("Effect '" + dat.effectName + "' not found", command, tan); } -void JsonAPI::handleCreateEffectCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleCreateEffectCommand(const QJsonObject& message, const QString& command, int tan) { const QString resultMsg = API::saveEffect(message); resultMsg.isEmpty() ? sendSuccessReply(command, tan) : sendErrorReply(resultMsg, command, tan); } -void JsonAPI::handleDeleteEffectCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleDeleteEffectCommand(const QJsonObject& message, const QString& command, int tan) { const QString res = API::deleteEffect(message["name"].toString()); res.isEmpty() ? sendSuccessReply(command, tan) : sendErrorReply(res, command, tan); } -void JsonAPI::handleSysInfoCommand(const QJsonObject &, const QString &command, int tan) +void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, int tan) { // create result QJsonObject result; @@ -304,7 +304,7 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject &, const QString &command, emit callbackMessage(result); } -void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& command, int tan) { QJsonObject info; @@ -315,9 +315,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString activePriorities.removeAll(255); int currentPriority = _hyperion->getCurrentPriority(); - for(int priority : activePriorities) + for (int priority : activePriorities) { - const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(priority); + const Hyperion::InputInfo& priorityInfo = _hyperion->getPriorityInfo(priority); QJsonObject item; item["priority"] = priority; if (priorityInfo.timeoutTime_ms > 0) @@ -349,9 +349,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString // add HSL Value to Array QJsonArray HSLValue; ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red, - priorityInfo.ledColors.begin()->green, - priorityInfo.ledColors.begin()->blue, - Hue, Saturation, Luminace); + priorityInfo.ledColors.begin()->green, + priorityInfo.ledColors.begin()->blue, + Hue, Saturation, Luminace); HSLValue.append(Hue); HSLValue.append(Saturation); @@ -362,8 +362,8 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString } (priority == currentPriority) - ? priorities.prepend(item) - : priorities.append(item); + ? priorities.prepend(item) + : priorities.append(item); } info["priorities"] = priorities; @@ -371,9 +371,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString // collect adjustment information QJsonArray adjustmentArray; - for (const QString &adjustmentId : _hyperion->getAdjustmentIds()) + for (const QString& adjustmentId : _hyperion->getAdjustmentIds()) { - const ColorAdjustment *colorAdjustment = _hyperion->getAdjustment(adjustmentId); + const ColorAdjustment* colorAdjustment = _hyperion->getAdjustment(adjustmentId); if (colorAdjustment == nullptr) { Error(_log, "Incorrect color adjustment id: %s", QSTRING_CSTR(adjustmentId)); @@ -440,8 +440,8 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString // collect effect info QJsonArray effects; - const std::list &effectsDefinitions = _hyperion->getEffects(); - for (const EffectDefinition &effectDefinition : effectsDefinitions) + const std::list& effectsDefinitions = _hyperion->getEffects(); + for (const EffectDefinition& effectDefinition : effectsDefinitions) { QJsonObject effect; effect["name"] = effectDefinition.name; @@ -469,7 +469,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString #if defined(ENABLE_DISPMANX) || defined(ENABLE_V4L2) || defined(ENABLE_FB) || defined(ENABLE_AMLOGIC) || defined(ENABLE_OSX) || defined(ENABLE_X11) || defined(ENABLE_XCB) || defined(ENABLE_QT) - if ( GrabberWrapper::getInstance() != nullptr ) + if (GrabberWrapper::getInstance() != nullptr) { grabbers["active"] = GrabberWrapper::getInstance()->getActive(); } @@ -547,7 +547,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString // add sessions QJsonArray sessions; #ifdef ENABLE_AVAHI - for (auto session: BonjourBrowserWrapper::getInstance()->getAllServices()) + for (auto session : BonjourBrowserWrapper::getInstance()->getAllServices()) { if (session.port < 0) continue; @@ -564,7 +564,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString #endif // add instance info QJsonArray instanceInfo; - for (const auto &entry : API::getAllInstanceData()) + for (const auto& entry : API::getAllInstanceData()) { QJsonObject obj; obj.insert("friendly_name", entry["friendly_name"].toString()); @@ -586,7 +586,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString // TRANSFORM INFORMATION (DEFAULT VALUES) QJsonArray transformArray; - for (const QString &transformId : _hyperion->getAdjustmentIds()) + for (const QString& transformId : _hyperion->getAdjustmentIds()) { QJsonObject transform; QJsonArray blacklevel, whitelevel, gamma, threshold; @@ -617,7 +617,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString // ACTIVE EFFECT INFO QJsonArray activeEffects; - for (const ActiveEffectDefinition &activeEffectDefinition : _hyperion->getActiveEffects()) + for (const ActiveEffectDefinition& activeEffectDefinition : _hyperion->getActiveEffects()) { if (activeEffectDefinition.priority != PriorityMuxer::LOWEST_PRIORITY - 1) { @@ -634,15 +634,15 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString // ACTIVE STATIC LED COLOR QJsonArray activeLedColors; - const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(_hyperion->getCurrentPriority()); + const Hyperion::InputInfo& priorityInfo = _hyperion->getPriorityInfo(_hyperion->getCurrentPriority()); if (priorityInfo.componentId == hyperion::COMP_COLOR && !priorityInfo.ledColors.empty()) { QJsonObject LEDcolor; // check if LED Color not Black (0,0,0) if ((priorityInfo.ledColors.begin()->red + - priorityInfo.ledColors.begin()->green + - priorityInfo.ledColors.begin()->blue != - 0)) + priorityInfo.ledColors.begin()->green + + priorityInfo.ledColors.begin()->blue != + 0)) { QJsonObject LEDcolor; @@ -659,9 +659,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString // add HSL Value to Array QJsonArray HSLValue; ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red, - priorityInfo.ledColors.begin()->green, - priorityInfo.ledColors.begin()->blue, - Hue, Saturation, Luminace); + priorityInfo.ledColors.begin()->green, + priorityInfo.ledColors.begin()->blue, + Hue, Saturation, Luminace); HSLValue.append(Hue); HSLValue.append(Saturation); @@ -706,7 +706,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString } } -void JsonAPI::handleClearCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleClearCommand(const QJsonObject& message, const QString& command, int tan) { emit forwardJsonMessage(message); int priority = message["priority"].toInt(); @@ -720,7 +720,7 @@ void JsonAPI::handleClearCommand(const QJsonObject &message, const QString &comm sendSuccessReply(command, tan); } -void JsonAPI::handleClearallCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleClearallCommand(const QJsonObject& message, const QString& command, int tan) { emit forwardJsonMessage(message); QString replyMsg; @@ -728,12 +728,12 @@ void JsonAPI::handleClearallCommand(const QJsonObject &message, const QString &c sendSuccessReply(command, tan); } -void JsonAPI::handleAdjustmentCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleAdjustmentCommand(const QJsonObject& message, const QString& command, int tan) { - const QJsonObject &adjustment = message["adjustment"].toObject(); + const QJsonObject& adjustment = message["adjustment"].toObject(); const QString adjustmentId = adjustment["id"].toString(_hyperion->getAdjustmentIds().first()); - ColorAdjustment *colorAdjustment = _hyperion->getAdjustment(adjustmentId); + ColorAdjustment* colorAdjustment = _hyperion->getAdjustment(adjustmentId); if (colorAdjustment == nullptr) { Warning(_log, "Incorrect adjustment identifier: %s", adjustmentId.toStdString().c_str()); @@ -742,39 +742,39 @@ void JsonAPI::handleAdjustmentCommand(const QJsonObject &message, const QString if (adjustment.contains("red")) { - const QJsonArray &values = adjustment["red"].toArray(); + const QJsonArray& values = adjustment["red"].toArray(); colorAdjustment->_rgbRedAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); } if (adjustment.contains("green")) { - const QJsonArray &values = adjustment["green"].toArray(); + const QJsonArray& values = adjustment["green"].toArray(); colorAdjustment->_rgbGreenAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); } if (adjustment.contains("blue")) { - const QJsonArray &values = adjustment["blue"].toArray(); + const QJsonArray& values = adjustment["blue"].toArray(); colorAdjustment->_rgbBlueAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); } if (adjustment.contains("cyan")) { - const QJsonArray &values = adjustment["cyan"].toArray(); + const QJsonArray& values = adjustment["cyan"].toArray(); colorAdjustment->_rgbCyanAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); } if (adjustment.contains("magenta")) { - const QJsonArray &values = adjustment["magenta"].toArray(); + const QJsonArray& values = adjustment["magenta"].toArray(); colorAdjustment->_rgbMagentaAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); } if (adjustment.contains("yellow")) { - const QJsonArray &values = adjustment["yellow"].toArray(); + const QJsonArray& values = adjustment["yellow"].toArray(); colorAdjustment->_rgbYellowAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); } if (adjustment.contains("white")) { - const QJsonArray &values = adjustment["white"].toArray(); + const QJsonArray& values = adjustment["white"].toArray(); colorAdjustment->_rgbWhiteAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); } @@ -814,7 +814,7 @@ void JsonAPI::handleAdjustmentCommand(const QJsonObject &message, const QString sendSuccessReply(command, tan); } -void JsonAPI::handleSourceSelectCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleSourceSelectCommand(const QJsonObject& message, const QString& command, int tan) { if (message.contains("auto")) { @@ -832,7 +832,7 @@ void JsonAPI::handleSourceSelectCommand(const QJsonObject &message, const QStrin sendSuccessReply(command, tan); } -void JsonAPI::handleConfigCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleConfigCommand(const QJsonObject& message, const QString& command, int tan) { QString subcommand = message["subcommand"].toString(""); QString full_command = command + "-" + subcommand; @@ -876,14 +876,14 @@ void JsonAPI::handleConfigCommand(const QJsonObject &message, const QString &com } } -void JsonAPI::handleConfigSetCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleConfigSetCommand(const QJsonObject& message, const QString& command, int tan) { if (message.contains("config")) { QJsonObject config = message["config"].toObject(); if (API::isHyperionEnabled()) { - if ( API::saveSettings(config) ) + if (API::saveSettings(config)) { sendSuccessReply(command, tan); } @@ -897,7 +897,7 @@ void JsonAPI::handleConfigSetCommand(const QJsonObject &message, const QString & } } -void JsonAPI::handleSchemaGetCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleSchemaGetCommand(const QJsonObject& message, const QString& command, int tan) { // create result QJsonObject schemaJson, alldevices, properties; @@ -912,7 +912,7 @@ void JsonAPI::handleSchemaGetCommand(const QJsonObject &message, const QString & { schemaJson = QJsonFactory::readSchema(schemaFile); } - catch (const std::runtime_error &error) + catch (const std::runtime_error& error) { throw std::runtime_error(error.what()); } @@ -923,36 +923,25 @@ void JsonAPI::handleSchemaGetCommand(const QJsonObject &message, const QString & properties.insert("alldevices", alldevices); // collect all available effect schemas - QJsonObject pyEffectSchemas, pyEffectSchema; - QJsonArray in, ex; - const std::list &effectsSchemas = _hyperion->getEffectSchemas(); - for (const EffectSchema &effectSchema : effectsSchemas) + QJsonArray schemaList; + const std::list& effectsSchemas = _hyperion->getEffectSchemas(); + for (const EffectSchema& effectSchema : effectsSchemas) { - if (effectSchema.pyFile.mid(0, 1) == ":") + QJsonObject schema; + schema.insert("script", effectSchema.pyFile); + schema.insert("schemaLocation", effectSchema.schemaFile); + schema.insert("schemaContent", effectSchema.pySchema); + if (effectSchema.pyFile.startsWith(':')) { - QJsonObject internal; - internal.insert("script", effectSchema.pyFile); - internal.insert("schemaLocation", effectSchema.schemaFile); - internal.insert("schemaContent", effectSchema.pySchema); - in.append(internal); + schema.insert("type", "system"); } else { - QJsonObject external; - external.insert("script", effectSchema.pyFile); - external.insert("schemaLocation", effectSchema.schemaFile); - external.insert("schemaContent", effectSchema.pySchema); - ex.append(external); + schema.insert("type", "custom"); } + schemaList.append(schema); } - - if (!in.empty()) - pyEffectSchema.insert("internal", in); - if (!ex.empty()) - pyEffectSchema.insert("external", ex); - - pyEffectSchemas = pyEffectSchema; - properties.insert("effectSchemas", pyEffectSchemas); + properties.insert("effectSchemas", schemaList); schemaJson.insert("properties", properties); @@ -960,9 +949,9 @@ void JsonAPI::handleSchemaGetCommand(const QJsonObject &message, const QString & sendSuccessDataReply(QJsonDocument(schemaJson), command, tan); } -void JsonAPI::handleComponentStateCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleComponentStateCommand(const QJsonObject& message, const QString& command, int tan) { - const QJsonObject &componentState = message["componentstate"].toObject(); + const QJsonObject& componentState = message["componentstate"].toObject(); QString comp = componentState["component"].toString("invalid"); bool compState = componentState["state"].toBool(true); QString replyMsg; @@ -975,7 +964,7 @@ void JsonAPI::handleComponentStateCommand(const QJsonObject &message, const QStr sendSuccessReply(command, tan); } -void JsonAPI::handleLedColorsCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString& command, int tan) { // create result QString subcommand = message["subcommand"].toString(""); @@ -989,22 +978,22 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject &message, const QString & _streaming_leds_reply["command"] = command + "-ledstream-update"; _streaming_leds_reply["tan"] = tan; - connect(_hyperion, &Hyperion::rawLedColors, this, [=](const std::vector &ledValues) { + connect(_hyperion, &Hyperion::rawLedColors, this, [=](const std::vector& ledValues) { _currentLedValues = ledValues; // necessary because Qt::UniqueConnection for lambdas does not work until 5.9 // see: https://bugreports.qt.io/browse/QTBUG-52438 if (!_ledStreamConnection) _ledStreamConnection = connect(_ledStreamTimer, &QTimer::timeout, this, [=]() { - emit streamLedcolorsUpdate(_currentLedValues); - }, - Qt::UniqueConnection); + emit streamLedcolorsUpdate(_currentLedValues); + }, + Qt::UniqueConnection); // start the timer if (!_ledStreamTimer->isActive() || _ledStreamTimer->interval() != streaming_interval) _ledStreamTimer->start(streaming_interval); - }, - Qt::UniqueConnection); + }, + Qt::UniqueConnection); // push once _hyperion->update(); } @@ -1034,7 +1023,7 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject &message, const QString & sendSuccessReply(command + "-" + subcommand, tan); } -void JsonAPI::handleLoggingCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleLoggingCommand(const QJsonObject& message, const QString& command, int tan) { // create result QString subcommand = message["subcommand"].toString(""); @@ -1076,25 +1065,25 @@ void JsonAPI::handleLoggingCommand(const QJsonObject &message, const QString &co } } -void JsonAPI::handleProcessingCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleProcessingCommand(const QJsonObject& message, const QString& command, int tan) { API::setLedMappingType(ImageProcessor::mappingTypeToInt(message["mappingType"].toString("multicolor_mean"))); sendSuccessReply(command, tan); } -void JsonAPI::handleVideoModeCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleVideoModeCommand(const QJsonObject& message, const QString& command, int tan) { API::setVideoMode(parse3DMode(message["videoMode"].toString("2D"))); sendSuccessReply(command, tan); } -void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleAuthorizeCommand(const QJsonObject& message, const QString& command, int tan) { - const QString &subc = message["subcommand"].toString().trimmed(); - const QString &id = message["id"].toString().trimmed(); - const QString &password = message["password"].toString().trimmed(); - const QString &newPassword = message["newPassword"].toString().trimmed(); - const QString &comment = message["comment"].toString().trimmed(); + const QString& subc = message["subcommand"].toString().trimmed(); + const QString& id = message["id"].toString().trimmed(); + const QString& password = message["password"].toString().trimmed(); + const QString& newPassword = message["newPassword"].toString().trimmed(); + const QString& comment = message["comment"].toString().trimmed(); // catch test if auth is required if (subc == "tokenRequired") @@ -1205,8 +1194,8 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString & if (subc == "requestToken") { // use id/comment - const QString &comment = message["comment"].toString().trimmed(); - const bool &acc = message["accept"].toBool(true); + const QString& comment = message["comment"].toString().trimmed(); + const bool& acc = message["accept"].toBool(true); if (acc) API::setNewTokenRequest(comment, id, tan); else @@ -1222,7 +1211,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString & if (API::getPendingTokenRequests(vec)) { QJsonArray arr; - for (const auto &entry : vec) + for (const auto& entry : vec) { QJsonObject obj; obj["comment"] = entry.comment; @@ -1244,7 +1233,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString & if (subc == "answerRequest") { // use id - const bool &accept = message["accept"].toBool(false); + const bool& accept = message["accept"].toBool(false); if (!API::handlePendingTokenRequest(id, accept)) sendErrorReply("No Authorization", command + "-" + subc, tan); return; @@ -1257,7 +1246,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString & if (API::getTokenList(defVect)) { QJsonArray tArr; - for (const auto &entry : defVect) + for (const auto& entry : defVect) { QJsonObject subO; subO["comment"] = entry.comment; @@ -1276,7 +1265,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString & // login if (subc == "login") { - const QString &token = message["token"].toString().trimmed(); + const QString& token = message["token"].toString().trimmed(); // catch token if (!token.isEmpty()) @@ -1324,11 +1313,11 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString & } } -void JsonAPI::handleInstanceCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleInstanceCommand(const QJsonObject& message, const QString& command, int tan) { - const QString &subc = message["subcommand"].toString(); - const quint8 &inst = message["instance"].toInt(); - const QString &name = message["name"].toString(); + const QString& subc = message["subcommand"].toString(); + const quint8& inst = message["instance"].toInt(); + const QString& name = message["name"].toString(); if (subc == "switchTo") { @@ -1345,7 +1334,7 @@ void JsonAPI::handleInstanceCommand(const QJsonObject &message, const QString &c if (subc == "startInstance") { - connect(this, &API::onStartInstanceResponse, [=] (const int &tan) { sendSuccessReply(command + "-" + subc, tan); }); + connect(this, &API::onStartInstanceResponse, [=](const int& tan) { sendSuccessReply(command + "-" + subc, tan); }); if (!API::startInstance(inst, tan)) sendErrorReply("Can't start Hyperion instance index " + QString::number(inst), command + "-" + subc, tan); @@ -1395,12 +1384,12 @@ void JsonAPI::handleInstanceCommand(const QJsonObject &message, const QString &c } } -void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString &command, int tan) +void JsonAPI::handleLedDeviceCommand(const QJsonObject& message, const QString& command, int tan) { - Debug(_log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData() ); + Debug(_log, "message: [%s]", QString(QJsonDocument(message).toJson(QJsonDocument::Compact)).toUtf8().constData()); - const QString &subc = message["subcommand"].toString().trimmed(); - const QString &devType = message["ledDeviceType"].toString().trimmed(); + const QString& subc = message["subcommand"].toString().trimmed(); + const QString& devType = message["ledDeviceType"].toString().trimmed(); QString full_command = command + "-" + subc; @@ -1410,7 +1399,7 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString & sendErrorReply("Unknown device", full_command, tan); } else -*/ { +*/ { QJsonObject config; config.insert("type", devType); LedDevice* ledDevice = nullptr; @@ -1418,27 +1407,27 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString & if (subc == "discover") { ledDevice = LedDeviceFactory::construct(config); - const QJsonObject ¶ms = message["params"].toObject(); + const QJsonObject& params = message["params"].toObject(); const QJsonObject devicesDiscovered = ledDevice->discover(params); - Debug(_log, "response: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() ); + Debug(_log, "response: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); sendSuccessDataReply(QJsonDocument(devicesDiscovered), full_command, tan); } else if (subc == "getProperties") { ledDevice = LedDeviceFactory::construct(config); - const QJsonObject ¶ms = message["params"].toObject(); + const QJsonObject& params = message["params"].toObject(); const QJsonObject deviceProperties = ledDevice->getProperties(params); - Debug(_log, "response: [%s]", QString(QJsonDocument(deviceProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() ); + Debug(_log, "response: [%s]", QString(QJsonDocument(deviceProperties).toJson(QJsonDocument::Compact)).toUtf8().constData()); sendSuccessDataReply(QJsonDocument(deviceProperties), full_command, tan); } else if (subc == "identify") { ledDevice = LedDeviceFactory::construct(config); - const QJsonObject ¶ms = message["params"].toObject(); + const QJsonObject& params = message["params"].toObject(); ledDevice->identify(params); sendSuccessReply(full_command, tan); @@ -1452,12 +1441,12 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString & } } -void JsonAPI::handleNotImplemented(const QString &command, int tan) +void JsonAPI::handleNotImplemented(const QString& command, int tan) { sendErrorReply("Command not implemented", command, tan); } -void JsonAPI::sendSuccessReply(const QString &command, int tan) +void JsonAPI::sendSuccessReply(const QString& command, int tan) { // create reply QJsonObject reply; @@ -1469,7 +1458,7 @@ void JsonAPI::sendSuccessReply(const QString &command, int tan) emit callbackMessage(reply); } -void JsonAPI::sendSuccessDataReply(const QJsonDocument &doc, const QString &command, int tan) +void JsonAPI::sendSuccessDataReply(const QJsonDocument& doc, const QString& command, int tan) { QJsonObject reply; reply["success"] = true; @@ -1483,7 +1472,7 @@ void JsonAPI::sendSuccessDataReply(const QJsonDocument &doc, const QString &comm emit callbackMessage(reply); } -void JsonAPI::sendErrorReply(const QString &error, const QString &command, int tan) +void JsonAPI::sendErrorReply(const QString& error, const QString& command, int tan) { // create reply QJsonObject reply; @@ -1496,12 +1485,12 @@ void JsonAPI::sendErrorReply(const QString &error, const QString &command, int t emit callbackMessage(reply); } -void JsonAPI::streamLedcolorsUpdate(const std::vector &ledColors) +void JsonAPI::streamLedcolorsUpdate(const std::vector& ledColors) { QJsonObject result; QJsonArray leds; - for (const auto &color : ledColors) + for (const auto& color : ledColors) { leds << QJsonValue(color.red) << QJsonValue(color.green) << QJsonValue(color.blue); } @@ -1513,9 +1502,9 @@ void JsonAPI::streamLedcolorsUpdate(const std::vector &ledColors) emit callbackMessage(_streaming_leds_reply); } -void JsonAPI::setImage(const Image &image) +void JsonAPI::setImage(const Image& image) { - QImage jpgImage((const uint8_t *)image.memptr(), image.width(), image.height(), 3 * image.width(), QImage::Format_RGB888); + QImage jpgImage((const uint8_t*)image.memptr(), image.width(), image.height(), 3 * image.width(), QImage::Format_RGB888); QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); @@ -1527,7 +1516,7 @@ void JsonAPI::setImage(const Image &image) emit callbackMessage(_streaming_image_reply); } -void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE &msg) +void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE& msg) { QJsonObject result, message; QJsonArray messageArray; @@ -1535,7 +1524,7 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE &msg) if (!_streaming_logging_activated) { _streaming_logging_activated = true; - const QList *logBuffer = LoggerManager::getInstance()->getLogMessageBuffer(); + const QList* logBuffer = LoggerManager::getInstance()->getLogMessageBuffer(); for (int i = 0; i < logBuffer->length(); i++) { message["appName"] = logBuffer->at(i).appName; @@ -1571,7 +1560,7 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE &msg) emit callbackMessage(_streaming_logging_reply); } -void JsonAPI::newPendingTokenRequest(const QString &id, const QString &comment) +void JsonAPI::newPendingTokenRequest(const QString& id, const QString& comment) { QJsonObject obj; obj["comment"] = comment; @@ -1581,7 +1570,7 @@ void JsonAPI::newPendingTokenRequest(const QString &id, const QString &comment) sendSuccessDataReply(QJsonDocument(obj), "authorize-tokenRequest", 1); } -void JsonAPI::handleTokenResponse(bool success, const QString &token, const QString &comment, const QString &id, const int &tan) +void JsonAPI::handleTokenResponse(bool success, const QString& token, const QString& comment, const QString& id, const int& tan) { const QString cmd = "authorize-requestToken"; QJsonObject result; @@ -1595,7 +1584,7 @@ void JsonAPI::handleTokenResponse(bool success, const QString &token, const QStr sendErrorReply("Token request timeout or denied", cmd, tan); } -void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, const QString &name) +void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name) { switch (state) { diff --git a/libsrc/effectengine/EffectFileHandler.cpp b/libsrc/effectengine/EffectFileHandler.cpp index d3d07dd5..6e4dc1df 100644 --- a/libsrc/effectengine/EffectFileHandler.cpp +++ b/libsrc/effectengine/EffectFileHandler.cpp @@ -11,10 +11,10 @@ #include // createEffect helper -struct find_schema: std::unary_function +struct find_schema : std::unary_function { QString pyFile; - find_schema(QString pyFile):pyFile(pyFile) { } + find_schema(QString pyFile) :pyFile(std::move(pyFile)) { } bool operator()(EffectSchema const& schema) const { return schema.pyFile == pyFile; @@ -22,10 +22,10 @@ struct find_schema: std::unary_function }; // deleteEffect helper -struct find_effect: std::unary_function +struct find_effect : std::unary_function { QString effectName; - find_effect(QString effectName) :effectName(effectName) { } + find_effect(QString effectName) :effectName(std::move(effectName)) { } bool operator()(EffectDefinition const& effectDefinition) const { return effectDefinition.name == effectName; @@ -36,7 +36,6 @@ EffectFileHandler* EffectFileHandler::efhInstance; EffectFileHandler::EffectFileHandler(const QString& rootPath, const QJsonDocument& effectConfig, QObject* parent) : QObject(parent) - , _effectConfig() , _log(Logger::getInstance("EFFECTFILES")) , _rootPath(rootPath) { @@ -50,7 +49,7 @@ EffectFileHandler::EffectFileHandler(const QString& rootPath, const QJsonDocumen void EffectFileHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config) { - if(type == settings::EFFECTS) + if (type == settings::EFFECTS) { _effectConfig = config.object(); // update effects and schemas @@ -67,15 +66,17 @@ QString EffectFileHandler::deleteEffect(const QString& effectName) if (it != effectsDefinition.end()) { QFileInfo effectConfigurationFile(it->file); - if (effectConfigurationFile.absoluteFilePath().mid(0, 1) != ":" ) + if (!effectConfigurationFile.absoluteFilePath().startsWith(':')) { if (effectConfigurationFile.exists()) { - if ( (it->script == ":/effects/gif.py") && !it->args.value("image").toString("").isEmpty()) + if ((it->script == ":/effects/gif.py") && !it->args.value("image").toString("").isEmpty()) { - QFileInfo effectImageFile(effectConfigurationFile.absolutePath() + "/" + it->args.value("image").toString()); - if (effectImageFile.exists()) - QFile::remove(effectImageFile.absoluteFilePath()); + QFileInfo effectImageFile(it->args.value("image").toString()); + if (effectImageFile.exists()) + { + QFile::remove(effectImageFile.absoluteFilePath()); + } } bool result = QFile::remove(effectConfigurationFile.absoluteFilePath()); @@ -83,15 +84,27 @@ QString EffectFileHandler::deleteEffect(const QString& effectName) if (result) { updateEffects(); - return ""; - } else + resultMsg = ""; + } + else + { resultMsg = "Can't delete effect configuration file: " + effectConfigurationFile.absoluteFilePath() + ". Please check permissions"; - } else + } + } + else + { resultMsg = "Can't find effect configuration file: " + effectConfigurationFile.absoluteFilePath(); - } else + } + } + else + { resultMsg = "Can't delete internal effect: " + effectName; - } else + } + } + else + { resultMsg = "Effect " + effectName + " not found"; + } return resultMsg; } @@ -101,17 +114,14 @@ QString EffectFileHandler::saveEffect(const QJsonObject& message) QString resultMsg; if (!message["args"].toObject().isEmpty()) { - QString scriptName; - (message["script"].toString().mid(0, 1) == ":" ) - ? scriptName = ":/effects//" + message["script"].toString().mid(1) - : scriptName = message["script"].toString(); + QString scriptName = message["script"].toString(); std::list effectsSchemas = getEffectSchemas(); std::list::iterator it = std::find_if(effectsSchemas.begin(), effectsSchemas.end(), find_schema(scriptName)); if (it != effectsSchemas.end()) { - if(!JsonUtils::validate("EffectFileHandler", message["args"].toObject(), it->schemaFile, _log)) + if (!JsonUtils::validate("EffectFileHandler", message["args"].toObject(), it->schemaFile, _log)) { return "Error during arg validation against schema, please consult the Hyperion Log"; } @@ -120,9 +130,9 @@ QString EffectFileHandler::saveEffect(const QJsonObject& message) QJsonArray effectArray; effectArray = _effectConfig["paths"].toArray(); - if (effectArray.size() > 0) + if (!effectArray.empty()) { - if (message["name"].toString().trimmed().isEmpty() || message["name"].toString().trimmed().startsWith(".")) + if (message["name"].toString().trimmed().isEmpty() || message["name"].toString().trimmed().startsWith(":")) { return "Can't save new effect. Effect name is empty or begins with a dot."; } @@ -138,41 +148,56 @@ QString EffectFileHandler::saveEffect(const QJsonObject& message) if (iter != availableEffects.end()) { newFileName.setFile(iter->file); - if (newFileName.absoluteFilePath().mid(0, 1) == ":") + if (newFileName.absoluteFilePath().startsWith(':')) { - return "The effect name '" + message["name"].toString() + "' is assigned to an internal effect. Please rename your effekt."; + return "The effect name '" + message["name"].toString() + "' is assigned to an internal effect. Please rename your effect."; } - } else + } + else { - // TODO global special keyword handling - QString f = effectArray[0].toString().replace("$ROOT",_rootPath) + "/" + message["name"].toString().replace(QString(" "), QString("")) + QString(".json"); + QString f = effectArray[0].toString().replace("$ROOT", _rootPath) + '/' + message["name"].toString().replace(QString(" "), QString("")) + QString(".json"); newFileName.setFile(f); } - //TODO check if filename exist if (!message["imageData"].toString("").isEmpty() && !message["args"].toObject().value("image").toString("").isEmpty()) { - QFileInfo imageFileName(effectArray[0].toString().replace("$ROOT",_rootPath) + "/" + message["args"].toObject().value("image").toString()); - if(!FileUtils::writeFile(imageFileName.absoluteFilePath(), QByteArray::fromBase64(message["imageData"].toString("").toUtf8()), _log)) + QJsonObject args = message["args"].toObject(); + QString imageFilePath = effectArray[0].toString().replace("$ROOT", _rootPath) + '/' + args.value("image").toString(); + + QFileInfo imageFileName(imageFilePath); + if (!FileUtils::writeFile(imageFileName.absoluteFilePath(), QByteArray::fromBase64(message["imageData"].toString("").toUtf8()), _log)) { return "Error while saving image file '" + message["args"].toObject().value("image").toString() + ", please check the Hyperion Log"; } + + //Update json with image file location + args["image"] = imageFilePath; + effectJson["args"] = args; } - if(!JsonUtils::write(newFileName.absoluteFilePath(), effectJson, _log)) + if (!JsonUtils::write(newFileName.absoluteFilePath(), effectJson, _log)) { return "Error while saving effect, please check the Hyperion Log"; } Info(_log, "Reload effect list"); updateEffects(); - return ""; - } else + resultMsg = ""; + } + else + { resultMsg = "Can't save new effect. Effect path empty"; - } else + } + } + else + { resultMsg = "Missing schema file for Python script " + message["script"].toString(); - } else + } + } + else + { resultMsg = "Missing or empty Object 'args'"; + } return resultMsg; } @@ -184,50 +209,56 @@ void EffectFileHandler::updateEffects() _effectSchemas.clear(); // read all effects - const QJsonArray & paths = _effectConfig["paths"].toArray(); - const QJsonArray & disabledEfx = _effectConfig["disable"].toArray(); + const QJsonArray& paths = _effectConfig["paths"].toArray(); + const QJsonArray& disabledEfx = _effectConfig["disable"].toArray(); QStringList efxPathList; efxPathList << ":/effects/"; QStringList disableList; - for(auto p : paths) + for (const auto& p : paths) { - efxPathList << p.toString().replace("$ROOT",_rootPath); + QString effectPath = p.toString(); + if (!effectPath.endsWith('/')) + { + effectPath.append('/'); + } + efxPathList << effectPath.replace("$ROOT", _rootPath); } - for(auto efx : disabledEfx) + + for (const auto& efx : disabledEfx) { disableList << efx.toString(); } QMap availableEffects; - for (const QString & path : efxPathList ) + for (const QString& path : qAsConst(efxPathList)) { QDir directory(path); if (!directory.exists()) { - if(directory.mkpath(path)) + if (directory.mkpath(path)) { - Info(_log, "New Effect path \"%s\" created successfully", QSTRING_CSTR(path) ); + Info(_log, "New Effect path \"%s\" created successfully", QSTRING_CSTR(path)); } else { - Warning(_log, "Failed to create Effect path \"%s\", please check permissions", QSTRING_CSTR(path) ); + Warning(_log, "Failed to create Effect path \"%s\", please check permissions", QSTRING_CSTR(path)); } } else { int efxCount = 0; QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase); - for (const QString & filename : filenames) + for (const QString& filename : qAsConst(filenames)) { EffectDefinition def; if (loadEffectDefinition(path, filename, def)) { InfoIf(availableEffects.find(def.name) != availableEffects.end(), _log, - "effect overload effect '%s' is now taken from '%s'", QSTRING_CSTR(def.name), QSTRING_CSTR(path) ); + "effect overload effect '%s' is now taken from '%s'", QSTRING_CSTR(def.name), QSTRING_CSTR(path)); - if ( disableList.contains(def.name) ) + if (disableList.contains(def.name)) { Info(_log, "effect '%s' not loaded, because it is disabled in hyperion config", QSTRING_CSTR(def.name)); } @@ -242,64 +273,65 @@ void EffectFileHandler::updateEffects() // collect effect schemas efxCount = 0; - directory.setPath(path.endsWith("/") ? (path + "schema/") : (path + "/schema/")); - QStringList pynames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase); - for (const QString & pyname : pynames) + + QString schemaPath = path + "schema" + '/'; + directory.setPath(schemaPath); + QStringList schemaFileNames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase); + for (const QString& schemaFileName : qAsConst(schemaFileNames)) { EffectSchema pyEffect; - if (loadEffectSchema(path, pyname, pyEffect)) + if (loadEffectSchema(path, directory.filePath(schemaFileName), pyEffect)) { _effectSchemas.push_back(pyEffect); efxCount++; } } - InfoIf(efxCount > 0, _log, "%d effect schemas loaded from directory %s", efxCount, QSTRING_CSTR((path + "schema/"))); + InfoIf(efxCount > 0, _log, "%d effect schemas loaded from directory %s", efxCount, QSTRING_CSTR(schemaPath)); } } - for(auto item : availableEffects) + for (const auto& item : qAsConst(availableEffects)) { _availableEffects.push_back(item); } - ErrorIf(_availableEffects.size()==0, _log, "no effects found, check your effect directories"); + ErrorIf(_availableEffects.empty(), _log, "no effects found, check your effect directories"); emit effectListChanged(); } -bool EffectFileHandler::loadEffectDefinition(const QString &path, const QString &effectConfigFile, EffectDefinition & effectDefinition) +bool EffectFileHandler::loadEffectDefinition(const QString& path, const QString& effectConfigFile, EffectDefinition& effectDefinition) { - QString fileName = path + QDir::separator() + effectConfigFile; + QString fileName = path + effectConfigFile; // Read and parse the effect json config file QJsonObject configEffect; - if(!JsonUtils::readFile(fileName, configEffect, _log)) + if (!JsonUtils::readFile(fileName, configEffect, _log)) { return false; + } // validate effect config with effect schema(path) - if(!JsonUtils::validate(fileName, configEffect, ":effect-schema", _log)) + if (!JsonUtils::validate(fileName, configEffect, ":effect-schema", _log)) { return false; + } // setup the definition effectDefinition.file = fileName; QJsonObject config = configEffect; QString scriptName = config["script"].toString(); effectDefinition.name = config["name"].toString(); - if (scriptName.isEmpty()) + if (scriptName.isEmpty()) { return false; + } QFile fileInfo(scriptName); - - if (scriptName.mid(0, 1) == ":" ) + if (!fileInfo.exists()) { - (!fileInfo.exists()) - ? effectDefinition.script = ":/effects/"+scriptName.mid(1) - : effectDefinition.script = scriptName; - } else + effectDefinition.script = path + scriptName; + } + else { - (!fileInfo.exists()) - ? effectDefinition.script = path + QDir::separator() + scriptName - : effectDefinition.script = scriptName; + effectDefinition.script = scriptName; } effectDefinition.args = config["args"].toObject(); @@ -307,31 +339,31 @@ bool EffectFileHandler::loadEffectDefinition(const QString &path, const QString return true; } -bool EffectFileHandler::loadEffectSchema(const QString &path, const QString &effectSchemaFile, EffectSchema & effectSchema) +bool EffectFileHandler::loadEffectSchema(const QString& path, const QString& schemaFilePath, EffectSchema& effectSchema) { - QString fileName = path + "schema/" + QDir::separator() + effectSchemaFile; - // Read and parse the effect schema file QJsonObject schemaEffect; - if(!JsonUtils::readFile(fileName, schemaEffect, _log)) - return false; - - // setup the definition - QString scriptName = schemaEffect["script"].toString(); - effectSchema.schemaFile = fileName; - fileName = path + QDir::separator() + scriptName; - QFile pyFile(fileName); - - if (scriptName.isEmpty() || !pyFile.open(QIODevice::ReadOnly)) + if (!JsonUtils::readFile(schemaFilePath, schemaEffect, _log)) { - fileName = path + "schema/" + QDir::separator() + effectSchemaFile; - Error( _log, "Python script '%s' in effect schema '%s' could not be loaded", QSTRING_CSTR(scriptName), QSTRING_CSTR(fileName)); return false; } - pyFile.close(); + // setup the definition + QString scriptName = schemaEffect["script"].toString(); + effectSchema.schemaFile = schemaFilePath; - effectSchema.pyFile = (scriptName.mid(0, 1) == ":" ) ? ":/effects/"+scriptName.mid(1) : path + QDir::separator() + scriptName; + QString scriptFilePath = path + scriptName; + QFile pyScriptFile(scriptFilePath); + + if (scriptName.isEmpty() || !pyScriptFile.open(QIODevice::ReadOnly)) + { + Error(_log, "Python script '%s' in effect schema '%s' could not be loaded", QSTRING_CSTR(scriptName), QSTRING_CSTR(schemaFilePath)); + return false; + } + + pyScriptFile.close(); + + effectSchema.pyFile = scriptFilePath; effectSchema.pySchema = schemaEffect; return true; diff --git a/libsrc/webserver/WebSocketClient.cpp b/libsrc/webserver/WebSocketClient.cpp index 0e522072..0d04d65f 100644 --- a/libsrc/webserver/WebSocketClient.cpp +++ b/libsrc/webserver/WebSocketClient.cpp @@ -85,6 +85,17 @@ void WebSocketClient::handleWebSocketFrame() case OPCODE::BINARY: case OPCODE::TEXT: { + // A fragmented message consists of a single frame with the FIN bit + // clear and an opcode other than 0, followed by zero or more frames + // with the FIN bit clear and the opcode set to 0, and terminated by + // a single frame with the FIN bit set and an opcode of 0. + // + // Store frame type given by first frame + if (_wsh.opCode != OPCODE::CONTINUATION ) + { + _frameOpCode = _wsh.opCode; + } + // check for protocol violations if (_onContinuation && !isContinuation) { @@ -117,15 +128,15 @@ void WebSocketClient::handleWebSocketFrame() if (_wsh.fin) { _onContinuation = false; - if (_wsh.opCode == OPCODE::TEXT) - { + if (_frameOpCode == OPCODE::TEXT) + { _jsonAPI->handleMessage(QString(_wsReceiveBuffer)); - } - else - { - handleBinaryMessage(_wsReceiveBuffer); - } + } + else + { + handleBinaryMessage(_wsReceiveBuffer); + } _wsReceiveBuffer.clear(); } diff --git a/libsrc/webserver/WebSocketClient.h b/libsrc/webserver/WebSocketClient.h index 5c90f8cb..0b056fb1 100644 --- a/libsrc/webserver/WebSocketClient.h +++ b/libsrc/webserver/WebSocketClient.h @@ -52,6 +52,9 @@ private: // websocket header store WebSocketHeader _wsh; + //opCode of first frame (in case of fragmented frames) + quint8 _frameOpCode; + // masks for fields in the basic header static uint8_t const BHB0_OPCODE = 0x0F; static uint8_t const BHB0_RSV3 = 0x10;