Jump to content

Channel of Tv Shows


bakes82

Recommended Posts

Is there a way to make a channel of tv series?

EX I have a list of TVDBIds and I want to have them appear on a channel.  For movies it was pretty simple, but I dont see a "series" option, just episode.

image.png.0bd2ee8ed86ddff93fc656a14acddb03.png

Link to comment
Share on other sites

Series and seasons are folders, not items, so you need to create Emby SDK Reference: ChannelItemInfo with Emby SDK Reference: ChannelItemType.Folder and set the Emby SDK Reference: ChannelItemInfo.FolderType to Emby SDK Reference: ChannelFolderType.Series.

A series folder should contain one or more folders of Emby SDK Reference: ChannelFolderType.Season.
For each season folder, you need to set the Emby SDK Reference: ChannelItemInfo.ParentIndexNumber to the season number.

Finally, you add episodes to the season folders. Here,  you need to set the Emby SDK Reference: ChannelItemInfo.ParentIndexNumber to the episode number.

Link to comment
Share on other sites

11 minutes ago, softworkz said:

Series and seasons are folders, not items, so you need to create Emby SDK Reference: ChannelItemInfo with Emby SDK Reference: ChannelItemType.Folder and set the Emby SDK Reference: ChannelItemInfo.FolderType to Emby SDK Reference: ChannelFolderType.Series.

A series folder should contain one or more folders of Emby SDK Reference: ChannelFolderType.Season.
For each season folder, you need to set the Emby SDK Reference: ChannelItemInfo.ParentIndexNumber to the season number.

Finally, you add episodes to the season folders. Here,  you need to set the Emby SDK Reference: ChannelItemInfo.ParentIndexNumber to the episode number.

You lost me after each series needs seasons....  Are the seasons added as media sources?

var newTv = new ChannelItemInfo
                                           {
                                               Name         = embyTv.Name,
                                               ImageUrl     = embyTv.PrimaryImagePath,
                                               Id           = embyTv.Id.ToString(),
                                               Type         = ChannelItemType.Folder,
                                               FolderType   = ChannelFolderType.Series, 
                                               IsLiveStream = false,
                                               SeriesName = embyTv.OriginalTitle
                                           };


var mediaItems = libraryManager.GetItemList(new InternalItemsQuery
                                                            {
                                                                IncludeItemTypes = new[]
                                                                                   {
                                                                                       nameof(Series)
                                                                                   },
                                                                IsVirtualItem = false,
                                                                OrderBy = new[]
                                                                          {
                                                                              new ValueTuple<string, SortOrder>(ItemSortBy.PremiereDate, SortOrder.Descending),
                                                                              new ValueTuple<string, SortOrder>(ItemSortBy.SeriesSortNameOrSortName, SortOrder.Ascending)
                                                                          }
                                                            })
                                               .ToList();

BaseItem foundTv = null;
                    
                    if(listData.Where(x => !x.TvdbId.IsNullOrEmpty()).Select(x => x.TvdbId.ToLower()).Contains(tvdb.ToLower()))
                    {
                        foundTv = tv;
                    }

var embyTv = libraryManager.GetItemById(tv.Id);

 

Link to comment
Share on other sites

Why use libraryManager? Is the source of your data local library content?

 

4 minutes ago, bakes82 said:

You lost me after each series needs seasons....  Are the seasons added as media sources?

"Media Sources"? No no...

ChannelItemInfo can represent either a folder or a media item. If you say you got it working for movies, then you already know how your channel plugin is being queried for ChannelItemInfo items.

When you return a ChannelItemInfo object which represents a folder instead of an item, then Emby SDK Reference: IChannel.GetChannelItems(InternalChannelItemQuery, CancellationToken) will be called again for each folder you provide. And at that time, you return the seasons for a series. Seasons are folders as well, so your GetChannelItems will be called again for each season folder, and those are the calls where you return the episodes eventually.

Link to comment
Share on other sites

4 minutes ago, softworkz said:

Why use libraryManager? Is the source of your data local library content?

 

"Media Sources"? No no...

ChannelItemInfo can represent either a folder or a media item. If you say you got it working for movies, then you already know how your channel plugin is being queried for ChannelItemInfo items.

When you return a ChannelItemInfo object which represents a folder instead of an item, then Emby SDK Reference: IChannel.GetChannelItems(InternalChannelItemQuery, CancellationToken) will be called again for each folder you provide. And at that time, you return the seasons for a series. Seasons are folders as well, so your GetChannelItems will be called again for each season folder, and those are the calls where you return the episodes eventually.

I have no idea how it works for Movies :P, it just works since its only 1 level id assume?  How would I know what level its at so I could do the requery and send over the proper stuff :P?

Yes my library is my source, Im taking a list from an API like "Most Watch Series This Week", or "IMDB Top 250".  In Plex this would be done as a collection and you can just associate series to it, and it handled the rest.

public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken)
{
    var helper = new ChannelHelper();
    return await helper.GetChannelItemResult(Id, UserManager, LibraryManager, HttpClient, JsonSerializer, Logger, PluginOptions.Channel1, PluginOptions.ApiKey, cancellationToken);
}


var newItems = new List<ChannelItemInfo>();
var embyMove = libraryManager.GetItemById(movie.Id);
if (embyMove != null)
{
    foundMovies.Add(embyMove.Name);

    var newMovie = new ChannelItemInfo
                   {
                       Name         = embyMove.Name,
                       ImageUrl     = embyMove.PrimaryImagePath,
                       Id           = embyMove.Id.ToString(),
                       Type         = ChannelItemType.Media,
                       ContentType  = ChannelMediaContentType.Movie,
                       MediaType    = ChannelMediaType.Video,
                       IsLiveStream = false,
                       MediaSources = new List<MediaSourceInfo>
                                      {
                                          new MediaSourceInfo
                                          {
                                              Path     = embyMove.Path,
                                              Protocol = MediaProtocol.File
                                          }
                                      },
                       OriginalTitle = embyMove.OriginalTitle
                   };
    
    if (newItems.All(x => x.Id != embyMove.Id.ToString()))
    {
        if (!foundMoviesPaths.Contains(embyMove.Path))
        {
            newItems.Add(newMovie);
            foundMoviesPaths.Add(embyMove.Path);
        }
    }

return await Task.FromResult(new ChannelItemResult
                             {
                                 Items = newItems.ToList()
                             });
Link to comment
Share on other sites

3 minutes ago, bakes82 said:

How would I know what level its at so I could do the requery and send over the proper stuff :P?

This goes purely by IDs. You need to use the FolderId from the ChannelItemQuery to know which child items you need to return. But the IDs are up to your choosing, i.e. what you provide as Emby SDK Reference: ChannelItemInfo.Id.

One possible approach is to use composite ids, so you don't need to determine the upper parents each times. For example:

111_222_333 where 111 is series id, 222 is season id and 333 is episode id, but you would specify:

111 for the series
111_222 for the season
111_222_333 for the episode

Then you can split the id for processing like here:

image.png.74df79d5ec5ab3313e7a2c406c8d8965.png

Link to comment
Share on other sites

The IDs need to be unique for all items, but only in the scope of your plugin (or precisely: each Emby SDK Reference: IChannel implementation).

If you don't have appropriate IDs, you need to create your own - but in that case, you'll need to store and track them (don't generate new ids, each time).

If you want to generate random IDs, GUIDs are always a good choice but somewhat long and unhandy. As an alternative, you can use the Emby SDK Reference: Luid type. It's similar to GUIDs but  It's meant to be "Locally unique" rather than "Globally unique", so you're dealing with much shorter ids.

Link to comment
Share on other sites

26 minutes ago, softworkz said:

This goes purely by IDs. You need to use the FolderId from the ChannelItemQuery to know which child items you need to return. But the IDs are up to your choosing, i.e. what you provide as Emby SDK Reference: ChannelItemInfo.Id.

One possible approach is to use composite ids, so you don't need to determine the upper parents each times. For example:

111_222_333 where 111 is series id, 222 is season id and 333 is episode id, but you would specify:

111 for the series
111_222 for the season
111_222_333 for the episode

Then you can split the id for processing like here:

image.png.74df79d5ec5ab3313e7a2c406c8d8965.png

How would I know the upper bounds.  Doesnt the library manager only ever do it at one level?

This is what I was thinking
 

//Get From "folder" split on _    //EX SetFolderId = SeriesId_123 || SeasonId_123 || EpisodeId_123
long parentId = 1;
string queryType = "Series";

//When Season
var mediaItems = libraryManager.GetItemList(new InternalItemsQuery
                                            {
                                                IncludeItemTypes = new[]
                                                                   {
                                                                       nameof(Series)
                                                                   },
                                                IsVirtualItem = false,
                                                OrderBy = new[]
                                                          {
                                                              new ValueTuple<string, SortOrder>(ItemSortBy.PremiereDate, SortOrder.Descending),
                                                              new ValueTuple<string, SortOrder>(ItemSortBy.SeriesSortNameOrSortName, SortOrder.Ascending)
                                                          }
                                            })
                               .ToList();



var mediaItems2 = libraryManager.GetItemList(new InternalItemsQuery
                                            {
                                                IncludeItemTypes = new[]
                                                                   {
                                                                       nameof(Season)
                                                                   },
                                                IsVirtualItem = false,
                                                ParentIds = new []{ parentId },
                                                OrderBy = new[]
                                                          {
                                                              new ValueTuple<string, SortOrder>(ItemSortBy.PremiereDate, SortOrder.Descending),
                                                              new ValueTuple<string, SortOrder>(ItemSortBy.SeriesSortNameOrSortName, SortOrder.Ascending)
                                                          }
                                            })
                               .ToList();

var mediaItems3 = libraryManager.GetItemList(new InternalItemsQuery
                                             {
                                                 IncludeItemTypes = new[]
                                                                    {
                                                                        nameof(Episode)
                                                                    },
                                                 IsVirtualItem = false,
                                                 ParentIds     = new []{ parentId },
                                                 OrderBy = new[]
                                                           {
                                                               new ValueTuple<string, SortOrder>(ItemSortBy.PremiereDate, SortOrder.Descending),
                                                               new ValueTuple<string, SortOrder>(ItemSortBy.SeriesSortNameOrSortName, SortOrder.Ascending)
                                                           }
                                             })
                                .ToList();
Link to comment
Share on other sites

3 minutes ago, bakes82 said:

How would I know the upper bounds.  Doesnt the library manager only ever do it at one level?

Channel plugins are for presenting external content in Emby. But it doesn't matter from where and how you get the data - you will need to provide the data in a hierarchical way like I described and that's on your own to figure out. It might not work to make a libraryManager call for each call you get on IChannelGetChannelItems. In that case, you need to prepare the data up-front and have it all ready with proper relation and ids, so that you just need to provide the appropriate items depending on the folder id.
That's the way how it's usually done. It's quite ineffective anyway to do so many single libraryManager calls. Better try to get more data at once and shape the results together as needed.

Link to comment
Share on other sites

You can also create a class derived from Emby SDK Reference: ChannelItemInfo and add a property like

public List<ChannelItemInfo> Children { get; }

Or also 

public ChannelItemInfo Parent { get; }

And/or you can create a dictionary somewhere for being able to quickly find an item (and its children), like

public Dictionary<string, ChannelItemInfo> AllItems;

 

Edited by softworkz
Link to comment
Share on other sites

12 minutes ago, softworkz said:

And/or you can create a dictionary somewhere for being able to quickly find an item (and its children), like

How do I get all the items, or are you just saying use the calls I'm currently making to populate the dictionary, but Im assuming I would set that data somewhere else other than in the GetChannelItems.

One of the main issues I have, is there is no way for me test/debug via a test case/console app.  Ideally it would be great if we could make an emby client and pass in the URL/API key and we could see whats happening with some of the calls.  Unless I'm missing that someplace?

So you understand conceptually what Im trying to do, I see there are collections, but I don't see a way to "pin" a collection to the menu/home, would that be a better ask/route?

Link to comment
Share on other sites

4 minutes ago, bakes82 said:

How do I get all the items, or are you just saying use the calls I'm currently making to populate the dictionary

Yes.

5 minutes ago, bakes82 said:

but Im assuming I would set that data somewhere else other than in the GetChannelItems.

You can do that in the first call to GetChannelItems. It's async and it doesn't when when it takes a while until you get all data together.

6 minutes ago, bakes82 said:

One of the main issues I have, is there is no way for me test/debug

Why? You can perfectly debug your plugin running inside Emby Server. You can even use Edit+Continue (Hot Reload) to make changes while running.

Please see here: https://betadev.emby.media/doc/plugins/dev/index.html#debugging

Link to comment
Share on other sites

36 minutes ago, softworkz said:

Why? You can perfectly debug your plugin running inside Emby Server. You can even use Edit+Continue (Hot Reload) to make changes while running.

I run in a docker in my NAS…. Emby dev isn’t my full time gig. 

Link to comment
Share on other sites

1 minute ago, bakes82 said:

I run in a docker in my NAS…. Emby dev isn’t my full time gig. 

It doesn't have to be. But a test server installation on Windows and Visual Studio for development will save you a lot of time.

Link to comment
Share on other sites

Now you want me to use windows and visual studio!?!?!  I dont even think most devs at mircrosft use windows or visual studio lol.  Mac and Jetbrains ;)

Link to comment
Share on other sites

15 minutes ago, bakes82 said:

I dont even think most devs at mircrosft use windows or visual studio. Mac and Jetbrains

Surely not. VS for Mac has just been cancelled btw.

16 minutes ago, bakes82 said:

Now you want me to use windows and visual studio!?!?!

I don't want anything from you to do. You complained that it's tedious to develop like you are doing currently and I was just telling you how you can have a joyful experience when developing. 

Link to comment
Share on other sites

How can I find the channel Im creating?  Im currently using "name" but that can be overridden from the UI.  I would say Id set the ID, but that not an option.

Link to comment
Share on other sites

This query also seems to be pulling in the "Channel" series, Does the isvirtualitem not work?

6c62d0fb-a0d8-5328-bec4-77fd5a32be7a is correct as it has a path, but the 4fbfa778-6779-4826-8c9d-630153bc96fb doesnt and I wouldnt expect it to come as part of the query.

2023-09-18 06:47:11.541 Info App: Count of series in emby 8
2023-09-18 06:47:11.542 Info App: ["Wednesday","Wednesday","Wednesday","Wednesday","Wednesday","Wednesday","Wednesday","100 Day Dream Home"]
2023-09-18 06:47:11.543 Info App: Start - Series Wednesday
2023-09-18 06:47:11.544 Info App: Tvdb 397060
2023-09-18 06:47:11.544 Info App: FoundTv 6c62d0fb-a0d8-5328-bec4-77fd5a32be7a Wednesday /media/TV/Wednesday (2022)
2023-09-18 06:47:11.545 Debug SqliteItemRepository: GetitemById Series 136 /media/TV/Wednesday (2022)
2023-09-18 06:47:11.546 Info App: Count of Seasons 1
2023-09-18 06:47:11.547 Info App: Count of episodes 1
2023-09-18 06:47:11.547 Info App: Start - Series 100 Day Dream Home
2023-09-18 06:47:11.547 Info App: Tvdb 374810
2023-09-18 06:47:11.547 Info App: Start - Series Wednesday
2023-09-18 06:47:11.547 Info App: TV already on channel Wednesday
2023-09-18 06:47:11.547 Info App: Start - Series Wednesday
2023-09-18 06:47:11.547 Info App: Tvdb 397060
2023-09-18 06:47:11.547 Info App: FoundTv 4fbfa778-6779-4826-8c9d-630153bc96fb Wednesday 

 

var mediaItems = libraryManager.GetItemList(new InternalItemsQuery
                                            {
                                                IncludeItemTypes = new[]
                                                                   {
                                                                       nameof(Series)
                                                                   },
                                                IsVirtualItem = false,
                                                OrderBy = new[]
                                                          {
                                                              new ValueTuple<string, SortOrder>(ItemSortBy.PremiereDate, SortOrder.Descending),
                                                              new ValueTuple<string, SortOrder>(ItemSortBy.SeriesSortNameOrSortName, SortOrder.Ascending)
                                                          }
                                            })
                               .ToList();
Link to comment
Share on other sites

Only episodes and seasons utilize the IsVirtualItem field. The best way to locate your series is to know the parent id of the library and put that into the query.

Link to comment
Share on other sites

1 hour ago, Luke said:

Only episodes and seasons utilize the IsVirtualItem field. The best way to locate your series is to know the parent id of the library and put that into the query.

So basically just use where Path != null?  Id think most people have multi libraries, IE Sports, Foreign, 4k.  Any my plugin is creating a channel from a list of tvdbs from an API so those could span multiple libraries.  Is there not an alternate way to do this?  Is there a way to get all the items where a type is series and tvdbid is not null?

Link to comment
Share on other sites

9 minutes ago, bakes82 said:

So basically just use where Path != null? 

I you want the items to be playable, you might want to actually specify a path...

10 minutes ago, bakes82 said:

Id think most people have multi libraries, IE Sports, Foreign, 4k.  Any my plugin is creating a channel from a list of tvdbs from an API so those could span multiple libraries. 

Well, you can get the IDs of all relevant (or make it configurable) libraries and limit your query to those like @Lukesuggested above.

11 minutes ago, bakes82 said:

Is there not an alternate way to do this?

You could maybe use the "CanDelete" value as a (ugly) workaround, as you know that your plugin will always have full permissions, so when this is false, it usually means that it can't be deleted because it's "virtual" (not in the sense of the Emby property), not due to lacking permissions, but there might be other implications when doing so.

Link to comment
Share on other sites

@softworkzIn one of my channels, if an episode has more than 1 source, why do I need to create 2 episodes for it?

if I just pass the sources to the MediaSources it doesnt show it having 2 options, but if I make 2 episodes, I guess the UI knows to group it and looks like whats in the library with a drop down to pick the version.

var sources = pilot.GetMediaSources(true, false, new LibraryOptions());

foreach (var source in sources)
{
    Logger.Info($"Source = {source.Path}");
    episodes.Add(new ChannelItemInfo
                 {
                     Name         = pilot.Name,
                     ImageUrl     = pilot.PrimaryImagePath,
                     Id           = $"{pilot.Series.Id}|{pilot.Season.Id}|{pilot.Id}",
                     Type         = ChannelItemType.Media,
                     ContentType  = ChannelMediaContentType.Episode,
                     MediaType    = ChannelMediaType.Video,
                     IsLiveStream = false,
                     MediaSources = new List<MediaSourceInfo>
                                    {
                                        new MediaSourceInfo
                                        {
                                            Path     = source.Path,
                                            Protocol = MediaProtocol.File
                                        }
                                    },
                     OriginalTitle = pilot.OriginalTitle
                 });
}
Link to comment
Share on other sites

51 minutes ago, bakes82 said:

@softworkzIn one of my channels, if an episode has more than 1 source, why do I need to create 2 episodes for it?

if I just pass the sources to the MediaSources it doesnt show it having 2 options, but if I make 2 episodes, I guess the UI knows to group it and looks like whats in the library with a drop down to pick the version.

Yes, this is currently broken. I had forgotten to pick this up again and talk to @Lukeabout getting this back working again under certain conditions or whether we should mark this as [Obsolete].
The reason why it was disabled is that many Emby SDK Reference: IChannel plugin developers were confused about which information to provide and often got it wrong, which was causing playback errors in turn. Remember, the typical use case for channel plugins is to integrate external media and getting the "right" MediaSource information needs (ff)probing - something you don't want to do for each item at the moment when you are just adding them. So, you normally just provide the path to the media (local or external stream) and Emby does the probing just right in the moment when you start playing.

Link to comment
Share on other sites

7 hours ago, softworkz said:

Yes, this is currently broken. I had forgotten to pick this up again and talk to @Lukeabout getting this back working again under certain conditions or whether we should mark this as [Obsolete].
The reason why it was disabled is that many Emby SDK Reference: IChannel plugin developers were confused about which information to provide and often got it wrong, which was causing playback errors in turn. Remember, the typical use case for channel plugins is to integrate external media and getting the "right" MediaSource information needs (ff)probing - something you don't want to do for each item at the moment when you are just adding them. So, you normally just provide the path to the media (local or external stream) and Emby does the probing just right in the moment when you start playing.

So I would be okay using "collections" if the feature set would allow pinning collections to the home.  It would also be way more dynamic. @Luke @softworkz

So Latest Pilots Past 14 days is a "channel" and it just looks great in this format.  You can also move it around in the UI from the library order.  Whereas collections are all just thrown at the bottom, you need to click in to it.  For pilots a "channel" might make sense just because I click the post it takes me to s01e01 as its the only item added, but if you did "True Crime Shows" it would obvi show all the seaons for the show but that could be a collection, but the ability to pin it to home for the people that want that specific one in their face is whats missing
image.png.b4f057087308e26d2482cf8217a4cd04.png

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