Jump to content

How to turn Swagger Api into JS call from Plugin?


mickle026

Recommended Posts

mickle026

Hi , new to plugins so please help ..

 

I finally have 2 test plugins working the basic calls, 1 in vb.net and 1 in c#.  By basic I mean they load , show the config page and button clicking works between the html and the javascript.

 

Now I understand a little Javascript (still learning it) , how to get the buttons and Javascript interacting with Emby core itself?

 

So how do I turn a swagger call into an api call within the plugin?

 

such as trying to list the virtual library folders actual drive paths

GET​/Library​/PhysicalPaths
Gets a list of physical paths from virtual folders
http://192.168.1.xxx:8096/emby/Library/PhysicalPaths?api_key=my_api_key

I have tried to update the textfield 'txtDebug' (just a testing text field name) with a function call mostly copied from the Auto-organise plugin.

JS File


            // submit inside the form tags
            $('.STRMHelperConfigurationForm', page).on('submit', function (e) {
                Dashboard.showLoadingMsg();
                var form = this; // reloads the form
                $('#txtDebug', page).val(ApiClient.GetLibraryPaths);
                Dashboard.hideLoadingMsg();
                // Disable default form submission
                return false;
            });


Here is the function which I thought would do this for me

JS File


        ApiClient.GetLibraryPaths = function () {

        var Swaggerurl = this.getUrl("Library/PhysicalPaths?api_key=< My Api Key Here >");

        return this.ajax({
            type: "POST",
            url: Swaggerurl,
            data: JSON.stringify(options),
            contentType: 'application/json'
        });

Any help would be appreciated, im still a bit baffled by it ;)

Edited by mickle026
Link to comment
Share on other sites

rechigo

Okay, a couple of things here:

   1) Request type should be a GET and not a POST, all we're doing here is fetching data (as per swagger documentation for the endpoint)

   2) You don't need to set the data or content type since we only need to make a GET request here

   3) The reason that you're not seeing any changes is because this.ajax returns a promise, which you need to wait for to resolve (by either using the async/await or by chaining the GetLibraryPaths call with a .then)

Here is an example of how I would do it:

ApiClient.GetLibraryPaths = function() { 
    var Swaggerurl = this.getUrl("Library/PhysicalPaths"); // we format a URL, no need for API key as the API client will automatically set it in the headers for you

    return this.ajax({
        type: "GET", // we make get request
        url: Swaggerurl // to this URL
    });
}

ApiClient.GetLibraryPaths().then(res => {
    return res.json(); // this will parse the response body as JSON
}).then(res => {
    console.log(res); // this will log the result of our request as a JSON object
})
Edited by rechigo
Link to comment
Share on other sites

mickle026

Thanks for taking the time to reply.

 

Either I am doing it wrong or I am still not "getting" it (understanding it).  I am really new to C# having done vb.net (forms) as an amateur for years I am trying to understand whats and whys of what I am doing instead of blindly copying code.

 

So in the above code it is requesting the url because in the log I am getting an entry

2020-03-16 23:06:50.461 Info HttpServer: HTTP Response 200 to 127.0.0.1. Time: 6ms. http://localhost:8096/emby/Library/PhysicalPaths

But i am getting nothing in my textbox #txtDebug, which I didnt think I would as I cannot see a path for the data to return, but did think maybe into the log.  So i tried changing it to make it a variable.

The variable then equals the reurn json from the api call 'var PathData = ApiClient.GetLibraryPaths;'. Or so I assumed, then show that data in txtDebug...

            // submit inside the form tags
            $('.STRMHelperConfigurationForm', page).on('submit', function (e) {
                Dashboard.showLoadingMsg();
                var form = this; // reloads the form
                var PathData = ApiClient.GetLibraryPaths;
                $('#txtDebug', page).val(PathData);
                Dashboard.hideLoadingMsg();
                // Disable default form submission
                return false;
            });

Nothing, just a spinning circle, so the call isn't completing ...

 

So first I tried wrapping it it a call like i have seen in numerous other plugins ( I have no idea here what I am doing here, I have no idea why Im doing a call with the Guid)

            $('.STRMHelperConfigurationForm', page).on('submit', function (e) {
                Dashboard.showLoadingMsg();
                var form = this; // reloads the form
                ApiClient.GetLibraryPaths(STRMHelperConfigurationPage.pluginUniqueId).then($('#txtDebug', page).val(PathData));
                Dashboard.hideLoadingMsg();
                // Disable default form submission
                return false;
            });

Nothing...

So what I am trying to do is get the library paths in to the textbox, ie return any call or json into a text field or even a string.

 

rechigo Your call is obviously working, but mine isn't and I am blind to see what I am doing wrong.  Can I call on you to shed me some light again :unsure: ?

 

Obviously the call is running but how to receive that into the textbox?

 

What I am getting is [Object Promise] is that an Array?

 

5e7010fde2342_1.png

 

 

** UPDATE

 

So an object promise is not a string or an array, so I need a .then statement somewhere.

 

just have to say learning another 2 languages on the fly is not easy is it ... LOL

 

I actually thought that was what this was doing return .then(res => {
return res.json();

 

 

Many thanks

Edited by mickle026
Link to comment
Share on other sites

chef

Here are some code Samples.

 

Use "ApiClient" object in JS

 ApiClient.getJSON(ApiClient.getUrl("Users")).then((users) => { //Change "Users" to anyother endpoint in the Swagger API
    users.forEach(user => { });
 });

Here is the Page outline

define(["require", "loading", "dialogHelper", "emby-checkbox", "emby-select"], //Or any other Emby object/module you want to load into your javascript - this refers to the js file
    function(require, loading, dialogHelper) { //This refers to the object the module created
        var pluginId = "YOUR_PLUGIN_ID";        
        

        return function(view) {
            
            view.addEventListener('viewshow', () => { //This is where you plugin elements have shown themselves in the DOM - we can attach events to them now
             
                 //Use view object to select elements on the plugin page
                 view.querySelector('#YOUR_BUTTON').addEventListener('click', () => { 
                 
                 });

             });
});

Edited by chef
Link to comment
Share on other sites

chef

Here is accessing and saving the configuration XML

ApiClient.getPluginConfiguration(pluginId).then((config) => { 
    //Change or edit Config stuff
    ApiClient.updatePluginConfiguration(pluginId, config).then(() => { 
      //Config is updated 
    });
});
Edited by chef
Link to comment
Share on other sites

mickle026

Thanks @@chef

 

I have figured out the basic structure of the plugin, done that - i am sorted there which only took me a few weeks ..... jeeez. :mellow:

 

I kinda figured out the config and how to get:set values because thats in loads of plugins I cloned to checkout the code. :)

 

I am having issues understanding how the whole thing meshes, how to call the api and get the information i want and how to call routines in the c# or vb.net code. from clicks in the html.  I am going to read up on DOM, this looks like it might be the root of my misunderstandings.  I understand that its the nodes etc of the document tree, but how they interact I dont know.....  I still dont know if I'll be able to do what i ultimately wanted to do, and that is add options to the popup 3 dot menus.  Although there is loads of examples and quite a lot of information, it feels like theres virtually none to get you started...... :(

 

I can do the swagger from a winforms application, thats quite easy and I can make my initial idea that way, but i wanted to be able to access it from the server dashboard - thats not proving easy to do.

Link to comment
Share on other sites

mickle026

You can create a service endpoint in the .net.

 

Using the IService

 

Here you would add endpoints to embys API (they will show up in the swagger)

 

Then you would use the same ApiClient in the frontend code to GET or POST data back to the service. Which in turn would run your .net code.

 

I'll post some more code snippets for you so you can see what I mean

 

 

I think? I can see what you mean, I've been thinking wtf in an endpoint!? ;) , and found this that kinda explains that it is the communication contract between emby and my plugin, and defines how it works.

 

https://www.c-sharpcorner.com/UploadFile/0c1bb2/endpoints-in-wcf/

 

.

 

https://www.c-sharpcorner.com/UploadFile/0c1bb2/ajax-enabled-wcf-service/

 

 

So in the javascript I call the "endpoint" I created to pass and receive the information.

 

ApiClient.getJSON(ApiClient.getUrl("InternalRoute?PassedString=" + The_Passed_String).then(

                            (result) => {  Process Here }

 

Then in the C# define the Function

 

    [Route("/InternalRoute", "GET", Summary = "Get Info From Internal Routine")]

    public class InternalRouteClassName: IReturn<string>

    {

        [ApiMember(Name = "PassedString", Description = "My Description", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]

       

        public string PassedString{ get; set; }

 

 

    }

 

 

 

Here you would add endpoints to embys API (they will show up in the swagger)

 

So from this quote I am kinda understanding that any endpoint I create is going to show up in swagger then, but only on my server?

 

 

 

 

I thought I knew quite a lot, I know f**k all ! :wacko:

 

Like I said in my post above, im going to try to digest this information, try and understand it, and then try to impliment it - wish me luck :huh:

Edited by mickle026
Link to comment
Share on other sites

chef

By George he's got it LOL!

 

Yes, the endpoint will be accessible to your own server. They a server specific, so anyone who installs your plugin will have there own versions of your endpoints.

 

The data that you want to pass back and forth should be JSON. So get to know your JsonSerializer interface object.

 

Also any plugin settings can be saved in the PluginConfiguration. This is XML. That is any element in the DOM, or piece of data where you want to persist it's state,

 

Let me know how your get on, it sounds like you have the basics down so far.

Edited by chef
Link to comment
Share on other sites

mickle026

When this is logged

2020-03-19 16:02:52.197 Info HttpServer: HTTP GET http://localhost:8096/emby/Library/PhysicalPaths. UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
2020-03-19 16:02:52.207 Info HttpServer: HTTP Response 200 to ::1. Time: 10ms. http://localhost:8096/emby/Library/PhysicalPaths

(HTTP Response 200 to ::1.)

 

I assumue that means that the api route is working and communicating with the endpoint, am I correct in assuming that ?

 

Its so hard to tell where you are in the code and where it fails.

 

I need to impliment logging to the debug log or my own log somewhere as this cannot be run in the compiler and i am not used to c# at all , its new to me and the brackets { } are something i keep tripping up on.  :unsure:, but I dont know i missed one until the plugin hangs (thats a big clue now I realised what causes it).

Link to comment
Share on other sites

mickle026

Well I think I am now understanding jsut about understanding the logger.  Its logging my silly messages.

 

The calls to functions are pretty similar to vb.net.

 

var data = PhysicalPaths();
TestCall();

 

What I am still not getting is why its logging an error twice?  Does the code run twice? - "Error HttpServer: Error processing request"

 

I get that the Api call fails because it doesn't return anything, but why twice?

namespace STRMHelper
{

    public class ServerApiEntryPoint : IService  
    {

        private IJsonSerializer JsonSerializer { get; set; }
        private readonly ILogger logger;
        public IRequest Request { get; set; }

        public ServerApiEntryPoint(ILogManager logManager)
        {
            logger = logManager.GetLogger(GetType().Namespace);
        }


        [Route("/Library/PhysicalPaths", "GET", Summary = "Get Library Physical Paths")]
        public class LibraryPaths : IReturn<string>
        {
 
        }


        public string Get(LibraryPaths request)
        {
            var data = PhysicalPaths();
            TestCall();
            return JsonSerializer.SerializeToString(data);
        }

        private string PhysicalPaths()
        {
            logger.Info("Testing ApiCall: We have entered the PhysicalPaths Function!", null);
            logger.Info(".........Logging works from function calls........", null);
            return null;
        }

        private string TestCall()
        {
            logger.Info("Testing ApiCall: We have entered the TestCall Function!", null);
            logger.Info(".........Logging Definately works from function calls........", null);
            return null;
        }
    }

 

2020-03-20 16:12:05.533 Info HttpServer: HTTP GET http://localhost:8096/emby/web/configurationpages?Name=STRMHelper. UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
2020-03-20 16:12:05.535 Info HttpServer: HTTP GET http://localhost:8096/web/configurationpage?name=STRMHelper&v=4.3.1.0&r=0. UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
2020-03-20 16:12:05.535 Info HttpServer: HTTP Response 200 to ::1. Time: 2ms. http://localhost:8096/emby/web/configurationpages?Name=STRMHelper
2020-03-20 16:12:05.552 Info HttpServer: HTTP Response 200 to ::1. Time: 17ms. http://localhost:8096/web/configurationpage?name=STRMHelper&v=4.3.1.0&r=0
2020-03-20 16:12:05.629 Info HttpServer: HTTP GET http://localhost:8096/emby/Library/PhysicalPaths. UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
2020-03-20 16:12:05.630 Info STRMHelper: Testing ApiCall: We have entered the PhysicalPaths Function!
2020-03-20 16:12:05.630 Info STRMHelper: .........Logging works from function calls........
2020-03-20 16:12:05.631 Info STRMHelper: Testing ApiCall: We have entered the TestCall Function!
2020-03-20 16:12:05.631 Info STRMHelper: .........Logging Definately works from function calls........
2020-03-20 16:12:05.633 Error HttpServer: Error processing request
	*** Error Report ***
	Version: 4.3.1.0
	Command line: C:\Users\mike\AppData\Roaming\Emby-Server\system\EmbyServer.dll -noautorunwebapp
	Operating system: Microsoft Windows NT 6.2.9200.0
	64-Bit OS: True
	64-Bit Process: True
	User Interactive: True
	Runtime: file:///C:/Users/mike/AppData/Roaming/Emby-Server/system/System.Private.CoreLib.dll
	Processor count: 4
	Program data path: C:\Users\mike\AppData\Roaming\Emby-Server\programdata
	Application directory: C:\Users\mike\AppData\Roaming\Emby-Server\system
	System.NullReferenceException: System.NullReferenceException: Object reference not set to an instance of an object.
	   at STRMHelper.ServerApiEntryPoint.Get(LibraryPaths request)
	   at Emby.Server.Implementations.Services.ServiceController.Execute(HttpListenerHost appHost, Object requestDto, IRequest req)
	   at Emby.Server.Implementations.Services.ServiceHandler.ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, RestPath restPath, String responseContentType, CancellationToken cancellationToken)
	   at Emby.Server.Implementations.HttpServer.HttpListenerHost.RequestHandler(IRequest httpReq, ReadOnlyMemory`1 urlString, ReadOnlyMemory`1 localPath, CancellationToken cancellationToken)
	Source: STRMHelper
	TargetSite: System.String Get(LibraryPaths)
	
2020-03-20 16:12:05.634 Info HttpServer: HTTP Response 500 to ::1. Time: 6ms. http://localhost:8096/emby/Library/PhysicalPaths
2020-03-20 16:12:07.264 Info HttpServer: HTTP GET http://localhost:8096/emby/Library/PhysicalPaths. UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
2020-03-20 16:12:07.265 Info STRMHelper: Testing ApiCall: We have entered the PhysicalPaths Function!
2020-03-20 16:12:07.265 Info STRMHelper: .........Logging works from function calls........
2020-03-20 16:12:07.265 Info STRMHelper: Testing ApiCall: We have entered the TestCall Function!
2020-03-20 16:12:07.265 Info STRMHelper: .........Logging Definately works from function calls........
2020-03-20 16:12:07.270 Error HttpServer: Error processing request
	*** Error Report ***
	Version: 4.3.1.0
	Command line: C:\Users\mike\AppData\Roaming\Emby-Server\system\EmbyServer.dll -noautorunwebapp
	Operating system: Microsoft Windows NT 6.2.9200.0
	64-Bit OS: True
	64-Bit Process: True
	User Interactive: True
	Runtime: file:///C:/Users/mike/AppData/Roaming/Emby-Server/system/System.Private.CoreLib.dll
	Processor count: 4
	Program data path: C:\Users\mike\AppData\Roaming\Emby-Server\programdata
	Application directory: C:\Users\mike\AppData\Roaming\Emby-Server\system
	System.NullReferenceException: System.NullReferenceException: Object reference not set to an instance of an object.
	   at STRMHelper.ServerApiEntryPoint.Get(LibraryPaths request)
	   at Emby.Server.Implementations.Services.ServiceController.Execute(HttpListenerHost appHost, Object requestDto, IRequest req)
	   at Emby.Server.Implementations.Services.ServiceHandler.ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, RestPath restPath, String responseContentType, CancellationToken cancellationToken)
	   at Emby.Server.Implementations.HttpServer.HttpListenerHost.RequestHandler(IRequest httpReq, ReadOnlyMemory`1 urlString, ReadOnlyMemory`1 localPath, CancellationToken cancellationToken)
	Source: STRMHelper
	TargetSite: System.String Get(LibraryPaths)
	
2020-03-20 16:12:07.277 Info HttpServer: HTTP Response 500 to ::1. Time: 13ms. http://localhost:8096/emby/Library/PhysicalPaths

 

Link to comment
Share on other sites

mickle026

Forget that above, I do this often spend an hour confused and then as soon as I ask a question figure it out.

 

In my Html I was calling the Api twice! - Duh !

 

 

Doing it once now, I have removed the bottom Call from the html

            // function (GetLibraryPaths) Api call
            ApiClient.GetLibraryPaths = function () {
                var Swaggerurl = this.getUrl("Library/PhysicalPaths"); // we format a URL, no need for API key as the API client will automatically set it in the headers for you

                return this.ajax({
                    type: "GET", // we make get request
                    url: Swaggerurl // to this URL
                })
            };

            // Call a function (GetLibraryPaths) then process the response, the log it.
           ApiClient.GetLibraryPaths().then(res => {
               //return res.json(); // this will parse the response body as JSON
               return res.toString();
            }).then(res => {
                console.log(res); // this will log the result of our request as a JSON object
            });

 

2020-03-20 16:23:36.696 Info HttpServer: HTTP GET http://localhost:8096/emby/web/configurationpages?Name=STRMHelper. UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
2020-03-20 16:23:36.699 Info HttpServer: HTTP GET http://localhost:8096/web/configurationpage?name=STRMHelper&v=4.3.1.0&r=0. UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
2020-03-20 16:23:36.700 Info HttpServer: HTTP Response 200 to ::1. Time: 3ms. http://localhost:8096/emby/web/configurationpages?Name=STRMHelper
2020-03-20 16:23:36.704 Info HttpServer: HTTP Response 200 to ::1. Time: 5ms. http://localhost:8096/web/configurationpage?name=STRMHelper&v=4.3.1.0&r=0
2020-03-20 16:23:38.014 Info HttpServer: HTTP GET http://localhost:8096/emby/Library/PhysicalPaths. UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
2020-03-20 16:23:38.017 Info STRMHelper: Testing ApiCall: We have entered the PhysicalPaths Function!
2020-03-20 16:23:38.017 Info STRMHelper: .........Logging works from function calls........
2020-03-20 16:23:38.017 Info STRMHelper: Testing ApiCall: We have entered the TestCall Function!
2020-03-20 16:23:38.017 Info STRMHelper: .........Logging Definately works from function calls........
2020-03-20 16:23:38.025 Error HttpServer: Error processing request
	*** Error Report ***
	Version: 4.3.1.0
	Command line: C:\Users\mike\AppData\Roaming\Emby-Server\system\EmbyServer.dll -noautorunwebapp
	Operating system: Microsoft Windows NT 6.2.9200.0
	64-Bit OS: True
	64-Bit Process: True
	User Interactive: True
	Runtime: file:///C:/Users/mike/AppData/Roaming/Emby-Server/system/System.Private.CoreLib.dll
	Processor count: 4
	Program data path: C:\Users\mike\AppData\Roaming\Emby-Server\programdata
	Application directory: C:\Users\mike\AppData\Roaming\Emby-Server\system
	System.NullReferenceException: System.NullReferenceException: Object reference not set to an instance of an object.
	   at STRMHelper.ServerApiEntryPoint.Get(LibraryPaths request)
	   at Emby.Server.Implementations.Services.ServiceController.Execute(HttpListenerHost appHost, Object requestDto, IRequest req)
	   at Emby.Server.Implementations.Services.ServiceHandler.ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, RestPath restPath, String responseContentType, CancellationToken cancellationToken)
	   at Emby.Server.Implementations.HttpServer.HttpListenerHost.RequestHandler(IRequest httpReq, ReadOnlyMemory`1 urlString, ReadOnlyMemory`1 localPath, CancellationToken cancellationToken)
	Source: STRMHelper
	TargetSite: System.String Get(LibraryPaths)
	
2020-03-20 16:23:38.027 Info HttpServer: HTTP Response 500 to ::1. Time: 13ms. http://localhost:8096/emby/Library/PhysicalPaths

 

Edited by mickle026
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...