Fetch media info and provide metadata #153
@@ -35,7 +35,7 @@ namespace Jellyfin.Xtream;
|
||||
/// The Xtream Codes API channel.
|
||||
/// </summary>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
|
||||
public class CatchupChannel(ILogger<CatchupChannel> logger) : IChannel
|
||||
public class CatchupChannel(ILogger<CatchupChannel> logger) : IChannel, IDisableMediaSourceDisplay
|
||||
{
|
||||
private readonly ILogger<CatchupChannel> _logger = logger;
|
||||
|
||||
|
46
Jellyfin.Xtream/Client/Models/AudioInfo.cs
Normal file
46
Jellyfin.Xtream/Client/Models/AudioInfo.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
|
||||
#pragma warning disable CS1591
|
||||
namespace Jellyfin.Xtream.Client.Models;
|
||||
|
||||
public class AudioInfo
|
||||
{
|
||||
[JsonProperty("index")]
|
||||
public int Index { get; set; }
|
||||
|
||||
[JsonProperty("codec_name")]
|
||||
public string CodecName { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("profile")]
|
||||
public string Profile { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("sample_fmt")]
|
||||
public string SampleFormat { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("sample_rate")]
|
||||
public int SampleRate { get; set; }
|
||||
|
||||
[JsonProperty("channels")]
|
||||
public int Channels { get; set; }
|
||||
|
||||
[JsonProperty("channel_layout")]
|
||||
public string ChannelLayout { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("bit_rate")]
|
||||
public int Bitrate { get; set; }
|
||||
}
|
@@ -22,20 +22,28 @@ namespace Jellyfin.Xtream.Client.Models;
|
||||
public class EpisodeInfo
|
||||
{
|
||||
[JsonProperty("movie_image")]
|
||||
public string MovieImage { get; set; } = string.Empty;
|
||||
public string? MovieImage { get; set; }
|
||||
|
||||
[JsonProperty("plot")]
|
||||
public string Plot { get; set; } = string.Empty;
|
||||
public string? Plot { get; set; }
|
||||
|
||||
[JsonProperty("releasedate")]
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
[JsonProperty("rating")]
|
||||
public decimal Rating { get; set; }
|
||||
public decimal? Rating { get; set; }
|
||||
|
||||
[JsonProperty("duration_secs")]
|
||||
public int DurationSecs { get; set; }
|
||||
public int? DurationSecs { get; set; }
|
||||
|
||||
[JsonProperty("bitrate")]
|
||||
public int Bitrate { get; set; }
|
||||
public int? Bitrate { get; set; }
|
||||
|
||||
[JsonProperty("video")]
|
||||
[JsonConverter(typeof(OnlyObjectConverter<VideoInfo>))]
|
||||
public VideoInfo? Video { get; set; }
|
||||
|
||||
[JsonProperty("audio")]
|
||||
[JsonConverter(typeof(OnlyObjectConverter<AudioInfo>))]
|
||||
public AudioInfo? Audio { get; set; }
|
||||
}
|
||||
|
64
Jellyfin.Xtream/Client/Models/VideoInfo.cs
Normal file
64
Jellyfin.Xtream/Client/Models/VideoInfo.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
|
||||
#pragma warning disable CS1591
|
||||
namespace Jellyfin.Xtream.Client.Models;
|
||||
|
||||
public class VideoInfo
|
||||
{
|
||||
[JsonProperty("index")]
|
||||
public int Index { get; set; }
|
||||
|
||||
[JsonProperty("codec_name")]
|
||||
public string CodecName { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("profile")]
|
||||
public string Profile { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("width")]
|
||||
public int Width { get; set; }
|
||||
|
||||
[JsonProperty("height")]
|
||||
public int Height { get; set; }
|
||||
|
||||
[JsonProperty("display_aspect_ratio")]
|
||||
public string AspectRatio { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("pix_fmt")]
|
||||
public string PixelFormat { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("level")]
|
||||
public int Level { get; set; }
|
||||
|
||||
[JsonProperty("color_range")]
|
||||
public string ColorRange { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("color_space")]
|
||||
public string ColorSpace { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("color_transfer")]
|
||||
public string ColorTransfer { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("color_primaries")]
|
||||
public string ColorPrimaries { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("is_avc")]
|
||||
public bool IsAVC { get; set; }
|
||||
|
||||
[JsonProperty("bits_per_raw_sample")]
|
||||
public int BitsPerRawSample { get; set; }
|
||||
}
|
58
Jellyfin.Xtream/Client/Models/VodInfo.cs
Normal file
58
Jellyfin.Xtream/Client/Models/VodInfo.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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 System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
#pragma warning disable CS1591
|
||||
namespace Jellyfin.Xtream.Client.Models;
|
||||
|
||||
public class VodInfo
|
||||
{
|
||||
[JsonProperty("movie_image")]
|
||||
public string? MovieImage { get; set; }
|
||||
|
||||
[JsonProperty("genre")]
|
||||
public string? Genre { get; set; }
|
||||
|
||||
[JsonProperty("plot")]
|
||||
public string? Plot { get; set; }
|
||||
|
||||
[JsonProperty("director")]
|
||||
public string? Director { get; set; }
|
||||
|
||||
[JsonProperty("rating")]
|
||||
public decimal? Rating { get; set; }
|
||||
|
||||
[JsonProperty("releasedate")]
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
[JsonProperty("duration_secs")]
|
||||
public int? DurationSecs { get; set; }
|
||||
|
||||
[JsonProperty("tmdb_id")]
|
||||
public int? TmdbId { get; set; }
|
||||
|
||||
[JsonProperty("bitrate")]
|
||||
public int Bitrate { get; set; }
|
||||
|
||||
[JsonProperty("video")]
|
||||
[JsonConverter(typeof(OnlyObjectConverter<VideoInfo>))]
|
||||
public VideoInfo? Video { get; set; }
|
||||
|
||||
[JsonProperty("audio")]
|
||||
[JsonConverter(typeof(OnlyObjectConverter<AudioInfo>))]
|
||||
public AudioInfo? Audio { get; set; }
|
||||
}
|
30
Jellyfin.Xtream/Client/Models/VodStreamInfo.cs
Normal file
30
Jellyfin.Xtream/Client/Models/VodStreamInfo.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
|
||||
#pragma warning disable CS1591
|
||||
namespace Jellyfin.Xtream.Client.Models;
|
||||
|
||||
public class VodStreamInfo
|
||||
{
|
||||
[JsonProperty("info")]
|
||||
[JsonConverter(typeof(OnlyObjectConverter<VodInfo>))]
|
||||
public VodInfo? Info { get; set; }
|
||||
|
||||
[JsonProperty("movie_data")]
|
||||
[JsonConverter(typeof(OnlyObjectConverter<StreamInfo>))]
|
||||
public StreamInfo? MovieData { get; set; }
|
||||
}
|
@@ -71,6 +71,12 @@ public class XtreamClient(HttpClient client) : IDisposable
|
||||
$"/player_api.php?username={connectionInfo.UserName}&password={connectionInfo.Password}&action=get_vod_streams&category_id={categoryId}",
|
||||
cancellationToken);
|
||||
|
||||
public Task<VodStreamInfo> GetVodInfoAsync(ConnectionInfo connectionInfo, int streamId, CancellationToken cancellationToken) =>
|
||||
QueryApi<VodStreamInfo>(
|
||||
connectionInfo,
|
||||
$"/player_api.php?username={connectionInfo.UserName}&password={connectionInfo.Password}&action=get_vod_info&vod_id={streamId}",
|
||||
cancellationToken);
|
||||
|
||||
public Task<List<StreamInfo>> GetLiveStreamsByCategoryAsync(ConnectionInfo connectionInfo, int categoryId, CancellationToken cancellationToken) =>
|
||||
QueryApi<List<StreamInfo>>(
|
||||
connectionInfo,
|
||||
|
@@ -54,6 +54,11 @@ public class PluginConfiguration : BasePluginConfiguration
|
||||
/// </summary>
|
||||
public bool IsVodVisible { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the Video On-demand channel is visible.
|
||||
/// </summary>
|
||||
public bool IsTmdbVodOverride { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the channels displayed in Live TV.
|
||||
/// </summary>
|
||||
|
@@ -9,6 +9,12 @@
|
||||
<span>Show this channel to users</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" id="TmdbOverride" name="TmdbOverride" type="checkbox" />
|
||||
<span>Override metadata with TMDB</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">Video On-demand selection</h2>
|
||||
</div>
|
||||
|
@@ -11,6 +11,8 @@ export default function (view) {
|
||||
const getConfig = ApiClient.getPluginConfiguration(pluginId);
|
||||
const visible = view.querySelector("#Visible");
|
||||
getConfig.then((config) => visible.checked = config.IsVodVisible);
|
||||
const tmdbOverride = view.querySelector("#TmdbOverride");
|
||||
getConfig.then((config) => TmdbOverride.checked = config.IsTmdbVodOverride);
|
||||
const table = view.querySelector('#VodContent');
|
||||
Xtream.populateCategoriesTable(
|
||||
table,
|
||||
@@ -23,6 +25,7 @@ export default function (view) {
|
||||
|
||||
ApiClient.getPluginConfiguration(pluginId).then((config) => {
|
||||
config.IsVodVisible = visible.checked;
|
||||
config.IsTmdbVodOverride = tmdbOverride.checked;
|
||||
config.Vod = data;
|
||||
ApiClient.updatePluginConfiguration(pluginId, config).then((result) => {
|
||||
Dashboard.processPluginConfigurationUpdateResult(result);
|
||||
|
@@ -13,10 +13,12 @@
|
||||
// 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 Jellyfin.Xtream.Providers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Jellyfin.Xtream;
|
||||
@@ -31,5 +33,6 @@ public class PluginServiceRegistrator : IPluginServiceRegistrator
|
||||
serviceCollection.AddSingleton<IChannel, CatchupChannel>();
|
||||
serviceCollection.AddSingleton<IChannel, SeriesChannel>();
|
||||
serviceCollection.AddSingleton<IChannel, VodChannel>();
|
||||
serviceCollection.AddSingleton<IPreRefreshProvider, XtreamVodProvider>();
|
||||
}
|
||||
}
|
||||
|
116
Jellyfin.Xtream/Providers/XtreamVodProvider.cs
Normal file
116
Jellyfin.Xtream/Providers/XtreamVodProvider.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Xtream.Client;
|
||||
using Jellyfin.Xtream.Client.Models;
|
||||
using Jellyfin.Xtream.Service;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Xtream.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// The Xtream Codes VOD metadata provider.
|
||||
/// </summary>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
|
||||
/// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
|
||||
public class XtreamVodProvider(ILogger<VodChannel> logger, IProviderManager providerManager) : ICustomMetadataProvider<Movie>, IPreRefreshProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the provider.
|
||||
/// </summary>
|
||||
public const string ProviderName = "XtreamVodProvider";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Name => ProviderName;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
string? idStr = item.GetProviderId(ProviderName);
|
||||
if (idStr is not null)
|
||||
{
|
||||
logger.LogDebug("Getting metadata for movie {Id}", idStr);
|
||||
int id = int.Parse(idStr, CultureInfo.InvariantCulture);
|
||||
using XtreamClient client = new();
|
||||
VodStreamInfo vod = await client.GetVodInfoAsync(Plugin.Instance.Creds, id, cancellationToken).ConfigureAwait(false);
|
||||
VodInfo? i = vod.Info;
|
||||
|
||||
if (i is null)
|
||||
{
|
||||
return ItemUpdateType.None;
|
||||
}
|
||||
|
||||
item.Overview ??= i.Plot;
|
||||
item.PremiereDate ??= i.ReleaseDate;
|
||||
item.RunTimeTicks ??= i.DurationSecs is not null ? TimeSpan.TicksPerSecond * i.DurationSecs : null;
|
||||
item.TotalBitrate ??= i.Bitrate;
|
||||
|
||||
if (i.Genre is string genres)
|
||||
{
|
||||
item.Genres ??= genres.Split(',').Select(genre => genre.Trim()).ToArray();
|
||||
}
|
||||
|
||||
if (!item.HasProviderId(MetadataProvider.Tmdb))
|
||||
{
|
||||
if (i.TmdbId is int tmdbId)
|
||||
{
|
||||
options.ReplaceAllMetadata = true;
|
||||
item.SetProviderId(MetadataProvider.Tmdb, tmdbId.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
else if (Plugin.Instance.Configuration.IsTmdbVodOverride)
|
||||
{
|
||||
MovieInfo queryInfo = new()
|
||||
{
|
||||
Name = StreamService.ParseName(vod.MovieData?.Name ?? string.Empty).Title,
|
||||
Year = item.PremiereDate?.Year,
|
||||
};
|
||||
// Try to fetch the TMDB id to get proper metadata.
|
||||
RemoteSearchQuery<MovieInfo> query = new()
|
||||
{
|
||||
SearchInfo = queryInfo,
|
||||
SearchProviderName = "TheMovieDb",
|
||||
};
|
||||
IEnumerable<RemoteSearchResult> results = await providerManager.GetRemoteSearchResults<Movie, MovieInfo>(query, cancellationToken).ConfigureAwait(false);
|
||||
if (results.Any())
|
||||
{
|
||||
RemoteSearchResult tmdbMovie = results.First();
|
||||
if (tmdbMovie.HasProviderId(MetadataProvider.Tmdb))
|
||||
{
|
||||
string? queryId = tmdbMovie.GetProviderId(MetadataProvider.Tmdb);
|
||||
if (queryId is not null)
|
||||
{
|
||||
options.ReplaceAllMetadata = true;
|
||||
item.SetProviderId(MetadataProvider.Tmdb, queryId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ItemUpdateType.MetadataImport;
|
||||
}
|
||||
}
|
@@ -35,7 +35,7 @@ namespace Jellyfin.Xtream;
|
||||
/// The Xtream Codes API channel.
|
||||
/// </summary>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
|
||||
public class SeriesChannel(ILogger<SeriesChannel> logger) : IChannel
|
||||
public class SeriesChannel(ILogger<SeriesChannel> logger) : IChannel, IDisableMediaSourceDisplay
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string? Name => "Xtream Series";
|
||||
@@ -198,20 +198,19 @@ public class SeriesChannel(ILogger<SeriesChannel> logger) : IChannel
|
||||
{
|
||||
Client.Models.SeriesInfo serie = series.Info;
|
||||
ParsedName parsedName = StreamService.ParseName(episode.Title);
|
||||
List<MediaSourceInfo> sources = [
|
||||
Plugin.Instance.StreamService.GetMediaSourceInfo(StreamType.Series, episode.EpisodeId, episode.ContainerExtension)
|
||||
List<MediaSourceInfo> sources =
|
||||
[
|
||||
Plugin.Instance.StreamService.GetMediaSourceInfo(
|
||||
StreamType.Series,
|
||||
episode.EpisodeId,
|
||||
episode.ContainerExtension,
|
||||
videoInfo: episode.Info?.Video,
|
||||
audioInfo: episode.Info?.Audio)
|
||||
];
|
||||
|
||||
string? cover = episode.Info?.MovieImage;
|
||||
if (string.IsNullOrEmpty(cover) && season != null)
|
||||
{
|
||||
cover = season.Cover;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(cover))
|
||||
{
|
||||
cover = serie.Cover;
|
||||
}
|
||||
cover ??= season?.Cover;
|
||||
cover ??= serie.Cover;
|
||||
|
||||
return new()
|
||||
{
|
||||
|
@@ -24,6 +24,7 @@ using Jellyfin.Xtream.Client;
|
||||
using Jellyfin.Xtream.Client.Models;
|
||||
using Jellyfin.Xtream.Configuration;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
@@ -367,6 +368,8 @@ public partial class StreamService
|
||||
/// <param name="restream">Boolean indicating whether or not restreaming is used.</param>
|
||||
/// <param name="start">The datetime representing the start time of catcup TV.</param>
|
||||
/// <param name="durationMinutes">The duration in minutes of the catcup TV stream.</param>
|
||||
/// <param name="videoInfo">The Xtream video info if known.</param>
|
||||
/// <param name="audioInfo">The Xtream audio info if known.</param>
|
||||
/// <returns>The media source info as <see cref="MediaSourceInfo"/> class.</returns>
|
||||
public MediaSourceInfo GetMediaSourceInfo(
|
||||
StreamType type,
|
||||
@@ -374,7 +377,9 @@ public partial class StreamService
|
||||
string? extension = null,
|
||||
bool restream = false,
|
||||
DateTime? start = null,
|
||||
int durationMinutes = 0)
|
||||
int durationMinutes = 0,
|
||||
VideoInfo? videoInfo = null,
|
||||
AudioInfo? audioInfo = null)
|
||||
{
|
||||
string prefix = string.Empty;
|
||||
switch (type)
|
||||
@@ -403,22 +408,41 @@ public partial class StreamService
|
||||
bool isLive = type == StreamType.Live;
|
||||
return new MediaSourceInfo()
|
||||
{
|
||||
Container = extension,
|
||||
EncoderProtocol = MediaProtocol.Http,
|
||||
Id = ToGuid(MediaSourcePrefix, (int)type, id, 0).ToString(),
|
||||
IsInfiniteStream = isLive,
|
||||
IsRemote = true,
|
||||
// Define media sources with unknown index and interlaced to improve compatibility.
|
||||
MediaStreams = [
|
||||
MediaStreams =
|
||||
[
|
||||
new()
|
||||
{
|
||||
AspectRatio = videoInfo?.AspectRatio,
|
||||
BitDepth = videoInfo?.BitsPerRawSample,
|
||||
Codec = videoInfo?.CodecName,
|
||||
ColorPrimaries = videoInfo?.ColorPrimaries,
|
||||
ColorRange = videoInfo?.ColorRange,
|
||||
ColorSpace = videoInfo?.ColorSpace,
|
||||
ColorTransfer = videoInfo?.ColorTransfer,
|
||||
Height = videoInfo?.Height,
|
||||
Index = videoInfo?.Index ?? -1,
|
||||
IsAVC = videoInfo?.IsAVC,
|
||||
IsInterlaced = true,
|
||||
Level = videoInfo?.Level,
|
||||
PixelFormat = videoInfo?.PixelFormat,
|
||||
Profile = videoInfo?.Profile,
|
||||
Type = MediaStreamType.Video,
|
||||
Index = -1,
|
||||
IsInterlaced = true
|
||||
},
|
||||
new()
|
||||
{
|
||||
BitRate = audioInfo?.Bitrate,
|
||||
ChannelLayout = audioInfo?.ChannelLayout,
|
||||
Channels = audioInfo?.Channels,
|
||||
Codec = audioInfo?.CodecName,
|
||||
Index = audioInfo?.Index ?? -1,
|
||||
Profile = audioInfo?.Profile,
|
||||
SampleRate = audioInfo?.SampleRate,
|
||||
Type = MediaStreamType.Audio,
|
||||
Index = -1
|
||||
}
|
||||
],
|
||||
Name = "default",
|
||||
|
@@ -20,6 +20,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Xtream.Client.Models;
|
||||
using Jellyfin.Xtream.Providers;
|
||||
using Jellyfin.Xtream.Service;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
@@ -34,7 +35,7 @@ namespace Jellyfin.Xtream;
|
||||
/// The Xtream Codes API channel.
|
||||
/// </summary>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
|
||||
public class VodChannel(ILogger<VodChannel> logger) : IChannel
|
||||
public class VodChannel(ILogger<VodChannel> logger) : IChannel, IDisableMediaSourceDisplay
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string? Name => "Xtream Video On-Demand";
|
||||
@@ -113,19 +114,23 @@ public class VodChannel(ILogger<VodChannel> logger) : IChannel
|
||||
}
|
||||
}
|
||||
|
||||
private ChannelItemInfo CreateChannelItemInfo(StreamInfo stream)
|
||||
private Task<ChannelItemInfo> CreateChannelItemInfo(StreamInfo stream)
|
||||
{
|
||||
long added = long.Parse(stream.Added, CultureInfo.InvariantCulture);
|
||||
ParsedName parsedName = StreamService.ParseName(stream.Name);
|
||||
List<MediaSourceInfo> sources = [
|
||||
Plugin.Instance.StreamService.GetMediaSourceInfo(StreamType.Vod, stream.StreamId, stream.ContainerExtension)
|
||||
|
||||
List<MediaSourceInfo> sources =
|
||||
[
|
||||
Plugin.Instance.StreamService.GetMediaSourceInfo(
|
||||
StreamType.Vod,
|
||||
stream.StreamId,
|
||||
stream.ContainerExtension)
|
||||
];
|
||||
|
||||
return new()
|
||||
ChannelItemInfo result = new ChannelItemInfo()
|
||||
{
|
||||
ContentType = ChannelMediaContentType.Movie,
|
||||
DateCreated = DateTimeOffset.FromUnixTimeSeconds(added).DateTime,
|
||||
FolderType = ChannelFolderType.Container,
|
||||
Id = $"{StreamService.StreamPrefix}{stream.StreamId}",
|
||||
ImageUrl = stream.StreamIcon,
|
||||
IsLiveStream = false,
|
||||
@@ -134,7 +139,10 @@ public class VodChannel(ILogger<VodChannel> logger) : IChannel
|
||||
Name = parsedName.Title,
|
||||
Tags = new List<string>(parsedName.Tags),
|
||||
Type = ChannelItemType.Media,
|
||||
ProviderIds = { { XtreamVodProvider.ProviderName, stream.StreamId.ToString(CultureInfo.InvariantCulture) } },
|
||||
};
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
private async Task<ChannelItemResult> GetCategories(CancellationToken cancellationToken)
|
||||
@@ -152,8 +160,8 @@ public class VodChannel(ILogger<VodChannel> logger) : IChannel
|
||||
private async Task<ChannelItemResult> GetStreams(int categoryId, CancellationToken cancellationToken)
|
||||
{
|
||||
IEnumerable<StreamInfo> streams = await Plugin.Instance.StreamService.GetVodStreams(categoryId, cancellationToken).ConfigureAwait(false);
|
||||
List<ChannelItemInfo> items = new List<ChannelItemInfo>(streams.Select(CreateChannelItemInfo));
|
||||
ChannelItemResult result = new()
|
||||
List<ChannelItemInfo> items = [.. await Task.WhenAll(streams.Select(CreateChannelItemInfo)).ConfigureAwait(false)];
|
||||
ChannelItemResult result = new ChannelItemResult()
|
||||
{
|
||||
Items = items,
|
||||
TotalRecordCount = items.Count
|
||||
|
@@ -63,7 +63,7 @@
|
||||
<!-- disable warning CA1040: Avoid empty interfaces -->
|
||||
<Rule Id="CA1040" Action="Info" />
|
||||
<!-- disable warning CA1062: Validate arguments of public methods -->
|
||||
<Rule Id="CA1062" Action="Info" />
|
||||
<Rule Id="CA1062" Action="None" />
|
||||
<!-- TODO: enable when false positives are fixed -->
|
||||
<!-- disable warning CA1508: Avoid dead conditional code -->
|
||||
<Rule Id="CA1508" Action="Info" />
|
||||
|
Reference in New Issue
Block a user