Skip to content

Commit

Permalink
Merge pull request #14 from Kevinjil/feature/restreaming
Browse files Browse the repository at this point in the history
Feature/restreaming
  • Loading branch information
Kevinjil authored Jun 20, 2022
2 parents 925026b + 19b9dfe commit 4aee443
Show file tree
Hide file tree
Showing 8 changed files with 580 additions and 103 deletions.
3 changes: 3 additions & 0 deletions Jellyfin.Xtream/LiveChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ private async Task<ChannelItemResult> GetVideos(string categoryId, CancellationT
Name = channel.Name,
Path = uri,
Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
SupportsDirectPlay = false,
SupportsDirectStream = true,
SupportsProbing = true,
}
};
items.Add(new ChannelItemInfo()
Expand Down
92 changes: 50 additions & 42 deletions Jellyfin.Xtream/LiveTvService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Xtream.Client;
using Jellyfin.Xtream.Client.Models;
using Jellyfin.Xtream.Configuration;
using Jellyfin.Xtream.Service;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
Expand All @@ -35,19 +38,24 @@ namespace Jellyfin.Xtream
/// <summary>
/// Class LiveTvService.
/// </summary>
public class LiveTvService : ILiveTvService
public class LiveTvService : ILiveTvService, ISupportsDirectStreamProvider
{
private readonly IServerApplicationHost appHost;
private readonly IHttpClientFactory httpClientFactory;
private readonly ILogger<LiveTvService> logger;
private readonly IMemoryCache memoryCache;
private int liveStreams;

/// <summary>
/// Initializes a new instance of the <see cref="LiveTvService"/> class.
/// </summary>
/// <param name="appHost">Instance of the <see cref="IServerApplicationHost"/> interface.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="memoryCache">Instance of the <see cref="IMemoryCache"/> interface.</param>
public LiveTvService(ILogger<LiveTvService> logger, IMemoryCache memoryCache)
public LiveTvService(IServerApplicationHost appHost, IHttpClientFactory httpClientFactory, ILogger<LiveTvService> logger, IMemoryCache memoryCache)
{
this.appHost = appHost;
this.httpClientFactory = httpClientFactory;
this.logger = logger;
this.memoryCache = memoryCache;
}
Expand Down Expand Up @@ -177,44 +185,7 @@ public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string cha
/// <inheritdoc />
public Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
{
Plugin? plugin = Plugin.Instance;
if (plugin == null)
{
throw new ArgumentException("Plugin not initialized!");
}

PluginConfiguration config = plugin.Configuration;
logger.LogInformation("Start livestream {ChannelId}", channelId);
liveStreams++;

string uri = $"{config.BaseUrl}/{config.Username}/{config.Password}/{channelId}";
var mediaSourceInfo = new MediaSourceInfo
{
Id = liveStreams.ToString(CultureInfo.InvariantCulture),
Path = uri,
Protocol = MediaProtocol.Http,
RequiresOpening = true,
MediaStreams = new List<MediaStream>
{
new MediaStream
{
Type = MediaStreamType.Video,
IsInterlaced = true,
// Set the index to -1 because we don't know the exact index of the video stream within the container
Index = -1,
},
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
}
},
Container = "mpegts",
SupportsProbing = true
};

return Task.FromResult(mediaSourceInfo);
throw new NotImplementedException();
}

/// <inheritdoc />
Expand Down Expand Up @@ -292,5 +263,42 @@ public Task ResetTuner(string id, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

/// <inheritdoc />
public Task<ILiveStream> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
ILiveStream? stream = currentLiveStreams.Find(stream => stream.TunerHostId == Restream.TunerHost && stream.MediaSource.Id == channelId);
if (stream != null)
{
return Task.FromResult(stream);
}

Plugin? plugin = Plugin.Instance;
if (plugin == null)
{
throw new ArgumentException("Plugin not initialized!");
}

PluginConfiguration config = plugin.Configuration;

string uri = $"{config.BaseUrl}/{config.Username}/{config.Password}/{channelId}";
MediaSourceInfo mediaSourceInfo = new MediaSourceInfo()
{
EncoderProtocol = MediaProtocol.Http,
Id = channelId,
IsInfiniteStream = true,
IsRemote = true,
Path = uri,
Protocol = MediaProtocol.Http,
SupportsDirectPlay = false,
SupportsDirectStream = true,
SupportsProbing = true,
RequiresOpening = true,
RequiresClosing = true,
};

stream = new Restream(appHost, httpClientFactory, logger, mediaSourceInfo);
return Task.FromResult(stream);
}
}
}
124 changes: 63 additions & 61 deletions Jellyfin.Xtream/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,76 +26,78 @@

namespace Jellyfin.Xtream
{
/// <summary>
/// The main plugin.
/// </summary>
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
{
private readonly ILogger<Plugin> _logger;

/// <summary>
/// Initializes a new instance of the <see cref="Plugin"/> class.
/// The main plugin.
/// </summary>
/// <param name="applicationPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
/// <param name="xmlSerializer">Instance of the <see cref="IXmlSerializer"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer, ILogger<Plugin> logger)
: base(applicationPaths, xmlSerializer)
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
{
_logger = logger;
Instance = this;
}
private readonly ILogger<Plugin> _logger;

/// <inheritdoc />
public override string Name => "Jellyfin Xtream";
/// <summary>
/// Initializes a new instance of the <see cref="Plugin"/> class.
/// </summary>
/// <param name="applicationPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
/// <param name="xmlSerializer">Instance of the <see cref="IXmlSerializer"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer, ILogger<Plugin> logger)
: base(applicationPaths, xmlSerializer)
{
_logger = logger;
Instance = this;
}

/// <inheritdoc />
public override Guid Id => Guid.Parse("5d774c35-8567-46d3-a950-9bb8227a0c5d");
/// <inheritdoc />
public override string Name => "Jellyfin Xtream";

/// <summary>
/// Gets the Xtream connection info with credentials.
/// </summary>
public ConnectionInfo Creds
{
get => new ConnectionInfo(this.Configuration.BaseUrl, this.Configuration.Username, this.Configuration.Password);
}
/// <inheritdoc />
public override Guid Id => Guid.Parse("5d774c35-8567-46d3-a950-9bb8227a0c5d");

/// <summary>
/// Gets the current plugin instance.
/// </summary>
public static Plugin? Instance { get; private set; }
/// <summary>
/// Gets the Xtream connection info with credentials.
/// </summary>
public ConnectionInfo Creds
{
get => new ConnectionInfo(this.Configuration.BaseUrl, this.Configuration.Username, this.Configuration.Password);
}

/// <inheritdoc />
public IEnumerable<PluginPageInfo> GetPages()
{
return new[]
/// <summary>
/// Gets the current plugin instance.
/// </summary>
public static Plugin? Instance { get; private set; }

/// <inheritdoc />
public IEnumerable<PluginPageInfo> GetPages()
{
new PluginPageInfo
{
Name = "XtreamCredentials",
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.Web.XtreamCredentials.html", GetType().Namespace)
},
new PluginPageInfo
CultureInfo ci = CultureInfo.InvariantCulture;
string? ns = GetType().Namespace;
return new[]
{
Name = "XtreamCredentials.js",
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.Web.XtreamCredentials.js", GetType().Namespace)
},
new PluginPageInfo
{
Name = "XtreamLive",
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.Web.XtreamLive.html", GetType().Namespace)
},
new PluginPageInfo
{
Name = "XtreamLive.js",
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.Web.XtreamLive.js", GetType().Namespace)
},
new PluginPageInfo
{
Name = "Xtream.js",
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.Web.Xtream.js", GetType().Namespace)
}
};
new PluginPageInfo
{
Name = "XtreamCredentials",
EmbeddedResourcePath = string.Format(ci, "{0}.Configuration.Web.XtreamCredentials.html", ns)
},
new PluginPageInfo
{
Name = "XtreamCredentials.js",
EmbeddedResourcePath = string.Format(ci, "{0}.Configuration.Web.XtreamCredentials.js", ns)
},
new PluginPageInfo
{
Name = "XtreamLive",
EmbeddedResourcePath = string.Format(ci, "{0}.Configuration.Web.XtreamLive.html", ns)
},
new PluginPageInfo
{
Name = "XtreamLive.js",
EmbeddedResourcePath = string.Format(ci, "{0}.Configuration.Web.XtreamLive.js", ns)
},
new PluginPageInfo
{
Name = "Xtream.js",
EmbeddedResourcePath = string.Format(ci, "{0}.Configuration.Web.Xtream.js", ns)
}
};
}
}
}
}
3 changes: 3 additions & 0 deletions Jellyfin.Xtream/SerieChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@ private async Task<ChannelItemResult> GetEpisodes(string seriesId, int season, C
Name = episode.Title,
Path = uri,
Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
SupportsDirectPlay = false,
SupportsDirectStream = true,
SupportsProbing = true,
}
};
items.Add(new ChannelItemInfo()
Expand Down
Loading

0 comments on commit 4aee443

Please sign in to comment.