nack 2 Posted April 10, 2025 Posted April 10, 2025 Hi guys, First of all thank you for this wonderful SDK, it's a masterclass in dependency injection. It's a beautiful thing. However, I can't work out how to stop my channel caching everything at once. I'm using IHasChangeEvent, which when triggered calls IChannel.GetChannelItems and caches everything at once, rather than calling IChannel.GetChannelItem as the user navigates the structure. What am I doing wrong? Thanks
Luke 42077 Posted April 10, 2025 Posted April 10, 2025 Quote channel caching everything at once Hi, what exactly do you mean by this?
nack 2 Posted April 10, 2025 Author Posted April 10, 2025 7 minutes ago, Luke said: Hi, what exactly do you mean by this? I'm creating channel items for categories, which are ChannelItemType.Folder, and channel items for remote videos in those categories. It's works well. But upon content change, IChannel.GetChannelItems is called immediately. My IChannel.GetChannelItems gets the video paths and data from the remote, which takes a long while if it's all done immediately and not on demand as the channel is navigated.
Luke 42077 Posted April 10, 2025 Posted April 10, 2025 Have your channel implement IHasChangeEvent and trigger the ContentChanged event.
nack 2 Posted April 10, 2025 Author Posted April 10, 2025 7 minutes ago, Luke said: Have your channel implement IHasChangeEvent and trigger the ContentChanged event. As mentioned I have already implemented the interface and ContentChanged is triggered. But that is what calls GetChannelItems. I just want GetChannelItems to be called when the user navigates an item. Not for hundreds of thousands of items, immediately and recursively, before they're navigated to. The method GetChannelItems first creates some top level category folders, and before the user navigates those folders, GetChannelItems is called again to create the items within those folders. I probably don't understand what's going on, any help is appreciated.
Luke 42077 Posted April 10, 2025 Posted April 10, 2025 Quote I just want GetChannelItems to be called when the user navigates an item. That's not how channels work, sorry. Everything gets preloaded into the server database in order to use all of emby's features.
Luke 42077 Posted April 10, 2025 Posted April 10, 2025 We actually used to do this a long time ago. When we fetch on the fly, it essentially turns us into a front-end for that content. Now you can argue whether that's good or not, but it causes a lot of Emby features to not be possible. This results in lots of buttons in the UI that don't work - unless we build some major infrastructure to allow the server to inform the UI of everything that is supported. That's a bit of an undertaking. It's doable, but personal media is our core focus area. Additionally, and just as importantly, when the server hosting the content is responding slowly or not at all, this could cause the Emby UI to stall. Then users come in here and tell us that something is wrong with our software. So I think things got to a point where we just couldn't take that anymore.
nack 2 Posted April 10, 2025 Author Posted April 10, 2025 2 minutes ago, Luke said: We actually used to do this a long time ago. When we fetch on the fly, it essentially turns us into a front-end for that content. Now you can argue whether that's good or not, but it causes a lot of Emby features to not be possible. This results in lots of buttons in the UI that don't work - unless we build some major infrastructure to allow the server to inform the UI of everything that is supported. That's a bit of an undertaking. It's doable, but personal media is our core focus area. Additionally, and just as importantly, when the server hosting the content is responding slowly or not at all, this could cause the Emby UI to stall. Then users come in here and tell us that something is wrong with our software. So I think things got to a point where we just couldn't take that anymore. Yeah that's fair enough It does makes very large remote libraries difficult tho; it can take a long time to populate, some items don't appear for hours, updating content is no trivial thing so it has to be done on an interval, leaving dead items until it's all updated again. Granted this is an edge case and I'm trying to fit a square peg in a round hole! If I were to create multiple channels, can emby update them in parallel? Is it possible to hide channels from the users? I could have multiple hidden channels update in parallel and have a master channel pull from them. Or is this insane?
Luke 42077 Posted April 10, 2025 Posted April 10, 2025 Quote If I were to create multiple channels, can emby update them in parallel? Yes.
Luke 42077 Posted April 10, 2025 Posted April 10, 2025 Quote Is it possible to hide channels from the users? Only using normal channel access configured by the admin in user permissions. There is no way for the channel to force it.
softworkz 5066 Posted April 10, 2025 Posted April 10, 2025 1 hour ago, nack said: It does makes very large remote libraries difficult tho Not that much. We have an upcoming channel plugin which can add tens-of-thousands of items without problems. If your remote queries are taking a long time, I would recommend to not do this from a call to GetChannelItems. Instead: Create your own scheduled task Cache the data that you retrieve locally in some way If it takes very long to acquire the data, prepare for interruption (e.g. Emby Server restart) and resuming retrieval When all data is complete, trigger the event and let Emby Server retrieve all the data from your local cache, so that the whole retrieval is a relatively quick operation This gives you a much greate flexibility. You can decide which parts of the data to retrieve more frequently or less frequently and it allows you to do targeted and punctual updates (e.g. for removing stale items) and get those reflected in Emby Server quickly. Also make sure that the IDs you are assigning to items are persistent and don't change across updates.
nack 2 Posted April 10, 2025 Author Posted April 10, 2025 3 minutes ago, softworkz said: Not that much. We have an upcoming channel plugin which can add tens-of-thousands of items without problems. If your remote queries are taking a long time, I would recommend to not do this from a call to GetChannelItems. Instead: Create your own scheduled task Cache the data that you retrieve locally in some way If it takes very long to acquire the data, prepare for interruption (e.g. Emby Server restart) and resuming retrieval When all data is complete, trigger the event and let Emby Server retrieve all the data from your local cache, so that the whole retrieval is a relatively quick operation This gives you a much greate flexibility. You can decide which parts of the data to retrieve more frequently or less frequently and it allows you to do targeted and punctual updates (e.g. for removing stale items) and get those reflected in Emby Server quickly. Also make sure that the IDs you are assigning to items are persistent and don't change across updates. Thank you for the advice! This is actually how I did it originally, but I had some problems with the path of the remote media, here last year : So I ended up having to cache everything and place the paths within local strm files. This seems fixed now though., so I can probably cache the metadata but not have to use strm files. 1
softworkz 5066 Posted April 10, 2025 Posted April 10, 2025 21 minutes ago, nack said: Thank you for the advice! This is actually how I did it originally, but I had some problems with the path of the remote media, here last year It's also possible to create a Emby SDK Reference: IMediaSourceProvider for providing the URLs if the need to be consrtructed dynamically. I have an example somewhere.
softworkz 5066 Posted April 10, 2025 Posted April 10, 2025 Here's a function for adding mediasource information in a sane way: private static void AddMediaSourceInfo(ChannelItemInfo channelItem, string path, string container) { MediaProtocol protocol = MediaProtocol.File; if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { protocol = MediaProtocol.Http; } else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase)) { protocol = MediaProtocol.Rtmp; } else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase)) { protocol = MediaProtocol.Rtsp; } else if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase)) { protocol = MediaProtocol.Udp; } else if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase)) { protocol = MediaProtocol.Rtmp; } var httpHeaders = new Dictionary<string, string>(); if (protocol == MediaProtocol.Http) { httpHeaders["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.85 Safari/537.36"; } var streams = new List<MediaStream>(); if (channelItem.MediaType == ChannelMediaType.Video) { streams.Add( new MediaStream { Type = MediaStreamType.Video, // Set the index to -1 because we don't know the exact index of the video stream within the container Index = -1, }); } else { streams.Add( new MediaStream { Type = MediaStreamType.Audio, // Set the index to -1 because we don't know the exact index of the audio stream within the container Index = -1, Channels = 2, }); } var mediaSource = new MediaSourceInfo { Name = channelItem.Name, Path = path, Protocol = protocol, RunTimeTicks = channelItem.RunTimeTicks, Container = container, MediaStreams = streams, RequiresOpening = true, RequiresClosing = true, RequiresLooping = false, Id = GetMD5(path).ToString("N"), ////IsInfiniteStream = true, IsRemote = true, ////SupportsDirectPlay = supportsDirectPlay, RequiredHttpHeaders = httpHeaders, }; mediaSource.InferTotalBitrate(); channelItem.MediaSources = new List<MediaSourceInfo> { mediaSource }; }
softworkz 5066 Posted April 10, 2025 Posted April 10, 2025 (edited) 16 minutes ago, softworkz said: It's also possible to create a Emby SDK Reference: IMediaSourceProvider for providing the URLs if the need to be consrtructed dynamically. Actually, the easier way for providing dynamic media source info (almost forgot about it) is to implement Emby SDK Reference: IRequiresMediaInfoCallback in your plugin class. The code snipped in the post above is best for static URLs. Edited April 10, 2025 by softworkz
nack 2 Posted April 10, 2025 Author Posted April 10, 2025 Thank you @softworkz, this is really really helpful.
nack 2 Posted April 14, 2025 Author Posted April 14, 2025 Hi again! So I've got this working over the weekend. Meta data is cached, videos play. The only problem so far is an IO error when downloading subtitles. 2025-04-14 16:19:48.216 Error SubtitleManager: Error searching for subtitles on Open Subtitles *** Error Report *** Version: 4.8.11.0 Command line: D:\LocalLibrary\Apps\embyserver-win-x64-4.8.10.0\system\EmbyServer.dll -nointerface Operating system: Microsoft Windows 10.0.22631 Framework: .NET 6.0.36 OS/Process: x64/x64 Runtime: D:/LocalLibrary/Apps/embyserver-win-x64-4.8.10.0/system/System.Private.CoreLib.dll Processor count: 16 Data path: D:\LocalLibrary\Apps\embyserver-win-x64-4.8.10.0\programdata Application path: D:\LocalLibrary\Apps\embyserver-win-x64-4.8.10.0\system System.IO.IOException: System.IO.IOException: The filename, directory name, or volume label syntax is incorrect. : 'D:\LocalLibrary\Apps\embyserver-win-x64-4.8.10.0\system\http:\192.168.1.2\video\1895801.mp4' at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options) at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize) at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize) at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize) at System.IO.Strategies.FileStreamHelpers.ChooseStrategy(FileStream fileStream, String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, Int64 preallocationSize) at System.IO.File.OpenRead(String path) at Emby.Server.Implementations.IO.ManagedFileSystem.OpenRead(String path) at OpenSubtitles.OpenSubtitleComDownloader.SearchInternal(SubtitleSearchRequest request, Boolean useFilename, OpenSubtitleOptions options, CancellationToken cancellationToken) at OpenSubtitles.OpenSubtitleComDownloader.Search(SubtitleSearchRequest request, OpenSubtitleOptions options, CancellationToken cancellationToken) at Emby.Providers.Subtitles.SubtitleManager.<>c__DisplayClass17_0.<<SearchSubtitles>b__3>d.MoveNext() Source: System.Private.CoreLib TargetSite: Microsoft.Win32.SafeHandles.SafeFileHandle CreateFile(System.String, System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare, System.IO.FileOptions) Here is the meat of the code in GetChannelItems. var item = new ChannelItemInfo(); item.ContentType = ChannelMediaContentType.Movie; item.Id = $@"{movie.id}"; item.MediaType = ChannelMediaType.Video; item.ParentIndexNumber = 0; item.Name = movie.name; item.Type = ChannelItemType.Media; item.ForceUpdate = KBBLPlugin.Options.ForceUpdate; item.IndexNumber = int.Parse(movie.id); var mediaSourceInfo = new MediaSourceInfo(); mediaSourceInfo.Protocol = MediaProtocol.Http; mediaSourceInfo.Container = movie.ext; mediaSourceInfo.Name = movie.name; mediaSourceInfo.Id = path.GetMD5().ToString("N"); mediaSourceInfo.Path = path; mediaSourceInfo.SupportsDirectStream = true; mediaSourceInfo.SupportsDirectPlay = true; mediaSourceInfo.IsRemote = true; mediaSourceInfo.MediaStreams = new List<MediaStream> { new MediaStream { Type = MediaStreamType.Video, Index = -1, }, }; item.MediaSources = new List<MediaSourceInfo> { mediaSourceInfo }; mediaSourceInfo.InferTotalBitrate(); items.Add(item); Thanks for looking if you get the time, guys Cheers.
nack 2 Posted April 14, 2025 Author Posted April 14, 2025 1 minute ago, Luke said: bad url: http:\192.168.1.2\video\1895801.mp4 The url works when playing the video. Emby is trying something weird with it, combining it with the local path of the system folder? "D:\LocalLibrary\Apps\embyserver-win-x64-4.8.10.0\system\http:\192.168.1.2\video\1895801.mp4"
nack 2 Posted April 14, 2025 Author Posted April 14, 2025 Bit more info, the path fed to emby is "http:\\192.168.1.2\video\1895801.mp4". The path the subtitle manager is attempting to save user downloaded subtitles looks like it's that path escaped and appended to the emby system path. I have also: ChannelItemInfo.IsRemote = true ChannelFeatures.LibraryOptions.SaveSubtitlesWithMedia = false,
nack 2 Posted April 16, 2025 Author Posted April 16, 2025 @softworkzdo you have any suggestions? Sorry to ping you.
nack 2 Posted April 16, 2025 Author Posted April 16, 2025 Correction, the URL is "http://192.168.1.2/video/1895796.mkv". Apologies.
Luke 42077 Posted April 16, 2025 Posted April 16, 2025 Right it seemed like your slashes were reversed.
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