Skip to content

Commit

Permalink
Scripting: print2 function with log level support
Browse files Browse the repository at this point in the history
  • Loading branch information
KarlStraussberger committed Jan 14, 2024
1 parent 49fa8cc commit 00561ed
Show file tree
Hide file tree
Showing 21 changed files with 192 additions and 382 deletions.
42 changes: 27 additions & 15 deletions doc/scripting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ This section will give you some overview on how virtual objects work and on how

.. Note::

In order to use the import scripting feature you have to change the layout type from builtin to js in ``config.xml``
In order to use the import scripting feature you have to change the attribute ``<virtual-layout type=..`` from ``builtin`` to ``js`` in ``config.xml``

.. Note::

Expand Down Expand Up @@ -309,10 +309,10 @@ transcoding are not available. The autoscan entry corresponding to the active ob

.. code-block:: js
print(config['/server/name']);
print(config['/import/library-options/id3/auxdata/add-data'][0]);
print(config['/import/layout/path']['Directories']);
print(config['/import/autoscan/directory'][object_autoscan_id].location);
print2("Info", config['/server/name']);
print2("Error", "Empty setting", config['/import/library-options/id3/auxdata/add-data'][0]);
print2("Warning", "Active mapping for", config['/import/layout/path']['Directories']);
print2("Debug", "Checking", config['/import/autoscan/directory'][object_autoscan_id].location);
Constants
Expand Down Expand Up @@ -427,38 +427,50 @@ within the import and/or the playlist script:

.. js:function:: print(...)

This function is useful for debugging scripts, it simply
prints to the standard output.
This function is useful for debugging scripts, it simply prints an info message in the log.
It it recommended to use `print2` instead to set the message level. The function may be removed in the future.

.. js:function:: f2i(string)
.. js:function:: print2(level, ...)

This function is useful for debugging scripts, it prints a message in the log with the set level.

:param string level: Set the output message level.
The following case insensitive values for ``level`` are allowed:

#) ``error``: Print an error message
#) ``warning``: Print a warning message
#) ``info``: Print an info message
#) ``debug``: Print a debug message, which is only visible if running with `--debug` or setting `debug-mode="script"` in `server` section of the config file.

.. js:function:: f2i(text)

Converts filesystem charset to internal UTF-8.

:param string string:
:param string text:

`The 'from' charsets can be defined in the server configuration`

.. js:function:: m2i(string)
.. js:function:: m2i(text)

Converts metadata charset to internal UTF-8.

:param string string:
:param string text:

`The 'from' charsets can be defined in the server configuration`

.. js:function:: p2i(string)
.. js:function:: p2i(text)

Converts playlist charset to internal UTF-8.

:param string string:
:param string text:

`The 'from' charsets can be defined in the server configuration`

.. js:function:: j2i(string)
.. js:function:: j2i(text)

Converts js charset to internal UTF-8.

:param string string:
:param string text:

`The 'from' charsets can be defined in the server configuration`

Expand Down
6 changes: 3 additions & 3 deletions scripts/js/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ function abcbox(stringtobox, boxtype, divchar) {
charttl = charttl + boxwidth[cb];
}
if (charttl != 26) {
print("Error in box-definition, length is " + charttl + ". Check the file common.js" );
print2("Error", "In box-definition, total length is " + charttl + ". Check the file common.js" );
// maybe an exit call here to stop processing the media ??
return "???";
}
Expand Down Expand Up @@ -1294,7 +1294,7 @@ function addPlaylistItem(playlist_title, playlistLocation, entry, playlistChain,

var cds = getCdsObject(entry.location);
if (!cds) {
print("Playlist " + playlist_title + " Skipping entry: " + entry.location);
print2("Warning", "Playlist '" + playlist_title + "' Skipping unknown entry: " + entry.location);
return false;
}

Expand All @@ -1307,7 +1307,7 @@ function addPlaylistItem(playlist_title, playlistLocation, entry, playlistChain,

item.extra = entry.extra;
item.writeThrough = entry.writeThrough;
// print("Playlist " + item.title + " Adding entry: " + item.playlistOrder + " " + item.location);
print2("Debug", "Playlist '" + item.title + "' Adding entry: " + item.playlistOrder + " " + item.location);

addCdsObject(item, playlistChain);
}
Expand Down
6 changes: 3 additions & 3 deletions scripts/js/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ function importItem(item, cont) {
case "object.item.textItem":
case "object.item.bookmarkItem":
case "object.item.playlistItem":
print("Unable to handle upnp class '" + item.upnpclass + "' for " + obj.location);
print2("Error", "Unable to handle upnp class '" + item.upnpclass + "' for " + obj.location);
break;
default:
print("Unable to handle unknown upnp class '" + item.upnpclass + "' for " + obj.location);
print2("Warning", "Unable to handle unknown upnp class '" + item.upnpclass + "' for " + obj.location);
if (mime === 'video' && obj.onlineservice === ONLINE_SERVICE_APPLE_TRAILERS) {
mime = 'trailer';
} else if (item.mimetype === 'application/ogg') {
Expand All @@ -94,7 +94,7 @@ function importItem(item, cont) {
addImage(obj, cont, object_script_path, grb_container_type_image);
break;
default:
print("Unable to handle mime type '" + item.mimetype + "' for " + obj.location);
print2("Error", "Unable to handle mime type '" + item.mimetype + "' for " + obj.location);
break;
}
break;
Expand Down
2 changes: 1 addition & 1 deletion scripts/js/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

function importMetadata(meta, cont, rootPath, autoscanId, containerType) {
const arr = rootPath.split('.');
print("Processing metafile: " + rootPath + " for " + meta.location + " " + arr[arr.length-1].toLowerCase());
print2("Info", "Processing metafile: " + rootPath + " for " + meta.location + " " + arr[arr.length-1].toLowerCase());
switch (arr[arr.length-1].toLowerCase()) {
case "nfo":
parseNfo(meta, rootPath);
Expand Down
6 changes: 3 additions & 3 deletions scripts/js/playlists.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@

function importPlaylist(obj, cont, rootPath, autoscanId, containerType) {
if (!obj || obj === undefined) {
print("Playlist undefined");
print2("Error", "Playlist undefined");
}

print("Processing playlist: " + obj.location);
print2("Info", "Processing playlist: " + obj.location);

const objLocation = obj.location.substring(0, obj.location.lastIndexOf('/') + 1);
const type = getPlaylistType(obj.mimetype);
Expand Down Expand Up @@ -77,7 +77,7 @@ function importPlaylist(obj, cont, rootPath, autoscanId, containerType) {
}

if (type === '') {
print("Unknown playlist mimetype: '" + obj.mimetype + "' of playlist '" + obj.location + "'");
print2("Error", "Unknown playlist mimetype: '" + obj.mimetype + "' of playlist '" + obj.location + "'");
} else if (type === 'm3u') {
readM3uPlaylist(obj_title, objLocation, objChain, objDirChain);
} else if (type === 'pls') {
Expand Down
26 changes: 24 additions & 2 deletions src/content/scripting/js_functions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,35 @@

// extern "C" {

duk_ret_t js_print2(duk_context* ctx)
{
std::string mode = toUpper(duk_get_string(ctx, -1));
duk_pop(ctx);
duk_push_string(ctx, " ");
duk_insert(ctx, 0);
duk_join(ctx, duk_get_top(ctx) - 1);
std::string message = duk_get_string(ctx, 0);
if (mode == "ERROR")
log_error("{}", message);
else if (mode == "WARNING")
log_warning("{}", message);
else if (mode == "DEBUG")
log_debug("{}", message);
else
log_info("{}", message);
duk_push_string(ctx, fmt::format("{}: {}", mode, message).c_str());
return 1;
}

duk_ret_t js_print(duk_context* ctx)
{
duk_push_string(ctx, " ");
duk_insert(ctx, 0);
duk_join(ctx, duk_get_top(ctx) - 1);
log_debug("{}", duk_get_string(ctx, 0));
return 0;
auto message = duk_get_string(ctx, 0);
log_info("{}", message);
duk_push_string(ctx, message);
return 1;
}

duk_ret_t js_copyObject(duk_context* ctx)
Expand Down
2 changes: 2 additions & 0 deletions src/content/scripting/js_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

extern "C" {

/// \brief Log output with log level.
duk_ret_t js_print2(duk_context* ctx);
/// \brief Log output.
duk_ret_t js_print(duk_context* ctx);

Expand Down
7 changes: 4 additions & 3 deletions src/content/scripting/script.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#endif

static constexpr auto jsGlobalFunctions = std::array {
duk_function_list_entry { "print2", js_print2, DUK_VARARGS },
duk_function_list_entry { "print", js_print, DUK_VARARGS },
duk_function_list_entry { "addCdsObject", js_addCdsObject, 3 },
duk_function_list_entry { "addContainerTree", js_addContainerTree, 1 },
Expand Down Expand Up @@ -337,15 +338,15 @@ Script::Script(const std::shared_ptr<ContentManager>& content, const std::string
std::string commonFdrPath = config->getOption(CFG_IMPORT_SCRIPTING_COMMON_FOLDER);

if (commonScrPath.empty() && commonFdrPath.empty()) {
log_js("Common script disabled in configuration");
log_warning("Common script disabled in configuration");
} else if (commonScrPath.empty()) {
loadFolder(commonFdrPath);
} else {
try {
_load(commonScrPath);
_execute();
} catch (const std::runtime_error& e) {
log_js("Unable to load {}: {}", commonScrPath, e.what());
log_error("Unable to load {}: {}", commonScrPath, e.what());
}
}
std::string customScrPath = config->getOption(CFG_IMPORT_SCRIPTING_CUSTOM_SCRIPT);
Expand All @@ -355,7 +356,7 @@ Script::Script(const std::shared_ptr<ContentManager>& content, const std::string
_load(customScrPath);
_execute();
} catch (const std::runtime_error& e) {
log_js("Unable to load {}: {}", customScrPath, e.what());
log_error("Unable to load {}: {}", customScrPath, e.what());
}
} else if (!customFdrPath.empty()) {
loadFolder(customFdrPath);
Expand Down
1 change: 0 additions & 1 deletion src/util/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
#define log_info SPDLOG_INFO
#define log_warning SPDLOG_WARN
#define log_error SPDLOG_ERROR
#define log_js SPDLOG_INFO

enum class log_facility_t {
thread,
Expand Down
2 changes: 2 additions & 0 deletions test/scripting/mock/common_script_mock.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class CommonScriptInterface {
virtual ~CommonScriptInterface() = default;
virtual duk_ret_t getPlaylistType(std::string type) = 0;
virtual duk_ret_t print(std::string text) = 0;
virtual duk_ret_t print2(std::string mode, std::string text) = 0;
virtual duk_ret_t addContainerTree(std::vector<std::string> tree) = 0;
virtual duk_ret_t createContainerChain(std::vector<std::string> chain) = 0;
virtual duk_ret_t getLastPath(std::string path) = 0;
Expand All @@ -52,6 +53,7 @@ class CommonScriptMock : public CommonScriptInterface {
public:
MOCK_METHOD1(getPlaylistType, duk_ret_t(std::string type));
MOCK_METHOD1(print, duk_ret_t(std::string text));
MOCK_METHOD2(print2, duk_ret_t(std::string mode, std::string text));
MOCK_METHOD1(addContainerTree, duk_ret_t(std::vector<std::string> tree));
MOCK_METHOD1(createContainerChain, duk_ret_t(std::vector<std::string> chain));
MOCK_METHOD1(getLastPath, duk_ret_t(std::string path));
Expand Down
9 changes: 9 additions & 0 deletions test/scripting/mock/script_test_fixture.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Gerbera - https://gerbera.io/
#include "duk_helper.h"
#include "script_test_fixture.h"

std::unique_ptr<CommonScriptMock> CommonScriptTestFixture::commonScriptMock;

void ScriptTestFixture::SetUp()
{
ctx = duk_create_heap(nullptr, nullptr, nullptr, nullptr, nullptr);
Expand Down Expand Up @@ -522,6 +524,13 @@ std::string ScriptTestFixture::getPlaylistType(duk_context* ctx)
return playlistMimeType;
}

std::tuple<std::string, std::string> ScriptTestFixture::print2(duk_context* ctx)
{
std::string mode = duk_to_string(ctx, 0);
std::string result = duk_to_string(ctx, 1);
return {mode, result};
}

std::string ScriptTestFixture::print(duk_context* ctx)
{
std::string result = duk_to_string(ctx, 0);
Expand Down
47 changes: 47 additions & 0 deletions test/scripting/mock/script_test_fixture.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include <tuple>

#include "util/string_converter.h"

Expand Down Expand Up @@ -132,6 +133,7 @@ class ScriptTestFixture : public ::testing::Test {
// Mimics the print of text
// Returns the string sent by the script to print
static std::string print(duk_context* ctx);
static std::tuple<std::string, std::string> print2(duk_context* ctx);

// Proxy the common.js script with `getYear`
// Mimics the parsing of YYYY
Expand Down Expand Up @@ -175,5 +177,50 @@ class ScriptTestFixture : public ::testing::Test {
duk_context* ctx;
};

class CommonScriptTestFixture : public ScriptTestFixture {
public:
// As Duktape requires static methods, so must the mock expectations be
static std::unique_ptr<CommonScriptMock> commonScriptMock;

CommonScriptTestFixture()
{
commonScriptMock = std::make_unique<::testing::NiceMock<CommonScriptMock>>();
}

~CommonScriptTestFixture() override
{
commonScriptMock.reset();
}

static inline duk_ret_t js_print(duk_context* ctx)
{
std::string msg = ScriptTestFixture::print(ctx);
return CommonScriptTestFixture::commonScriptMock->print(msg);
}

static inline duk_ret_t js_print2(duk_context* ctx)
{
auto [mode, msg] = ScriptTestFixture::print2(ctx);
return CommonScriptTestFixture::commonScriptMock->print2(mode, msg);
}

static inline duk_ret_t js_getPlaylistType(duk_context* ctx)
{
std::string playlistMimeType = ScriptTestFixture::getPlaylistType(ctx);
return CommonScriptTestFixture::commonScriptMock->getPlaylistType(playlistMimeType);
}

static inline duk_ret_t js_createContainerChain(duk_context* ctx)
{
std::vector<std::string> array = ScriptTestFixture::createContainerChain(ctx);
return CommonScriptTestFixture::commonScriptMock->createContainerChain(array);
}

static inline duk_ret_t js_getLastPath(duk_context* ctx)
{
std::string inputPath = ScriptTestFixture::getLastPath(ctx);
return CommonScriptTestFixture::commonScriptMock->getLastPath(inputPath);
}
};
#endif //GERBERA_SCRIPTTESTFIXTURE_H
#endif //HAVE_JS

0 comments on commit 00561ed

Please sign in to comment.