Jump to content

New Plugin - Custom Scripting | Emby ScripterX


Anthony Musgrove

Recommended Posts

Anthony Musgrove

Thank you so so so much maegibbons ❤❤ new release is now on the catalog 2.3.4 :))))))

Link to comment
Share on other sites

PenkethBoy

if you are going to add drag and drop to the parameters section can you add " " around the non integer/number variables or just all variables which would be easier i suspect

 

Also

 

1. can you also add a file picker dialog to pick the script file

1.1 and on basis of script file extension add "-File" , /d etc etc

 

2. Make Interpreter a drop down list of supported interpreters powershell, pwsh, cmd etc

 

with all the above this would eliminate most of the typing and be mostly mouse driven :)

Edited by PenkethBoy
  • Like 2
Link to comment
Share on other sites

maegibbons

if you are going to add drag and drop to the parameters section can you add " " around the non integer/number variables or just all variables which would be easier i suspect

 

Also

 

1. can you also add a file picker dialog to pick the script file

1.1 and on basis of script file extension add "-File" , /d etc etc

 

2. Make Interpreter a drop down list of supported interpreters powershell, pwsh, cmd etc

 

with all the above this would eliminate most of the typing and be mostly mouse driven :)

Yes. I agree whole heartedly with that lot!!

 

Krs

 

Mark

 

A 'like' is always appreciated!

  • Like 1
Link to comment
Share on other sites

PenkethBoy

Anthony

 

Testing the new onMediaItemAddedComplete

 

Good news is it generally is working. However....

 

Testing with Music

 

1. If i add an AlbumArtist directory and the AA has a single album - then i get the results i would expect

One event for the folder (AA) being added

One event for the MusicAlbum being added

An event for each song (Audio) being added

There is nothing for the Album Artist being add - but there is a githug issue alrady open for this

 

So all good for a single album

 

2. Adding a new album to an existing AA folder

one event for MusicAlbum added

An event for each Song added

 

So again all good for a single album

 

3. Adding an AA with more than one album or a large number of songs

e.g. Add the folder for the AA and its associated album folders in one go

One event for AA Folder - correct

13 events for MusicAlbum - incorrect - only 8 albums added - so 5 duplicate events

167 events for Songs - incorrect - only 105 songs added - so 62 duplicate events

 

so a total of 181 events instead of 114

 

Have tried the above more than once and multiple albums cause multiple Musicalbum and Song events - i.e. duplicates

 

What appears to be happening is...

 

Part way through the process of Emby adding the Albums and songs - the AddedComplete event fires for a song before its been completely added

 

In the attached log - see line 53 to 114 - 62 events that are "early" and get repeated a bit later in the log - these can be seen to not be fully added as they still have their Full filename (01 songname) prior to emby getting their name via lookup

 

When these songs then get updated later - that also triggers further MusicAlbum added events - hence the duplicate MusicAlbum events - as songs which are part of the Album finish updating - hence why i get 5 duplicate MusicAlbum events. 

 

I suspect this is down to the duration you wait before "assuming" that the adds are complete as it works fine for single albums with lowish numbers of songs

 

 

all albums - talking heads.log

 

 

if i try with an AA with two albums it appears to work ok - but i suspect it's actually down to the number of songs being added 

 

AA with three albums have duplicates

 

I tested a single album with 34 songs - and got no duplicates

 

I tested with a single album with 54 songs and got 11 duplicate for songs and a duplicate for the album

 

testing with three albums and 38 songs gave two duplicate songs and 1 duplicate album

 

 

I have tested this on my main PC with a test server which has little added to it which will be quicker than most servers to do the lookup etc - so for me its not a server speed issue - although a slow NAS might be worse

 

Its probably down to the time it takes for multiple lookups to occur - IIRC emby might add a small delay for multiple lookups in a short period of time - but doubt thats that significant

 

Have fun :)

 

Link to comment
Share on other sites

PenkethBoy

5ec2c8103e208_Annotation20200518183756.j

 

Anthony

 

Why no parameters for transcoding Audio????

No transcoding.height either?

Edited by PenkethBoy
Link to comment
Share on other sites

PenkethBoy

Anthony

 

was just testing onRecordingStart for LiveTV

 

and recording.path - is not returned

 

can this be fixed please as i want to use the full path to Pre-process the file for commercials

 

I guess its likely you need to wait a bit longer to get the info from emby - like you have done for addedcomplete

  • Like 1
Link to comment
Share on other sites

PenkethBoy

Anthony

 

Looking at my log while recordings are happening shows that ItemAddedComplete is returning multiple events for the episodes being recorded

 

Due to Emby "updating" every few seconds - I think you will have to treat recordings differently

 

Example log

Recordings.log

Link to comment
Share on other sites

Anthony Musgrove

Thank you so much for testing mate, I will go through these issues one by one and sort them out :)

 

Thank you again, so so so so so so much!

Link to comment
Share on other sites

Anthony Musgrove

5ec2c8103e208_Annotation20200518183756.j

 

Anthony

 

Why no parameters for transcoding Audio????

No transcoding.height either?

 

Hey mate, just going through bit by bit :) I've just fixed this up.  Transcoding tokens are now :

 

%is.transcoding%,%transcoding.cpu.usage%,%transcoding.avgcpu.usage%,%transcoding.video.codec%,%transcoding.video.decoder%,%transcoding.video.decoder.hwaccel%,%transcoding.is.decoder.hardware%,%transcoding.video.decoder.mediatype%,%transcoding.video.encoder%,%transcoding.video.encoder.hwaccel%,%transcoding.is.encoder.hardware%,%transcoding.video.encoder.mediatype%,%transcoding.height%,%transcoding.width%,%transcoding.audio.channels%,%transcoding.audio.codec%,%transcoding.bitrate%,%transcoding.container%,%transcoding.framerate%,%transcoding.reasons%

  • Like 1
Link to comment
Share on other sites

Anthony Musgrove

Anthony

 

Looking at my log while recordings are happening shows that ItemAddedComplete is returning multiple events for the episodes being recorded

 

Due to Emby "updating" every few seconds - I think you will have to treat recordings differently

 

Example log

attachicon.gifRecordings.log

 

This ones my fault mate.  I found the issue and rectified it.. I'll be releasing this one with all these changes tonight!

  • Like 1
Link to comment
Share on other sites

maegibbons

Anthony

 

was just testing onRecordingStart for LiveTV

 

and recording.path - is not returned

 

can this be fixed please as i want to use the full path to Pre-process the file for commercials

 

I guess its likely you need to wait a bit longer to get the info from emby - like you have done for addedcomplete

Hi Anthony

 

How about this one as this one affects me too!

 

Krs

 

Mark

 

A 'like' is always appreciated!

  • Like 1
Link to comment
Share on other sites

Anthony Musgrove

Absolutely mate, will get this one sorted right now <3

Link to comment
Share on other sites

maegibbons

Absolutely mate, will get this one sorted right now <3

So when I am in Newcastle, I will have to look you up and buy you a drink on behalf of the UK users!

 

Unfortunately no current plans due to Covid! So you will have to stay thirsty for a while.

 

Krs

 

Mark

 

A 'like' is always appreciated!

  • Like 1
Link to comment
Share on other sites

Anthony Musgrove

Hehehe, that sounds awesome mate, would love to catch up if you're ever over this way!

 

Update to 2.3.7 from the catalog mate, the recording path is now resolved

 

2020-05-19 23:52:01.714 Info Emby ScripterX: onLiveTVRecordingStart: START "Major Crimes" "/home/medius/media/recordings/Major Crimes/Major Crimes 2020_05_19_23_21_00 - 1.ts"

 

<3

  • Like 1
Link to comment
Share on other sites

Anthony Musgrove

if you are going to add drag and drop to the parameters section can you add " " around the non integer/number variables or just all variables which would be easier i suspect

 

Also

 

1. can you also add a file picker dialog to pick the script file

1.1 and on basis of script file extension add "-File" , /d etc etc

 

2. Make Interpreter a drop down list of supported interpreters powershell, pwsh, cmd etc

 

with all the above this would eliminate most of the typing and be mostly mouse driven :)

 

Absolutely mate, this sounds like an awesome idea!  Leave it with me :)

  • Like 1
Link to comment
Share on other sites

PenkethBoy

Hey mate, just going through bit by bit :) I've just fixed this up.  Transcoding tokens are now :

 

%is.transcoding%,%transcoding.cpu.usage%,%transcoding.avgcpu.usage%,%transcoding.video.codec%,%transcoding.video.decoder%,%transcoding.video.decoder.hwaccel%,%transcoding.is.decoder.hardware%,%transcoding.video.decoder.mediatype%,%transcoding.video.encoder%,%transcoding.video.encoder.hwaccel%,%transcoding.is.encoder.hardware%,%transcoding.video.encoder.mediatype%,%transcoding.height%,%transcoding.width%,%transcoding.audio.channels%,%transcoding.audio.codec%,%transcoding.bitrate%,%transcoding.container%,%transcoding.framerate%,%transcoding.reasons%

Hi Anthony

 

Just installed 2.3.7 and not seeing the new parameters?

 

or are they coming in the next version?

Link to comment
Share on other sites

PenkethBoy

Hehehe, that sounds awesome mate, would love to catch up if you're ever over this way!

 

Update to 2.3.7 from the catalog mate, the recording path is now resolved

 

2020-05-19 23:52:01.714 Info Emby ScripterX: onLiveTVRecordingStart: START "Major Crimes" "/home/medius/media/recordings/Major Crimes/Major Crimes 2020_05_19_23_21_00 - 1.ts"

 

<3

Confirmed as working

 

Thanks

Link to comment
Share on other sites

Anthony Musgrove

So damn excited for the next framework addition to ScripterX.   So this will be called Packages, and it will be in place of the old 'Community' tab, packages can be community written and a catalog will eventually be available.  So once I get the framework ready, community members can start making ScripterX 'Packages' for all sorts of things, like Discord Notifications, anything really.

 

Packages will be written in pure javascript.  

 

My test syntax at the moment (this is a package I call 'scripterx_test_package' located in my packages folder):

/* Scripter-X Test Package */

/* _package_init() 
 *
 * This function is called when loading your package.
 *
 */
function _package_init()
{
	//2020-05-21 21:01:52.555 Info Emby ScripterX: scripterx_test_package: This is a test log entry, whee!
	ScripterX.Log.Info("This is a test log entry, whee!");
	
	
	var response = ScripterX.Web.Post("http://192.168.1.10/hook.php", '{"cartypes": [ "Ford", "BMW", "Fiat" ]}');
	
	//2020-05-21 22:39:18.855 Info Emby ScripterX: scripterx_test_package: CAR TYPE {"cartypes":["Ford","BMW","Fiat"]}
	//2020-05-21 22:39:18.857 Info Emby ScripterX: scripterx_test_package: CAR TYPE 0 = Ford
	//2020-05-21 22:39:18.858 Info Emby ScripterX: scripterx_test_package: CAR TYPE 1 = BMW
	//2020-05-21 22:39:18.858 Info Emby ScripterX: scripterx_test_package: CAR TYPE 2 = Fiat	
	
	for(var t=0; t<response.cartypes.length; t++)
	{
		ScripterX.Log.Info("CAR TYPE " + t + " = " + response.cartypes[t]);
	}

	
	return(true);
}


function _onAuthenticationFailed(context)
{
	ScripterX.Log.Info("DEBUG from package - authentication failed.. %username% = " + context.Token("%username%").value);
}


Link to comment
Share on other sites

Anthony Musgrove

So clean too ...

 

2020-05-21 23:16:59.278 Info Emby ScripterX: Invoke _onSessionStarted on scripterx_test_package
2020-05-21 23:16:59.278 Debug Emby ScripterX: Event function does not exist in 'scripterx_test_package' for: _onSessionStarted

Link to comment
Share on other sites

Anthony Musgrove

Created timers! 

 

Syntax:

 

ScripterX.Timers.createRepeating("mytimer", 10000, "mytimer_elapsed", null);
ScripterX.Timers.Start("mytimer");
 
and ScripterX.Timers.List(), and ScripterX.Timers.Stop(name) and ScripterX.Timers.Delete(name).

 

2020-05-21 23:57:51.007 Info Emby ScripterX: scripterx_test_package: Timer has elapsed!, name = mytimer, with an interval of 10000
2020-05-21 23:58:00.986 Info Emby ScripterX: scripterx_test_package: Timer has elapsed!, name = mytimer, with an interval of 10000
2020-05-21 23:58:10.999 Info Emby ScripterX: scripterx_test_package: Timer has elapsed!, name = mytimer, with an interval of 10000
2020-05-21 23:58:20.987 Info Emby ScripterX: scripterx_test_package: Timer has elapsed!, name = mytimer, with an interval of 10000
2020-05-21 23:58:30.997 Info Emby ScripterX: scripterx_test_package: Timer has elapsed!, name = mytimer, with an interval of 10000

Link to comment
Share on other sites

Anthony Musgrove

So I just wrote a quick package for adding new TV media, 
 
It's rough as guts, because its just a quick throw-together, but you should see how it works.  The output isn't entirely formatted correctly, but it does exactly what you need it to (just fix the json formatting up).
 
Code:
 

/* Scripter-X Test Package */

/* _package_init() 
 *
 * This function is called when loading your package.
 *
 */
 
 var pending_tv = [];
 ScripterX.Timers.createOnce("tmrCheckPendingTV", 120000, "tmrCheckPendingTV_Elapsed", null);
 
 function get_series(series)
 {
	 for(var x=0; x<pending_tv.length; x++)
	 {
		 var this_series = pending_tv[x];
		 
		 if(this_series.name == series)
			 return(this_series);
	 }

	 //got to here, series didn't exist, lets create it.
	 var new_series = {name: series, seasons: []};
	 pending_tv.push(new_series);
	 
	 return(new_series);
 }
 
 function get_season(series, season_number)
 {
	 var the_series = get_series(series);
	 
	 for(var x=0; x<the_series.seasons.length; x++)
	 {
		 this_season = the_series.seasons[x];
		 
		 if(this_season.number == season_number)
			 return(this_season);
	 }
	 
	 //got to here, season didn't exist, lets create it.
	 var new_season = {number: season_number, episodes: []};
	 the_series.seasons.push(new_season);
	 
	 return(new_season);
 }
 
 function get_episode(series, season_number, episode_number)
 {
	 var the_season = get_season(series, season_number);
	 
	 for(var x=0; x<the_season.episodes.length; x++)
	 {
		 this_episode = the_season.episodes[x];
		 
		 if(this_episode.number == episode_number)
			 return(this_episode);
	 }
	 
	 //got to here, episode didn't exist, lets create it.
	 var new_episode = {number: episode_number}
	 the_season.episodes.push(new_episode);
	 
	 return(new_episode);
 }
 
 
function _package_init()
{
	//2020-05-21 21:01:52.555 Info Emby ScripterX: scripterx_test_package: This is a test log entry, whee!
	ScripterX.Log.Info("This is a test log entry, whee!");
	return(true);
}


function tmrCheckPendingTV_Elapsed(timer_name, timer_interval, objects)
{
	//basically check if theres anything in the pending_tv array, if so, submit it.  If not, do nothing.
	if(pending_tv.length > 0)
	{
		ScripterX.Log.Info("New TV content added: " + JSON.stringify(pending_tv));
		pending_tv = [];
	}
}

function _onAuthenticationFailed(context)
{
	ScripterX.Log.Info("DEBUG from package - authentication failed.. %username% = " + context.Token("%username%").value);
}

function _onMediaItemAddedComplete(context)
{
	if((context.Token("%item.type%").value == "Episode") || (context.Token("%item.type%").value == "Series") || (context.Token("%item.type%").value == "Season"))
	{	
		tv_media_added(context);
		//restart the pending tv timer..
		ScripterX.Timers.Restart("tmrCheckPendingTV");
		return;
	}
}

function tv_media_added(context)
{
	switch(context.Token("%item.type%").value.toUpperCase())
	{
		case "SERIES":
			var series = get_series(series);
			series.path = context.Token("%item.path%").value;
		break;
		
		case "SEASON":
			var season = get_season(context.Token("%series.name%").value, context.Token("%season.number%").value);
			season.name = context.Token("%item.name%").value;
			season.path = context.Token("%item.path%").value;
		break;
		
		case "EPISODE":
		//(series, season_number, episode_number)
			var episode = get_episode(context.Token("%series.name%").value, context.Token("%season.number%").value, context.Token("%episode.number%").value);
			episode.name = context.Token("%item.name%").value;
			episode.path = context.Token("%item.path%").value;
		break;
	}
}

Output in the emby log:
 

2020-05-22 00:35:10.071 Info Emby ScripterX: scripterx_test_package: New TV content added: 

[{"seasons":[],"path":"D:\\Media\\TV\\The Witcher"},{"name":"The Witcher","seasons":[{"number":"1","episodes":[{"number":"1","name":"The End's Beginning","path":"D:\\Media\\TV\\The Witcher\\Season 01\\The.Witcher.S01E01.720p.NF.WEBRip.x264-GalaxyTV.mkv"},{"number":"2","name":"Four Marks","path":"D:\\Media\\TV\\The Witcher\\Season 01\\The.Witcher.S01E02.720p.NF.WEBRip.x264-GalaxyTV.mkv"},{"number":"3","name":"Betrayer Moon","path":"D:\\Media\\TV\\The Witcher\\Season 01\\The.Witcher.S01E03.720p.NF.WEBRip.x264-GalaxyTV.mkv"},{"number":"4","name":"Of Banquets, Bastards and Burials","path":"D:\\Media\\TV\\The Witcher\\Season 01\\The.Witcher.S01E04.720p.NF.WEBRip.x264-GalaxyTV.mkv"},{"number":"5","name":"Bottled Appetites","path":"D:\\Media\\TV\\The Witcher\\Season 01\\The.Witcher.S01E05.720p.NF.WEBRip.x264-GalaxyTV.mkv"},{"number":"6","name":"Rare Species","path":"D:\\Media\\TV\\The Witcher\\Season 01\\The.Witcher.S01E06.720p.NF.WEBRip.x264-GalaxyTV.mkv"},{"number":"7","name":"Before a Fall","path":"D:\\Media\\TV\\The Witcher\\Season 01\\The.Witcher.S01E07.720p.NF.WEBRip.x264-GalaxyTV.mkv"},{"number":"8","name":"Much More","path":"D:\\Media\\TV\\The Witcher\\Season 01\\The.Witcher.S01E08.720p.NF.WEBRip.x264-GalaxyTV.mkv"}],"name":"Season 1","path":"D:\\Media\\TV\\The Witcher\\Season 01"}]}]

So basically, this "Package" simply does the following:

 

onMediaItemAddedComplete, for Series, Season and Episode, it stores the information temporarily in an array, with a timer that restarts everytime new information is added to that particular series.  The timer is 120seconds, so basically it waits 120 seconds from the LAST update to that particular series (season, episode etc), and then treats the data as a 'New series, season, episode' etc added.   So it only gets called once, for the entire series,season,episode.

 

I'm sure there are plenty of improvements to be done, but the framework is almost there in ScripterX for people to develop their own packages :)

 

 

 

ss

Link to comment
Share on other sites

Anthony Musgrove

Almost ready for a release :)

 

2020-05-23 12:48:34.715 Info Emby ScripterX: Loaded package 'scripterx_test_package' as ScripterX Test Package Test Install from d:\embyscripts\packages\scripterx_test_package

 

:) :) 

Link to comment
Share on other sites

Anthony Musgrove

Interface so far for Packages.. Need to implement package installer and uninstaller and it will be ready for beta release.

 

5ec8ac5181423_ScripterXPackagesInterface

Link to comment
Share on other sites

ginjaninja

Anthony, is it possible that ScripterX prevents posts to the api whilst scripts, that ScripterX has launched, are running?

 

my logging script READS data from the api to complete the log and that seems to work fine.

 

but if i launch my processs script  which POSTS to the api, from scripterX event (as opposed to windows task scheduler) ,  then the script and scripterx and aspects of emby starting pausing.

 

 

the reason i think scripterx is pausing, is my process script when called from scripterx does not even show this line,and subsequent script output, in emby log until after i quit the now hung pwsh.exe task.

2020-05-24 09:38:41.042 Info Emby ScripterX: Output from script '-FILE "A:\PSScript\Playlist\Process.ps1" ':

and the last line in log for the script's output  is just before i post to the api.

 

the post i am doing is writing to a playlist.

 

edit

i have since got my process script to run from ScripterX by launching process.ps1 from a 'helper script' in ScripterX which ensures a return to scripterX whilst it invokes process.ps1 'asynchronously'.

which i think is evidence that there is a constraint here (possibly about writing to the api) when launching scripts which target emby.

  • Like 1
Link to comment
Share on other sites

PenkethBoy

@@ginjaninja

 

just tested this - and it think i am getting the same

 

tested with a script that goes through and updates missing premiere dates for albums - run directly from ScripterX - not via a "proxy" script

 

it works fine until it gets to an album to actually update (then the POST event) seems to lockup - can still see the pwsh process running (but idle) - log of the script is only partially complete

 

also the log of emby is now hung as well

 

and

 

5eca8f8776fdc_Annotation20200524161453.j

 

i used the onLibScanComplete event - and as you can see its stopped emby - NOTE - the onlibscancomplete fires before its actually complete - save that for another post

 

Killing the pwsh process allowed the lib scan above to complete normally

 

[edit] - looking at this in more detail - the script never gets to a post event it just stops/hangs after a period of time so its not the post event - see my later posts below

Edited by PenkethBoy
  • Like 1
Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...