Playing a movie basically works
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2024-11-28 10:30:48 +01:00
parent affd2fc4a4
commit fe9bd797a7
2 changed files with 112 additions and 32 deletions

View File

@@ -304,7 +304,7 @@ public abstract class StreamCinemaFilterFolder : Folder
//item.Tags = info.Tags.ToArray();
item.OriginalTitle = media.info_labels?.originaltitle;
string? artUriS = loc?.art?.poster ?? loc?.art?.fanart;
string? artUriS = loc?.art?.poster;
Uri? artUri;
if (artUriS != null)
{
@@ -315,6 +315,13 @@ public abstract class StreamCinemaFilterFolder : Folder
}
item.SetImagePath(ImageType.Primary, artUri.ToString());
}
artUriS = loc?.art?.fanart;
if (artUriS != null)
{
artUri = new Uri(artUriS);
item.SetImagePath(ImageType.Backdrop, artUri.ToString());
}
}
// Identify the item as it originates from StreamCinema

View File

@@ -7,19 +7,23 @@ using System.Globalization;
using System.Text;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using StreamCinema.Webshare;
using StreamCinemaLib.API;
namespace Jellyfin.Plugin.StreamCinema;
public class StreamCinemaMediaSourceManager : IMediaSourceManager
{
private static readonly TimeSpan VersionValidityTimeout = TimeSpan.FromMinutes(60);
private const bool IsWebshareFreeAccount = true;
private static readonly TimeSpan VersionValidityTimeout = TimeSpan.FromMinutes(180);
private static StreamCinemaMediaSourceManager? _instance;
@@ -134,11 +138,6 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
return _inner.GetPathProtocol(path);
}
public Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
{
return _inner.GetPlaybackMediaSources(item, user, allowMediaProbe, enablePathSubstitution, cancellationToken);
}
public Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken)
{
return _inner.GetRecordingStreamMediaSources(info, cancellationToken);
@@ -157,13 +156,17 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
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);*/
VersionSetEntry ver = GetVersionSet(video, out BaseItem? videoPrimary, default).GetAwaiter().GetResult();
if (ver.Versions != null)
{
foreach (var i in ver.Versions)
result.Add(GetVersionInfo(video, i.Meta));
// Make sure BaseItem exists for each version
switch (video)
{
case StreamCinemaMovie movie: GetVideoVersionsEnumerate<StreamCinemaMovie>(movie, videoPrimary!, ver.Versions).Last(); break;
}
}
break;
}
@@ -174,6 +177,52 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
return result;
}
public async Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
{
// Intercept for StreamCinemaItems
if (item == null
|| item.ProviderIds == null
|| !item.ProviderIds.ContainsKey(StreamCinemaPlugin.StreamCinemaProviderName)
|| string.IsNullOrEmpty(item.ExternalId))
return await _inner.GetPlaybackMediaSources(item, user, allowMediaProbe, enablePathSubstitution, cancellationToken);
List<MediaSourceInfo> result = new List<MediaSourceInfo>();
switch (item)
{
case Video video:
VersionEntry? ver = await GetVersion(video, cancellationToken);
if (ver != null)
{
Uri? path = ver.DownloadLink;
if (path == null)
{
switch (ver.Meta.provider)
{
case "webshare": path = await LinkGenerator.GenerateDownloadLinkAsync(ver.Meta.ident, ver.Meta.name, cancellationToken); break;
default: path = null; break;
}
ver.DownloadLink = path;
}
MediaSourceInfo a = GetVersionInfo(video, ver.Meta);
if (path != null)
a.Path = path.ToString();
// Propagate "-seekable 0" to ffmpeg as free accounts on Webshare cannot use "Range: bytes" HTTP header
// HACK: We misuse the user-agent as GetExtraArguments in MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
// does not properly escape its value for command line
if (IsWebshareFreeAccount)
{
if (a.RequiredHttpHeaders == null)
a.RequiredHttpHeaders = new Dictionary<string, string>();
a.RequiredHttpHeaders["User-Agent"] = "Lavf/60\" -seekable \"0";
}
result.Add(a);
}
break;
}
return result;
}
public Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
{
return _inner.OpenLiveStream(request, cancellationToken);
@@ -207,10 +256,10 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
VersionSetEntry ver = await GetVersionSet(item, out BaseItem? primary, cancel);
return ver.Streams == null ? null : GetVideoVersionsEnumerate(item, primary!, ver.Streams);
return ver.Versions == null ? null : GetVideoVersionsEnumerate(item, primary!, ver.Versions);
}
private IEnumerable<T> GetVideoVersionsEnumerate<T>(T item, BaseItem primary, StreamCinemaLib.API.Stream[] versions)
private IEnumerable<T> GetVideoVersionsEnumerate<T>(T item, BaseItem primary, VersionEntry[] versions)
where T : Video, new()
{
bool isFirst = true;
@@ -225,7 +274,7 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
}
else
{
a = StreamCinemaFilterFolder.GetMediaItemById<T>(item.ExternalId, i._id, out bool isNew);
a = StreamCinemaFilterFolder.GetMediaItemById<T>(item.ExternalId, i.Meta._id, out bool isNew);
if (isNew)
{
// Copy properties from the parent version
@@ -293,21 +342,27 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
VersionSetEntry result;
if (!_videoVersions.TryGetValue(primaryId, out result) || result.ValidUntil < now)
{
result.Streams = await Metadata.StreamsAsync(csId, cancel);
var a = await Metadata.StreamsAsync(csId, cancel);
if (a != null) {
result.Versions = new VersionEntry[a.Length];
for (int i = 0; i < a.Length; i++)
result.Versions[i] = new VersionEntry(a[i]);
} else
result.Versions = null;
result.ValidUntil = now + VersionValidityTimeout;
_videoVersions[primaryId] = result;
}
return result;
}
private async Task<StreamCinemaLib.API.Stream?> GetVersion(Video item, CancellationToken cancel)
private async Task<VersionEntry?> GetVersion(Video item, CancellationToken cancel)
{
VersionSetEntry vers = await GetVersionSet(item, out BaseItem? primary, cancel);
if (vers.Streams == null)
if (vers.Versions == null)
return null;
bool isFirst = true;
foreach (var i in vers.Streams)
foreach (var i in vers.Versions)
{
Guid id;
if (isFirst)
@@ -317,7 +372,7 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
id = primary!.Id;
}
else
id = StreamCinemaFilterFolder.GetMediaItemId(primary!.ExternalId, i._id);
id = StreamCinemaFilterFolder.GetMediaItemId(primary!.ExternalId, i.Meta._id);
if (id == item.Id)
return i;
@@ -330,18 +385,18 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
if (item == null)
throw new ArgumentNullException();
StreamCinemaLib.API.Stream? ver = await GetVersion(item, cancel);
VersionEntry? ver = await GetVersion(item, cancel);
if (ver == null)
return null;
return GetVersionInfo(item, ver);
return GetVersionInfo(item, ver.Meta);
}
private MediaSourceInfo GetVersionInfo(Video item, StreamCinemaLib.API.Stream ver)
{
MediaSourceInfo result = new MediaSourceInfo();
result.VideoType = VideoType.VideoFile;
result.Id = ver._id;
result.Id = item.Id.ToString("N", CultureInfo.InvariantCulture);
result.Protocol = MediaProtocol.Http;
// Warning: We set some properties on item that get used below
result.MediaStreams = VersionToMediaStreams(item, ver);
@@ -350,7 +405,7 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
+ $" {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
@@ -360,26 +415,30 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
name += " " + firstVid.Width.ToString() + "x" + firstVid.Height.ToString() + hdrS + is3DS;
}
StringBuilder audioAndSub = new StringBuilder();
if (ver.audio != null) {
if (ver.audio != null)
{
foreach (StreamAudio i in ver.audio)
audioAndSub.Append($"{i.language} {i.codec} {i.channels}").Append(",");
if (audioAndSub.Length != 0) {
if (audioAndSub.Length != 0)
{
audioAndSub.Remove(audioAndSub.Length - 1, 1);
name += " {" + audioAndSub + "}";
audioAndSub.Clear();
}
}
if (ver.subtitles != null) {
if (ver.subtitles != null)
{
foreach (StreamSubtitle i in ver.subtitles)
audioAndSub.Append($"{i.language}").Append(",");
if (audioAndSub.Length != 0) {
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;
@@ -506,6 +565,20 @@ public class StreamCinemaMediaSourceManager : IMediaSourceManager
struct VersionSetEntry
{
internal DateTime ValidUntil;
internal StreamCinemaLib.API.Stream[]? Streams;
internal VersionEntry[]? Versions;
}
class VersionEntry
{
private readonly StreamCinemaLib.API.Stream _meta;
internal VersionEntry(StreamCinemaLib.API.Stream meta)
{
this._meta = meta;
}
internal StreamCinemaLib.API.Stream Meta => _meta;
internal Uri? DownloadLink { get; set; }
}
}