Jump to content

Plugin config pages for users soon to be supported


Luke

Recommended Posts

You'll soon be able to create config screens that show up in user menus, rather than the server menu. The first recipient of this will be Trakt, so now each user can setup their Trakt integration without requiring the server admin to handle it for them:

image.png

And a feature will be registered that allows admins to control access to the feature:

image.png

I'm not sure who might have a use for this or not, but Trakt certainly does.

@chef

  • Like 8
Link to comment
Share on other sites

Cheesegeezer

That's awesome @Luke

MediaInfo Toolbox can benefit from this also, allowing different users to display the info how they like it.

Can you share the Interface class with use please.

Great work as always fella!!

  • Thanks 2
Link to comment
Share on other sites

2 minutes ago, Cheesegeezer said:

That's awesome @Luke

MediaInfo Toolbox can benefit from this also, allowing different users to display the info how they like it.

Can you share the Interface class with use please.

Great work as always fella!!

Yup I'll have more info when the next beta drops. Trakt will be using it going forward.

  • Thanks 1
Link to comment
Share on other sites

This could be how @rbjtech gets his user based recommendations plugin rockin'.

You could even allow users to build their own Top Picks. Endless possibilities. 👍

 

  • Like 1
  • Agree 1
Link to comment
Share on other sites

rbjtech

Sounds good - having a way to distribute administration/engagement to users is a big leap forward.

I also came across the 'possibility' of adding context menu's options from a plugin as well.

Looking forward to seeing what we can do !

 

  • Agree 2
Link to comment
Share on other sites

Junglejim

Yep this sounds great! My users don't have any admin access (that includes me :) , except for sub downloads).

This sounds like a nice step forward to give users some control of some plugins etc. 👍

  • Agree 1
Link to comment
Share on other sites

Cheesegeezer

Structurally how does this work.

  1. admin sets plugin accessibility 
  2. User then sees the plugin UI
  3. and this is linked to a List<UserPluginPrefs> By name or pluginId.
  4. Or is it written to the plugin config.xml?

if there are any examples to share after release it would be nice. Or comment the params for each interface method that woukd be lovely 👍👍

 

Link to comment
Share on other sites

  • 1 month later...
Cheesegeezer

@Luke @softworkz

Where can i find this in the beta nugets.  is there any sample code available to see the structure and requirements.

Or can you point me to the correct interface to use please.  

Cheers

image.png.24cdbbef6ddd1b2981f63ba4dc32651c.png

Link to comment
Share on other sites

BillOatman
On 11/29/2022 at 2:50 PM, chef said:

I can absolutely think of ways this can be very useful.

 

I bet you can :)

  • Haha 2
Link to comment
Share on other sites

Cheesegeezer

I’ve almost cracked it i think. My issues are getting current user, but ill muddle thru tomorrow. And hopefully have it finished 

Link to comment
Share on other sites

Cheesegeezer


 

Doh!! I just need to pass from the UI as a parameter to my Iservice class route!! What an idiot lol 😂 🤦‍♂️

Edited by Cheesegeezer
Link to comment
Share on other sites

  • 3 months later...
Cheesegeezer
1 hour ago, mickle026 said:

Is there any examples of this yet?

I kinda have it working a little in my YTPlugin.

image.png.53ce8f118dc4c52370c0491ac205113b.png

 

image.png.1fb77a1b39272fb096ceaf8d5374e3c5.png

Need beta 28 or later i think. 

It's a lot more involved.

Interface IConfigurationFactory (emby.media)

Class ConfigurationStore (emby.media)

using Emby.YouTube.Configuration.UserSettings;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using System.Collections.Generic;

namespace Emby.YouTube.Configuration
{
    public class ConfigurationFactory : IUserConfigurationFactory
    {
        public static string ConfigKey = "YTChannel";

        public IEnumerable<ConfigurationStore> GetConfigurations()
        {
            PluginConfigStore[] array = new PluginConfigStore[1];
            PluginConfigStore YTConfigStore = new PluginConfigStore();
            YTConfigStore.ConfigurationType = typeof(YTPluginUser);
            YTConfigStore.Key = ConfigKey;
            array[0] = YTConfigStore;
            return array;
        }
    }
    
}

 

using Emby.YouTube.Configuration.UserSettings;
using MediaBrowser.Model.Plugins;
using System;

namespace Emby.YouTube.Configuration
{
    public class YTPluginConfiguration : BasePluginConfiguration
    {
        public YTPluginUser[] YTPluginUsers { get; set; } = Array.Empty<YTPluginUser>();
        
    }
}
using System;

namespace Emby.YouTube.Configuration.UserSettings
{
    public class YTPluginUser
    {
        public string YouTubeUsername { get; set; }

        public string YouTubePasswordHash { get; set; }

        public string YouTubeAuthTokenHash { get; set; }
    }
}

Then register this in the PluginConfigStore

using System;
using Emby.YouTube.Configuration.UserSettings;
using MediaBrowser.Common.Configuration;

namespace Emby.YouTube.Configuration;

public class PluginConfigStore : ConfigurationStore, IValidatingConfiguration
{
	public void Validate(object oldConfig, object newConfig)
	{
		if (oldConfig is YTPluginUser oldYTUser)
		{
			YTPluginUser newYTUser = (YTPluginUser)newConfig;
			if (!string.Equals(newYTUser.YouTubeUsername, oldYTUser.YouTubeUsername, StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty(newYTUser.YouTubeUsername))
            {
                newYTUser.YouTubeUsername = string.Empty;
                newYTUser.YouTubePasswordHash = string.Empty;
            }
		}
	}
}

The JS side

//EMBY FUNCTIONS
        async function getUser() {
            return await ApiClient.getCurrentUserId();
        }

        function fetchExistingConfiguration(userId) {

            return ApiClient.getTypedUserSettings(userId, 'YTChannel');
        }

        function loadUserConfiguration(userId, view) {

            fetchExistingConfiguration(userId).then(function (config) {                
                config.LocationsExcluded = config.LocationsExcluded || [];

            });
        }

inside the view function

return function(view) {
            view.addEventListener('viewshow', async() => {

                loading.show();

                mainTabsManager.setTabs(this, 0, getTabs);
                userId = await getUser();

                loadUserConfiguration(userId, view);                

                loading.hide();
              
              //other functions to be carried out on load
            }
                                  }

That's as far as i've got so far...

i've not implemented the c# side for getting configs etc, but that should be pretty straight forward.

 

Anyone let me know if there are ways i can improve this.

 

Cheers

Edited by Cheesegeezer
  • Thanks 1
Link to comment
Share on other sites

shure
On 5/12/2023 at 8:25 PM, Cheesegeezer said:

I kinda have it working a little in my YTPlugin.

image.png.53ce8f118dc4c52370c0491ac205113b.png

 

image.png.1fb77a1b39272fb096ceaf8d5374e3c5.png

Need beta 28 or later i think. 

It's a lot more involved.

Interface IConfigurationFactory (emby.media)

Class ConfigurationStore (emby.media)

using Emby.YouTube.Configuration.UserSettings;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using System.Collections.Generic;

namespace Emby.YouTube.Configuration
{
    public class ConfigurationFactory : IUserConfigurationFactory
    {
        public static string ConfigKey = "YTChannel";

        public IEnumerable<ConfigurationStore> GetConfigurations()
        {
            PluginConfigStore[] array = new PluginConfigStore[1];
            PluginConfigStore YTConfigStore = new PluginConfigStore();
            YTConfigStore.ConfigurationType = typeof(YTPluginUser);
            YTConfigStore.Key = ConfigKey;
            array[0] = YTConfigStore;
            return array;
        }
    }
    
}

 

using Emby.YouTube.Configuration.UserSettings;
using MediaBrowser.Model.Plugins;
using System;

namespace Emby.YouTube.Configuration
{
    public class YTPluginConfiguration : BasePluginConfiguration
    {
        public YTPluginUser[] YTPluginUsers { get; set; } = Array.Empty<YTPluginUser>();
        
    }
}
using System;

namespace Emby.YouTube.Configuration.UserSettings
{
    public class YTPluginUser
    {
        public string YouTubeUsername { get; set; }

        public string YouTubePasswordHash { get; set; }

        public string YouTubeAuthTokenHash { get; set; }
    }
}

Then register this in the PluginConfigStore

using System;
using Emby.YouTube.Configuration.UserSettings;
using MediaBrowser.Common.Configuration;

namespace Emby.YouTube.Configuration;

public class PluginConfigStore : ConfigurationStore, IValidatingConfiguration
{
	public void Validate(object oldConfig, object newConfig)
	{
		if (oldConfig is YTPluginUser oldYTUser)
		{
			YTPluginUser newYTUser = (YTPluginUser)newConfig;
			if (!string.Equals(newYTUser.YouTubeUsername, oldYTUser.YouTubeUsername, StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty(newYTUser.YouTubeUsername))
            {
                newYTUser.YouTubeUsername = string.Empty;
                newYTUser.YouTubePasswordHash = string.Empty;
            }
		}
	}
}

The JS side

//EMBY FUNCTIONS
        async function getUser() {
            return await ApiClient.getCurrentUserId();
        }

        function fetchExistingConfiguration(userId) {

            return ApiClient.getTypedUserSettings(userId, 'YTChannel');
        }

        function loadUserConfiguration(userId, view) {

            fetchExistingConfiguration(userId).then(function (config) {                
                config.LocationsExcluded = config.LocationsExcluded || [];

            });
        }

inside the view function

return function(view) {
            view.addEventListener('viewshow', async() => {

                loading.show();

                mainTabsManager.setTabs(this, 0, getTabs);
                userId = await getUser();

                loadUserConfiguration(userId, view);                

                loading.hide();
              
              //other functions to be carried out on load
            }
                                  }

That's as far as i've got so far...

i've not implemented the c# side for getting configs etc, but that should be pretty straight forward.

 

Anyone let me know if there are ways i can improve this.

 

Cheers

Hi,  After using EnableInUserMenu, the plugin page can be viewed by user. but it won't work on mobile apps

I found trakt works fine on mobile apps, May I ask how to make it happened.

                new PluginPageInfo
                {
                    Name = "customcssjs",
                    DisplayName = "Custom Css and JavaScript",
                    EmbeddedResourcePath = GetType().Namespace + ".Configuration.customcssjs.html",
                    EnableInMainMenu = false,
                    EnableInUserMenu = true,
                    IsMainConfigPage = true,
                    MenuIcon = "tune"
                },
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...