Intercept GetStaticMediaSources instead of GetMediaStreams. Movie versions show in UI.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -315,11 +315,16 @@ public abstract class StreamCinemaFilterFolder : Folder
|
|||||||
}
|
}
|
||||||
item.SetImagePath(ImageType.Primary, artUri.ToString());
|
item.SetImagePath(ImageType.Primary, artUri.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identify the item as it originates from StreamCinema
|
|
||||||
item.ProviderIds.Add(StreamCinemaPlugin.StreamCinemaProviderName, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Identify the item as it originates from StreamCinema
|
||||||
|
// Note: This is getting lost in persisted items
|
||||||
|
if (item.ProviderIds == null)
|
||||||
|
item.ProviderIds = new Dictionary<string, string>();
|
||||||
|
item.ProviderIds[StreamCinemaPlugin.StreamCinemaProviderName] = "";
|
||||||
|
|
||||||
|
item.Path = "https://a/b";
|
||||||
|
|
||||||
if (item is IHasArtist hasArtists)
|
if (item is IHasArtist hasArtists)
|
||||||
{
|
{
|
||||||
hasArtists.Artists = media.info_labels?.writer;
|
hasArtists.Artists = media.info_labels?.writer;
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\StreamCinemaLib/StreamCinemaLib.csproj" />
|
<ProjectReference Include="..\StreamCinemaLib/StreamCinemaLib.csproj" />
|
||||||
<!--PackageReference Include="Jellyfin.Controller" Version="10.10.3" /-->
|
<PackageReference Include="Jellyfin.Controller" Version="10.10.3" />
|
||||||
<ProjectReference Include="..\..\..\jellyfin/MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
|
<!--ProjectReference Include="..\..\..\jellyfin/MediaBrowser.Controller\MediaBrowser.Controller.csproj" /-->
|
||||||
<!--PackageReference Include="Jellyfin.Model" Version="10.10.3" /-->
|
<PackageReference Include="Jellyfin.Model" Version="10.10.3" />
|
||||||
<ProjectReference Include="..\..\..\jellyfin\MediaBrowser.Model\MediaBrowser.Model.csproj" />
|
<!--ProjectReference Include="..\..\..\jellyfin\MediaBrowser.Model\MediaBrowser.Model.csproj" /-->
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Debug'">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Debug'">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Collections.Concurrent;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
@@ -107,6 +108,8 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
|
|||||||
|
|
||||||
public List<MediaStream> GetMediaStreams(Guid itemId)
|
public List<MediaStream> GetMediaStreams(Guid itemId)
|
||||||
{
|
{
|
||||||
|
return _inner.GetMediaStreams(itemId);
|
||||||
|
/*
|
||||||
// Intercept for StreamCinemaItems
|
// Intercept for StreamCinemaItems
|
||||||
Video? item = _libraryManager.GetItemById<Video>(itemId);
|
Video? item = _libraryManager.GetItemById<Video>(itemId);
|
||||||
if (item == null
|
if (item == null
|
||||||
@@ -115,68 +118,10 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
|
|||||||
|| string.IsNullOrEmpty(item.ExternalId))
|
|| string.IsNullOrEmpty(item.ExternalId))
|
||||||
return _inner.GetMediaStreams(itemId);
|
return _inner.GetMediaStreams(itemId);
|
||||||
|
|
||||||
StreamCinemaLib.API.Stream? stream = GetVersion(item, default).GetAwaiter().GetResult();
|
StreamCinemaLib.API.Stream? ver = GetVersion(item, default).GetAwaiter().GetResult();
|
||||||
|
|
||||||
List<MediaStream> result = new List<MediaStream>();
|
return ver != null ? VersionToMediaStreams(item, ver) : new List<MediaStream>();
|
||||||
if (stream != null)
|
*/
|
||||||
{
|
|
||||||
// HACK: Those values are read after we return (not before)
|
|
||||||
item.Size = stream.size;
|
|
||||||
//stream_id;
|
|
||||||
//stream.name;
|
|
||||||
//stream.provider
|
|
||||||
//stream.ident;
|
|
||||||
//stream.media
|
|
||||||
//stream.date_added
|
|
||||||
|
|
||||||
if (stream.video != null)
|
|
||||||
{
|
|
||||||
foreach (StreamVideo j in stream.video)
|
|
||||||
{
|
|
||||||
MediaStream a = new MediaStream();
|
|
||||||
result.Add(a);
|
|
||||||
a.Type = MediaStreamType.Video;
|
|
||||||
a.Width = j.width;
|
|
||||||
a.Height = j.height;
|
|
||||||
a.Codec = j.codec;
|
|
||||||
a.AspectRatio = j.aspect.ToString();
|
|
||||||
ConvertHdr(j.hasHdr, j.hdrQuality, a);
|
|
||||||
|
|
||||||
// HACK: Those values are read after we return (not before)
|
|
||||||
item.RunTimeTicks = (long?)(j.duration * TimeSpan.TicksPerSecond);
|
|
||||||
if (j.is3d)
|
|
||||||
// Just a probability guess
|
|
||||||
item.Video3DFormat = Video3DFormat.HalfSideBySide;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream.audio != null)
|
|
||||||
{
|
|
||||||
foreach (StreamAudio j in stream.audio)
|
|
||||||
{
|
|
||||||
MediaStream a = new MediaStream();
|
|
||||||
result.Add(a);
|
|
||||||
a.Type = MediaStreamType.Audio;
|
|
||||||
a.Language = j.language;
|
|
||||||
a.Codec = j.codec;
|
|
||||||
a.Channels = j.channels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream.subtitles != null)
|
|
||||||
{
|
|
||||||
foreach (StreamSubtitle j in stream.subtitles)
|
|
||||||
{
|
|
||||||
MediaStream a = new MediaStream();
|
|
||||||
result.Add(a);
|
|
||||||
a.Type = MediaStreamType.Subtitle;
|
|
||||||
a.Language = j.language;
|
|
||||||
a.IsForced = j.forced;
|
|
||||||
a.Path = j.src;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MediaStream> GetMediaStreams(MediaStreamQuery query)
|
public List<MediaStream> GetMediaStreams(MediaStreamQuery query)
|
||||||
@@ -201,7 +146,32 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
|
|||||||
|
|
||||||
public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User? user = null)
|
public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User? user = null)
|
||||||
{
|
{
|
||||||
return _inner.GetStaticMediaSources(item, enablePathSubstitution, user);
|
// Intercept for StreamCinemaItems
|
||||||
|
if (item == null
|
||||||
|
|| item.ProviderIds == null
|
||||||
|
|| !item.ProviderIds.ContainsKey(StreamCinemaPlugin.StreamCinemaProviderName)
|
||||||
|
|| string.IsNullOrEmpty(item.ExternalId))
|
||||||
|
return _inner.GetStaticMediaSources(item, enablePathSubstitution, user);
|
||||||
|
|
||||||
|
List<MediaSourceInfo> result = new List<MediaSourceInfo>();
|
||||||
|
switch (item)
|
||||||
|
{
|
||||||
|
case Video video:
|
||||||
|
VersionSetEntry ver = GetVersionSet(video, out _, default).GetAwaiter().GetResult();
|
||||||
|
if (ver.Streams != null)
|
||||||
|
foreach (var i in ver.Streams)
|
||||||
|
result.Add(GetVersionInfo(video, i));
|
||||||
|
/*MediaSourceInfo? videoInfo = GetVersionInfo(video, default).GetAwaiter().GetResult();
|
||||||
|
if (videoInfo != null)
|
||||||
|
result.Add(videoInfo);*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK Prevent crash
|
||||||
|
if (result.Count == 0)
|
||||||
|
result.Append(new MediaSourceInfo() { MediaStreams = new List<MediaStream>() });
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
|
public Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
|
||||||
@@ -330,7 +300,6 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<StreamCinemaLib.API.Stream?> GetVersion(Video item, CancellationToken cancel)
|
private async Task<StreamCinemaLib.API.Stream?> GetVersion(Video item, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
VersionSetEntry vers = await GetVersionSet(item, out BaseItem? primary, cancel);
|
VersionSetEntry vers = await GetVersionSet(item, out BaseItem? primary, cancel);
|
||||||
@@ -356,6 +325,131 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<MediaSourceInfo?> GetVersionInfo(Video item, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
|
||||||
|
StreamCinemaLib.API.Stream? ver = await GetVersion(item, cancel);
|
||||||
|
if (ver == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return GetVersionInfo(item, ver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MediaSourceInfo GetVersionInfo(Video item, StreamCinemaLib.API.Stream ver)
|
||||||
|
{
|
||||||
|
MediaSourceInfo result = new MediaSourceInfo();
|
||||||
|
result.VideoType = VideoType.VideoFile;
|
||||||
|
result.Id = ver._id;
|
||||||
|
result.Protocol = MediaProtocol.Http;
|
||||||
|
// Warning: We set some properties on item that get used below
|
||||||
|
result.MediaStreams = VersionToMediaStreams(item, ver);
|
||||||
|
|
||||||
|
string name = $"{Math.Round((double)(ver.size ?? 0) / (1024 * 1024 * 1024), 2)} GB ({ver.date_added?.ToShortDateString()})"
|
||||||
|
+ $" {Math.Round((item.RunTimeTicks ?? 0.0) / TimeSpan.TicksPerMinute)} min";
|
||||||
|
MediaStream? firstVid = result.MediaStreams.Where(x => x.Type == MediaStreamType.Video).FirstOrDefault();
|
||||||
|
if (firstVid != null)
|
||||||
|
{
|
||||||
|
var hdr = firstVid.VideoRangeType;
|
||||||
|
var hdr2 = firstVid.VideoDoViTitle;
|
||||||
|
string hdrS = hdr == Data.Enums.VideoRangeType.Unknown || hdr == Data.Enums.VideoRangeType.SDR
|
||||||
|
? " SDR"
|
||||||
|
: " HDR" + (string.IsNullOrEmpty(hdr2) ? "" : "[" + hdr2 + "]");
|
||||||
|
string is3DS = ver!.video!.First().is3d ? " (3D)" : "";
|
||||||
|
name += " " + firstVid.Width.ToString() + "x" + firstVid.Height.ToString() + hdrS + is3DS;
|
||||||
|
}
|
||||||
|
StringBuilder audioAndSub = new StringBuilder();
|
||||||
|
if (ver.audio != null) {
|
||||||
|
foreach (StreamAudio i in ver.audio)
|
||||||
|
audioAndSub.Append($"{i.language} {i.codec} {i.channels}").Append(",");
|
||||||
|
if (audioAndSub.Length != 0) {
|
||||||
|
audioAndSub.Remove(audioAndSub.Length - 1, 1);
|
||||||
|
name += " {" + audioAndSub + "}";
|
||||||
|
audioAndSub.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ver.subtitles != null) {
|
||||||
|
foreach (StreamSubtitle i in ver.subtitles)
|
||||||
|
audioAndSub.Append($"{i.language}").Append(",");
|
||||||
|
if (audioAndSub.Length != 0) {
|
||||||
|
audioAndSub.Remove(audioAndSub.Length - 1, 1);
|
||||||
|
name += " [" + audioAndSub + "]";
|
||||||
|
audioAndSub.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.Name = name;
|
||||||
|
|
||||||
|
//Path = enablePathSubstitution ? GetMappedPath(item, item.Path, protocol) : item.Path,
|
||||||
|
result.Container = item.Container;
|
||||||
|
result.RunTimeTicks = item.RunTimeTicks;
|
||||||
|
result.Size = item.Size;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<MediaStream> VersionToMediaStreams(Video item, StreamCinemaLib.API.Stream ver)
|
||||||
|
{
|
||||||
|
List<MediaStream> result = new List<MediaStream>();
|
||||||
|
// HACK: Propagate some values to the item also
|
||||||
|
item.Size = ver.size;
|
||||||
|
//stream_id;
|
||||||
|
//stream.name;
|
||||||
|
//stream.provider
|
||||||
|
//stream.ident;
|
||||||
|
//stream.media
|
||||||
|
//stream.date_added
|
||||||
|
|
||||||
|
if (ver.video != null)
|
||||||
|
{
|
||||||
|
foreach (StreamVideo j in ver.video)
|
||||||
|
{
|
||||||
|
MediaStream a = new MediaStream();
|
||||||
|
result.Add(a);
|
||||||
|
a.Type = MediaStreamType.Video;
|
||||||
|
a.Width = j.width;
|
||||||
|
a.Height = j.height;
|
||||||
|
a.Codec = j.codec;
|
||||||
|
a.AspectRatio = j.aspect.ToString();
|
||||||
|
ConvertHdr(j.hasHdr, j.hdrQuality, a);
|
||||||
|
|
||||||
|
// HACK: Those values are read after we return (not before)
|
||||||
|
item.RunTimeTicks = (long?)(j.duration * TimeSpan.TicksPerSecond);
|
||||||
|
if (j.is3d)
|
||||||
|
// Just a probability guess
|
||||||
|
item.Video3DFormat = Video3DFormat.HalfSideBySide;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ver.audio != null)
|
||||||
|
{
|
||||||
|
foreach (StreamAudio j in ver.audio)
|
||||||
|
{
|
||||||
|
MediaStream a = new MediaStream();
|
||||||
|
result.Add(a);
|
||||||
|
a.Type = MediaStreamType.Audio;
|
||||||
|
a.Language = j.language;
|
||||||
|
a.Codec = j.codec;
|
||||||
|
a.Channels = j.channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ver.subtitles != null)
|
||||||
|
{
|
||||||
|
foreach (StreamSubtitle j in ver.subtitles)
|
||||||
|
{
|
||||||
|
MediaStream a = new MediaStream();
|
||||||
|
result.Add(a);
|
||||||
|
a.Type = MediaStreamType.Subtitle;
|
||||||
|
a.Language = j.language;
|
||||||
|
a.IsForced = j.forced;
|
||||||
|
a.Path = j.src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static void ConvertHdr(bool isHdr, string? hdr, MediaStream dst)
|
private static void ConvertHdr(bool isHdr, string? hdr, MediaStream dst)
|
||||||
{
|
{
|
||||||
if (!isHdr)
|
if (!isHdr)
|
||||||
|
|||||||
@@ -8,14 +8,25 @@ namespace Jellyfin.Plugin.StreamCinema;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Movie media item from Stream Cinema.
|
/// Movie media item from Stream Cinema.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StreamCinemaMovie : Movie {
|
public class StreamCinemaMovie : Movie
|
||||||
|
{
|
||||||
public sealed override string GetClientTypeName() => BaseItemKind.Movie.ToString();
|
public sealed override string GetClientTypeName() => BaseItemKind.Movie.ToString();
|
||||||
|
|
||||||
protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources() {
|
protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources()
|
||||||
|
{
|
||||||
var result = this.VideoGetAllItemsForMediaSources();
|
var result = this.VideoGetAllItemsForMediaSources();
|
||||||
if (result == null)
|
if (result == null)
|
||||||
return base.GetAllItemsForMediaSources();
|
return base.GetAllItemsForMediaSources();
|
||||||
else
|
else
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
|
||||||
|
{
|
||||||
|
var result = this.VideoGetMediaSources(enablePathSubstitution);
|
||||||
|
if (result == null)
|
||||||
|
return base.GetMediaSources(enablePathSubstitution);
|
||||||
|
else
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -33,4 +33,21 @@ static class StreamCinemaVideoExtensions
|
|||||||
foreach (T i in versions)
|
foreach (T i in versions)
|
||||||
yield return (i, MediaSourceType.Grouping);
|
yield return (i, MediaSourceType.Grouping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<MediaSourceInfo> VideoGetMediaSources<T>(this T @this, bool enablePathSubstitution)
|
||||||
|
where T : Video, new()
|
||||||
|
{
|
||||||
|
List<MediaSourceInfo> result = new List<MediaSourceInfo>();
|
||||||
|
|
||||||
|
// Our media source manager must be present
|
||||||
|
StreamCinemaMediaSourceManager? msm;
|
||||||
|
if (!StreamCinemaMediaSourceManager.TryGetInstance(out msm))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
var a = msm.GetVersionInfo(@this, default).GetAwaiter().GetResult();
|
||||||
|
if (a != null)
|
||||||
|
result.Add(a);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user