Update for Jellyfin 10.8
- Change branding from Vu+ to Enigma2 - Fix wrong default hostname - Add new options UseLoginForStreams, UseHTTPSForStreams - Use new GUID to avoid clashing with the older version of plugin
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
using MediaBrowser.Model.Plugins;
|
||||
|
||||
namespace Jellyfin.Plugin.Enigma2.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Class PluginConfiguration
|
||||
/// </summary>
|
||||
public class PluginConfiguration : BasePluginConfiguration
|
||||
{
|
||||
public string HostName { get; set; }
|
||||
public string StreamingPort { get; set; }
|
||||
public string WebInterfacePort { get; set; }
|
||||
public string WebInterfaceUsername { get; set; }
|
||||
public string WebInterfacePassword { get; set; }
|
||||
public bool UseLoginForStreams { get; set; }
|
||||
public bool UseSecureHTTPS { get; set; }
|
||||
public bool UseSecureHTTPSForStreams { get; set; }
|
||||
public bool OnlyOneBouquet { get; set; }
|
||||
public string TVBouquet { get; set; }
|
||||
public bool ZapToChannel { get; set; }
|
||||
public bool FetchPiconsFromWebInterface { get; set; }
|
||||
public string PiconsPath { get; set; }
|
||||
|
||||
public string RecordingPath { get; set; }
|
||||
|
||||
public bool EnableDebugLogging { get; set; }
|
||||
|
||||
|
||||
public PluginConfiguration()
|
||||
{
|
||||
HostName = "localhost";
|
||||
StreamingPort = "8001";
|
||||
WebInterfacePort = "8000";
|
||||
WebInterfaceUsername = "";
|
||||
WebInterfacePassword = "";
|
||||
UseLoginForStreams = false;
|
||||
UseSecureHTTPS = false;
|
||||
UseSecureHTTPSForStreams = false;
|
||||
OnlyOneBouquet = true;
|
||||
TVBouquet = "Favourites (TV)";
|
||||
ZapToChannel = false;
|
||||
FetchPiconsFromWebInterface = true;
|
||||
PiconsPath = "";
|
||||
|
||||
RecordingPath = "";
|
||||
|
||||
EnableDebugLogging = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Enigma2</title>
|
||||
</head>
|
||||
<body>
|
||||
<div data-role="page" id="Enigma2ConfigurationPage" class="page type-interior pluginConfigurationPage Enigma2ConfigurationPage" data-require="emby-button,emby-checkbox,emby-input">
|
||||
|
||||
<div data-role="content">
|
||||
<div class="content-primary">
|
||||
|
||||
<h1>Enigma2</h1>
|
||||
|
||||
<form id="Enigma2ConfigurationForm" class="Enigma2ConfigurationForm">
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtHostName" type="text" label="Enigma2 hostname or ip address:" />
|
||||
<div class="fieldDescription">
|
||||
The host name (address) or ip address of your receiver
|
||||
</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtStreamingPort" type="text" label="Enigma2 streaming port:" />
|
||||
<div class="fieldDescription">
|
||||
The Streaming port of your receiver eg. 8001
|
||||
</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtWebInterfacePort" type="text" label="Enigma2 Web Interface port:" />
|
||||
<div class="fieldDescription">
|
||||
The web interface port of your receiver eg. 8000
|
||||
</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtWebInterfaceUsername" type="text" label="Enigma2 Web Interface username:" />
|
||||
<div class="fieldDescription">
|
||||
The web interface username of your receiver (optional)
|
||||
</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtWebInterfacePassword" type="password" label="Enigma2 Web Interface password:" />
|
||||
<div class="fieldDescription">
|
||||
The web interface password of your receiver (optional)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkUseLoginForStreams" />
|
||||
<span>Use login for streaming</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">
|
||||
Use web interface login for streaming
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkUseSecureHTTPS" />
|
||||
<span>Use secure HTTP (HTTPS)</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">
|
||||
Use HTTPS to connect to your receiver
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkUseSecureHTTPSForStreams" />
|
||||
<span>Use secure HTTP (HTTPS) for streams</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">
|
||||
Use HTTPS to connect to streaming port
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkOnlyOneBouquet" />
|
||||
<span>Fetch Only one TV bouquet</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">
|
||||
Limit channels to only those contained within the specified TV Bouquet below (optional)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtTVBouquet" type="text" label="Enigma2 TVBouquet:" />
|
||||
<div class="fieldDescription">
|
||||
The TV Bouquet to load channels for (optional)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkZapToChannel" />
|
||||
<span>Zap before Channel switch (single tuner)</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">
|
||||
Set if only one tuner within receiver
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkFetchPiconsFromWebInterface" />
|
||||
<span>Fetch picons from webinterface</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">
|
||||
Set if you want to retrieve Picons from the web interface of the receiver
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtPiconsPath" type="text" label="Picons Path:" />
|
||||
<div class="fieldDescription">
|
||||
The local location of your Picons eg. C:\Picons\ (optional)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtRecordingPath" type="text" label="Recording Path:" />
|
||||
<div class="fieldDescription">
|
||||
The location to store your recordings on your receiver eg. /hdd/movie/ (optional)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="checkboxContainer">
|
||||
<input is="emby-checkbox" type="checkbox" id="chkDebugLogging" />
|
||||
<span>Enable Enigma2 debug logging</span>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block"><span>Save</span></button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
var Enigma2ConfigurationPage = {
|
||||
pluginUniqueId: "193f29f9-ea6c-4595-a6f6-55e79d7c590a",
|
||||
|
||||
loadConfiguration: function () {
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
ApiClient.getPluginConfiguration(Enigma2ConfigurationPage.pluginUniqueId).then(function (config) {
|
||||
document.getElementById('txtHostName').value = config.HostName || "";
|
||||
document.getElementById('txtStreamingPort').value = config.StreamingPort || "";
|
||||
document.getElementById('txtWebInterfacePort').value = config.WebInterfacePort || "";
|
||||
document.getElementById('txtWebInterfaceUsername').value = config.WebInterfaceUsername || "";
|
||||
document.getElementById('txtWebInterfacePassword').value = config.WebInterfacePassword || "";
|
||||
document.getElementById('chkUseLoginForStreams').checked = config.UseLoginForStreams || false;
|
||||
document.getElementById('chkUseSecureHTTPS').checked = config.UseSecureHTTPS || false;
|
||||
document.getElementById('chkUseSecureHTTPSForStreams').checked = config.UseSecureHTTPSForStreams || false;
|
||||
document.getElementById('chkOnlyOneBouquet').checked = config.OnlyOneBouquet || false;
|
||||
document.getElementById('txtTVBouquet').value = config.TVBouquet || "";
|
||||
document.getElementById('chkZapToChannel').checked = config.ZapToChannel || false;
|
||||
document.getElementById('chkFetchPiconsFromWebInterface').checked = config.FetchPiconsFromWebInterface || false;
|
||||
document.getElementById('txtPiconsPath').value = config.PiconsPath || "";
|
||||
document.getElementById('txtRecordingPath').value = config.RecordingPath || "";
|
||||
document.getElementById('chkDebugLogging').checked = config.EnableDebugLogging || false;
|
||||
|
||||
Dashboard.hideLoadingMsg();
|
||||
});
|
||||
},
|
||||
|
||||
saveConfiguration: function () {
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
ApiClient.getPluginConfiguration(Enigma2ConfigurationPage.pluginUniqueId).then(function (config) {
|
||||
config.HostName = document.getElementById('txtHostName').value;
|
||||
config.StreamingPort = document.getElementById('txtStreamingPort').value;
|
||||
config.WebInterfacePort = document.getElementById('txtWebInterfacePort').value;
|
||||
config.WebInterfaceUsername = document.getElementById('txtWebInterfaceUsername').value;
|
||||
config.WebInterfacePassword = document.getElementById('txtWebInterfacePassword').value;
|
||||
config.UseLoginForStreams = document.getElementById('chkUseLoginForStreams').checked;
|
||||
config.UseSecureHTTPS = document.getElementById('chkUseSecureHTTPS').checked;
|
||||
config.UseSecureHTTPSForStreams = document.getElementById('chkUseSecureHTTPSForStreams').checked;
|
||||
config.OnlyOneBouquet = document.getElementById('chkOnlyOneBouquet').checked;
|
||||
config.TVBouquet = document.getElementById('txtTVBouquet').value;
|
||||
config.ZapToChannel = document.getElementById('chkZapToChannel').checked;
|
||||
config.FetchPiconsFromWebInterface = document.getElementById('chkFetchPiconsFromWebInterface').checked;
|
||||
config.PiconsPath = document.getElementById('txtPiconsPath').value;
|
||||
config.RecordingPath = document.getElementById('txtRecordingPath').value;
|
||||
config.EnableDebugLogging = document.getElementById('chkDebugLogging').checked;
|
||||
|
||||
ApiClient.updatePluginConfiguration(Enigma2ConfigurationPage.pluginUniqueId, config).then(Dashboard.processPluginConfigurationUpdateResult);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('Enigma2ConfigurationPage').addEventListener('pageshow', function () {
|
||||
Enigma2ConfigurationPage.loadConfiguration();
|
||||
});
|
||||
|
||||
document.getElementById('Enigma2ConfigurationForm').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
Enigma2ConfigurationPage.saveConfiguration();
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Plugin.Enigma2.Helpers
|
||||
{
|
||||
public static class ChannelHelper
|
||||
{
|
||||
public static ChannelType GetChannelType(string channelType)
|
||||
{
|
||||
var type = new ChannelType();
|
||||
|
||||
if (channelType == "0x1")
|
||||
{
|
||||
type = ChannelType.TV;
|
||||
}
|
||||
else if (channelType == "0xa")
|
||||
{
|
||||
type = ChannelType.Radio;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
public static class UtilsHelper
|
||||
{
|
||||
public static void DebugInformation(ILogger<LiveTvService> logger, string message)
|
||||
{
|
||||
var config = Plugin.Instance.Configuration;
|
||||
var enableDebugLogging = config.EnableDebugLogging;
|
||||
|
||||
if (enableDebugLogging)
|
||||
{
|
||||
logger.LogDebug(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class RecordingHelper
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static class ApiHelper
|
||||
{
|
||||
private static readonly DateTime UnixEpoch =
|
||||
new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
public static long GetCurrentUnixTimestampMillis()
|
||||
{
|
||||
return (long)(DateTime.UtcNow - UnixEpoch).TotalMilliseconds;
|
||||
}
|
||||
|
||||
public static DateTime DateTimeFromUnixTimestampMillis(long millis)
|
||||
{
|
||||
return UnixEpoch.AddMilliseconds(millis);
|
||||
}
|
||||
|
||||
public static long GetCurrentUnixTimestampSeconds(DateTime date)
|
||||
{
|
||||
return (long)(date - UnixEpoch).TotalSeconds;
|
||||
}
|
||||
|
||||
public static DateTime DateTimeFromUnixTimestampSeconds(long seconds)
|
||||
{
|
||||
return UnixEpoch.AddSeconds(seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<AssemblyVersion>5.0.0.0</AssemblyVersion>
|
||||
<FileVersion>5.0.0.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net6.0|AnyCPU'">
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Configuration\configPage.html" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Configuration\configPage.html" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Jellyfin.Common" Version="10.8.0" />
|
||||
<PackageReference Include="Jellyfin.Naming" Version="10.8.0" />
|
||||
<PackageReference Include="Jellyfin.Controller" Version="10.8.0" />
|
||||
<PackageReference Include="Jellyfin.Model" Version="10.8.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using Jellyfin.Plugin.Enigma2.Configuration;
|
||||
|
||||
namespace Jellyfin.Plugin.Enigma2
|
||||
{
|
||||
/// <summary>
|
||||
/// Class Plugin
|
||||
/// </summary>
|
||||
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
{
|
||||
public Plugin(
|
||||
IApplicationPaths applicationPaths,
|
||||
IXmlSerializer xmlSerializer
|
||||
) : base(applicationPaths, xmlSerializer)
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the plugin
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public override string Name => "Enigma2";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description.
|
||||
/// </summary>
|
||||
/// <value>The description.</value>
|
||||
public override string Description => "Provides live TV using Enigma2 PVR as a back-end.";
|
||||
|
||||
private Guid _id = new Guid("193f29f9-ea6c-4595-a6f6-55e79d7c590a");
|
||||
public override Guid Id => _id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance.
|
||||
/// </summary>
|
||||
/// <value>The instance.</value>
|
||||
public static Plugin Instance { get; private set; }
|
||||
|
||||
public IEnumerable<PluginPageInfo> GetPages()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new PluginPageInfo
|
||||
{
|
||||
Name = "enigma2",
|
||||
EmbeddedResourcePath = "Jellyfin.Plugin.Enigma2.Configuration.configPage.html"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,523 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Channels;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
|
||||
namespace Jellyfin.Plugin.Enigma2
|
||||
{
|
||||
public class RecordingsChannel : IChannel, IHasCacheKey, ISupportsDelete, ISupportsLatestMedia, ISupportsMediaProbe, IHasFolderAttributes
|
||||
{
|
||||
public ILiveTvManager _liveTvManager;
|
||||
|
||||
public RecordingsChannel(ILiveTvManager liveTvManager)
|
||||
{
|
||||
_liveTvManager = liveTvManager;
|
||||
}
|
||||
|
||||
public string Name => "Enigma2 Recordings";
|
||||
|
||||
public string[] Attributes => new[] { "Recordings" };
|
||||
|
||||
public string Description => "Enigma2 Recordings";
|
||||
|
||||
public string DataVersion => "1";
|
||||
|
||||
public string HomePageUrl => null;
|
||||
|
||||
public ChannelParentalRating ParentalRating => ChannelParentalRating.GeneralAudience;
|
||||
|
||||
public string GetCacheKey(string userId)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var values = new List<string>
|
||||
{
|
||||
now.DayOfYear.ToString(CultureInfo.InvariantCulture),
|
||||
now.Hour.ToString(CultureInfo.InvariantCulture)
|
||||
};
|
||||
|
||||
double minute = now.Minute;
|
||||
minute /= 5;
|
||||
|
||||
values.Add(Math.Floor(minute).ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
values.Add(GetService().LastRecordingChange.Ticks.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
return string.Join("-", values.ToArray());
|
||||
}
|
||||
|
||||
public InternalChannelFeatures GetChannelFeatures()
|
||||
{
|
||||
return new InternalChannelFeatures
|
||||
{
|
||||
ContentTypes = new List<ChannelMediaContentType>
|
||||
{
|
||||
ChannelMediaContentType.Movie,
|
||||
ChannelMediaContentType.Episode,
|
||||
ChannelMediaContentType.Clip
|
||||
},
|
||||
MediaTypes = new List<ChannelMediaType>
|
||||
{
|
||||
ChannelMediaType.Audio,
|
||||
ChannelMediaType.Video
|
||||
},
|
||||
SupportsContentDownloading = true
|
||||
};
|
||||
}
|
||||
|
||||
public Task<DynamicImageResponse> GetChannelImage(ImageType type, CancellationToken cancellationToken)
|
||||
{
|
||||
if (type == ImageType.Primary)
|
||||
{
|
||||
return Task.FromResult(new DynamicImageResponse
|
||||
{
|
||||
Path = "https://github.com/CompoUK/MediaBrowser.Plugins.VuPlus/blob/master/src/Images/Vu+.png?raw=true",
|
||||
Protocol = MediaProtocol.Http,
|
||||
HasImage = true
|
||||
});
|
||||
}
|
||||
|
||||
return Task.FromResult(new DynamicImageResponse
|
||||
{
|
||||
HasImage = false
|
||||
});
|
||||
}
|
||||
|
||||
public IEnumerable<ImageType> GetSupportedChannelImages()
|
||||
{
|
||||
return new List<ImageType>
|
||||
{
|
||||
ImageType.Primary
|
||||
};
|
||||
}
|
||||
|
||||
public bool IsEnabledFor(string userId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private LiveTvService GetService()
|
||||
{
|
||||
return _liveTvManager.Services.OfType<LiveTvService>().First();
|
||||
}
|
||||
|
||||
public bool CanDelete(BaseItem item)
|
||||
{
|
||||
return !item.IsFolder;
|
||||
}
|
||||
|
||||
public Task DeleteItem(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
return GetService().DeleteRecordingAsync(id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ChannelItemInfo>> GetLatestMedia(ChannelLatestMediaSearch request, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await GetChannelItems(new InternalChannelItemQuery(), i => true, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return result.Items.OrderByDescending(i => i.DateCreated ?? DateTime.MinValue);
|
||||
}
|
||||
|
||||
public Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(query.FolderId))
|
||||
{
|
||||
return GetRecordingGroups(query, cancellationToken);
|
||||
}
|
||||
|
||||
if (query.FolderId.StartsWith("series_", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var hash = query.FolderId.Split('_')[1];
|
||||
return GetChannelItems(query, i => i.IsSeries && string.Equals(i.Name.GetMD5().ToString("N"), hash, StringComparison.Ordinal), cancellationToken);
|
||||
}
|
||||
|
||||
if (string.Equals(query.FolderId, "kids", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return GetChannelItems(query, i => i.IsKids, cancellationToken);
|
||||
}
|
||||
|
||||
if (string.Equals(query.FolderId, "movies", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return GetChannelItems(query, i => i.IsMovie, cancellationToken);
|
||||
}
|
||||
|
||||
if (string.Equals(query.FolderId, "news", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return GetChannelItems(query, i => i.IsNews, cancellationToken);
|
||||
}
|
||||
|
||||
if (string.Equals(query.FolderId, "sports", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return GetChannelItems(query, i => i.IsSports, cancellationToken);
|
||||
}
|
||||
|
||||
if (string.Equals(query.FolderId, "others", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return GetChannelItems(query, i => !i.IsSports && !i.IsNews && !i.IsMovie && !i.IsKids && !i.IsSeries, cancellationToken);
|
||||
}
|
||||
|
||||
var result = new ChannelItemResult()
|
||||
{
|
||||
Items = new List<ChannelItemInfo>()
|
||||
};
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, Func<MyRecordingInfo, bool> filter, CancellationToken cancellationToken)
|
||||
{
|
||||
var service = GetService();
|
||||
var allRecordings = await service.GetAllRecordingsAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new ChannelItemResult()
|
||||
{
|
||||
Items = allRecordings.Where(filter).Select(ConvertToChannelItem).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private ChannelItemInfo ConvertToChannelItem(MyRecordingInfo item)
|
||||
{
|
||||
var path = string.IsNullOrEmpty(item.Path) ? item.Url : item.Path;
|
||||
|
||||
var channelItem = new ChannelItemInfo
|
||||
{
|
||||
Name = string.IsNullOrEmpty(item.EpisodeTitle) ? item.Name : item.EpisodeTitle,
|
||||
SeriesName = !string.IsNullOrEmpty(item.EpisodeTitle) || item.IsSeries ? item.Name : null,
|
||||
OfficialRating = item.OfficialRating,
|
||||
CommunityRating = item.CommunityRating,
|
||||
ContentType = item.IsMovie ? ChannelMediaContentType.Movie : (item.IsSeries ? ChannelMediaContentType.Episode : ChannelMediaContentType.Clip),
|
||||
Genres = item.Genres,
|
||||
ImageUrl = item.ImageUrl,
|
||||
//HomePageUrl = item.HomePageUrl
|
||||
Id = item.Id,
|
||||
//IndexNumber = item.IndexNumber,
|
||||
MediaType = item.ChannelType == ChannelType.TV ? ChannelMediaType.Video : ChannelMediaType.Audio,
|
||||
MediaSources = new List<MediaSourceInfo>
|
||||
{
|
||||
new MediaSourceInfo
|
||||
{
|
||||
Path = path,
|
||||
Protocol = path.StartsWith("http", StringComparison.OrdinalIgnoreCase) ? MediaProtocol.Http : MediaProtocol.File,
|
||||
Id = item.Id
|
||||
}
|
||||
},
|
||||
//ParentIndexNumber = item.ParentIndexNumber,
|
||||
PremiereDate = item.OriginalAirDate,
|
||||
//ProductionYear = item.ProductionYear,
|
||||
//Studios = item.Studios,
|
||||
Type = ChannelItemType.Media,
|
||||
DateModified = item.DateLastUpdated,
|
||||
Overview = item.Overview,
|
||||
//People = item.People
|
||||
IsLiveStream = item.Status == RecordingStatus.InProgress,
|
||||
Etag = item.Status.ToString()
|
||||
};
|
||||
|
||||
return channelItem;
|
||||
}
|
||||
|
||||
private async Task<ChannelItemResult> GetRecordingGroups(InternalChannelItemQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
var service = GetService();
|
||||
|
||||
var allRecordings = await service.GetAllRecordingsAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var series = allRecordings
|
||||
.Where(i => i.IsSeries)
|
||||
.ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var items = new List<ChannelItemInfo>();
|
||||
|
||||
items.AddRange(series.OrderBy(i => i.Key).Select(i => new ChannelItemInfo
|
||||
{
|
||||
Name = i.Key,
|
||||
FolderType = ChannelFolderType.Container,
|
||||
Id = "series_" + i.Key.GetMD5().ToString("N"),
|
||||
Type = ChannelItemType.Folder,
|
||||
ImageUrl = i.First().ImageUrl
|
||||
}));
|
||||
|
||||
var kids = allRecordings.FirstOrDefault(i => i.IsKids);
|
||||
|
||||
if (kids != null)
|
||||
{
|
||||
items.Add(new ChannelItemInfo
|
||||
{
|
||||
Name = "Kids",
|
||||
FolderType = ChannelFolderType.Container,
|
||||
Id = "kids",
|
||||
Type = ChannelItemType.Folder,
|
||||
ImageUrl = kids.ImageUrl
|
||||
});
|
||||
}
|
||||
|
||||
var movies = allRecordings.FirstOrDefault(i => i.IsMovie);
|
||||
if (movies != null)
|
||||
{
|
||||
items.Add(new ChannelItemInfo
|
||||
{
|
||||
Name = "Movies",
|
||||
FolderType = ChannelFolderType.Container,
|
||||
Id = "movies",
|
||||
Type = ChannelItemType.Folder,
|
||||
ImageUrl = movies.ImageUrl
|
||||
});
|
||||
}
|
||||
|
||||
var news = allRecordings.FirstOrDefault(i => i.IsNews);
|
||||
if (news != null)
|
||||
{
|
||||
items.Add(new ChannelItemInfo
|
||||
{
|
||||
Name = "News",
|
||||
FolderType = ChannelFolderType.Container,
|
||||
Id = "news",
|
||||
Type = ChannelItemType.Folder,
|
||||
ImageUrl = news.ImageUrl
|
||||
});
|
||||
}
|
||||
|
||||
var sports = allRecordings.FirstOrDefault(i => i.IsSports);
|
||||
if (sports != null)
|
||||
{
|
||||
items.Add(new ChannelItemInfo
|
||||
{
|
||||
Name = "Sports",
|
||||
FolderType = ChannelFolderType.Container,
|
||||
Id = "sports",
|
||||
Type = ChannelItemType.Folder,
|
||||
ImageUrl = sports.ImageUrl
|
||||
});
|
||||
}
|
||||
|
||||
var other = allRecordings.FirstOrDefault(i => !i.IsSports && !i.IsNews && !i.IsMovie && !i.IsKids && !i.IsSeries);
|
||||
if (other != null)
|
||||
{
|
||||
items.Add(new ChannelItemInfo
|
||||
{
|
||||
Name = "Others",
|
||||
FolderType = ChannelFolderType.Container,
|
||||
Id = "others",
|
||||
Type = ChannelItemType.Folder,
|
||||
ImageUrl = other.ImageUrl
|
||||
});
|
||||
}
|
||||
|
||||
return new ChannelItemResult()
|
||||
{
|
||||
Items = items
|
||||
};
|
||||
}
|
||||
}
|
||||
public class MyRecordingInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Id of the recording.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the series timer identifier.
|
||||
/// </summary>
|
||||
/// <value>The series timer identifier.</value>
|
||||
public string SeriesTimerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timer identifier.
|
||||
/// </summary>
|
||||
/// <value>The timer identifier.</value>
|
||||
public string TimerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ChannelId of the recording.
|
||||
/// </summary>
|
||||
public string ChannelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the channel.
|
||||
/// </summary>
|
||||
/// <value>The type of the channel.</value>
|
||||
public ChannelType ChannelType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the recording.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path.
|
||||
/// </summary>
|
||||
/// <value>The path.</value>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URL.
|
||||
/// </summary>
|
||||
/// <value>The URL.</value>
|
||||
public string Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the overview.
|
||||
/// </summary>
|
||||
/// <value>The overview.</value>
|
||||
public string Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The start date of the recording, in UTC.
|
||||
/// </summary>
|
||||
public DateTime StartDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The end date of the recording, in UTC.
|
||||
/// </summary>
|
||||
public DateTime EndDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the program identifier.
|
||||
/// </summary>
|
||||
/// <value>The program identifier.</value>
|
||||
public string ProgramId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status.
|
||||
/// </summary>
|
||||
/// <value>The status.</value>
|
||||
public RecordingStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Genre of the program.
|
||||
/// </summary>
|
||||
public List<string> Genres { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is repeat.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
|
||||
public bool IsRepeat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the episode title.
|
||||
/// </summary>
|
||||
/// <value>The episode title.</value>
|
||||
public string EpisodeTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is hd.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
|
||||
public bool? IsHD { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio.
|
||||
/// </summary>
|
||||
/// <value>The audio.</value>
|
||||
public ProgramAudio? Audio { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the original air date.
|
||||
/// </summary>
|
||||
/// <value>The original air date.</value>
|
||||
public DateTime? OriginalAirDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is movie.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value>
|
||||
public bool IsMovie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is sports.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
|
||||
public bool IsSports { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is series.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
|
||||
public bool IsSeries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is live.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
|
||||
public bool IsLive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is news.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
|
||||
public bool IsNews { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is kids.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
|
||||
public bool IsKids { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is premiere.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
|
||||
public bool IsPremiere { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the official rating.
|
||||
/// </summary>
|
||||
/// <value>The official rating.</value>
|
||||
public string OfficialRating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the community rating.
|
||||
/// </summary>
|
||||
/// <value>The community rating.</value>
|
||||
public float? CommunityRating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Supply the image path if it can be accessed directly from the file system
|
||||
/// </summary>
|
||||
/// <value>The image path.</value>
|
||||
public string ImagePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Supply the image url if it can be downloaded
|
||||
/// </summary>
|
||||
/// <value>The image URL.</value>
|
||||
public string ImageUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance has image.
|
||||
/// </summary>
|
||||
/// <value><c>null</c> if [has image] contains no value, <c>true</c> if [has image]; otherwise, <c>false</c>.</value>
|
||||
public bool? HasImage { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the show identifier.
|
||||
/// </summary>
|
||||
/// <value>The show identifier.</value>
|
||||
public string ShowId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date last updated.
|
||||
/// </summary>
|
||||
/// <value>The date last updated.</value>
|
||||
public DateTime DateLastUpdated { get; set; }
|
||||
|
||||
public MyRecordingInfo()
|
||||
{
|
||||
Genres = new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user