Jump to content

Channel Plugin & Users ?!


tindoo83

Recommended Posts

tindoo83

Hi there

currently playing around by writing a custom channel plugin connecting to an internal data source. When I first digged into the IChannel#GetChannelItems method, it looked, like I'm able use the given InternalChannelItemQuery.userId field to control, which user can see which items within the channel. 

Didn't worked out as expected: Looks like this field is always 0 and channels get queried in the background and written in the database for performance reasons...

Then I switched the strategy and rewrote my plugin to create multiple channels: So 1 unique channel per user allowing me to control, which user is allowed to watch which item. Looked great on first spot, each user only sees his channel and only his items on the start page. 

Then I logged in as one of my test users and saw, that, when I click on the search button, again, the user is able to seach / see / craw all items of all channels again. So this whole "manage, which user is allowed to see which channel in admin interface is sort of pointless for me". Not sure, if this is intended or simply a security issue/bug.

Question is: Since my channel items get written in the emby database anyway and I can subscribe to the ItemAdded/Itemupdated events for this items (to manipulate all metadata and not just the limited ChannelItemInfo's) ... two question:

1) Is there a way to control here, which user is allowed to see which items in a more safe way (without breakout possibilities?). Can live with any sort of hack here ;-)

2) What do I need to do in the LibraryManager.ItemAdded event to write user data like "favorit information, play count, current playback position" etc. for all users where I have these informtions for an item? 

Link to comment
Share on other sites

bakes82

Yeah I think the "Enable All Channels" is defaulted to yes.  So if you make a new user they will see them all.

I have a plugin where I take a trakt list and then generate a channel for it of the items from the database.  You basically need to loop all your users/channels and block them in the user policy, unless you can find a better solution then let me know ;)  My code that sets the user permissions is run in the "Refresh" job runs as part of the getchannelitems.  I suppose you could make it in to another task if you wanted.  I have my code broken out so I can do that.

image.png.426f740b57955b1d218d1fe435087eec.png

image.png.3774066dcd8400885bac7a17004bcafe.png

public void SetChannelUserPermissions(IUserManager userManager,
                                              ILibraryManager libraryManager,
                                              TraktChannel channelToConfigure,
                                              ILogger logger)
        {
            var users = userManager.Users;

            var channels = libraryManager.GetItemList(new InternalItemsQuery
                                                      {
                                                          Recursive        = true,
                                                          IncludeItemTypes = new[] { "Channel" }
                                                      })
                                         .Where(x => !string.IsNullOrEmpty(x.Name))
                                         .ToList();
            logger.Info("channels " + string.Join(",", channels.Select(x => x.Name.ToString()).ToArray()));
            logger.Info($"Current Channel Name {channelToConfigure.ChannelName}");
            
            var currentChannel = channels.First(x => x.Name == channelToConfigure.ChannelName)
                                         .Id.ToString()
                                         .Replace("-", string.Empty);
            
            logger.Info($"Current Channel Id {currentChannel}");

            if (!channelToConfigure.Enabled)
                foreach (var user in users)
                {
                    user.Policy.EnableAllChannels = false;

                    var enabledChannels = user.Policy.EnabledChannels != null
                        ? user.Policy.EnabledChannels.ToList()
                        : new List<string>();
                    var disabledChannels = user.Policy.BlockedChannels != null
                        ? user.Policy.BlockedChannels.ToList()
                        : new List<string>();

                    logger.Info("Enabled " + string.Join(",", enabledChannels.Select(x => x.ToString()).ToArray()));
                    logger.Info("Blocked " + string.Join(",", disabledChannels.Select(x => x.ToString()).ToArray()));

                    if (!disabledChannels.Contains(currentChannel)) disabledChannels.Add(currentChannel);
                    logger.Info("Added To Disabled " + currentChannel);

                    if (enabledChannels.Any())
                    {
                        if (enabledChannels.Contains(currentChannel)) enabledChannels.Remove(currentChannel);
                    }
                    else
                    {
                        foreach (var channel in channels.Where(x => x.Name != channelToConfigure.ChannelName))
                            enabledChannels.Add(channel.Id.ToString()
                                                       .Replace("-", string.Empty));
                    }

                    user.Policy.EnabledChannels = enabledChannels.ToArray();
                    user.Policy.BlockedChannels = disabledChannels.ToArray();

                    logger.Info("Enabled " + string.Join(",", enabledChannels.Select(x => x.ToString())
                                                                             .ToArray()));
                    logger.Info("Blocked " + string.Join(",", disabledChannels.Select(x => x.ToString())
                                                                              .ToArray()));

                    userManager.UpdateUser(user);
                }
            else
                foreach (var user in users)
                {
                    user.Policy.EnableAllChannels = false;

                    var enabledChannels = user.Policy.EnabledChannels != null
                        ? user.Policy.EnabledChannels.ToList()
                        : new List<string>();
                    var disabledChannels = user.Policy.BlockedChannels != null
                        ? user.Policy.BlockedChannels.ToList()
                        : new List<string>();

                    //Logger.Info("Enabled " + string.Join(",", enabledChannels.Select(x => x.ToString()).ToArray()));
                    //Logger.Info("Blocked " + string.Join(",", disabledChannels.Select(x => x.ToString()).ToArray()));

                    if (disabledChannels.Contains(currentChannel)) disabledChannels.Remove(currentChannel);

                    if (enabledChannels.Any())
                    {
                        if (enabledChannels.Contains(currentChannel)) continue;

                        enabledChannels.Add(currentChannel);
                    }
                    else
                    {
                        foreach (var channel in channels)
                            enabledChannels.Add(channel.Id.ToString()
                                                       .Replace("-", string.Empty));
                    }

                    user.Policy.EnabledChannels = enabledChannels.ToArray();
                    user.Policy.BlockedChannels = disabledChannels.ToArray();

                    logger.Info("Enabled " + string.Join(",", enabledChannels.Select(x => x.ToString())
                                                                             .ToArray()));
                    logger.Info("Blocked " + string.Join(",", disabledChannels.Select(x => x.ToString())
                                                                              .ToArray()));

                    userManager.UpdateUser(user);
                }
        }

 

 

Edited by bakes82
Link to comment
Share on other sites

Hi, there's no way for you to control visibility per user in the plugin.

We used to do this but removed it because it's more preferable to filler and sort from the emby database rather than the channel.

This could come back, but first there would need to be a way to do it with regular media items and then you could hook into that 

Link to comment
Share on other sites

Ok you're talking about the whole channel. I thought he was asking about individual items.

Link to comment
Share on other sites

bakes82
1 minute ago, Luke said:

Ok you're talking about the whole channel. I thought he was asking about individual items.

Sounds like he prob wants to make a "watch list" type feature and just have it in 1 channel.  So it would have UserId 123, MovieID  123, UserId 122, MovieId 653.  Then when the channel loads it it would only show that users lists of media, but from what I can tell the channel creates its records on the "refresh internet" sched task, and isnt dynamic, so I guess if the UserID was avail he would pull all the movie objects find where the ids for them exist in the subset and just display those.  So then the channel would appear for EVERYONE but if a user went to it that didnt have a subset list, it would return 0 records.

Link to comment
Share on other sites

tindoo83

I have a movie database (metadata + content) in an already existing backend-server. The database contains, which of my users is allowed to watch which subset of movies. Backend even handles, which user is allowed to see which metadata per movie. So even a single movie might have different metadata (covers, descriptions, cast pictures etc) in my backend for different users due to different native languages of the user or other settings. (Know, this is only possible in emby with data duplication, which is okay)

So my first implementation was litteraly:

public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) {
    return _myBackend.AllItemsForUserAsync(query.userId); 
}

Didn't worked, since, query.userId is always 0 and GetChannelItems is called globally by a scheduler instead. So the query parameter seems more like a historic feature...

Changed my code to create dynamically a unique channel per user, so my backend returns a list of all available channels, where 1 channel is exactly for 1 user: 

var template = _channelManager.GetChannel<Channel>();           
_channelManager.AddParts(_client.AllChannels().Select(x => template.CopyWithConfiguration(x)));

I did the code snippet, which bakes82 posted (permission of user<->channel) manuelly using the admin ui for testing purposes. 

Looked damn good on the first spot: So if I login as "Tom" I see "Tom's Private Movie Channel" and in this Channel, only the movies, which Tom is allowed to watch with the metadata set, which is for Tom. 

But if I, logged in as Tom, click on the Search magnifier in emby, I'm still able to see/search ALL movies of ALL channels. This is, what I'm currently trying to avoid at most. 

Link to comment
Share on other sites

bakes82

I dont understand, do you not have a "movies" library?

Also do you not have a class for each channel?  How are you dynamically creating each one, I could probably use that :P

Link to comment
Share on other sites

tindoo83

Oh no, of course I have a "movies" library. A huge one. A really huge one. Like most movie nerds, I would guess 😉 But the library is not managed/maintained in emby. Using a self-written application here instead since ages. 

Yeah, in my poc plugin, I use a single channel class to realize a dynamic list of channels. Since it implements IChannel, its already available in the ChannelManager. I use this instance just as an template, so added a "copy" method, which takes a channel configuration and returns a new instance of Channel holding the given configuration. On startup, I query a REST endpoint in my application, which returns a list of virtual channel configurations, eg.:

[{"id": 1, "name": "Horror"}, {"id": 2, "name": "Biggest files"}]

So for each channel configuration, I copy the template instance and put everything into the channel manager:

var template = _channelManager.GetChannel<Channel>();           
_channelManager.AddParts(_client.AllChannels().Select(x => template.CopyWithConfiguration(x)));

Worked quite good in my tests. Haven't tried to dynamically change the channels after initial startup, but don't see any reason, why it shouldn't work

Link to comment
Share on other sites

bakes82
6 hours ago, tindoo83 said:

Oh no, of course I have a "movies" library. A huge one. A really huge one. Like most movie nerds, I would guess 😉 But the library is not managed/maintained in emby. Using a self-written application here instead since ages. 

Yeah, in my poc plugin, I use a single channel class to realize a dynamic list of channels. Since it implements IChannel, its already available in the ChannelManager. I use this instance just as an template, so added a "copy" method, which takes a channel configuration and returns a new instance of Channel holding the given configuration. On startup, I query a REST endpoint in my application, which returns a list of virtual channel configurations, eg.:

[{"id": 1, "name": "Horror"}, {"id": 2, "name": "Biggest files"}]

So for each channel configuration, I copy the template instance and put everything into the channel manager:

var template = _channelManager.GetChannel<Channel>();           
_channelManager.AddParts(_client.AllChannels().Select(x => template.CopyWithConfiguration(x)));

Worked quite good in my tests. Haven't tried to dynamically change the channels after initial startup, but don't see any reason, why it shouldn't work

you got a GitHub with this, am still confused by this but maybe im just being dense because I would love to remove my 10 channel classes and just each one call my method to the the items for the channel.

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...