Anthony Musgrove 171 Posted May 19, 2020 Share Posted May 19, 2020 (edited) Hi guys, I just thought I'd introduce the latest feature of Emby Scripter-X (v2.3.5+) out now on the catalog. Emby Scripter-X Webhooks Why? I notice that a lot of Scripter-X users use the plugin to perform some type of webhook(s) based on certain events, so why not natively support it? So how does it work? Pretty much as-per-usual with the Actions interface. The interface will eventually change input field labels based on the selection in the interpreter box, but it is as above: Run [script], where [script] is the URL of your webhook script (webhook endpoint/api endpoint, etc), in my example above, I have a php script running on my local webserver at http://192.168.1.10/hook.php. [interpreter] should be set to web:post (to post data), web:get is also available, however it does exactly that - doesn't post data. [Parameters] instead of listing parameters to send over the command-line, this is the path to your json content template - where your data is formatted from. Here I specify d:\embyscripts\hooktemplates\authenticationfailed.json, and in this file is the following content: { "user": { "username": "%username%", "password": "%password%" }, "device": { "id": "%device.id%", "name": "%device.name%", "ip_address": "%device.remote.ipaddress%" }, "scripterx_version": "%scripterx.version%", "emby_server_version": "%server.version%" } It should be self explanatory from here. So ScripterX will read in your template, substitute for the token values, and post it to the specified URL. So my PHP script simply takes $_POST['data'], casts it as a JSON object, and outputs to a file on my webserver: Array ( [user] => Array ( [username] => Anthony [password] => asdfasdfasdffasgfsasgasdfasdf ) [device] => Array ( [id] => 427a7309-5fc2-489d-9a83-0664c619c779 [name] => Firefox [ip_address] => 192.168.1.124 ) [scripterx_version] => 2.3.4.0 [emby_server_version] => 4.4.2.0 But as you can see --- these templates are entirely customisable, so you can adjust these templates to suit whatever you're posting your data to. The only thing currently that I haven't made customisable is the POST variable that contains the json data, which is set to 'data'. I will eventually extend the functionality such that the JSON data can be submitted without the need for POST variables. Any comments/feedback/suggestions are greatly appreciated as always. Edited May 19, 2020 by Anthony.Musgrove 2 Link to comment Share on other sites More sharing options...
spaceman07 8 Posted May 28, 2020 Share Posted May 28, 2020 hello.. can you provide the php script you are using? i will try this out and would like to explore the options more. thanks Link to comment Share on other sites More sharing options...
Anthony Musgrove 171 Posted May 28, 2020 Author Share Posted May 28, 2020 hello.. can you provide the php script you are using? i will try this out and would like to explore the options more. thanks Hi mate, that is so much appreciated. This hook.php file was very simple - was just designed to take in some JSON data and basically spit it back out to the requesting connection. It's: <?php $post_json_data = $_POST["data"]; $json_data_object = json_decode($post_json_data,true); file_put_contents("/home/medius/public_html/auth_hook.txt", "Hook Called!:\r\n", FILE_APPEND); file_put_contents("/home/medius/public_html/auth_hook.txt", print_r($json_data_object,true), FILE_APPEND); header('Content-type: application/json'); echo json_encode($json_data_object); Link to comment Share on other sites More sharing options...
dual-o 18 Posted September 24, 2021 Share Posted September 24, 2021 (edited) Hello, I have a question: I have done this with a workaround under linux. ScripterX call a shell script via /bin/bash interpreter to post webhook using curl. script: curl -X POST https://webhook.example.com/mywebhookurl -H "Content-Type: application/json" -d '{"movie":"$1","year":"$2","library":"$3"}' ScripterX config: I have tested it via web:post interpreter but the post is empty. Would be nice to have a leaner solution in the futur Edited September 24, 2021 by dual-o Link to comment Share on other sites More sharing options...
Moekie 0 Posted September 27, 2021 Share Posted September 27, 2021 Hi Anthony, this was just what I was looking for to control my lighting during movie night. Thanks! Quick question. is it also possible to add OnPlaybackPause as a trigger? This way I turn on the lights at a low level of brightness if somebody needs a toilet break Again, thanks a lot for your work! Link to comment Share on other sites More sharing options...
horstepipe 354 Posted October 30, 2021 Share Posted October 30, 2021 On 9/24/2021 at 7:26 PM, dual-o said: Hello, I have a question: I have done this with a workaround under linux. ScripterX call a shell script via /bin/bash interpreter to post webhook using curl. script: curl -X POST https://webhook.example.com/mywebhookurl -H "Content-Type: application/json" -d '{"movie":"$1","year":"$2","library":"$3"}' ScripterX config: I have tested it via web:post interpreter but the post is empty. Would be nice to have a leaner solution in the futur Hello folks, I'd like to setup the same but I'm struggling with it. It is for a new content notification for a Synapse/Matrix homeserver. I setup the webhook gateway and it basically works (I can receive test messages on a channel). But I'm struggling setting this up with ScripterX and I'm unsure whether @dual-os instructions are complete. So on the Webhook's side, the developer gave me a plugin - which I guess is the same as he also gave to @dual-o https://github.com/geluk/matrix-webhook-gateway/issues/25#issuecomment-955253060 But now I'm unsure how to setup ScripterX. If I'm doing it like above it does not work, I'm getting lots of errors by the webhook gateway. 2021-10-30 18:13:08.268.000 ERROR [webhook-srv] Failed to handle webhook invocation: Error Cannot read properties of undefined (reading 'formatPlain') error stack: • formatting.ts:26 toPlain src/formatting/formatting.ts:26:24 • formatting.ts:231 formatPlain src/formatting/formatting.ts:231:23 • formatting.ts:26 toPlain src/formatting/formatting.ts:26:25 • formatting.ts:36 formatPlain src/formatting/formatting.ts:36:30 • formatting.ts:26 toPlain src/formatting/formatting.ts:26:25 • MatrixBridge.ts:53 <anonymous> src/bridge/MatrixBridge.ts:53:19 • MatrixBridge.js:27 <anonymous> src/bridge/MatrixBridge.js:27:71 But are the instructions from @dual-o above really valid or did he probably made changes and didn't post them? Maybe I'm currently a little slow lol. hopefully somebody is willing to help me out here. Best regards Link to comment Share on other sites More sharing options...
dual-o 18 Posted October 31, 2021 Share Posted October 31, 2021 (edited) Hey guys, here is my little quick-and-drity how-to... hope it helps The idea behind the scenes is that ScripterX passes all the needed informations to the webhook. Unfortunately this does not work direct. So I write this little shell script as transmitter. First of all we need to configure ScripterX to pass the needed parameters when "OnMediaItemAddedComplete" is triggered. For movies we need Name, Year, IMDB ID and the Library. The IMDB ID is required for later link-creation and that false scraped elements without IMDB ID wouldn't sent to the webhook. for series... for seasons... note: "%item.isvirtual% Equals FALSE" prevent notifications for upcoming or missing seasons webhook.sh shell script: #!/bin/bash # Variables imdb_pattern='^tt[0-9]*$' webhook_url='https://webhook.domain.com/hook/pvp***f53' webhook() { curl -X POST "$webhook_url/emby" -H "Content-Type: application/json" -d "$1" } if [[ $1 == Movie ]] && [[ "$4" =~ $imdb_pattern ]]; then # Check if item.type is Movie and item.meta.imdb is set webhook "{\"movie\":\"$2\",\"year\":\"$3\",\"imdb\":\"$4\",\"library\":\"$5\"}" elif [[ $1 == Series ]] && [[ "$3" =~ $imdb_pattern ]]; then # Check if item.type is Series and item.meta.imdb is set webhook "{\"series\":\"$2\",\"imdb\":\"$3\",\"library\":\"$4\"}" elif [[ $1 == Season ]] && [[ "$4" =~ $imdb_pattern ]]; then # Check if item.type is Season and item.meta.imdb is set webhook "{\"season\":\"$2\",\"series\":\"$3\",\"imdb\":\"$4\"}" fi note: The if statement checks the first parameter ($1), we remember it was "%item.type%", for movie, series or season that was added to the library. The second parameter ($2) is the IMDB ID "%xxx.meta.imdb%". We remember false scraped elements without IMDB ID souldn't sent to the webhook. The IMDB ID will be checked via regex (imdb_pattern). After that, we send a different hook for movies series and seasons. I have made this separation so that the emby-plugin from the matrix-webhook-gateway can differentiate between the messages to be sent. emby.ts plugin: import { is } from 'typescript-is'; import { PluginBase, WebhookMessage } from '../../src/pluginApi/v2'; import { a, strong, fmt, } from '../../src/formatting/formatting'; type movie = { movie: string; year: string; imdb: string; library: string; }; type series = { series: string; imdb: string; library: string; }; type season = { season: string; series: string; imdb: string; }; export const format = 'emby'; export default class EmbyPlugin extends PluginBase { // This function will be executed once, on startup. async init(): Promise<void> { this.logger.info('emby plugin starting up'); } // This function will be executed every time a webhook with a matching // format is posted. It should either return a `WebhookMessage`, if the // webhook is to be executed, or `undefined`, if the webhook is to be // rejected. async transform(body: unknown): Promise<WebhookMessage | undefined> { // You can make use of 'typescript-is' to perform runtime type checks on // input data. This makes it easy to reject invalid webhooks. if (is<movie>(body)) { var link = 'https://www.imdb.com/title/' + body.imdb; var titel = body.movie + ' (' + body.year + ')'; return { // username: 'Emby Bot', text: fmt( 'The Movie ', strong(a(link,titel)), ' was added to the ', body.library, ' library', ), }; } if (is<series>(body)) { var link = 'https://www.imdb.com/title/' + body.imdb; return { // username: 'Emby Bot', text: fmt( 'The Series ', strong(a(link,body.series)), ' was added to the ', body.library, ' library', ), }; } if ((is<season>(body) { var link = 'https://www.imdb.com/title/' + body.imdb; return { // username: 'Emby Bot', text: fmt( 'Season ', body.season, ' was added to ', strong(a(link,body.series)), ), }; } else { this.logger.warn('Invalid webhook'); this.logger.warn(body); return undefined; } } } note: as you can see the plugin also differentiate between the type movie, series and seasons. Additionally we use the IMDB ID to generate a IMDB link inside the message. When all is configured right you get the messages from the bot. (and click on the blue link in the message to open the IMDB page of the movie/series) movie example: season example: series example: This doesn't work. Seems like ScripterX doesn't check series "OnMediaItemAddedComplete"... @Anthony Musgrove can you help with this behavior? Finally it would be nice to have some feedback to make emby notifications via matrix cleaner, leaner or more efficiant regards Dual-O Edited October 31, 2021 by dual-o 1 Link to comment Share on other sites More sharing options...
horstepipe 354 Posted November 1, 2021 Share Posted November 1, 2021 thank you very much @dual-o !! Link to comment Share on other sites More sharing options...
horstepipe 354 Posted November 1, 2021 Share Posted November 1, 2021 (edited) @dual-o so you didn't set it up to send a message if you add a single episode? Just wondering whether you didn't have the need for it or whether there are other reasons Edited November 1, 2021 by horstepipe Link to comment Share on other sites More sharing options...
dual-o 18 Posted November 1, 2021 Share Posted November 1, 2021 3 minutes ago, horstepipe said: @dual-o so you didn't set it up to send a message if you add a single episode? Just wondering whether you didn't have the need for it or whether there are other reasons I've done this in the past, but it was a little bit like a spam bot. Example: add the whole Simpson Series... and you will receive 716 messages... but yes we can add this to a updated version of the shell script and make it optional 1 Link to comment Share on other sites More sharing options...
horstepipe 354 Posted November 1, 2021 Share Posted November 1, 2021 56 minutes ago, dual-o said: I've done this in the past, but it was a little bit like a spam bot. Example: add the whole Simpson Series... and you will receive 716 messages... but yes we can add this to a updated version of the shell script and make it optional yeah okay, but without that you won't be notified if a single new episode comes up I guess? That's quite important here, will see if I can figure it out. Link to comment Share on other sites More sharing options...
horstepipe 354 Posted November 1, 2021 Share Posted November 1, 2021 hm no luck, it triggers the same as for seaons. not sure what I did wrong. Will take another look later. Link to comment Share on other sites More sharing options...
horstepipe 354 Posted November 1, 2021 Share Posted November 1, 2021 so here are my edits. webhook.sh: #!/bin/bash # Variables imdb_pattern='^tt[0-9]*$' webhook_url='127.0.0.1:8020/hook/xxxxx' webhook() { curl -X POST "$webhook_url/emby" -H "Content-Type: application/json" -d "$1" } if [[ $1 == Movie ]] && [[ "$4" =~ $imdb_pattern ]]; then # Check if item.type is Movie and item.meta.imdb is set webhook "{\"movie\":\"$2\",\"year\":\"$3\",\"imdb\":\"$4\",\"library\":\"$5\"}" elif [[ $1 == Series ]] && [[ "$3" =~ $imdb_pattern ]]; then # Check if item.type is Series and item.meta.imdb is set webhook "{\"series\":\"$2\",\"imdb\":\"$3\",\"library\":\"$4\"}" elif [[ $1 == Season ]] && [[ "$4" =~ $imdb_pattern ]]; then # Check if item.type is Season and item.meta.imdb is set webhook "{\"season\":\"$2\",\"series\":\"$3\",\"imdb\":\"$4\"}" elif [[ $1 == Episode ]] && [[ "$5" =~ $imdb_pattern ]]; then # Check if item.type is Season and item.meta.imdb is set webhook "{\"episode\":\"$2\",\"season\":\"$3\",\"series\":\"$4\",\"imdb\":\"$5\"}" fi EmbyWebhook.ts import { is } from 'typescript-is'; import { PluginBase, WebhookMessage } from '../../src/pluginApi/v2'; import { a, strong, fmt, } from '../../src/formatting/formatting'; type movie = { movie: string; year: string; imdb: string; library: string; }; type series = { series: string; imdb: string; library: string; }; type season = { season: string; series: string; imdb: string; } type episode = { episode: string; season: string; series: string; imdb: string; } ; export const format = 'emby'; export default class EmbyPlugin extends PluginBase { // This function will be executed once, on startup. async init(): Promise<void> { this.logger.info('emby plugin starting up'); } // This function will be executed every time a webhook with a matching // format is posted. It should either return a `WebhookMessage`, if the // webhook is to be executed, or `undefined`, if the webhook is to be // rejected. async transform(body: unknown): Promise<WebhookMessage | undefined> { // You can make use of 'typescript-is' to perform runtime type checks on // input data. This makes it easy to reject invalid webhooks. if (is<movie>(body)) { var link = 'https://www.imdb.com/title/' + body.imdb; var titel = body.movie + ' (' + body.year + ')'; return { // username: 'Emby Bot', text: fmt( 'Der Film ', strong(a(link,titel)), ' wurde hinzugefügt zu ', body.library, ' library', ), }; } if (is<series>(body)) { var link = 'https://www.imdb.com/title/' + body.imdb; return { // username: 'Emby Bot', text: fmt( 'The Series ', strong(a(link,body.series)), ' was added to the ', body.library, ' library', ), }; } if ((is<season>(body) { var link = 'https://www.imdb.com/title/' + body.imdb; return { // username: 'Emby Bot', text: fmt( 'Season ', body.season, ' was added to ', strong(a(link,body.series)), ), }; } if ((is<episode>(body) { var link = 'https://www.imdb.com/title/' + body.imdb; return { // username: 'Emby Bot', text: fmt( 'Season''Episode ', body.season, ' was added to ', strong(a(link,body.series)), ), }; } else { this.logger.warn('Invalid webhook'); this.logger.warn(body); return undefined; } } } Link to comment Share on other sites More sharing options...
horstepipe 354 Posted November 4, 2021 Share Posted November 4, 2021 (edited) any idea what's going wrong here? Info Emby ScripterX: onMediaItemAddedComplete: "Movie" "Beckett" "2021" "tt10230994" "error: System.IndexOutOfRangeException: Index was outside the bounds of the array. at EmbyScripterX.Core.ScripterXContextFactory.<>c__DisplayClass14_0.<Item>b__0(VirtualFolderInfo x) at System.Collections.Generic.List`1.Find(Predicate`1 match) at EmbyScripterX.Core.ScripterXContextFactory.Item(BaseItem item, BaseItem parent)" parameter are: "%item.type%" "item.name%" "%item.productionyear%" "%item.meta.imdb%" "%item.library.name%" Edited November 5, 2021 by horstepipe Link to comment Share on other sites More sharing options...
dual-o 18 Posted November 4, 2021 Share Posted November 4, 2021 Have you already tried removing each element one by one to see which is causing the error? 1 Link to comment Share on other sites More sharing options...
horstepipe 354 Posted November 5, 2021 Share Posted November 5, 2021 (edited) 12 hours ago, dual-o said: Have you already tried removing each element one by one to see which is causing the error? soo it is "%item.library.name%" which is causing the error. Do you or anyone else have multiple movie libraries and this setup working? But it is no dealbreaker Edited November 5, 2021 by horstepipe Link to comment Share on other sites More sharing options...
horstepipe 354 Posted November 7, 2021 Share Posted November 7, 2021 fyi I do have multiple movie libaries AND multiple folders in each library. Link to comment Share on other sites More sharing options...
horstepipe 354 Posted November 8, 2021 Share Posted November 8, 2021 this also happens for tv shows. Link to comment Share on other sites More sharing options...
horstepipe 354 Posted November 9, 2021 Share Posted November 9, 2021 anybody has an idea for a workaround? Looks like there is no option to add a "contains" statement? Then I could check for 4k folder / no 4k folder. Link to comment Share on other sites More sharing options...
vincentli806 0 Posted February 18, 2022 Share Posted February 18, 2022 (edited) On 5/19/2020 at 6:49 PM, Anthony Musgrove said: Hi guys, I just thought I'd introduce the latest feature of Emby Scripter-X (v2.3.5+) out now on the catalog. Emby Scripter-X Webhooks Why? I notice that a lot of Scripter-X users use the plugin to perform some type of webhook(s) based on certain events, so why not natively support it? So how does it work? Pretty much as-per-usual with the Actions interface. The interface will eventually change input field labels based on the selection in the interpreter box, but it is as above: Run [script], where [script] is the URL of your webhook script (webhook endpoint/api endpoint, etc), in my example above, I have a php script running on my local webserver at http://192.168.1.10/hook.php. [interpreter] should be set to web:post (to post data), web:get is also available, however it does exactly that - doesn't post data. [Parameters] instead of listing parameters to send over the command-line, this is the path to your json content template - where your data is formatted from. Here I specify d:\embyscripts\hooktemplates\authenticationfailed.json, and in this file is the following content: { "user": { "username": "%username%", "password": "%password%" }, "device": { "id": "%device.id%", "name": "%device.name%", "ip_address": "%device.remote.ipaddress%" }, "scripterx_version": "%scripterx.version%", "emby_server_version": "%server.version%" } It should be self explanatory from here. So ScripterX will read in your template, substitute for the token values, and post it to the specified URL. So my PHP script simply takes $_POST['data'], casts it as a JSON object, and outputs to a file on my webserver: Array ( [user] => Array ( [username] => Anthony [password] => asdfasdfasdffasgfsasgasdfasdf ) [device] => Array ( [id] => 427a7309-5fc2-489d-9a83-0664c619c779 [name] => Firefox [ip_address] => 192.168.1.124 ) [scripterx_version] => 2.3.4.0 [emby_server_version] => 4.4.2.0 But as you can see --- these templates are entirely customisable, so you can adjust these templates to suit whatever you're posting your data to. The only thing currently that I haven't made customisable is the POST variable that contains the json data, which is set to 'data'. I will eventually extend the functionality such that the JSON data can be submitted without the need for POST variables. Any comments/feedback/suggestions are greatly appreciated as always. Hi Anthony! Hope you are doing well and thanks for building this plugin which is great! I just learned from this thread that eventually you will 'extend the functionality such that the JSON data can be submitted without the need for POST variables.' May I know the progress about this? I am asking this because the http endpoint of my use case is not accepting 'data' variable and the endpoint not owned by me so I cannot change the parameter setting from server side to align with 'data' variable. Alternatively I have just created batch/shell script and called external libraries to resolve this. Although I can manage to implement what I want, it would be much appreciated if you can complete the next version with this feature - 'json submission without the need for POST variables.' Thank you. Edited February 18, 2022 by vincentli806 Link to comment Share on other sites More sharing options...
suwill 0 Posted February 28, 2022 Share Posted February 28, 2022 (edited) First of all, thank you for developing such a good plug-in, Now I can get the event of emby use the web:POST(use node-red to parse msg.data) Some questions about JSON templates: Is there any way to add the current state triggered by which event in the JSON template? Because I want to use the same JSON template to send web:POST For example: { "user": { "username": "%username%", "password": "%password%" }, "device": { "id": "%device.id%", "name": "%device.name%", "ip_address": "%device.remote.ipaddress%" }, "scripterx_version": "%scripterx.version%", "emby_server_version": "%server.version%", "action": "onAuthenticationSuccess" } in the last line "action": "onAuthenticationSuccess",I hope that “onAuthenticationSuccess” is a variable. Use Google translation, Sorry for my poor English,Thank you again. Edited February 28, 2022 by suwill Link to comment Share on other sites More sharing options...
suwill 0 Posted February 28, 2022 Share Posted February 28, 2022 4 hours ago, suwill said: First of all, thank you for developing such a good plug-in, Now I can get the event of emby use the web:POST(use node-red to parse msg.data) Some questions about JSON templates: Is there any way to add the current state triggered by which event in the JSON template? Because I want to use the same JSON template to send web:POST For example: { "user": { "username": "%username%", "password": "%password%" }, "device": { "id": "%device.id%", "name": "%device.name%", "ip_address": "%device.remote.ipaddress%" }, "scripterx_version": "%scripterx.version%", "emby_server_version": "%server.version%", "action": "onAuthenticationSuccess" } in the last line "action": "onAuthenticationSuccess",I hope that “onAuthenticationSuccess” is a variable. Use Google translation, Sorry for my poor English,Thank you again. Now, This problem has been solved. Write it to the tutorial when I'm not busy Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now