Playing a movie basically works
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -360,19 +415,23 @@ 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();
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user