Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[servicemapper]: Allow channel name mapping to be configurable, fixes #5599 #1272

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

azlm8t
Copy link
Contributor

@azlm8t azlm8t commented Apr 11, 2019

Allow user to specify the name they want for a service that is mapped
by the service mapper. For example, the UK has numerous regional
satellite channels called "ITV" which sometimes show different
programmes such as regional news. These should really be called "ITV
(London)", "ITV1 (Yorkshire)", etc., to avoid being merged together.

Also we have some channels we do not want mapped at all based on
criteria such as regex. Although we could manually deselect such
channels, it is easier to do it automatically.

So we allow user to do this via an embedded JavaScript function.

We embed the MIT licence duktape 2.3.0 JavaScript
https://duktape.org/
https://wiki.duktape.org/Portability.html

The duktape webpage suggests RAM overhead is minimal at less
that 100kb.

We use the default configuration from the release and use it in the
service mapper to allow a user to specify their own script for
performing advanced mapping of services to channels.

For example, they might want to rename channels such as "SE: Fjorton"
to "Fjorton SE", or to capitalize the channel names. These were
examples given in the issue #4715.

Although this could be possible via more tickboxes, it seems sensible
to allow the user to use a script. But should this be JavaScript?

So we pass an object containing the channel name and service id to the
user's script. If they return a name then we use it. If they return
null then we do not map the service at all. This allows the user to
exclude channels in which they are not interested such as shopping.

We also add a JavaScript function "print" to allow the user to log a
single string to our logs. This uses the new log level LS_JS.

The overhead for calling JavaScript only occurs if the user specifies a
script to use.

Currently the script is given at the UI such as:

({smMapName : function(svc) { print(svc.name + '/' + svc.sid); return svc.name; }})

Or

({
  smMapName : function(svc) {
    print(svc.name + '/' + svc.sid); 
    var svcmap = {21000: 'ITV1 HD (London)', 21020: 'ITV1 HD (Meridian, Anglia)'};
    var name = svcmap[svc.sid];
    return typeof name == 'undefined' ? svc.name : name; 
  }
})

The service_mapper looks up the function name "smMapName" and then
calls it with the object with a name and sid property.

We currently don't allow the script to be given as a filename on disk,
nor do we attempt to optimize the compilation of such a script to only
evaluate it once per mapping (the overhead seems negligible). However,
we could add a LRU cache of compiled functions in the future if
necessary.

One gotcha from using the duktape API is that if you call
"duk_safe_to_string" to do debug logging of return values then it
appears this coerces the return type on the stack so a subsequent
"get" or "is null" check will return incorrect results.

So, if the JS function returned null then duk_is_null would return
true, but if you do a log of "duk_safe_to_string" afterwards and then
check "duk_is_null" then it would return false.

Fixes: #5599

@azlm8t
Copy link
Contributor Author

azlm8t commented Apr 11, 2019

I think we need to consider:

  • whether this patch is an interface we want to support going forward;
  • whether JavaScript and this implementation of JS are the ones we want;
  • is this the way we want to call and use JS;
  • should we just stick to shell script.

If there is to be a major release in the next few months, then perhaps
we should delay until after then so we have plenty of time to
experiment in the next dev cycle.

I thought an embedded language would be nice since it allows us to
easily pass more complicated objects to user scripts and easily get
back values and can be used even in fairly critical paths.

I ruled out:

  • Perl/Python due to their large runtimes/compile time complexities;
  • Lua since its syntax aims to be similar to Pascal and doesn't support regex;
  • Guile since it never seemed to attract a large user base;
  • mujs since duktape seemed to have better support on github;
  • various other JS that were C++ based.

Although we could use an anonymous JS function as the callback, I
thought having a named function might make it easier to extend in the
future. I don't expect many users will use complicated scripts, but
I imagine some small scripts would be shared in the forum and
just copied+pasted by other people.

The JS itself is a relatively small runtime, but it does provide the
basics such as regex, string manipulation, etc. Perhaps this is
enough. JS doesn't support 64-bit numbers and perhaps we will
have complaints that we can't do X,Y,Z and
have to add more FFI calls/implement more API ourselves making
it tedious to support. I don't know.

This feature could have been done using a shell-script, but I can see
merit in allowing extensions to be run internally to the server where
overhead of launching a script could be costly. For example advanced
autorec rules (some people want !=, >, < in various fields or want to
search title and description, etc.); or perhaps in OTA EIT tidy-up, etc.,
and it could be useful in "recording filename" since some people want
very specific naming.

We could also use it for adding an event system so users could hook
in for debug or other reasons. For example a

    TVHEVENT("dvr_autorec_create", node)

I suspect this JS would be easily fast enough for these cases even on
a Pi, and the portability page says it works on most architectures.

Going forward, I think we should probably allow the user to either
write a function or put the name of a file containing JavaScript to
load and compile using a simple test of whether the JS matches
"^/[^/*].*js". This would allow longer scripts.

As they said in Jurassic Park, "Your scientists were so preoccupied
with whether or not they could, they didn’t stop to think if they
should." So, although I think this patch is possibly a good thing,
I'm equally happy for it to be rejected or delayed, or even accepted
and reverted.

Allow user to specify the name they want for a service that is mapped
by the service mapper. For example, the UK has numerous regional
satellite channels called "ITV" which sometimes show different
programmes such as regional news.  These should really be called "ITV
(London)", "ITV1 (Yorkshire)", etc., to avoid being merged together.

Also we have some channels we do not want mapped at all based on
criteria such as regex. Although we could manually deselect such
channels, it is easier to do it automatically.

So we allow user to do this via an embedded JavaScript function.

We embed the MIT licence duktape 2.3.0 JavaScript
  https://duktape.org/
  https://wiki.duktape.org/Portability.html

The duktape webpage suggests RAM overhead is minimal at less
that 100kb.

We use the default configuration from the release and use it in the
service mapper to allow a user to specify their own script for
performing advanced mapping of services to channels.

For example, they might want to rename channels such as "SE: Fjorton"
to "Fjorton SE", or to capitalize the channel names. These were
examples given in the issue #4715.

Although this could be possible via more tickboxes, it seems sensible
to allow the user to use a script.

So we pass an object containing the channel name and service id to the
user's script. If they return a name then we use it. If they return
null then we do not map the service at all. This allows the user to
exclude channels in which they are not interested such as shopping.

We also add a JavaScript function "print" to allow the user to log a
single string to our logs. This uses the new log level LS_JS.

The overhead for calling JavaScript only occurs if the user specifies a
script to use.

Currently the script is given at the UI such as:

  ({smMapName : function(svc) { print(svc.name + '/' + svc.sid); return svc.name; }})
Or
  ({smMapName : function(svc) { print(svc.name + '/' + svc.sid); var svcmap={21000: 'ITV1 HD (London)', 21020: 'ITV1 HD (Meridian, Anglia)'}; var name=svcmap[svc.sid]; return typeof name == 'undefined' ? svc.name : name; }})

The service_mapper looks up the function name "smMapName" and then
calls it with the object.

We currently don't allow the script to be given as a filename on disk,
nor do we attempt to optimize the compilation of such a script to only
evaluate it once per mapping (the overhead seems negligible). However,
we could add a LRU cache of compiled functions in the future if
necessary.

One gotcha from using the duktape API is that if you call
"duk_safe_to_string" to do debug logging of return values then it
appears this coerces the return type on the stack so a subsequent
"get" or "is null" check will return incorrect results.

So, if the JS function returned null then duk_is_null would return
true, but if you do a log of "duk_safe_to_string" afterwards and then
check "duk_is_null" then it would return false.

Fixes: #5599
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants