Jump to content

Which DisplayMessage call is correct


chef

Recommended Posts

chef

Hey guys,

 

I've tried a couple different ways to get the DisplayMessage working in the WebClient from my plugin.

 

This is not right (below):

 public static async void SendMessage()
        {
            var args = new Dictionary<string, string> {{"Header", "Test"}, {"Text", "Test"}};
            
            foreach (var session in SessionManager.Sessions)
            {

                await SessionManager.SendGeneralCommand(SessionManager.Sessions.FirstOrDefault().Id, session.Id,
                    new GeneralCommand
                    {
                        Arguments = args, 
                        ControllingUserId = UserManager.GetUserByName("Admin").Id.ToString(), 
                        Name ="DisplayMessage"
                    }, CancellationToken.None);
            }
        }

I think this is right (below), but I'm not getting a displayed message (it should be modal):

        public static async void SendMessage()
        {
            var args = new Dictionary<string, string> {{"Header", "Test"}, {"Text", "Test"}};
            //args.Add("TimeoutMs", "1200" );
                        
            foreach (var session in SessionManager.Sessions)
            {
                await session.SessionController.SendGeneralCommand(
                    new GeneralCommand
                    {
                        Arguments = args, 
                        ControllingUserId = UserManager.GetUserByName("Admin").Id.ToString(), 
                        Name ="DisplayMessage"
                    }, CancellationToken.None);
            }
        }

Many thanks for your time.  I'm attempting to fulfill some minor feature requests. So any help would be most welcome :)

Link to comment
Share on other sites

chef

are there any errors in your browser console?

Right! I'll double check the chrome console.

 

I have in the past had success when using the display message general command from an application. This is the first time I have attempted it from a plugin.

Edited by chef
Link to comment
Share on other sites

chef

It would seem that my ServerEntryPoint class is not loading in the plugin.

 

All my HTML loads fine and the plugin shows up in the plugin list, but that class just won't work.

 

This would explain why my message routine is not working.

 

Gotta figure out why...

Link to comment
Share on other sites

chef

Ah Ha! I know you'll want full logs but this is the error with the plugin here:

2017-06-09 09:26:10.5406 Error App: Error creating Messenger.ServerEntryPoint
	*** Error Report ***
	Version: 3.2.19.0
	Command line: C:\Users\MediaServer\AppData\Roaming\Emby-Server\System\MediaBrowser.ServerApplication.exe
	Operating system: Microsoft Windows NT 6.1.7601 Service Pack 1
	64-Bit OS: True
	64-Bit Process: True
	Processor count: 4
	Program data path: C:\Users\MediaServer\AppData\Roaming\Emby-Server
	Application directory: C:\Users\MediaServer\AppData\Roaming\Emby-Server\System
	SimpleInjector.ActivationException: No registration for type ServerEntryPoint could be found and an implicit registration could not be made. The constructor of type ServerEntryPoint contains the parameter with name 'con' and type ISessionController that is not registered. Please ensure ISessionController is registered, or change the constructor of ServerEntryPoint. ---> SimpleInjector.ActivationException: The constructor of type ServerEntryPoint contains the parameter with name 'con' and type ISessionController that is not registered. Please ensure ISessionController is registered, or change the constructor of ServerEntryPoint.
	   at SimpleInjector.Container.ThrowParameterTypeMustBeRegistered(InjectionTargetInfo target)
	   at SimpleInjector.Advanced.DefaultDependencyInjectionBehavior.GetInstanceProducer(InjectionConsumerInfo consumer, Boolean throwOnFailure)
	   at SimpleInjector.ContainerOptions.GetInstanceProducerFor(InjectionConsumerInfo consumer)
	   at SimpleInjector.Registration.BuildConstructorParameters(ConstructorInfo constructor)
	   at SimpleInjector.Registration.BuildNewExpression()
	   at SimpleInjector.Registration.BuildTransientExpression()
	   at SimpleInjector.InstanceProducer.BuildExpressionInternal()
	   at System.Lazy`1.CreateValue()
	   at System.Lazy`1.LazyInitValue()
	   at SimpleInjector.InstanceProducer.BuildInstanceCreator()
	   at SimpleInjector.InstanceProducer.BuildAndReplaceInstanceCreatorAndCreateFirstInstance()
	   at SimpleInjector.InstanceProducer.GetInstance()
	   --- End of inner exception stack trace ---
	   at SimpleInjector.InstanceProducer.GetInstance()
	   at SimpleInjector.Container.GetInstanceForRootType(Type serviceType)
	   at SimpleInjector.Container.GetInstance(Type serviceType)
	   at Emby.Common.Implementations.BaseApplicationHost`1.CreateInstanceSafe(Type type)
	SimpleInjector.ActivationException
	   at SimpleInjector.InstanceProducer.GetInstance()
	   at SimpleInjector.Container.GetInstanceForRootType(Type serviceType)
	   at SimpleInjector.Container.GetInstance(Type serviceType)
	   at Emby.Common.Implementations.BaseApplicationHost`1.CreateInstanceSafe(Type type)
	InnerException: SimpleInjector.ActivationException
	SimpleInjector.ActivationException: The constructor of type ServerEntryPoint contains the parameter with name 'con' and type ISessionController that is not registered. Please ensure ISessionController is registered, or change the constructor of ServerEntryPoint.
	   at SimpleInjector.Container.ThrowParameterTypeMustBeRegistered(InjectionTargetInfo target)
	   at SimpleInjector.Advanced.DefaultDependencyInjectionBehavior.GetInstanceProducer(InjectionConsumerInfo consumer, Boolean throwOnFailure)
	   at SimpleInjector.ContainerOptions.GetInstanceProducerFor(InjectionConsumerInfo consumer)
	   at SimpleInjector.Registration.BuildConstructorParameters(ConstructorInfo constructor)
	   at SimpleInjector.Registration.BuildNewExpression()
	   at SimpleInjector.Registration.BuildTransientExpression()
	   at SimpleInjector.InstanceProducer.BuildExpressionInternal()
	   at System.Lazy`1.CreateValue()
	   at System.Lazy`1.LazyInitValue()
	   at SimpleInjector.InstanceProducer.BuildInstanceCreator()
	   at SimpleInjector.InstanceProducer.BuildAndReplaceInstanceCreatorAndCreateFirstInstance()
	   at SimpleInjector.InstanceProducer.GetInstance()
	   at SimpleInjector.Container.ThrowParameterTypeMustBeRegistered(InjectionTargetInfo target)
	   at SimpleInjector.Advanced.DefaultDependencyInjectionBehavior.GetInstanceProducer(InjectionConsumerInfo consumer, Boolean throwOnFailure)
	   at SimpleInjector.ContainerOptions.GetInstanceProducerFor(InjectionConsumerInfo consumer)
	   at SimpleInjector.Registration.BuildConstructorParameters(ConstructorInfo constructor)
	   at SimpleInjector.Registration.BuildNewExpression()
	   at SimpleInjector.Registration.BuildTransientExpression()
	   at SimpleInjector.InstanceProducer.BuildExpressionInternal()
	   at System.Lazy`1.CreateValue()
	   at System.Lazy`1.LazyInitValue()
	   at SimpleInjector.InstanceProducer.BuildInstanceCreator()
	   at SimpleInjector.InstanceProducer.BuildAndReplaceInstanceCreatorAndCreateFirstInstance()
	   at SimpleInjector.InstanceProducer.GetInstance()
Edited by chef
Link to comment
Share on other sites

chef

I think it is because I imported the ISessionController Interface. That might be a "no-no".

 

 

Yes removed that Interface... (it was there to test it out), and now my EntreyPoint loads... But, I get a null exception when attemnpting to send a message.

 

But, definitely further along!

 

That Null Reference exception when trying to send a GeneralCommand/DisplayMessage from inside a plugin looks like this:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Messenger.ServerEntryPoint.<SendMessage>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__6_1(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
System.NullReferenceException
   at Messenger.ServerEntryPoint.<SendMessage>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__6_1(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()


That exception doesn't really elude to which parameter isn't working though.

 

What happens when I use the "SendMessage" routine? Is that different?

Edited by chef
Link to comment
Share on other sites

You would use the send general command and then pass Displaymessage as the command name.

 

The actual send message method is just to send something (anything) to the session.

Link to comment
Share on other sites

chef

Sweet! It's working!

 

Have you ever initiated a routine in the c# code from the JavaScript side? In other words pressing a button runs c# code?

 

I thought about passing a Boolean value into the PluginConfiguration which would trigger an event.

 

But it seems kinda "hacky".

Edited by chef
Link to comment
Share on other sites

You can implement your own API functions in your plugin and then call them from your configuration page.

Link to comment
Share on other sites

chef

So JavaScript would use the ApiClient Object? Like when you want to UpdatePluginConfiguration?

 

And, in my case, the SendMessage routine would live in the PluginConfiguration class or the ServerEntryPoint?

Link to comment
Share on other sites

chef

Sorry, when Eric mentioned you can call functions from the C# code, did he mean creating an endpoint with the IService interface? Or does he mean I can literally create a function in the configuration which would return information.

 

Thank you.

Link to comment
Share on other sites

There are a couple ways you could do it. you could build your own api endpoints then have your plugin page talk to those endpoints, and the receiver on the server will respond to the requests.

 

Or you could do it all in javascript just using the emby api.

Link to comment
Share on other sites

chef

Here is how I did it:

 

Create an Endpoint in the API like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Session;

namespace Messenger
{

    [Route("/Message", "GET")]
    public class GetMessage : IReturn<string>
    {

    }

   
    public class MessageService : IService
    {
        private readonly ISessionManager SessionManager;
        private readonly IUserManager UserManager;
        private readonly ILogger Logger;

        public MessageService(ISessionManager ses, IUserManager user, ILogger log)
        {
            SessionManager = ses;
            UserManager = user;
            Logger = log;
        }
        public object Get(GetMessage request)
        {
            SendMessage();            
            return SessionManager.Sessions.Count();
        }

        public async void SendMessage()
        {
            foreach (var session in SessionManager.Sessions)
            {
                try
                {
                    //Logger.Info(Plugin.Instance.Name + ": Attempting Message to " + session.DeviceName);
                    var args = new Dictionary<string, string> {{"Header", "Test"}, {"Text", "Hello World"}};

                   // Logger.Info(Plugin.Instance.Name + ": Sending..." + session.DeviceName);

                    await session.SessionController.SendGeneralCommand(
                        new GeneralCommand
                        {
                            Arguments = args,
                            ControllingUserId = UserManager.GetUserByName("Admin").Id.ToString(),
                            Name = "DisplayMessage"
                        }, CancellationToken.None);



                   // Logger.Info(Plugin.Instance.Name + ": Message Sent to " + session.DeviceName);
                }
                catch
                {
                    //return SessionManager.Sessions.Count() + ": " + ex.Message;
                }
            }
        }
    }
   
}



Then call the endpoint in my plugin Javascript, like this:

              function SendMessage() {
                    var url = ApiClient.getUrl("/Message");

                    return ApiClient.ajax({
                        type: "GET",
                        url: url,
                        contentType: 'application/json'
                    });
                }

I just have to figure out how to send a body of information from my configuration page along with the ajax function.

 

This is toooo cool!

 

Thanks @@Luke!

Edited by chef
Link to comment
Share on other sites

You should prefix all endpoints like /myplugin/message, to avoid any possible collision with others.

Link to comment
Share on other sites

chef

You should prefix all endpoints like /myplugin/message, to avoid any possible collision with others.

 

Excellent piece if advance. Thank you.

Link to comment
Share on other sites

chef

I believe that the "Data" parameter in the "ApiClient.ajax" request is how I send extra info back to the Custom Endpoint.

 

I notice the use of "JSON.stringfy", which I believe serializes the extra data into JSON format.

 

At that point I believe the Endpoint class must contain a place to hold the data. SO it should be like this:

   [Route("/Messenger/Message", "GET")]
    public class GetMessage : IReturnVoid
    {
        public string Header { get; set; }
        public string Text { get; set; }
        public string User { get; set; }
    }

and in the javscript code:

               function SendMessage(message) {
                    var url = ApiClient.getUrl("/Messenger/Message");

                    return ApiClient.ajax({
                        type: "GET",
                        url: url,
                        data: JSON.stringify(message),
                        contentType: 'application/json'
                    });
                }

This is failing to work.

 

There is one thing that is occurring to me right now, and that is I have used the "GET"  key word as the type of communication to the Server Endpoint, when I think it should be "POST" because it contains extra data.

 

Edit: removing the "data" catalog in the ajax request allows my endpoint to work, but adding the "data" category breaks it.

 

Interesting...

Edited by chef
Link to comment
Share on other sites

Yes, if you want to send data then you need to POST (it is actually called post data).

Link to comment
Share on other sites

chef

The good news is that I have written the messaging plugin.

 

It works, each client which can accept messages do show them. But they are all modal. Whenever I add the "TimeoutMs" argument no messages display.

 

It is suppose to be a long int. So I added a "L" at the end of the large number.

 

It is a string in the arguments dictionary. I am thinking that the server converts it back into a long integer when it reads the arguments.

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