tindoo83 0 Posted April 6, 2022 Share Posted April 6, 2022 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 More sharing options...
bakes82 90 Posted April 6, 2022 Share Posted April 6, 2022 (edited) 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. 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 April 6, 2022 by bakes82 Link to comment Share on other sites More sharing options...
Luke 37065 Posted April 6, 2022 Share Posted April 6, 2022 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 More sharing options...
Luke 37065 Posted April 6, 2022 Share Posted April 6, 2022 Ok you're talking about the whole channel. I thought he was asking about individual items. Link to comment Share on other sites More sharing options...
bakes82 90 Posted April 6, 2022 Share Posted April 6, 2022 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 More sharing options...
tindoo83 0 Posted April 7, 2022 Author Share Posted April 7, 2022 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 More sharing options...
bakes82 90 Posted April 7, 2022 Share Posted April 7, 2022 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 Link to comment Share on other sites More sharing options...
tindoo83 0 Posted April 7, 2022 Author Share Posted April 7, 2022 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 More sharing options...
bakes82 90 Posted April 8, 2022 Share Posted April 8, 2022 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 More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now