Support Jellyfin 10.9 #96
4
.github/workflows/build.yaml
vendored
4
.github/workflows/build.yaml
vendored
@@ -17,5 +17,5 @@ jobs:
|
||||
call:
|
||||
uses: jellyfin/jellyfin-meta-plugins/.github/workflows/build.yaml@master
|
||||
with:
|
||||
dotnet-version: "6.0.*"
|
||||
dotnet-target: "net6.0"
|
||||
dotnet-version: "8.0.*"
|
||||
dotnet-target: "net8.0"
|
||||
|
4
.github/workflows/publish.yaml
vendored
4
.github/workflows/publish.yaml
vendored
@@ -10,8 +10,8 @@ jobs:
|
||||
build:
|
||||
uses: jellyfin/jellyfin-meta-plugins/.github/workflows/build.yaml@master
|
||||
with:
|
||||
dotnet-version: "6.0.*"
|
||||
dotnet-target: "net6.0"
|
||||
dotnet-version: "8.0.*"
|
||||
dotnet-target: "net8.0"
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
|
2
.github/workflows/scan-codeql.yaml
vendored
2
.github/workflows/scan-codeql.yaml
vendored
@@ -9,5 +9,5 @@ jobs:
|
||||
call:
|
||||
uses: jellyfin/jellyfin-meta-plugins/.github/workflows/scan-codeql.yaml@master
|
||||
with:
|
||||
dotnet-version: "6.0.*"
|
||||
dotnet-version: "8.0.*"
|
||||
repository-name: Kevinjil/Jellyfin.Xtream
|
||||
|
@@ -106,9 +106,8 @@ namespace Jellyfin.Xtream
|
||||
return await GetChannels(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
int separator = query.FolderId.IndexOf('-', StringComparison.InvariantCulture);
|
||||
int categoryId = int.Parse(query.FolderId.Substring(0, separator), CultureInfo.InvariantCulture);
|
||||
int channelId = int.Parse(query.FolderId.Substring(separator + 1), CultureInfo.InvariantCulture);
|
||||
Guid guid = Guid.Parse(query.FolderId);
|
||||
StreamService.FromGuid(guid, out int prefix, out int categoryId, out int channelId, out int _);
|
||||
return await GetStreams(categoryId, channelId, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -124,10 +123,10 @@ namespace Jellyfin.Xtream
|
||||
continue;
|
||||
}
|
||||
|
||||
ParsedName parsedName = plugin.StreamService.ParseName(channel.Name);
|
||||
ParsedName parsedName = StreamService.ParseName(channel.Name);
|
||||
items.Add(new ChannelItemInfo()
|
||||
{
|
||||
Id = $"{channel.CategoryId}-{channel.StreamId}",
|
||||
Id = StreamService.ToGuid(StreamService.CatchupPrefix, channel.CategoryId, channel.StreamId, 0).ToString(),
|
||||
ImageUrl = channel.StreamIcon,
|
||||
Name = parsedName.Title,
|
||||
Tags = new List<string>(parsedName.Tags),
|
||||
@@ -173,7 +172,7 @@ namespace Jellyfin.Xtream
|
||||
{
|
||||
ContentType = ChannelMediaContentType.TvExtra,
|
||||
FolderType = ChannelFolderType.Container,
|
||||
Id = $"fallback-{channelId}",
|
||||
Id = StreamService.ToGuid(StreamService.FallbackPrefix, channelId, 0, 0).ToString(),
|
||||
IsLiveStream = false,
|
||||
MediaSources = new List<MediaSourceInfo>()
|
||||
{
|
||||
@@ -194,7 +193,7 @@ namespace Jellyfin.Xtream
|
||||
foreach (EpgInfo epg in epgs.Listings.Where(epg => epg.Start < startBefore && epg.Start >= startAfter))
|
||||
{
|
||||
string id = epg.Id.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
ParsedName parsedName = plugin.StreamService.ParseName(epg.Title);
|
||||
ParsedName parsedName = StreamService.ParseName(epg.Title);
|
||||
int durationMinutes = (int)Math.Ceiling((epg.End - epg.Start).TotalMinutes);
|
||||
string dateTitle = epg.Start.ToLocalTime().ToString("ddd HH:mm", CultureInfo.InvariantCulture);
|
||||
List<MediaSourceInfo> sources = new List<MediaSourceInfo>()
|
||||
|
@@ -49,25 +49,6 @@ namespace Jellyfin.Xtream.Configuration
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the
|
||||
/// <see cref="SerializableDictionary<TKey, TValue>"/> class.
|
||||
/// </summary>
|
||||
/// <param name="info">A
|
||||
/// <see cref="System.Runtime.Serialization.SerializationInfo"/> object
|
||||
/// containing the information required to serialize the
|
||||
/// <see cref="System.Collections.Generic.Dictionary{TKey, TValue}"/>.
|
||||
/// </param>
|
||||
/// <param name="context">A
|
||||
/// <see cref="System.Runtime.Serialization.StreamingContext"/> structure
|
||||
/// containing the source and destination of the serialized stream
|
||||
/// associated with the
|
||||
/// <see cref="System.Collections.Generic.Dictionary{TKey, TValue}"/>.
|
||||
/// </param>
|
||||
private SerializableDictionary(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
private string ItemTagName => DefaultItemTag;
|
||||
|
||||
private string KeyTagName => DefaultKeyTag;
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>Jellyfin.Xtream</RootNamespace>
|
||||
<AssemblyVersion>0.6.1.0</AssemblyVersion>
|
||||
<FileVersion>0.6.1.0</FileVersion>
|
||||
<AssemblyVersion>0.6.2.0</AssemblyVersion>
|
||||
<FileVersion>0.6.2.0</FileVersion>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
@@ -13,8 +13,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Jellyfin.Controller" Version="10.8.4" />
|
||||
<PackageReference Include="Jellyfin.Model" Version="10.8.4" />
|
||||
<PackageReference Include="Jellyfin.Controller" Version="10.9.1" />
|
||||
<PackageReference Include="Jellyfin.Model" Version="10.9.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -70,7 +70,7 @@ namespace Jellyfin.Xtream
|
||||
List<ChannelInfo> items = new List<ChannelInfo>();
|
||||
await foreach (StreamInfo channel in plugin.StreamService.GetLiveStreamsWithOverrides(cancellationToken))
|
||||
{
|
||||
ParsedName parsed = plugin.StreamService.ParseName(channel.Name);
|
||||
ParsedName parsed = StreamService.ParseName(channel.Name);
|
||||
items.Add(new ChannelInfo()
|
||||
{
|
||||
Id = channel.StreamId.ToString(CultureInfo.InvariantCulture),
|
||||
@@ -170,7 +170,7 @@ namespace Jellyfin.Xtream
|
||||
{
|
||||
string key = $"xtream-epg-{channelId}";
|
||||
ICollection<ProgramInfo>? items = null;
|
||||
if (memoryCache.TryGetValue(key, out ICollection<ProgramInfo> o))
|
||||
if (memoryCache.TryGetValue(key, out ICollection<ProgramInfo>? o))
|
||||
{
|
||||
items = o;
|
||||
}
|
||||
@@ -204,12 +204,6 @@ namespace Jellyfin.Xtream
|
||||
select epg;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RecordLiveStream(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task ResetTuner(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@@ -132,15 +132,15 @@ namespace Jellyfin.Xtream
|
||||
// - This will update the TV channels.
|
||||
// - This will remove channels on credentials change.
|
||||
TaskService.CancelIfRunningAndQueue(
|
||||
"Emby.Server.Implementations",
|
||||
"Emby.Server.Implementations.LiveTv.RefreshGuideScheduledTask");
|
||||
"Jellyfin.LiveTv",
|
||||
"Jellyfin.LiveTv.Guide.RefreshGuideScheduledTask");
|
||||
|
||||
// Force a refresh of Channels on configuration update.
|
||||
// - This will update the channel entries.
|
||||
// - This will remove channel entries on credentials change.
|
||||
TaskService.CancelIfRunningAndQueue(
|
||||
"Emby.Server.Implementations",
|
||||
"Emby.Server.Implementations.Channels.RefreshChannelsScheduledTask");
|
||||
"Jellyfin.LiveTv",
|
||||
"Jellyfin.LiveTv.Channels.RefreshChannelsScheduledTask");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
35
Jellyfin.Xtream/PluginServiceRegistrator.cs
Normal file
35
Jellyfin.Xtream/PluginServiceRegistrator.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2022 Kevin Jilissen
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Jellyfin.Xtream;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class PluginServiceRegistrator : IPluginServiceRegistrator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void RegisterServices(IServiceCollection serviceCollection, IServerApplicationHost applicationHost)
|
||||
{
|
||||
serviceCollection.AddSingleton<ILiveTvService, LiveTvService>();
|
||||
serviceCollection.AddSingleton<IChannel, CatchupChannel>();
|
||||
serviceCollection.AddSingleton<IChannel, SeriesChannel>();
|
||||
serviceCollection.AddSingleton<IChannel, VodChannel>();
|
||||
}
|
||||
}
|
@@ -101,31 +101,25 @@ namespace Jellyfin.Xtream
|
||||
/// <inheritdoc />
|
||||
public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
Plugin plugin = Plugin.Instance;
|
||||
if (string.IsNullOrEmpty(query.FolderId))
|
||||
{
|
||||
return await GetCategories(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (plugin.StreamService.IsId(query.FolderId, StreamService.CategoryPrefix))
|
||||
Guid guid = Guid.Parse(query.FolderId);
|
||||
StreamService.FromGuid(guid, out int prefix, out int categoryId, out int seriesId, out int seasonId);
|
||||
if (prefix == StreamService.SeriesCategoryPrefix)
|
||||
{
|
||||
int categoryId = plugin.StreamService.ParseId(query.FolderId, StreamService.CategoryPrefix);
|
||||
return await GetSeries(categoryId, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (plugin.StreamService.IsId(query.FolderId, StreamService.SeriesPrefix))
|
||||
if (prefix == StreamService.SeriesPrefix)
|
||||
{
|
||||
int seriesId = plugin.StreamService.ParseId(query.FolderId, StreamService.SeriesPrefix);
|
||||
return await GetSeasons(seriesId, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (plugin.StreamService.IsId(query.FolderId, StreamService.SeasonPrefix))
|
||||
if (prefix == StreamService.SeasonPrefix)
|
||||
{
|
||||
string folder = query.FolderId.Substring(StreamService.SeasonPrefix.Length);
|
||||
string[] parts = folder.Split('-');
|
||||
int seriesId = int.Parse(parts[0], System.Globalization.CultureInfo.InvariantCulture);
|
||||
int seasonId = int.Parse(parts[1], System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
return await GetEpisodes(seriesId, seasonId, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -137,14 +131,14 @@ namespace Jellyfin.Xtream
|
||||
|
||||
private ChannelItemInfo CreateChannelItemInfo(Series series)
|
||||
{
|
||||
ParsedName parsedName = Plugin.Instance.StreamService.ParseName(series.Name);
|
||||
ParsedName parsedName = StreamService.ParseName(series.Name);
|
||||
return new ChannelItemInfo()
|
||||
{
|
||||
CommunityRating = (float)series.Rating5Based,
|
||||
DateModified = series.LastModified,
|
||||
// FolderType = ChannelFolderType.Series,
|
||||
Genres = GetGenres(series.Genre),
|
||||
Id = $"{StreamService.SeriesPrefix}{series.SeriesId}",
|
||||
Id = StreamService.ToGuid(StreamService.SeriesPrefix, series.CategoryId, series.SeriesId, 0).ToString(),
|
||||
ImageUrl = series.Cover,
|
||||
Name = parsedName.Title,
|
||||
People = GetPeople(series.Cast),
|
||||
@@ -178,7 +172,7 @@ namespace Jellyfin.Xtream
|
||||
Season? season = series.Seasons.FirstOrDefault(s => s.SeasonId == seasonId);
|
||||
if (season != null)
|
||||
{
|
||||
ParsedName parsedName = Plugin.Instance.StreamService.ParseName(season.Name);
|
||||
ParsedName parsedName = StreamService.ParseName(season.Name);
|
||||
name = parsedName.Title;
|
||||
tags.AddRange(parsedName.Tags);
|
||||
created = season.AirDate;
|
||||
@@ -194,7 +188,7 @@ namespace Jellyfin.Xtream
|
||||
DateCreated = created,
|
||||
// FolderType = ChannelFolderType.Season,
|
||||
Genres = GetGenres(serie.Genre),
|
||||
Id = $"{StreamService.SeasonPrefix}{seriesId}-{seasonId}",
|
||||
Id = StreamService.ToGuid(StreamService.SeasonPrefix, serie.CategoryId, seriesId, seasonId).ToString(),
|
||||
ImageUrl = cover,
|
||||
Name = name,
|
||||
Overview = overview,
|
||||
@@ -207,7 +201,7 @@ namespace Jellyfin.Xtream
|
||||
private ChannelItemInfo CreateChannelItemInfo(SeriesStreamInfo series, Season? season, Episode episode)
|
||||
{
|
||||
Jellyfin.Xtream.Client.Models.SeriesInfo serie = series.Info;
|
||||
ParsedName parsedName = Plugin.Instance.StreamService.ParseName(episode.Title);
|
||||
ParsedName parsedName = StreamService.ParseName(episode.Title);
|
||||
List<MediaSourceInfo> sources = new List<MediaSourceInfo>()
|
||||
{
|
||||
Plugin.Instance.StreamService.GetMediaSourceInfo(StreamType.Series, episode.EpisodeId, episode.ContainerExtension)
|
||||
@@ -229,7 +223,7 @@ namespace Jellyfin.Xtream
|
||||
ContentType = ChannelMediaContentType.Episode,
|
||||
DateCreated = DateTimeOffset.FromUnixTimeSeconds(episode.Added).DateTime,
|
||||
Genres = GetGenres(serie.Genre),
|
||||
Id = $"{StreamService.EpisodePrefix}{episode.EpisodeId}",
|
||||
Id = StreamService.ToGuid(StreamService.EpisodePrefix, 0, 0, episode.EpisodeId).ToString(),
|
||||
ImageUrl = cover,
|
||||
IsLiveStream = false,
|
||||
MediaSources = sources,
|
||||
@@ -246,7 +240,7 @@ namespace Jellyfin.Xtream
|
||||
{
|
||||
List<ChannelItemInfo> items = new List<ChannelItemInfo>(
|
||||
(await Plugin.Instance.StreamService.GetSeriesCategories(cancellationToken).ConfigureAwait(false))
|
||||
.Select((Category category) => Plugin.Instance.StreamService.CreateChannelItemInfo(category)));
|
||||
.Select((Category category) => StreamService.CreateChannelItemInfo(StreamService.SeriesCategoryPrefix, category)));
|
||||
return new ChannelItemResult()
|
||||
{
|
||||
Items = items,
|
||||
|
@@ -158,7 +158,7 @@ namespace Jellyfin.Xtream.Service
|
||||
throw new ArgumentNullException("copyTask");
|
||||
}
|
||||
|
||||
tokenSource.Cancel();
|
||||
await tokenSource.CancelAsync().ConfigureAwait(false);
|
||||
await copyTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
@@ -37,29 +37,49 @@ namespace Jellyfin.Xtream.Service
|
||||
public class StreamService
|
||||
{
|
||||
/// <summary>
|
||||
/// The id prefix for category channel items.
|
||||
/// The id prefix for VOD category channel items.
|
||||
/// </summary>
|
||||
public const string CategoryPrefix = "category-";
|
||||
public const int VodCategoryPrefix = 0x5d774c35;
|
||||
|
||||
/// <summary>
|
||||
/// The id prefix for stream channel items.
|
||||
/// </summary>
|
||||
public const string StreamPrefix = "stream-";
|
||||
public const int StreamPrefix = 0x5d774c36;
|
||||
|
||||
/// <summary>
|
||||
/// The id prefix for series channel items.
|
||||
/// The id prefix for series category channel items.
|
||||
/// </summary>
|
||||
public const string SeriesPrefix = "series-";
|
||||
public const int SeriesCategoryPrefix = 0x5d774c37;
|
||||
|
||||
/// <summary>
|
||||
/// The id prefix for series category channel items.
|
||||
/// </summary>
|
||||
public const int SeriesPrefix = 0x5d774c38;
|
||||
|
||||
/// <summary>
|
||||
/// The id prefix for season channel items.
|
||||
/// </summary>
|
||||
public const string SeasonPrefix = "seasons-";
|
||||
public const int SeasonPrefix = 0x5d774c39;
|
||||
|
||||
/// <summary>
|
||||
/// The id prefix for season channel items.
|
||||
/// </summary>
|
||||
public const string EpisodePrefix = "episode-";
|
||||
public const int EpisodePrefix = 0x5d774c3a;
|
||||
|
||||
/// <summary>
|
||||
/// The id prefix for catchup channel items.
|
||||
/// </summary>
|
||||
public const int CatchupPrefix = 0x5d774c3b;
|
||||
|
||||
/// <summary>
|
||||
/// The id prefix for fallback EPG items.
|
||||
/// </summary>
|
||||
public const int FallbackPrefix = 0x5d774c3c;
|
||||
|
||||
/// <summary>
|
||||
/// The id prefix for media source items.
|
||||
/// </summary>
|
||||
public const int MediaSourcePrefix = 0x5d774c3d;
|
||||
|
||||
private static readonly Regex TagRegex = new Regex(@"\[([^\]]+)\]|\|([^\|]+)\|");
|
||||
|
||||
@@ -89,7 +109,7 @@ namespace Jellyfin.Xtream.Service
|
||||
/// </summary>
|
||||
/// <param name="name">The name which should be parsed.</param>
|
||||
/// <returns>A <see cref="ParsedName"/> struct containing the cleaned title and parsed tags.</returns>
|
||||
public ParsedName ParseName(string name)
|
||||
public static ParsedName ParseName(string name)
|
||||
{
|
||||
List<string> tags = new List<string>();
|
||||
string title = TagRegex.Replace(
|
||||
@@ -115,28 +135,6 @@ namespace Jellyfin.Xtream.Service
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the id string is an id with the given prefix.
|
||||
/// </summary>
|
||||
/// <param name="id">The id string.</param>
|
||||
/// <param name="prefix">The prefix string.</param>
|
||||
/// <returns>Whether or not the id string has the given prefix.</returns>
|
||||
public bool IsId(string id, string prefix)
|
||||
{
|
||||
return id.StartsWith(prefix, StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the given id by removing the prefix.
|
||||
/// </summary>
|
||||
/// <param name="id">The id string.</param>
|
||||
/// <param name="prefix">The prefix string.</param>
|
||||
/// <returns>The parsed it as integer.</returns>
|
||||
public int ParseId(string id, string prefix)
|
||||
{
|
||||
return int.Parse(id.Substring(prefix.Length), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private bool IsConfigured(SerializableDictionary<int, HashSet<int>> config, int category, int id)
|
||||
{
|
||||
HashSet<int>? values;
|
||||
@@ -192,14 +190,15 @@ namespace Jellyfin.Xtream.Service
|
||||
/// <summary>
|
||||
/// Gets an channel item info for the category.
|
||||
/// </summary>
|
||||
/// <param name="prefix">The channel category prefix.</param>
|
||||
/// <param name="category">The Xtream category.</param>
|
||||
/// <returns>A channel item representing the category.</returns>
|
||||
public ChannelItemInfo CreateChannelItemInfo(Category category)
|
||||
public static ChannelItemInfo CreateChannelItemInfo(int prefix, Category category)
|
||||
{
|
||||
ParsedName parsedName = ParseName(category.CategoryName);
|
||||
return new ChannelItemInfo()
|
||||
{
|
||||
Id = $"{CategoryPrefix}{category.CategoryId}",
|
||||
Id = ToGuid(prefix, category.CategoryId, 0, 0).ToString(),
|
||||
Name = category.CategoryName,
|
||||
Tags = new List<string>(parsedName.Tags),
|
||||
Type = ChannelItemType.Folder,
|
||||
@@ -313,6 +312,63 @@ namespace Jellyfin.Xtream.Service
|
||||
}
|
||||
}
|
||||
|
||||
private static void StoreBytes(byte[] dst, int offset, int i)
|
||||
{
|
||||
byte[] intBytes = BitConverter.GetBytes(i);
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(intBytes);
|
||||
}
|
||||
|
||||
Buffer.BlockCopy(intBytes, 0, dst, offset, 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a GUID representing the four 32-bit integers.
|
||||
/// </summary>
|
||||
/// <param name="i0">Bytes 0-3.</param>
|
||||
/// <param name="i1">Bytes 4-7.</param>
|
||||
/// <param name="i2">Bytes 8-11.</param>
|
||||
/// <param name="i3">Bytes 12-15.</param>
|
||||
/// <returns>Guid.</returns>
|
||||
public static Guid ToGuid(int i0, int i1, int i2, int i3)
|
||||
{
|
||||
byte[] guid = new byte[16];
|
||||
StoreBytes(guid, 0, i0);
|
||||
StoreBytes(guid, 4, i1);
|
||||
StoreBytes(guid, 8, i2);
|
||||
StoreBytes(guid, 12, i3);
|
||||
return new Guid(guid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the four 32-bit integers represented in the GUID.
|
||||
/// </summary>
|
||||
/// <param name="guid">The input GUID.</param>
|
||||
/// <param name="i0">Bytes 0-3.</param>
|
||||
/// <param name="i1">Bytes 4-7.</param>
|
||||
/// <param name="i2">Bytes 8-11.</param>
|
||||
/// <param name="i3">Bytes 12-15.</param>
|
||||
public static void FromGuid(Guid guid, out int i0, out int i1, out int i2, out int i3)
|
||||
{
|
||||
byte[] tmp = guid.ToByteArray();
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(tmp);
|
||||
i0 = BitConverter.ToInt32(tmp, 12);
|
||||
i1 = BitConverter.ToInt32(tmp, 8);
|
||||
i2 = BitConverter.ToInt32(tmp, 4);
|
||||
i3 = BitConverter.ToInt32(tmp, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
i0 = BitConverter.ToInt32(tmp, 0);
|
||||
i1 = BitConverter.ToInt32(tmp, 4);
|
||||
i2 = BitConverter.ToInt32(tmp, 8);
|
||||
i3 = BitConverter.ToInt32(tmp, 12);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the media source information for the given Xtream stream.
|
||||
/// </summary>
|
||||
@@ -359,7 +415,7 @@ namespace Jellyfin.Xtream.Service
|
||||
return new MediaSourceInfo()
|
||||
{
|
||||
EncoderProtocol = MediaProtocol.Http,
|
||||
Id = id.ToString(CultureInfo.InvariantCulture),
|
||||
Id = ToGuid(MediaSourcePrefix, (int)type, id, 0).ToString(),
|
||||
IsInfiniteStream = isLive,
|
||||
IsRemote = true,
|
||||
Name = "default",
|
||||
|
@@ -18,26 +18,26 @@ namespace Jellyfin.Xtream.Service
|
||||
/// <summary>
|
||||
/// An enum describing the Xtream stream types.
|
||||
/// </summary>
|
||||
public enum StreamType
|
||||
public enum StreamType : int
|
||||
{
|
||||
/// <summary>
|
||||
/// Live IPTV.
|
||||
/// </summary>
|
||||
Live,
|
||||
Live = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Catch up IPTV.
|
||||
/// </summary>
|
||||
CatchUp,
|
||||
CatchUp = 1,
|
||||
|
||||
/// <summary>
|
||||
/// On-demand series grouped in seasons and episodes.
|
||||
/// </summary>
|
||||
Series,
|
||||
Series = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Video on-demand.
|
||||
/// </summary>
|
||||
Vod,
|
||||
Vod = 3,
|
||||
}
|
||||
}
|
||||
|
@@ -100,15 +100,15 @@ namespace Jellyfin.Xtream
|
||||
/// <inheritdoc />
|
||||
public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
Plugin plugin = Plugin.Instance;
|
||||
if (string.IsNullOrEmpty(query.FolderId))
|
||||
{
|
||||
return await GetCategories(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (plugin.StreamService.IsId(query.FolderId, StreamService.CategoryPrefix))
|
||||
Guid guid = Guid.Parse(query.FolderId);
|
||||
StreamService.FromGuid(guid, out int prefix, out int categoryId, out int _, out int _);
|
||||
if (prefix == StreamService.VodCategoryPrefix)
|
||||
{
|
||||
int categoryId = plugin.StreamService.ParseId(query.FolderId, StreamService.CategoryPrefix);
|
||||
return await GetStreams(categoryId, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace Jellyfin.Xtream
|
||||
private ChannelItemInfo CreateChannelItemInfo(StreamInfo stream)
|
||||
{
|
||||
long added = long.Parse(stream.Added, CultureInfo.InvariantCulture);
|
||||
ParsedName parsedName = Plugin.Instance.StreamService.ParseName(stream.Name);
|
||||
ParsedName parsedName = StreamService.ParseName(stream.Name);
|
||||
List<MediaSourceInfo> sources = new List<MediaSourceInfo>()
|
||||
{
|
||||
Plugin.Instance.StreamService.GetMediaSourceInfo(StreamType.Vod, stream.StreamId, stream.ContainerExtension)
|
||||
@@ -147,7 +147,7 @@ namespace Jellyfin.Xtream
|
||||
{
|
||||
List<ChannelItemInfo> items = new List<ChannelItemInfo>(
|
||||
(await Plugin.Instance.StreamService.GetVodCategories(cancellationToken).ConfigureAwait(false))
|
||||
.Select((Category category) => Plugin.Instance.StreamService.CreateChannelItemInfo(category)));
|
||||
.Select((Category category) => StreamService.CreateChannelItemInfo(StreamService.VodCategoryPrefix, category)));
|
||||
return new ChannelItemResult()
|
||||
{
|
||||
Items = items,
|
||||
|
@@ -1,9 +1,9 @@
|
||||
---
|
||||
name: "Jellyfin Xtream"
|
||||
guid: "5d774c35-8567-46d3-a950-9bb8227a0c5d"
|
||||
version: "0.6.1.0"
|
||||
version: "0.6.2.0"
|
||||
targetAbi: "10.8.4.0"
|
||||
framework: "net6.0"
|
||||
framework: "net8.0"
|
||||
overview: "Stream content from an Xtream-compatible server."
|
||||
description: >
|
||||
Stream Live IPTV, Video On-Demand, and Series from an Xtream-compatible server using this plugin.
|
||||
|
Reference in New Issue
Block a user