mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-02 14:56:31 +01:00
Merge branch 'master' into TVFix
This commit is contained in:
@@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
return targetFile;
|
||||
}
|
||||
|
||||
public Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
|
||||
public Task Record(IDirectStreamProvider? directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
|
||||
{
|
||||
if (directStreamProvider != null)
|
||||
{
|
||||
@@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
private async Task RecordFromDirectStreamProvider(IDirectStreamProvider directStreamProvider, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
||||
|
||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||
using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
@@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
_logger.LogInformation("Opened recording stream from tuner provider");
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
||||
|
||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||
await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -157,8 +159,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
try
|
||||
{
|
||||
var recordingFolders = GetRecordingFolders().ToArray();
|
||||
var virtualFolders = _libraryManager.GetVirtualFolders()
|
||||
.ToList();
|
||||
var virtualFolders = _libraryManager.GetVirtualFolders();
|
||||
|
||||
var allExistingPaths = virtualFolders.SelectMany(i => i.Locations).ToList();
|
||||
|
||||
@@ -175,7 +176,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
continue;
|
||||
}
|
||||
|
||||
var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo { Path = i }).ToArray();
|
||||
var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo(i)).ToArray();
|
||||
|
||||
var libraryOptions = new LibraryOptions
|
||||
{
|
||||
@@ -208,7 +209,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
foreach (var path in pathsToRemove)
|
||||
{
|
||||
await RemovePathFromLibrary(path).ConfigureAwait(false);
|
||||
await RemovePathFromLibraryAsync(path).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -217,13 +218,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RemovePathFromLibrary(string path)
|
||||
private async Task RemovePathFromLibraryAsync(string path)
|
||||
{
|
||||
_logger.LogDebug("Removing path from library: {0}", path);
|
||||
|
||||
var requiresRefresh = false;
|
||||
var virtualFolders = _libraryManager.GetVirtualFolders()
|
||||
.ToList();
|
||||
var virtualFolders = _libraryManager.GetVirtualFolders();
|
||||
|
||||
foreach (var virtualFolder in virtualFolders)
|
||||
{
|
||||
@@ -458,7 +458,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
if (!string.IsNullOrWhiteSpace(tunerChannel.TunerChannelId))
|
||||
{
|
||||
var tunerChannelId = tunerChannel.TunerChannelId;
|
||||
if (tunerChannelId.IndexOf(".json.schedulesdirect.org", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
if (tunerChannelId.Contains(".json.schedulesdirect.org", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tunerChannelId = tunerChannelId.Replace(".json.schedulesdirect.org", string.Empty, StringComparison.OrdinalIgnoreCase).TrimStart('I');
|
||||
}
|
||||
@@ -610,16 +610,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> CreateTimer(TimerInfo timer, CancellationToken cancellationToken)
|
||||
public Task<string> CreateTimer(TimerInfo info, CancellationToken cancellationToken)
|
||||
{
|
||||
var existingTimer = string.IsNullOrWhiteSpace(timer.ProgramId) ?
|
||||
var existingTimer = string.IsNullOrWhiteSpace(info.ProgramId) ?
|
||||
null :
|
||||
_timerProvider.GetTimerByProgramId(timer.ProgramId);
|
||||
_timerProvider.GetTimerByProgramId(info.ProgramId);
|
||||
|
||||
if (existingTimer != null)
|
||||
{
|
||||
if (existingTimer.Status == RecordingStatus.Cancelled ||
|
||||
existingTimer.Status == RecordingStatus.Completed)
|
||||
if (existingTimer.Status == RecordingStatus.Cancelled
|
||||
|| existingTimer.Status == RecordingStatus.Completed)
|
||||
{
|
||||
existingTimer.Status = RecordingStatus.New;
|
||||
existingTimer.IsManual = true;
|
||||
@@ -632,32 +632,32 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
}
|
||||
}
|
||||
|
||||
timer.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
|
||||
info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
|
||||
|
||||
LiveTvProgram programInfo = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(timer.ProgramId))
|
||||
if (!string.IsNullOrWhiteSpace(info.ProgramId))
|
||||
{
|
||||
programInfo = GetProgramInfoFromCache(timer);
|
||||
programInfo = GetProgramInfoFromCache(info);
|
||||
}
|
||||
|
||||
if (programInfo == null)
|
||||
{
|
||||
_logger.LogInformation("Unable to find program with Id {0}. Will search using start date", timer.ProgramId);
|
||||
programInfo = GetProgramInfoFromCache(timer.ChannelId, timer.StartDate);
|
||||
_logger.LogInformation("Unable to find program with Id {0}. Will search using start date", info.ProgramId);
|
||||
programInfo = GetProgramInfoFromCache(info.ChannelId, info.StartDate);
|
||||
}
|
||||
|
||||
if (programInfo != null)
|
||||
{
|
||||
CopyProgramInfoToTimerInfo(programInfo, timer);
|
||||
CopyProgramInfoToTimerInfo(programInfo, info);
|
||||
}
|
||||
|
||||
timer.IsManual = true;
|
||||
_timerProvider.Add(timer);
|
||||
info.IsManual = true;
|
||||
_timerProvider.Add(info);
|
||||
|
||||
TimerCreated?.Invoke(this, new GenericEventArgs<TimerInfo>(timer));
|
||||
TimerCreated?.Invoke(this, new GenericEventArgs<TimerInfo>(info));
|
||||
|
||||
return Task.FromResult(timer.Id);
|
||||
return Task.FromResult(info.Id);
|
||||
}
|
||||
|
||||
public async Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken)
|
||||
@@ -801,22 +801,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
public ActiveRecordingInfo GetActiveRecordingInfo(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
if (string.IsNullOrWhiteSpace(path) || _activeRecordings.IsEmpty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var recording in _activeRecordings.Values)
|
||||
foreach (var (_, recordingInfo) in _activeRecordings)
|
||||
{
|
||||
if (string.Equals(recording.Path, path, StringComparison.Ordinal) && !recording.CancellationTokenSource.IsCancellationRequested)
|
||||
if (string.Equals(recordingInfo.Path, path, StringComparison.Ordinal) && !recordingInfo.CancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
var timer = recording.Timer;
|
||||
var timer = recordingInfo.Timer;
|
||||
if (timer.Status != RecordingStatus.InProgress)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return recording;
|
||||
return recordingInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -911,18 +911,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
var epgChannel = await GetEpgChannelFromTunerChannel(provider.Item1, provider.Item2, channel, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
List<ProgramInfo> programs;
|
||||
|
||||
if (epgChannel == null)
|
||||
{
|
||||
_logger.LogDebug("EPG channel not found for tuner channel {0}-{1} from {2}-{3}", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty);
|
||||
programs = new List<ProgramInfo>();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
programs = (await provider.Item1.GetProgramsAsync(provider.Item2, epgChannel.Id, startDateUtc, endDateUtc, cancellationToken)
|
||||
|
||||
List<ProgramInfo> programs = (await provider.Item1.GetProgramsAsync(provider.Item2, epgChannel.Id, startDateUtc, endDateUtc, cancellationToken)
|
||||
.ConfigureAwait(false)).ToList();
|
||||
}
|
||||
|
||||
// Replace the value that came from the provider with a normalized value
|
||||
foreach (var program in programs)
|
||||
@@ -938,7 +934,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
}
|
||||
}
|
||||
|
||||
return new List<ProgramInfo>();
|
||||
return Enumerable.Empty<ProgramInfo>();
|
||||
}
|
||||
|
||||
private List<Tuple<IListingsProvider, ListingsProviderInfo>> GetListingProviders()
|
||||
@@ -1290,7 +1286,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
_logger.LogInformation("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
_logger.LogInformation("Writing file to path: " + recordPath);
|
||||
_logger.LogInformation("Writing file to: {Path}", recordPath);
|
||||
|
||||
Action onStarted = async () =>
|
||||
{
|
||||
@@ -1415,13 +1411,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
private void TriggerRefresh(string path)
|
||||
{
|
||||
_logger.LogInformation("Triggering refresh on {path}", path);
|
||||
_logger.LogInformation("Triggering refresh on {Path}", path);
|
||||
|
||||
var item = GetAffectedBaseItem(Path.GetDirectoryName(path));
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
_logger.LogInformation("Refreshing recording parent {path}", item.Path);
|
||||
_logger.LogInformation("Refreshing recording parent {Path}", item.Path);
|
||||
|
||||
_providerManager.QueueRefresh(
|
||||
item.Id,
|
||||
@@ -1456,7 +1452,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
if (item.GetType() == typeof(Folder) && string.Equals(item.Path, parentPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var parentItem = item.GetParent();
|
||||
if (parentItem != null && !(parentItem is AggregateFolder))
|
||||
if (parentItem != null && parentItem is not AggregateFolder)
|
||||
{
|
||||
item = parentItem;
|
||||
}
|
||||
@@ -1510,8 +1506,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
DeleteLibraryItemsForTimers(timersToDelete);
|
||||
|
||||
var librarySeries = _libraryManager.FindByPath(seriesPath, true) as Folder;
|
||||
if (librarySeries == null)
|
||||
if (_libraryManager.FindByPath(seriesPath, true) is not Folder librarySeries)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1621,9 +1616,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
}
|
||||
|
||||
return _activeRecordings
|
||||
.Values
|
||||
.ToList()
|
||||
.Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
||||
.Any(i => string.Equals(i.Value.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Value.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private IRecorder GetRecorder(MediaSourceInfo mediaSource)
|
||||
@@ -1667,7 +1660,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
_logger.LogInformation("Running recording post processor {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
||||
|
||||
process.Exited += Process_Exited;
|
||||
process.Exited += OnProcessExited;
|
||||
process.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1681,7 +1674,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
return arguments.Replace("{path}", path, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private void Process_Exited(object sender, EventArgs e)
|
||||
private void OnProcessExited(object sender, EventArgs e)
|
||||
{
|
||||
using (var process = (Process)sender)
|
||||
{
|
||||
@@ -2239,14 +2232,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
var enabledTimersForSeries = new List<TimerInfo>();
|
||||
foreach (var timer in allTimers)
|
||||
{
|
||||
var existingTimer = _timerProvider.GetTimer(timer.Id);
|
||||
|
||||
if (existingTimer == null)
|
||||
{
|
||||
existingTimer = string.IsNullOrWhiteSpace(timer.ProgramId)
|
||||
var existingTimer = _timerProvider.GetTimer(timer.Id)
|
||||
?? (string.IsNullOrWhiteSpace(timer.ProgramId)
|
||||
? null
|
||||
: _timerProvider.GetTimerByProgramId(timer.ProgramId);
|
||||
}
|
||||
: _timerProvider.GetTimerByProgramId(timer.ProgramId));
|
||||
|
||||
if (existingTimer == null)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -9,8 +11,9 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
@@ -307,22 +310,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
{
|
||||
using (var reader = new StreamReader(source))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
|
||||
{
|
||||
var line = await reader.ReadLineAsync().ConfigureAwait(false);
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
|
||||
|
||||
await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||
await target.WriteAsync(bytes.AsMemory()).ConfigureAwait(false);
|
||||
await target.FlushAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// TODO Investigate and properly fix.
|
||||
// Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error reading ffmpeg recording log");
|
||||
|
||||
@@ -6,10 +6,8 @@ using MediaBrowser.Controller.LiveTv;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
{
|
||||
|
||||
internal class EpgChannelData
|
||||
{
|
||||
|
||||
private readonly Dictionary<string, ChannelInfo> _channelsById;
|
||||
|
||||
private readonly Dictionary<string, ChannelInfo> _channelsByNumber;
|
||||
@@ -39,13 +37,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
}
|
||||
}
|
||||
|
||||
public ChannelInfo GetChannelById(string id)
|
||||
public ChannelInfo? GetChannelById(string id)
|
||||
=> _channelsById.GetValueOrDefault(id);
|
||||
|
||||
public ChannelInfo GetChannelByNumber(string number)
|
||||
public ChannelInfo? GetChannelByNumber(string number)
|
||||
=> _channelsByNumber.GetValueOrDefault(number);
|
||||
|
||||
public ChannelInfo GetChannelByName(string name)
|
||||
public ChannelInfo? GetChannelByName(string name)
|
||||
=> _channelsByName.GetValueOrDefault(name);
|
||||
|
||||
public static string NormalizeName(string value)
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
/// <summary>
|
||||
/// Records the specified media source.
|
||||
/// </summary>
|
||||
Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
|
||||
Task Record(IDirectStreamProvider? directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
|
||||
|
||||
string GetOutputPath(MediaSourceInfo mediaSource, string targetFile);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -5,7 +7,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
@@ -6,7 +6,7 @@ using MediaBrowser.Controller.LiveTv;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
{
|
||||
internal class RecordingHelper
|
||||
internal static class RecordingHelper
|
||||
{
|
||||
public static DateTime GetStartTime(TimerInfo timer)
|
||||
{
|
||||
@@ -70,17 +70,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
private static string GetDateString(DateTime date)
|
||||
{
|
||||
date = date.ToLocalTime();
|
||||
|
||||
return string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0}_{1}_{2}_{3}_{4}_{5}",
|
||||
date.Year.ToString("0000", CultureInfo.InvariantCulture),
|
||||
date.Month.ToString("00", CultureInfo.InvariantCulture),
|
||||
date.Day.ToString("00", CultureInfo.InvariantCulture),
|
||||
date.Hour.ToString("00", CultureInfo.InvariantCulture),
|
||||
date.Minute.ToString("00", CultureInfo.InvariantCulture),
|
||||
date.Second.ToString("00", CultureInfo.InvariantCulture));
|
||||
return date.ToLocalTime().ToString("yyyy_MM_dd_HH_mm_ss", CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
{
|
||||
}
|
||||
|
||||
public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
|
||||
public event EventHandler<GenericEventArgs<TimerInfo>>? TimerFired;
|
||||
|
||||
public void RestartTimers()
|
||||
{
|
||||
@@ -143,9 +143,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
}
|
||||
}
|
||||
|
||||
private void TimerCallback(object state)
|
||||
private void TimerCallback(object? state)
|
||||
{
|
||||
var timerId = (string)state;
|
||||
var timerId = (string?)state ?? throw new ArgumentNullException(nameof(state));
|
||||
|
||||
var timer = GetAll().FirstOrDefault(i => string.Equals(i.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
||||
if (timer != null)
|
||||
@@ -154,12 +154,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
}
|
||||
}
|
||||
|
||||
public TimerInfo GetTimer(string id)
|
||||
public TimerInfo? GetTimer(string id)
|
||||
{
|
||||
return GetAll().FirstOrDefault(r => string.Equals(r.Id, id, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public TimerInfo GetTimerByProgramId(string programId)
|
||||
public TimerInfo? GetTimerByProgramId(string programId)
|
||||
{
|
||||
return GetAll().FirstOrDefault(r => string.Equals(r.ProgramId, programId, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -12,8 +14,9 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
@@ -94,12 +97,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
var dates = GetScheduleRequestDates(startDateUtc, endDateUtc);
|
||||
|
||||
_logger.LogInformation("Channel Station ID is: {ChannelID}", channelId);
|
||||
var requestList = new List<ScheduleDirect.RequestScheduleForChannel>()
|
||||
var requestList = new List<RequestScheduleForChannelDto>()
|
||||
{
|
||||
new ScheduleDirect.RequestScheduleForChannel()
|
||||
new RequestScheduleForChannelDto()
|
||||
{
|
||||
stationID = channelId,
|
||||
date = dates
|
||||
StationId = channelId,
|
||||
Date = dates
|
||||
}
|
||||
};
|
||||
|
||||
@@ -111,61 +114,61 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
options.Headers.TryAddWithoutValidation("token", token);
|
||||
using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
|
||||
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
var dailySchedules = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Day>>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
var dailySchedules = await JsonSerializer.DeserializeAsync<List<DayDto>>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
_logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
|
||||
|
||||
using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
|
||||
programRequestOptions.Headers.TryAddWithoutValidation("token", token);
|
||||
|
||||
var programsID = dailySchedules.SelectMany(d => d.programs.Select(s => s.programID)).Distinct();
|
||||
var programsID = dailySchedules.SelectMany(d => d.Programs.Select(s => s.ProgramId)).Distinct();
|
||||
programRequestOptions.Content = new StringContent("[\"" + string.Join("\", \"", programsID) + "\"]", Encoding.UTF8, MediaTypeNames.Application.Json);
|
||||
|
||||
using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
|
||||
await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
var programDetails = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ProgramDetails>>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
var programDict = programDetails.ToDictionary(p => p.programID, y => y);
|
||||
var programDetails = await JsonSerializer.DeserializeAsync<List<ProgramDetailsDto>>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
var programDict = programDetails.ToDictionary(p => p.ProgramId, y => y);
|
||||
|
||||
var programIdsWithImages = programDetails
|
||||
.Where(p => p.hasImageArtwork).Select(p => p.programID)
|
||||
.Where(p => p.HasImageArtwork).Select(p => p.ProgramId)
|
||||
.ToList();
|
||||
|
||||
var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var programsInfo = new List<ProgramInfo>();
|
||||
foreach (ScheduleDirect.Program schedule in dailySchedules.SelectMany(d => d.programs))
|
||||
foreach (ProgramDto schedule in dailySchedules.SelectMany(d => d.Programs))
|
||||
{
|
||||
// _logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
|
||||
// " which corresponds to channel " + channelNumber + " and program id " +
|
||||
// schedule.programID + " which says it has images? " +
|
||||
// programDict[schedule.programID].hasImageArtwork);
|
||||
// schedule.ProgramId + " which says it has images? " +
|
||||
// programDict[schedule.ProgramId].hasImageArtwork);
|
||||
|
||||
if (images != null)
|
||||
{
|
||||
var imageIndex = images.FindIndex(i => i.programID == schedule.programID.Substring(0, 10));
|
||||
var imageIndex = images.FindIndex(i => i.ProgramId == schedule.ProgramId[..10]);
|
||||
if (imageIndex > -1)
|
||||
{
|
||||
var programEntry = programDict[schedule.programID];
|
||||
var programEntry = programDict[schedule.ProgramId];
|
||||
|
||||
var allImages = images[imageIndex].data ?? new List<ScheduleDirect.ImageData>();
|
||||
var imagesWithText = allImages.Where(i => string.Equals(i.text, "yes", StringComparison.OrdinalIgnoreCase));
|
||||
var imagesWithoutText = allImages.Where(i => string.Equals(i.text, "no", StringComparison.OrdinalIgnoreCase));
|
||||
var allImages = images[imageIndex].Data ?? new List<ImageDataDto>();
|
||||
var imagesWithText = allImages.Where(i => string.Equals(i.Text, "yes", StringComparison.OrdinalIgnoreCase));
|
||||
var imagesWithoutText = allImages.Where(i => string.Equals(i.Text, "no", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
const double DesiredAspect = 2.0 / 3;
|
||||
|
||||
programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
|
||||
programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
|
||||
GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
|
||||
|
||||
const double WideAspect = 16.0 / 9;
|
||||
|
||||
programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
|
||||
programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
|
||||
|
||||
// Don't supply the same image twice
|
||||
if (string.Equals(programEntry.primaryImage, programEntry.thumbImage, StringComparison.Ordinal))
|
||||
if (string.Equals(programEntry.PrimaryImage, programEntry.ThumbImage, StringComparison.Ordinal))
|
||||
{
|
||||
programEntry.thumbImage = null;
|
||||
programEntry.ThumbImage = null;
|
||||
}
|
||||
|
||||
programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
|
||||
programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
|
||||
|
||||
// programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
|
||||
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
|
||||
@@ -174,15 +177,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
}
|
||||
}
|
||||
|
||||
programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.programID]));
|
||||
programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.ProgramId]));
|
||||
}
|
||||
|
||||
return programsInfo;
|
||||
}
|
||||
|
||||
private static int GetSizeOrder(ScheduleDirect.ImageData image)
|
||||
private static int GetSizeOrder(ImageDataDto image)
|
||||
{
|
||||
if (int.TryParse(image.height, out int value))
|
||||
if (int.TryParse(image.Height, out int value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
@@ -190,53 +193,53 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static string GetChannelNumber(ScheduleDirect.Map map)
|
||||
private static string GetChannelNumber(MapDto map)
|
||||
{
|
||||
var channelNumber = map.logicalChannelNumber;
|
||||
var channelNumber = map.LogicalChannelNumber;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(channelNumber))
|
||||
{
|
||||
channelNumber = map.channel;
|
||||
channelNumber = map.Channel;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(channelNumber))
|
||||
{
|
||||
channelNumber = map.atscMajor + "." + map.atscMinor;
|
||||
channelNumber = map.AtscMajor + "." + map.AtscMinor;
|
||||
}
|
||||
|
||||
return channelNumber.TrimStart('0');
|
||||
}
|
||||
|
||||
private static bool IsMovie(ScheduleDirect.ProgramDetails programInfo)
|
||||
private static bool IsMovie(ProgramDetailsDto programInfo)
|
||||
{
|
||||
return string.Equals(programInfo.entityType, "movie", StringComparison.OrdinalIgnoreCase);
|
||||
return string.Equals(programInfo.EntityType, "movie", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private ProgramInfo GetProgram(string channelId, ScheduleDirect.Program programInfo, ScheduleDirect.ProgramDetails details)
|
||||
private ProgramInfo GetProgram(string channelId, ProgramDto programInfo, ProgramDetailsDto details)
|
||||
{
|
||||
var startAt = GetDate(programInfo.airDateTime);
|
||||
var endAt = startAt.AddSeconds(programInfo.duration);
|
||||
var startAt = GetDate(programInfo.AirDateTime);
|
||||
var endAt = startAt.AddSeconds(programInfo.Duration);
|
||||
var audioType = ProgramAudio.Stereo;
|
||||
|
||||
var programId = programInfo.programID ?? string.Empty;
|
||||
var programId = programInfo.ProgramId ?? string.Empty;
|
||||
|
||||
string newID = programId + "T" + startAt.Ticks + "C" + channelId;
|
||||
|
||||
if (programInfo.audioProperties != null)
|
||||
if (programInfo.AudioProperties != null)
|
||||
{
|
||||
if (programInfo.audioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
|
||||
if (programInfo.AudioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
audioType = ProgramAudio.Atmos;
|
||||
}
|
||||
else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
|
||||
else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
audioType = ProgramAudio.DolbyDigital;
|
||||
}
|
||||
else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd", StringComparison.OrdinalIgnoreCase)))
|
||||
else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "dd", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
audioType = ProgramAudio.DolbyDigital;
|
||||
}
|
||||
else if (programInfo.audioProperties.Exists(item => string.Equals(item, "stereo", StringComparison.OrdinalIgnoreCase)))
|
||||
else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "stereo", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
audioType = ProgramAudio.Stereo;
|
||||
}
|
||||
@@ -247,9 +250,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
}
|
||||
|
||||
string episodeTitle = null;
|
||||
if (details.episodeTitle150 != null)
|
||||
if (details.EpisodeTitle150 != null)
|
||||
{
|
||||
episodeTitle = details.episodeTitle150;
|
||||
episodeTitle = details.EpisodeTitle150;
|
||||
}
|
||||
|
||||
var info = new ProgramInfo
|
||||
@@ -258,22 +261,22 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
Id = newID,
|
||||
StartDate = startAt,
|
||||
EndDate = endAt,
|
||||
Name = details.titles[0].title120 ?? "Unknown",
|
||||
Name = details.Titles[0].Title120 ?? "Unknown",
|
||||
OfficialRating = null,
|
||||
CommunityRating = null,
|
||||
EpisodeTitle = episodeTitle,
|
||||
Audio = audioType,
|
||||
// IsNew = programInfo.@new ?? false,
|
||||
IsRepeat = programInfo.@new == null,
|
||||
IsSeries = string.Equals(details.entityType, "episode", StringComparison.OrdinalIgnoreCase),
|
||||
ImageUrl = details.primaryImage,
|
||||
ThumbImageUrl = details.thumbImage,
|
||||
IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase),
|
||||
IsSports = string.Equals(details.entityType, "sports", StringComparison.OrdinalIgnoreCase),
|
||||
IsRepeat = programInfo.New == null,
|
||||
IsSeries = string.Equals(details.EntityType, "episode", StringComparison.OrdinalIgnoreCase),
|
||||
ImageUrl = details.PrimaryImage,
|
||||
ThumbImageUrl = details.ThumbImage,
|
||||
IsKids = string.Equals(details.Audience, "children", StringComparison.OrdinalIgnoreCase),
|
||||
IsSports = string.Equals(details.EntityType, "sports", StringComparison.OrdinalIgnoreCase),
|
||||
IsMovie = IsMovie(details),
|
||||
Etag = programInfo.md5,
|
||||
IsLive = string.Equals(programInfo.liveTapeDelay, "live", StringComparison.OrdinalIgnoreCase),
|
||||
IsPremiere = programInfo.premiere || (programInfo.isPremiereOrFinale ?? string.Empty).IndexOf("premiere", StringComparison.OrdinalIgnoreCase) != -1
|
||||
Etag = programInfo.Md5,
|
||||
IsLive = string.Equals(programInfo.LiveTapeDelay, "live", StringComparison.OrdinalIgnoreCase),
|
||||
IsPremiere = programInfo.Premiere || (programInfo.IsPremiereOrFinale ?? string.Empty).IndexOf("premiere", StringComparison.OrdinalIgnoreCase) != -1
|
||||
};
|
||||
|
||||
var showId = programId;
|
||||
@@ -296,15 +299,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
|
||||
info.ShowId = showId;
|
||||
|
||||
if (programInfo.videoProperties != null)
|
||||
if (programInfo.VideoProperties != null)
|
||||
{
|
||||
info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
|
||||
info.Is3D = programInfo.videoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
|
||||
info.IsHD = programInfo.VideoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
|
||||
info.Is3D = programInfo.VideoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (details.contentRating != null && details.contentRating.Count > 0)
|
||||
if (details.ContentRating != null && details.ContentRating.Count > 0)
|
||||
{
|
||||
info.OfficialRating = details.contentRating[0].code.Replace("TV", "TV-", StringComparison.Ordinal)
|
||||
info.OfficialRating = details.ContentRating[0].Code.Replace("TV", "TV-", StringComparison.Ordinal)
|
||||
.Replace("--", "-", StringComparison.Ordinal);
|
||||
|
||||
var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" };
|
||||
@@ -314,15 +317,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
}
|
||||
}
|
||||
|
||||
if (details.descriptions != null)
|
||||
if (details.Descriptions != null)
|
||||
{
|
||||
if (details.descriptions.description1000 != null && details.descriptions.description1000.Count > 0)
|
||||
if (details.Descriptions.Description1000 != null && details.Descriptions.Description1000.Count > 0)
|
||||
{
|
||||
info.Overview = details.descriptions.description1000[0].description;
|
||||
info.Overview = details.Descriptions.Description1000[0].Description;
|
||||
}
|
||||
else if (details.descriptions.description100 != null && details.descriptions.description100.Count > 0)
|
||||
else if (details.Descriptions.Description100 != null && details.Descriptions.Description100.Count > 0)
|
||||
{
|
||||
info.Overview = details.descriptions.description100[0].description;
|
||||
info.Overview = details.Descriptions.Description100[0].Description;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,18 +335,18 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
|
||||
info.SeriesProviderIds[MetadataProvider.Zap2It.ToString()] = info.SeriesId;
|
||||
|
||||
if (details.metadata != null)
|
||||
if (details.Metadata != null)
|
||||
{
|
||||
foreach (var metadataProgram in details.metadata)
|
||||
foreach (var metadataProgram in details.Metadata)
|
||||
{
|
||||
var gracenote = metadataProgram.Gracenote;
|
||||
if (gracenote != null)
|
||||
{
|
||||
info.SeasonNumber = gracenote.season;
|
||||
info.SeasonNumber = gracenote.Season;
|
||||
|
||||
if (gracenote.episode > 0)
|
||||
if (gracenote.Episode > 0)
|
||||
{
|
||||
info.EpisodeNumber = gracenote.episode;
|
||||
info.EpisodeNumber = gracenote.Episode;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -352,25 +355,25 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(details.originalAirDate))
|
||||
if (!string.IsNullOrWhiteSpace(details.OriginalAirDate))
|
||||
{
|
||||
info.OriginalAirDate = DateTime.Parse(details.originalAirDate, CultureInfo.InvariantCulture);
|
||||
info.OriginalAirDate = DateTime.Parse(details.OriginalAirDate, CultureInfo.InvariantCulture);
|
||||
info.ProductionYear = info.OriginalAirDate.Value.Year;
|
||||
}
|
||||
|
||||
if (details.movie != null)
|
||||
if (details.Movie != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(details.movie.year)
|
||||
&& int.TryParse(details.movie.year, out int year))
|
||||
if (!string.IsNullOrEmpty(details.Movie.Year)
|
||||
&& int.TryParse(details.Movie.Year, out int year))
|
||||
{
|
||||
info.ProductionYear = year;
|
||||
}
|
||||
}
|
||||
|
||||
if (details.genres != null)
|
||||
if (details.Genres != null)
|
||||
{
|
||||
info.Genres = details.genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
|
||||
info.IsNews = details.genres.Contains("news", StringComparer.OrdinalIgnoreCase);
|
||||
info.Genres = details.Genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
|
||||
info.IsNews = details.Genres.Contains("news", StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (info.Genres.Contains("children", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
@@ -393,11 +396,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
return date;
|
||||
}
|
||||
|
||||
private string GetProgramImage(string apiUrl, IEnumerable<ScheduleDirect.ImageData> images, bool returnDefaultImage, double desiredAspect)
|
||||
private string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, bool returnDefaultImage, double desiredAspect)
|
||||
{
|
||||
var match = images
|
||||
.OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i)))
|
||||
.ThenByDescending(GetSizeOrder)
|
||||
.ThenByDescending(i => GetSizeOrder(i))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (match == null)
|
||||
@@ -405,7 +408,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
return null;
|
||||
}
|
||||
|
||||
var uri = match.uri;
|
||||
var uri = match.Uri;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(uri))
|
||||
{
|
||||
@@ -421,19 +424,19 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
}
|
||||
}
|
||||
|
||||
private static double GetAspectRatio(ScheduleDirect.ImageData i)
|
||||
private static double GetAspectRatio(ImageDataDto i)
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(i.width))
|
||||
if (!string.IsNullOrWhiteSpace(i.Width))
|
||||
{
|
||||
int.TryParse(i.width, out width);
|
||||
_ = int.TryParse(i.Width, out width);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(i.height))
|
||||
if (!string.IsNullOrWhiteSpace(i.Height))
|
||||
{
|
||||
int.TryParse(i.height, out height);
|
||||
_ = int.TryParse(i.Height, out height);
|
||||
}
|
||||
|
||||
if (height == 0 || width == 0)
|
||||
@@ -446,14 +449,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(
|
||||
private async Task<List<ShowImagesDto>> GetImageForPrograms(
|
||||
ListingsProviderInfo info,
|
||||
IReadOnlyList<string> programIds,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (programIds.Count == 0)
|
||||
{
|
||||
return new List<ScheduleDirect.ShowImages>();
|
||||
return new List<ShowImagesDto>();
|
||||
}
|
||||
|
||||
StringBuilder str = new StringBuilder("[", 1 + (programIds.Count * 13));
|
||||
@@ -477,13 +480,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
{
|
||||
using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
|
||||
await using var response = await innerResponse2.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
return await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ShowImages>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
return await JsonSerializer.DeserializeAsync<List<ShowImagesDto>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting image info from schedules direct");
|
||||
|
||||
return new List<ScheduleDirect.ShowImages>();
|
||||
return new List<ShowImagesDto>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,18 +509,18 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false);
|
||||
await using var response = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var root = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Headends>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
var root = await JsonSerializer.DeserializeAsync<List<HeadendsDto>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (root != null)
|
||||
{
|
||||
foreach (ScheduleDirect.Headends headend in root)
|
||||
foreach (HeadendsDto headend in root)
|
||||
{
|
||||
foreach (ScheduleDirect.Lineup lineup in headend.lineups)
|
||||
foreach (LineupDto lineup in headend.Lineups)
|
||||
{
|
||||
lineups.Add(new NameIdPair
|
||||
{
|
||||
Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
|
||||
Id = lineup.uri.Substring(18)
|
||||
Name = string.IsNullOrWhiteSpace(lineup.Name) ? lineup.Lineup : lineup.Name,
|
||||
Id = lineup.Uri[18..]
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -647,14 +650,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Token>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
if (string.Equals(root.message, "OK", StringComparison.Ordinal))
|
||||
var root = await JsonSerializer.DeserializeAsync<TokenDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
if (string.Equals(root.Message, "OK", StringComparison.Ordinal))
|
||||
{
|
||||
_logger.LogInformation("Authenticated with Schedules Direct token: " + root.token);
|
||||
return root.token;
|
||||
_logger.LogInformation("Authenticated with Schedules Direct token: {Token}", root.Token);
|
||||
return root.Token;
|
||||
}
|
||||
|
||||
throw new Exception("Could not authenticate with Schedules Direct Error: " + root.message);
|
||||
throw new Exception("Could not authenticate with Schedules Direct Error: " + root.Message);
|
||||
}
|
||||
|
||||
private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken)
|
||||
@@ -703,9 +706,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
httpResponse.EnsureSuccessStatusCode();
|
||||
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
using var response = httpResponse.Content;
|
||||
var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Lineups>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
var root = await JsonSerializer.DeserializeAsync<LineupsDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
|
||||
return root.Lineups.Any(i => string.Equals(info.ListingsId, i.Lineup, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
@@ -775,38 +778,35 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
|
||||
using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
|
||||
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Channel>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
_logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
|
||||
var root = await JsonSerializer.DeserializeAsync<ChannelDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
_logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.Map.Count);
|
||||
_logger.LogInformation("Mapping Stations to Channel");
|
||||
|
||||
var allStations = root.stations ?? new List<ScheduleDirect.Station>();
|
||||
var allStations = root.Stations ?? new List<StationDto>();
|
||||
|
||||
var map = root.map;
|
||||
var map = root.Map;
|
||||
var list = new List<ChannelInfo>(map.Count);
|
||||
foreach (var channel in map)
|
||||
{
|
||||
var channelNumber = GetChannelNumber(channel);
|
||||
|
||||
var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase));
|
||||
if (station == null)
|
||||
{
|
||||
station = new ScheduleDirect.Station
|
||||
var station = allStations.Find(item => string.Equals(item.StationId, channel.StationId, StringComparison.OrdinalIgnoreCase))
|
||||
?? new StationDto
|
||||
{
|
||||
stationID = channel.stationID
|
||||
StationId = channel.StationId
|
||||
};
|
||||
}
|
||||
|
||||
var channelInfo = new ChannelInfo
|
||||
{
|
||||
Id = station.stationID,
|
||||
CallSign = station.callsign,
|
||||
Id = station.StationId,
|
||||
CallSign = station.Callsign,
|
||||
Number = channelNumber,
|
||||
Name = string.IsNullOrWhiteSpace(station.name) ? channelNumber : station.name
|
||||
Name = string.IsNullOrWhiteSpace(station.Name) ? channelNumber : station.Name
|
||||
};
|
||||
|
||||
if (station.logo != null)
|
||||
if (station.Logo != null)
|
||||
{
|
||||
channelInfo.ImageUrl = station.logo.URL;
|
||||
channelInfo.ImageUrl = station.Logo.Url;
|
||||
}
|
||||
|
||||
list.Add(channelInfo);
|
||||
@@ -819,402 +819,5 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
{
|
||||
return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public class ScheduleDirect
|
||||
{
|
||||
public class Token
|
||||
{
|
||||
public int code { get; set; }
|
||||
|
||||
public string message { get; set; }
|
||||
|
||||
public string serverID { get; set; }
|
||||
|
||||
public string token { get; set; }
|
||||
}
|
||||
|
||||
public class Lineup
|
||||
{
|
||||
public string lineup { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
public string transport { get; set; }
|
||||
|
||||
public string location { get; set; }
|
||||
|
||||
public string uri { get; set; }
|
||||
}
|
||||
|
||||
public class Lineups
|
||||
{
|
||||
public int code { get; set; }
|
||||
|
||||
public string serverID { get; set; }
|
||||
|
||||
public string datetime { get; set; }
|
||||
|
||||
public List<Lineup> lineups { get; set; }
|
||||
}
|
||||
|
||||
public class Headends
|
||||
{
|
||||
public string headend { get; set; }
|
||||
|
||||
public string transport { get; set; }
|
||||
|
||||
public string location { get; set; }
|
||||
|
||||
public List<Lineup> lineups { get; set; }
|
||||
}
|
||||
|
||||
public class Map
|
||||
{
|
||||
public string stationID { get; set; }
|
||||
|
||||
public string channel { get; set; }
|
||||
|
||||
public string logicalChannelNumber { get; set; }
|
||||
|
||||
public int uhfVhf { get; set; }
|
||||
|
||||
public int atscMajor { get; set; }
|
||||
|
||||
public int atscMinor { get; set; }
|
||||
}
|
||||
|
||||
public class Broadcaster
|
||||
{
|
||||
public string city { get; set; }
|
||||
|
||||
public string state { get; set; }
|
||||
|
||||
public string postalcode { get; set; }
|
||||
|
||||
public string country { get; set; }
|
||||
}
|
||||
|
||||
public class Logo
|
||||
{
|
||||
public string URL { get; set; }
|
||||
|
||||
public int height { get; set; }
|
||||
|
||||
public int width { get; set; }
|
||||
|
||||
public string md5 { get; set; }
|
||||
}
|
||||
|
||||
public class Station
|
||||
{
|
||||
public string stationID { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
public string callsign { get; set; }
|
||||
|
||||
public List<string> broadcastLanguage { get; set; }
|
||||
|
||||
public List<string> descriptionLanguage { get; set; }
|
||||
|
||||
public Broadcaster broadcaster { get; set; }
|
||||
|
||||
public string affiliate { get; set; }
|
||||
|
||||
public Logo logo { get; set; }
|
||||
|
||||
public bool? isCommercialFree { get; set; }
|
||||
}
|
||||
|
||||
public class Metadata
|
||||
{
|
||||
public string lineup { get; set; }
|
||||
|
||||
public string modified { get; set; }
|
||||
|
||||
public string transport { get; set; }
|
||||
}
|
||||
|
||||
public class Channel
|
||||
{
|
||||
public List<Map> map { get; set; }
|
||||
|
||||
public List<Station> stations { get; set; }
|
||||
|
||||
public Metadata metadata { get; set; }
|
||||
}
|
||||
|
||||
public class RequestScheduleForChannel
|
||||
{
|
||||
public string stationID { get; set; }
|
||||
|
||||
public List<string> date { get; set; }
|
||||
}
|
||||
|
||||
public class Rating
|
||||
{
|
||||
public string body { get; set; }
|
||||
|
||||
public string code { get; set; }
|
||||
}
|
||||
|
||||
public class Multipart
|
||||
{
|
||||
public int partNumber { get; set; }
|
||||
|
||||
public int totalParts { get; set; }
|
||||
}
|
||||
|
||||
public class Program
|
||||
{
|
||||
public string programID { get; set; }
|
||||
|
||||
public string airDateTime { get; set; }
|
||||
|
||||
public int duration { get; set; }
|
||||
|
||||
public string md5 { get; set; }
|
||||
|
||||
public List<string> audioProperties { get; set; }
|
||||
|
||||
public List<string> videoProperties { get; set; }
|
||||
|
||||
public List<Rating> ratings { get; set; }
|
||||
|
||||
public bool? @new { get; set; }
|
||||
|
||||
public Multipart multipart { get; set; }
|
||||
|
||||
public string liveTapeDelay { get; set; }
|
||||
|
||||
public bool premiere { get; set; }
|
||||
|
||||
public bool repeat { get; set; }
|
||||
|
||||
public string isPremiereOrFinale { get; set; }
|
||||
}
|
||||
|
||||
public class MetadataSchedule
|
||||
{
|
||||
public string modified { get; set; }
|
||||
|
||||
public string md5 { get; set; }
|
||||
|
||||
public string startDate { get; set; }
|
||||
|
||||
public string endDate { get; set; }
|
||||
|
||||
public int days { get; set; }
|
||||
}
|
||||
|
||||
public class Day
|
||||
{
|
||||
public string stationID { get; set; }
|
||||
|
||||
public List<Program> programs { get; set; }
|
||||
|
||||
public MetadataSchedule metadata { get; set; }
|
||||
|
||||
public Day()
|
||||
{
|
||||
programs = new List<Program>();
|
||||
}
|
||||
}
|
||||
|
||||
public class Title
|
||||
{
|
||||
public string title120 { get; set; }
|
||||
}
|
||||
|
||||
public class EventDetails
|
||||
{
|
||||
public string subType { get; set; }
|
||||
}
|
||||
|
||||
public class Description100
|
||||
{
|
||||
public string descriptionLanguage { get; set; }
|
||||
|
||||
public string description { get; set; }
|
||||
}
|
||||
|
||||
public class Description1000
|
||||
{
|
||||
public string descriptionLanguage { get; set; }
|
||||
|
||||
public string description { get; set; }
|
||||
}
|
||||
|
||||
public class DescriptionsProgram
|
||||
{
|
||||
public List<Description100> description100 { get; set; }
|
||||
|
||||
public List<Description1000> description1000 { get; set; }
|
||||
}
|
||||
|
||||
public class Gracenote
|
||||
{
|
||||
public int season { get; set; }
|
||||
|
||||
public int episode { get; set; }
|
||||
}
|
||||
|
||||
public class MetadataPrograms
|
||||
{
|
||||
public Gracenote Gracenote { get; set; }
|
||||
}
|
||||
|
||||
public class ContentRating
|
||||
{
|
||||
public string body { get; set; }
|
||||
|
||||
public string code { get; set; }
|
||||
}
|
||||
|
||||
public class Cast
|
||||
{
|
||||
public string billingOrder { get; set; }
|
||||
|
||||
public string role { get; set; }
|
||||
|
||||
public string nameId { get; set; }
|
||||
|
||||
public string personId { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
public string characterName { get; set; }
|
||||
}
|
||||
|
||||
public class Crew
|
||||
{
|
||||
public string billingOrder { get; set; }
|
||||
|
||||
public string role { get; set; }
|
||||
|
||||
public string nameId { get; set; }
|
||||
|
||||
public string personId { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
public class QualityRating
|
||||
{
|
||||
public string ratingsBody { get; set; }
|
||||
|
||||
public string rating { get; set; }
|
||||
|
||||
public string minRating { get; set; }
|
||||
|
||||
public string maxRating { get; set; }
|
||||
|
||||
public string increment { get; set; }
|
||||
}
|
||||
|
||||
public class Movie
|
||||
{
|
||||
public string year { get; set; }
|
||||
|
||||
public int duration { get; set; }
|
||||
|
||||
public List<QualityRating> qualityRating { get; set; }
|
||||
}
|
||||
|
||||
public class Recommendation
|
||||
{
|
||||
public string programID { get; set; }
|
||||
|
||||
public string title120 { get; set; }
|
||||
}
|
||||
|
||||
public class ProgramDetails
|
||||
{
|
||||
public string audience { get; set; }
|
||||
|
||||
public string programID { get; set; }
|
||||
|
||||
public List<Title> titles { get; set; }
|
||||
|
||||
public EventDetails eventDetails { get; set; }
|
||||
|
||||
public DescriptionsProgram descriptions { get; set; }
|
||||
|
||||
public string originalAirDate { get; set; }
|
||||
|
||||
public List<string> genres { get; set; }
|
||||
|
||||
public string episodeTitle150 { get; set; }
|
||||
|
||||
public List<MetadataPrograms> metadata { get; set; }
|
||||
|
||||
public List<ContentRating> contentRating { get; set; }
|
||||
|
||||
public List<Cast> cast { get; set; }
|
||||
|
||||
public List<Crew> crew { get; set; }
|
||||
|
||||
public string entityType { get; set; }
|
||||
|
||||
public string showType { get; set; }
|
||||
|
||||
public bool hasImageArtwork { get; set; }
|
||||
|
||||
public string primaryImage { get; set; }
|
||||
|
||||
public string thumbImage { get; set; }
|
||||
|
||||
public string backdropImage { get; set; }
|
||||
|
||||
public string bannerImage { get; set; }
|
||||
|
||||
public string imageID { get; set; }
|
||||
|
||||
public string md5 { get; set; }
|
||||
|
||||
public List<string> contentAdvisory { get; set; }
|
||||
|
||||
public Movie movie { get; set; }
|
||||
|
||||
public List<Recommendation> recommendations { get; set; }
|
||||
}
|
||||
|
||||
public class Caption
|
||||
{
|
||||
public string content { get; set; }
|
||||
|
||||
public string lang { get; set; }
|
||||
}
|
||||
|
||||
public class ImageData
|
||||
{
|
||||
public string width { get; set; }
|
||||
|
||||
public string height { get; set; }
|
||||
|
||||
public string uri { get; set; }
|
||||
|
||||
public string size { get; set; }
|
||||
|
||||
public string aspect { get; set; }
|
||||
|
||||
public string category { get; set; }
|
||||
|
||||
public string text { get; set; }
|
||||
|
||||
public string primary { get; set; }
|
||||
|
||||
public string tier { get; set; }
|
||||
|
||||
public Caption caption { get; set; }
|
||||
}
|
||||
|
||||
public class ShowImages
|
||||
{
|
||||
public string programID { get; set; }
|
||||
|
||||
public List<ImageData> data { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Broadcaster dto.
|
||||
/// </summary>
|
||||
public class BroadcasterDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the city.
|
||||
/// </summary>
|
||||
[JsonPropertyName("city")]
|
||||
public string City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the state.
|
||||
/// </summary>
|
||||
[JsonPropertyName("state")]
|
||||
public string State { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the postal code.
|
||||
/// </summary>
|
||||
[JsonPropertyName("postalCode")]
|
||||
public string Postalcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the country.
|
||||
/// </summary>
|
||||
[JsonPropertyName("country")]
|
||||
public string Country { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Caption dto.
|
||||
/// </summary>
|
||||
public class CaptionDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the content.
|
||||
/// </summary>
|
||||
[JsonPropertyName("content")]
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the lang.
|
||||
/// </summary>
|
||||
[JsonPropertyName("lang")]
|
||||
public string Lang { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Cast dto.
|
||||
/// </summary>
|
||||
public class CastDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the billing order.
|
||||
/// </summary>
|
||||
[JsonPropertyName("billingOrder")]
|
||||
public string BillingOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the role.
|
||||
/// </summary>
|
||||
[JsonPropertyName("role")]
|
||||
public string Role { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("nameId")]
|
||||
public string NameId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the person id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("personId")]
|
||||
public string PersonId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the character name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("characterName")]
|
||||
public string CharacterName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Channel dto.
|
||||
/// </summary>
|
||||
public class ChannelDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the list of maps.
|
||||
/// </summary>
|
||||
[JsonPropertyName("map")]
|
||||
public List<MapDto> Map { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of stations.
|
||||
/// </summary>
|
||||
[JsonPropertyName("stations")]
|
||||
public List<StationDto> Stations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the metadata.
|
||||
/// </summary>
|
||||
[JsonPropertyName("metadata")]
|
||||
public MetadataDto Metadata { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Content rating dto.
|
||||
/// </summary>
|
||||
public class ContentRatingDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
[JsonPropertyName("body")]
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the code.
|
||||
/// </summary>
|
||||
[JsonPropertyName("code")]
|
||||
public string Code { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Crew dto.
|
||||
/// </summary>
|
||||
public class CrewDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the billing order.
|
||||
/// </summary>
|
||||
[JsonPropertyName("billingOrder")]
|
||||
public string BillingOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the role.
|
||||
/// </summary>
|
||||
[JsonPropertyName("role")]
|
||||
public string Role { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("nameId")]
|
||||
public string NameId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the person id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("personId")]
|
||||
public string PersonId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Day dto.
|
||||
/// </summary>
|
||||
public class DayDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DayDto"/> class.
|
||||
/// </summary>
|
||||
public DayDto()
|
||||
{
|
||||
Programs = new List<ProgramDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the station id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("stationID")]
|
||||
public string StationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of programs.
|
||||
/// </summary>
|
||||
[JsonPropertyName("programs")]
|
||||
public List<ProgramDto> Programs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the metadata schedule.
|
||||
/// </summary>
|
||||
[JsonPropertyName("metadata")]
|
||||
public MetadataScheduleDto Metadata { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Description 1_000 dto.
|
||||
/// </summary>
|
||||
public class Description1000Dto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the description language.
|
||||
/// </summary>
|
||||
[JsonPropertyName("descriptionLanguage")]
|
||||
public string DescriptionLanguage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description.
|
||||
/// </summary>
|
||||
[JsonPropertyName("description")]
|
||||
public string Description { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Description 100 dto.
|
||||
/// </summary>
|
||||
public class Description100Dto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the description language.
|
||||
/// </summary>
|
||||
[JsonPropertyName("descriptionLanguage")]
|
||||
public string DescriptionLanguage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description.
|
||||
/// </summary>
|
||||
[JsonPropertyName("description")]
|
||||
public string Description { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Descriptions program dto.
|
||||
/// </summary>
|
||||
public class DescriptionsProgramDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the list of description 100.
|
||||
/// </summary>
|
||||
[JsonPropertyName("description100")]
|
||||
public List<Description100Dto> Description100 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of description1000.
|
||||
/// </summary>
|
||||
[JsonPropertyName("description1000")]
|
||||
public List<Description1000Dto> Description1000 { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Event details dto.
|
||||
/// </summary>
|
||||
public class EventDetailsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the sub type.
|
||||
/// </summary>
|
||||
[JsonPropertyName("subType")]
|
||||
public string SubType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Gracenote dto.
|
||||
/// </summary>
|
||||
public class GracenoteDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the season.
|
||||
/// </summary>
|
||||
[JsonPropertyName("season")]
|
||||
public int Season { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the episode.
|
||||
/// </summary>
|
||||
[JsonPropertyName("episode")]
|
||||
public int Episode { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Headends dto.
|
||||
/// </summary>
|
||||
public class HeadendsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the headend.
|
||||
/// </summary>
|
||||
[JsonPropertyName("headend")]
|
||||
public string Headend { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transport.
|
||||
/// </summary>
|
||||
[JsonPropertyName("transport")]
|
||||
public string Transport { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the location.
|
||||
/// </summary>
|
||||
[JsonPropertyName("location")]
|
||||
public string Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of lineups.
|
||||
/// </summary>
|
||||
[JsonPropertyName("lineups")]
|
||||
public List<LineupDto> Lineups { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Image data dto.
|
||||
/// </summary>
|
||||
public class ImageDataDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the width.
|
||||
/// </summary>
|
||||
[JsonPropertyName("width")]
|
||||
public string Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height.
|
||||
/// </summary>
|
||||
[JsonPropertyName("height")]
|
||||
public string Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the uri.
|
||||
/// </summary>
|
||||
[JsonPropertyName("uri")]
|
||||
public string Uri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size.
|
||||
/// </summary>
|
||||
[JsonPropertyName("size")]
|
||||
public string Size { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the aspect.
|
||||
/// </summary>
|
||||
[JsonPropertyName("aspect")]
|
||||
public string aspect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the category.
|
||||
/// </summary>
|
||||
[JsonPropertyName("category")]
|
||||
public string Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text.
|
||||
/// </summary>
|
||||
[JsonPropertyName("text")]
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the primary.
|
||||
/// </summary>
|
||||
[JsonPropertyName("primary")]
|
||||
public string Primary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tier")]
|
||||
public string Tier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the caption.
|
||||
/// </summary>
|
||||
[JsonPropertyName("caption")]
|
||||
public CaptionDto Caption { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// The lineup dto.
|
||||
/// </summary>
|
||||
public class LineupDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the linup.
|
||||
/// </summary>
|
||||
[JsonPropertyName("lineup")]
|
||||
public string Lineup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the lineup name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transport.
|
||||
/// </summary>
|
||||
[JsonPropertyName("transport")]
|
||||
public string Transport { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the location.
|
||||
/// </summary>
|
||||
[JsonPropertyName("location")]
|
||||
public string Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the uri.
|
||||
/// </summary>
|
||||
[JsonPropertyName("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Lineups dto.
|
||||
/// </summary>
|
||||
public class LineupsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the response code.
|
||||
/// </summary>
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the server id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("serverID")]
|
||||
public string ServerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the datetime.
|
||||
/// </summary>
|
||||
[JsonPropertyName("datetime")]
|
||||
public string Datetime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of lineups.
|
||||
/// </summary>
|
||||
[JsonPropertyName("lineups")]
|
||||
public List<LineupDto> Lineups { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Logo dto.
|
||||
/// </summary>
|
||||
public class LogoDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the url.
|
||||
/// </summary>
|
||||
[JsonPropertyName("URL")]
|
||||
public string Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height.
|
||||
/// </summary>
|
||||
[JsonPropertyName("height")]
|
||||
public int Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width.
|
||||
/// </summary>
|
||||
[JsonPropertyName("width")]
|
||||
public int Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the md5.
|
||||
/// </summary>
|
||||
[JsonPropertyName("md5")]
|
||||
public string Md5 { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Map dto.
|
||||
/// </summary>
|
||||
public class MapDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the station id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("stationID")]
|
||||
public string StationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the channel.
|
||||
/// </summary>
|
||||
[JsonPropertyName("channel")]
|
||||
public string Channel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the logical channel number.
|
||||
/// </summary>
|
||||
[JsonPropertyName("logicalChannelNumber")]
|
||||
public string LogicalChannelNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the uhfvhf.
|
||||
/// </summary>
|
||||
[JsonPropertyName("uhfVhf")]
|
||||
public int UhfVhf { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the atsc major.
|
||||
/// </summary>
|
||||
[JsonPropertyName("atscMajor")]
|
||||
public int AtscMajor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the atsc minor.
|
||||
/// </summary>
|
||||
[JsonPropertyName("atscMinor")]
|
||||
public int AtscMinor { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Metadata dto.
|
||||
/// </summary>
|
||||
public class MetadataDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the linup.
|
||||
/// </summary>
|
||||
[JsonPropertyName("lineup")]
|
||||
public string Lineup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the modified timestamp.
|
||||
/// </summary>
|
||||
[JsonPropertyName("modified")]
|
||||
public string Modified { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transport.
|
||||
/// </summary>
|
||||
[JsonPropertyName("transport")]
|
||||
public string Transport { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Metadata programs dto.
|
||||
/// </summary>
|
||||
public class MetadataProgramsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the gracenote object.
|
||||
/// </summary>
|
||||
[JsonPropertyName("gracenote")]
|
||||
public GracenoteDto Gracenote { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Metadata schedule dto.
|
||||
/// </summary>
|
||||
public class MetadataScheduleDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the modified timestamp.
|
||||
/// </summary>
|
||||
[JsonPropertyName("modified")]
|
||||
public string Modified { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the md5.
|
||||
/// </summary>
|
||||
[JsonPropertyName("md5")]
|
||||
public string Md5 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start date.
|
||||
/// </summary>
|
||||
[JsonPropertyName("startDate")]
|
||||
public string StartDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end date.
|
||||
/// </summary>
|
||||
[JsonPropertyName("endDate")]
|
||||
public string EndDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the days count.
|
||||
/// </summary>
|
||||
[JsonPropertyName("days")]
|
||||
public int Days { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Movie dto.
|
||||
/// </summary>
|
||||
public class MovieDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the year.
|
||||
/// </summary>
|
||||
[JsonPropertyName("year")]
|
||||
public string Year { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the duration.
|
||||
/// </summary>
|
||||
[JsonPropertyName("duration")]
|
||||
public int Duration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of quality rating.
|
||||
/// </summary>
|
||||
[JsonPropertyName("qualityRating")]
|
||||
public List<QualityRatingDto> QualityRating { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Multipart dto.
|
||||
/// </summary>
|
||||
public class MultipartDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the part number.
|
||||
/// </summary>
|
||||
[JsonPropertyName("partNumber")]
|
||||
public int PartNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total parts.
|
||||
/// </summary>
|
||||
[JsonPropertyName("totalParts")]
|
||||
public int TotalParts { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Program details dto.
|
||||
/// </summary>
|
||||
public class ProgramDetailsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the audience.
|
||||
/// </summary>
|
||||
[JsonPropertyName("audience")]
|
||||
public string Audience { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the program id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("programID")]
|
||||
public string ProgramId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of titles.
|
||||
/// </summary>
|
||||
[JsonPropertyName("titles")]
|
||||
public List<TitleDto> Titles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the event details object.
|
||||
/// </summary>
|
||||
[JsonPropertyName("eventDetails")]
|
||||
public EventDetailsDto EventDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the descriptions.
|
||||
/// </summary>
|
||||
[JsonPropertyName("descriptions")]
|
||||
public DescriptionsProgramDto Descriptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the original air date.
|
||||
/// </summary>
|
||||
[JsonPropertyName("originalAirDate")]
|
||||
public string OriginalAirDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of genres.
|
||||
/// </summary>
|
||||
[JsonPropertyName("genres")]
|
||||
public List<string> Genres { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the episode title.
|
||||
/// </summary>
|
||||
[JsonPropertyName("episodeTitle150")]
|
||||
public string EpisodeTitle150 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of metadata.
|
||||
/// </summary>
|
||||
[JsonPropertyName("metadata")]
|
||||
public List<MetadataProgramsDto> Metadata { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of content raitings.
|
||||
/// </summary>
|
||||
[JsonPropertyName("contentRating")]
|
||||
public List<ContentRatingDto> ContentRating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of cast.
|
||||
/// </summary>
|
||||
[JsonPropertyName("cast")]
|
||||
public List<CastDto> Cast { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of crew.
|
||||
/// </summary>
|
||||
[JsonPropertyName("crew")]
|
||||
public List<CrewDto> Crew { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the entity type.
|
||||
/// </summary>
|
||||
[JsonPropertyName("entityType")]
|
||||
public string EntityType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the show type.
|
||||
/// </summary>
|
||||
[JsonPropertyName("showType")]
|
||||
public string ShowType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether there is image artwork.
|
||||
/// </summary>
|
||||
[JsonPropertyName("hasImageArtwork")]
|
||||
public bool HasImageArtwork { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the primary image.
|
||||
/// </summary>
|
||||
[JsonPropertyName("primaryImage")]
|
||||
public string PrimaryImage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the thumb image.
|
||||
/// </summary>
|
||||
[JsonPropertyName("thumbImage")]
|
||||
public string ThumbImage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the backdrop image.
|
||||
/// </summary>
|
||||
[JsonPropertyName("backdropImage")]
|
||||
public string BackdropImage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the banner image.
|
||||
/// </summary>
|
||||
[JsonPropertyName("bannerImage")]
|
||||
public string BannerImage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("imageID")]
|
||||
public string ImageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the md5.
|
||||
/// </summary>
|
||||
[JsonPropertyName("md5")]
|
||||
public string Md5 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of content advisory.
|
||||
/// </summary>
|
||||
[JsonPropertyName("contentAdvisory")]
|
||||
public List<string> ContentAdvisory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the movie object.
|
||||
/// </summary>
|
||||
[JsonPropertyName("movie")]
|
||||
public MovieDto Movie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of recommendations.
|
||||
/// </summary>
|
||||
[JsonPropertyName("recommendations")]
|
||||
public List<RecommendationDto> Recommendations { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Program dto.
|
||||
/// </summary>
|
||||
public class ProgramDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the program id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("programID")]
|
||||
public string ProgramId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the air date time.
|
||||
/// </summary>
|
||||
[JsonPropertyName("airDateTime")]
|
||||
public string AirDateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the duration.
|
||||
/// </summary>
|
||||
[JsonPropertyName("duration")]
|
||||
public int Duration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the md5.
|
||||
/// </summary>
|
||||
[JsonPropertyName("md5")]
|
||||
public string Md5 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of audio properties.
|
||||
/// </summary>
|
||||
[JsonPropertyName("audioProperties")]
|
||||
public List<string> AudioProperties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of video properties.
|
||||
/// </summary>
|
||||
[JsonPropertyName("videoProperties")]
|
||||
public List<string> VideoProperties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of ratings.
|
||||
/// </summary>
|
||||
[JsonPropertyName("ratings")]
|
||||
public List<RatingDto> Ratings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this program is new.
|
||||
/// </summary>
|
||||
[JsonPropertyName("new")]
|
||||
public bool? New { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the multipart object.
|
||||
/// </summary>
|
||||
[JsonPropertyName("multipart")]
|
||||
public MultipartDto Multipart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the live tape delay.
|
||||
/// </summary>
|
||||
[JsonPropertyName("liveTapeDelay")]
|
||||
public string LiveTapeDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this is the premiere.
|
||||
/// </summary>
|
||||
[JsonPropertyName("premiere")]
|
||||
public bool Premiere { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this is a repeat.
|
||||
/// </summary>
|
||||
[JsonPropertyName("repeat")]
|
||||
public bool Repeat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the premiere or finale.
|
||||
/// </summary>
|
||||
[JsonPropertyName("isPremiereOrFinale")]
|
||||
public string IsPremiereOrFinale { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Quality rating dto.
|
||||
/// </summary>
|
||||
public class QualityRatingDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the ratings body.
|
||||
/// </summary>
|
||||
[JsonPropertyName("ratingsBody")]
|
||||
public string RatingsBody { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rating.
|
||||
/// </summary>
|
||||
[JsonPropertyName("rating")]
|
||||
public string Rating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the min rating.
|
||||
/// </summary>
|
||||
[JsonPropertyName("minRating")]
|
||||
public string MinRating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the max rating.
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxRating")]
|
||||
public string MaxRating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the increment.
|
||||
/// </summary>
|
||||
[JsonPropertyName("increment")]
|
||||
public string Increment { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Rating dto.
|
||||
/// </summary>
|
||||
public class RatingDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
[JsonPropertyName("body")]
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the code.
|
||||
/// </summary>
|
||||
[JsonPropertyName("code")]
|
||||
public string Code { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Recommendation dto.
|
||||
/// </summary>
|
||||
public class RecommendationDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the program id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("programID")]
|
||||
public string ProgramId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title.
|
||||
/// </summary>
|
||||
[JsonPropertyName("title120")]
|
||||
public string Title120 { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Request schedule for channel dto.
|
||||
/// </summary>
|
||||
public class RequestScheduleForChannelDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the station id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("stationID")]
|
||||
public string StationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of dates.
|
||||
/// </summary>
|
||||
[JsonPropertyName("date")]
|
||||
public List<string> Date { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Show image dto.
|
||||
/// </summary>
|
||||
public class ShowImagesDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the program id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("programID")]
|
||||
public string ProgramId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of data.
|
||||
/// </summary>
|
||||
[JsonPropertyName("data")]
|
||||
public List<ImageDataDto> Data { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Station dto.
|
||||
/// </summary>
|
||||
public class StationDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the station id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("stationID")]
|
||||
public string StationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callsign.
|
||||
/// </summary>
|
||||
[JsonPropertyName("callsign")]
|
||||
public string Callsign { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the broadcast language.
|
||||
/// </summary>
|
||||
[JsonPropertyName("broadcastLanguage")]
|
||||
public List<string> BroadcastLanguage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description language.
|
||||
/// </summary>
|
||||
[JsonPropertyName("descriptionLanguage")]
|
||||
public List<string> DescriptionLanguage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the broadcaster.
|
||||
/// </summary>
|
||||
[JsonPropertyName("broadcaster")]
|
||||
public BroadcasterDto Broadcaster { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the affiliate.
|
||||
/// </summary>
|
||||
[JsonPropertyName("affiliate")]
|
||||
public string Affiliate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the logo.
|
||||
/// </summary>
|
||||
[JsonPropertyName("logo")]
|
||||
public LogoDto Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set a value indicating whether it is commercial free.
|
||||
/// </summary>
|
||||
[JsonPropertyName("isCommercialFree")]
|
||||
public bool? IsCommercialFree { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Title dto.
|
||||
/// </summary>
|
||||
public class TitleDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the title.
|
||||
/// </summary>
|
||||
[JsonPropertyName("title120")]
|
||||
public string Title120 { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// The token dto.
|
||||
/// </summary>
|
||||
public class TokenDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the response code.
|
||||
/// </summary>
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the response message.
|
||||
/// </summary>
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the server id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("serverID")]
|
||||
public string ServerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the token.
|
||||
/// </summary>
|
||||
[JsonPropertyName("token")]
|
||||
public string Token { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IConfigurationFactory" /> implementation for <see cref="LiveTvOptions" />.
|
||||
/// </summary>
|
||||
public class LiveTvConfigurationFactory : IConfigurationFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ConfigurationStore> GetConfigurations()
|
||||
{
|
||||
return new ConfigurationStore[]
|
||||
{
|
||||
new ConfigurationStore
|
||||
{
|
||||
ConfigurationType = typeof(LiveTvOptions),
|
||||
Key = "livetv"
|
||||
ConfigurationType = typeof(LiveTvOptions),
|
||||
Key = "livetv"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -63,6 +65,8 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
|
||||
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
|
||||
|
||||
private bool _disposed = false;
|
||||
|
||||
public LiveTvManager(
|
||||
IServerConfigurationManager config,
|
||||
ILogger<LiveTvManager> logger,
|
||||
@@ -401,7 +405,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
// Set the total bitrate if not already supplied
|
||||
mediaSource.InferTotalBitrate();
|
||||
|
||||
if (!(service is EmbyTV.EmbyTV))
|
||||
if (service is not EmbyTV.EmbyTV)
|
||||
{
|
||||
// We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
|
||||
// mediaSource.SupportsDirectPlay = false;
|
||||
@@ -518,7 +522,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
return item;
|
||||
}
|
||||
|
||||
private Tuple<LiveTvProgram, bool, bool> GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
|
||||
private (LiveTvProgram item, bool isNew, bool isUpdated) GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel)
|
||||
{
|
||||
var id = _tvDtoService.GetInternalProgramId(info.Id);
|
||||
|
||||
@@ -557,8 +561,6 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
|
||||
item.ParentId = channel.Id;
|
||||
|
||||
// item.ChannelType = channelType;
|
||||
|
||||
item.Audio = info.Audio;
|
||||
item.ChannelId = channel.Id;
|
||||
item.CommunityRating ??= info.CommunityRating;
|
||||
@@ -770,7 +772,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
item.OnMetadataChanged();
|
||||
}
|
||||
|
||||
return new Tuple<LiveTvProgram, bool, bool>(item, isNew, isUpdated);
|
||||
return (item, isNew, isUpdated);
|
||||
}
|
||||
|
||||
public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
|
||||
@@ -987,10 +989,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
var externalProgramId = programTuple.Item2;
|
||||
string externalSeriesId = programTuple.Item3;
|
||||
|
||||
if (timerList == null)
|
||||
{
|
||||
timerList = (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
|
||||
}
|
||||
timerList ??= (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
|
||||
|
||||
var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, externalProgramId, StringComparison.OrdinalIgnoreCase));
|
||||
var foundSeriesTimer = false;
|
||||
@@ -1018,10 +1017,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seriesTimerList == null)
|
||||
{
|
||||
seriesTimerList = (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
|
||||
}
|
||||
seriesTimerList ??= (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
|
||||
|
||||
var seriesTimer = seriesTimerList.FirstOrDefault(i => string.Equals(i.SeriesId, externalSeriesId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
@@ -1191,14 +1187,14 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
|
||||
foreach (var program in channelPrograms)
|
||||
{
|
||||
var programTuple = GetProgram(program, existingPrograms, currentChannel, currentChannel.ChannelType, service.Name, cancellationToken);
|
||||
var programItem = programTuple.Item1;
|
||||
var programTuple = GetProgram(program, existingPrograms, currentChannel);
|
||||
var programItem = programTuple.item;
|
||||
|
||||
if (programTuple.Item2)
|
||||
if (programTuple.isNew)
|
||||
{
|
||||
newPrograms.Add(programItem);
|
||||
}
|
||||
else if (programTuple.Item3)
|
||||
else if (programTuple.isUpdated)
|
||||
{
|
||||
updatedPrograms.Add(programItem);
|
||||
}
|
||||
@@ -1389,10 +1385,10 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
// var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
|
||||
|
||||
// return new QueryResult<BaseItem>
|
||||
//{
|
||||
// {
|
||||
// Items = items,
|
||||
// TotalRecordCount = items.Length
|
||||
//};
|
||||
// };
|
||||
|
||||
dtoOptions.Fields = dtoOptions.Fields.Concat(new[] { ItemFields.Tags }).Distinct().ToArray();
|
||||
}
|
||||
@@ -1429,16 +1425,15 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> tuples, IReadOnlyList<ItemFields> fields, User user = null)
|
||||
public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> programs, IReadOnlyList<ItemFields> fields, User user = null)
|
||||
{
|
||||
var programTuples = new List<Tuple<BaseItemDto, string, string>>();
|
||||
var hasChannelImage = fields.Contains(ItemFields.ChannelImage);
|
||||
var hasChannelInfo = fields.Contains(ItemFields.ChannelInfo);
|
||||
|
||||
foreach (var tuple in tuples)
|
||||
foreach (var (item, dto) in programs)
|
||||
{
|
||||
var program = (LiveTvProgram)tuple.Item1;
|
||||
var dto = tuple.Item2;
|
||||
var program = (LiveTvProgram)item;
|
||||
|
||||
dto.StartDate = program.StartDate;
|
||||
dto.EpisodeTitle = program.EpisodeTitle;
|
||||
@@ -1728,7 +1723,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
|
||||
await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
if (!(service is EmbyTV.EmbyTV))
|
||||
if (service is not EmbyTV.EmbyTV)
|
||||
{
|
||||
TimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>(new TimerEventInfo(id)));
|
||||
}
|
||||
@@ -1875,11 +1870,11 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
return _libraryManager.GetItemById(internalChannelId);
|
||||
}
|
||||
|
||||
public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> tuples, DtoOptions options, User user)
|
||||
public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> items, DtoOptions options, User user)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var channelIds = tuples.Select(i => i.Item2.Id).Distinct().ToArray();
|
||||
var channelIds = items.Select(i => i.Item2.Id).Distinct().ToArray();
|
||||
|
||||
var programs = options.AddCurrentProgram ? _libraryManager.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
@@ -1900,7 +1895,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
|
||||
var addCurrentProgram = options.AddCurrentProgram;
|
||||
|
||||
foreach (var tuple in tuples)
|
||||
foreach (var tuple in items)
|
||||
{
|
||||
var dto = tuple.Item1;
|
||||
var channel = tuple.Item2;
|
||||
@@ -1974,10 +1969,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
};
|
||||
}
|
||||
|
||||
if (service == null)
|
||||
{
|
||||
service = _services[0];
|
||||
}
|
||||
service ??= _services[0];
|
||||
|
||||
var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
|
||||
|
||||
@@ -2057,7 +2049,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
|
||||
_logger.LogInformation("New recording scheduled");
|
||||
|
||||
if (!(service is EmbyTV.EmbyTV))
|
||||
if (service is not EmbyTV.EmbyTV)
|
||||
{
|
||||
TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>(
|
||||
new TimerEventInfo(newTimerId)
|
||||
@@ -2125,17 +2117,13 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private bool _disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
@@ -2273,7 +2261,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
|
||||
if (dataSourceChanged)
|
||||
{
|
||||
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
|
||||
_taskManager.CancelIfRunningAndQueue<RefreshGuideScheduledTask>();
|
||||
}
|
||||
|
||||
return info;
|
||||
@@ -2316,7 +2304,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
|
||||
_config.SaveConfiguration("livetv", config);
|
||||
|
||||
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
|
||||
_taskManager.CancelIfRunningAndQueue<RefreshGuideScheduledTask>();
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -2328,23 +2316,23 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
config.ListingProviders = config.ListingProviders.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
|
||||
_config.SaveConfiguration("livetv", config);
|
||||
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
|
||||
_taskManager.CancelIfRunningAndQueue<RefreshGuideScheduledTask>();
|
||||
}
|
||||
|
||||
public async Task<TunerChannelMapping> SetChannelMapping(string providerId, string tunerChannelId, string providerChannelId)
|
||||
public async Task<TunerChannelMapping> SetChannelMapping(string providerId, string tunerChannelNumber, string providerChannelNumber)
|
||||
{
|
||||
var config = GetConfiguration();
|
||||
|
||||
var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase));
|
||||
listingsProviderInfo.ChannelMappings = listingsProviderInfo.ChannelMappings.Where(i => !string.Equals(i.Name, tunerChannelId, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
listingsProviderInfo.ChannelMappings = listingsProviderInfo.ChannelMappings.Where(i => !string.Equals(i.Name, tunerChannelNumber, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
|
||||
if (!string.Equals(tunerChannelId, providerChannelId, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.Equals(tunerChannelNumber, providerChannelNumber, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var list = listingsProviderInfo.ChannelMappings.ToList();
|
||||
list.Add(new NameValuePair
|
||||
{
|
||||
Name = tunerChannelId,
|
||||
Value = providerChannelId
|
||||
Name = tunerChannelNumber,
|
||||
Value = providerChannelNumber
|
||||
});
|
||||
listingsProviderInfo.ChannelMappings = list.ToArray();
|
||||
}
|
||||
@@ -2362,12 +2350,12 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
var tunerChannelMappings =
|
||||
tunerChannels.Select(i => GetTunerChannelMapping(i, mappings, providerChannels)).ToList();
|
||||
|
||||
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
|
||||
_taskManager.CancelIfRunningAndQueue<RefreshGuideScheduledTask>();
|
||||
|
||||
return tunerChannelMappings.First(i => string.Equals(i.Id, tunerChannelId, StringComparison.OrdinalIgnoreCase));
|
||||
return tunerChannelMappings.First(i => string.Equals(i.Id, tunerChannelNumber, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public TunerChannelMapping GetTunerChannelMapping(ChannelInfo tunerChannel, NameValuePair[] mappings, List<ChannelInfo> epgChannels)
|
||||
public TunerChannelMapping GetTunerChannelMapping(ChannelInfo tunerChannel, NameValuePair[] mappings, List<ChannelInfo> providerChannels)
|
||||
{
|
||||
var result = new TunerChannelMapping
|
||||
{
|
||||
@@ -2380,7 +2368,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
result.Name = tunerChannel.Number + " " + result.Name;
|
||||
}
|
||||
|
||||
var providerChannel = EmbyTV.EmbyTV.Current.GetEpgChannelFromTunerChannel(mappings, tunerChannel, epgChannels);
|
||||
var providerChannel = EmbyTV.EmbyTV.Current.GetEpgChannelFromTunerChannel(mappings, tunerChannel, providerChannels);
|
||||
|
||||
if (providerChannel != null)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
@@ -10,34 +9,55 @@ using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv
|
||||
{
|
||||
public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
|
||||
/// <summary>
|
||||
/// The "Refresh Guide" scheduled task.
|
||||
/// </summary>
|
||||
public class RefreshGuideScheduledTask : IScheduledTask, IConfigurableScheduledTask
|
||||
{
|
||||
private readonly ILiveTvManager _liveTvManager;
|
||||
private readonly IConfigurationManager _config;
|
||||
|
||||
public RefreshChannelsScheduledTask(ILiveTvManager liveTvManager, IConfigurationManager config)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RefreshGuideScheduledTask"/> class.
|
||||
/// </summary>
|
||||
/// <param name="liveTvManager">The live tv manager.</param>
|
||||
/// <param name="config">The configuration manager.</param>
|
||||
public RefreshGuideScheduledTask(ILiveTvManager liveTvManager, IConfigurationManager config)
|
||||
{
|
||||
_liveTvManager = liveTvManager;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "Refresh Guide";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "Downloads channel information from live tv services.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Category => "Live TV";
|
||||
|
||||
public Task Execute(System.Threading.CancellationToken cancellationToken, IProgress<double> progress)
|
||||
/// <inheritdoc />
|
||||
public bool IsHidden => _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Length == 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsLogged => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => "RefreshGuide";
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
var manager = (LiveTvManager)_liveTvManager;
|
||||
|
||||
return manager.RefreshChannels(progress, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the triggers that define when the task will run.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
return new[]
|
||||
@@ -51,13 +71,5 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
{
|
||||
return _config.GetConfiguration<LiveTvOptions>("livetv");
|
||||
}
|
||||
|
||||
public bool IsHidden => _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Length == 0;
|
||||
|
||||
public bool IsEnabled => true;
|
||||
|
||||
public bool IsLogged => true;
|
||||
|
||||
public string Key => "RefreshGuide";
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -38,6 +40,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
public virtual bool IsSupported => true;
|
||||
|
||||
protected abstract Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
|
||||
|
||||
public abstract string Type { get; }
|
||||
|
||||
public async Task<List<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
|
||||
@@ -155,7 +158,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
return new List<MediaSourceInfo>();
|
||||
}
|
||||
|
||||
protected abstract Task<ILiveStream> GetChannelStream(TunerHostInfo tuner, ChannelInfo channel, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken);
|
||||
protected abstract Task<ILiveStream> GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken);
|
||||
|
||||
public async Task<ILiveStream> GetChannelStream(string channelId, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
{
|
||||
internal class Channels
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -10,8 +12,9 @@ using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
@@ -74,7 +77,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
{
|
||||
var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(model.LineupURL, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(model.LineupURL ?? model.BaseURL + "/lineup.json", HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
var lineup = await JsonSerializer.DeserializeAsync<List<Channels>>(stream, _jsonOptions, cancellationToken)
|
||||
.ConfigureAwait(false) ?? new List<Channels>();
|
||||
@@ -92,17 +95,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
public bool IsLegacyTuner { get; set; }
|
||||
}
|
||||
|
||||
protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
|
||||
protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken)
|
||||
{
|
||||
var lineup = await GetLineup(info, cancellationToken).ConfigureAwait(false);
|
||||
var lineup = await GetLineup(tuner, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return lineup.Select(i => new HdHomerunChannelInfo
|
||||
{
|
||||
Name = i.GuideName,
|
||||
Number = i.GuideNumber,
|
||||
Id = GetChannelId(info, i),
|
||||
Id = GetChannelId(tuner, i),
|
||||
IsFavorite = i.Favorite,
|
||||
TunerHostId = info.Id,
|
||||
TunerHostId = tuner.Id,
|
||||
IsHD = i.HD,
|
||||
AudioCodec = i.AudioCodec,
|
||||
VideoCodec = i.VideoCodec,
|
||||
@@ -182,16 +185,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
using var sr = new StreamReader(stream, System.Text.Encoding.UTF8);
|
||||
var tuners = new List<LiveTvTunerInfo>();
|
||||
while (!sr.EndOfStream)
|
||||
await foreach (var line in sr.ReadAllLinesAsync().ConfigureAwait(false))
|
||||
{
|
||||
string line = StripXML(sr.ReadLine());
|
||||
if (line.Contains("Channel", StringComparison.Ordinal))
|
||||
string stripedLine = StripXML(line);
|
||||
if (stripedLine.Contains("Channel", StringComparison.Ordinal))
|
||||
{
|
||||
LiveTvTunerStatus status;
|
||||
var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
|
||||
var name = line.Substring(0, index - 1);
|
||||
var currentChannel = line.Substring(index + 7);
|
||||
if (currentChannel != "none")
|
||||
var index = stripedLine.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
|
||||
var name = stripedLine.Substring(0, index - 1);
|
||||
var currentChannel = stripedLine.Substring(index + 7);
|
||||
if (string.Equals(currentChannel, "none", StringComparison.Ordinal))
|
||||
{
|
||||
status = LiveTvTunerStatus.LiveTv;
|
||||
}
|
||||
@@ -421,10 +424,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
|
||||
string audioCodec = channelInfo.AudioCodec;
|
||||
|
||||
if (!videoBitrate.HasValue)
|
||||
{
|
||||
videoBitrate = isHd ? 15000000 : 2000000;
|
||||
}
|
||||
videoBitrate ??= isHd ? 15000000 : 2000000;
|
||||
|
||||
int? audioBitrate = isHd ? 448000 : 192000;
|
||||
|
||||
@@ -496,57 +496,53 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
return mediaSource;
|
||||
}
|
||||
|
||||
protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, ChannelInfo channelInfo, CancellationToken cancellationToken)
|
||||
protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, ChannelInfo channel, CancellationToken cancellationToken)
|
||||
{
|
||||
var list = new List<MediaSourceInfo>();
|
||||
|
||||
var channelId = channelInfo.Id;
|
||||
var channelId = channel.Id;
|
||||
var hdhrId = GetHdHrIdFromChannelId(channelId);
|
||||
|
||||
var hdHomerunChannelInfo = channelInfo as HdHomerunChannelInfo;
|
||||
|
||||
var isLegacyTuner = hdHomerunChannelInfo != null && hdHomerunChannelInfo.IsLegacyTuner;
|
||||
|
||||
if (isLegacyTuner)
|
||||
if (channel is HdHomerunChannelInfo hdHomerunChannelInfo && hdHomerunChannelInfo.IsLegacyTuner)
|
||||
{
|
||||
list.Add(GetMediaSource(info, hdhrId, channelInfo, "native"));
|
||||
list.Add(GetMediaSource(tuner, hdhrId, channel, "native"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
|
||||
var modelInfo = await GetModelInfo(tuner, false, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (modelInfo != null && modelInfo.SupportsTranscoding)
|
||||
{
|
||||
if (info.AllowHWTranscoding)
|
||||
if (tuner.AllowHWTranscoding)
|
||||
{
|
||||
list.Add(GetMediaSource(info, hdhrId, channelInfo, "heavy"));
|
||||
list.Add(GetMediaSource(tuner, hdhrId, channel, "heavy"));
|
||||
|
||||
list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet540"));
|
||||
list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet480"));
|
||||
list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet360"));
|
||||
list.Add(GetMediaSource(info, hdhrId, channelInfo, "internet240"));
|
||||
list.Add(GetMediaSource(info, hdhrId, channelInfo, "mobile"));
|
||||
list.Add(GetMediaSource(tuner, hdhrId, channel, "internet540"));
|
||||
list.Add(GetMediaSource(tuner, hdhrId, channel, "internet480"));
|
||||
list.Add(GetMediaSource(tuner, hdhrId, channel, "internet360"));
|
||||
list.Add(GetMediaSource(tuner, hdhrId, channel, "internet240"));
|
||||
list.Add(GetMediaSource(tuner, hdhrId, channel, "mobile"));
|
||||
}
|
||||
|
||||
list.Add(GetMediaSource(info, hdhrId, channelInfo, "native"));
|
||||
list.Add(GetMediaSource(tuner, hdhrId, channel, "native"));
|
||||
}
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
list.Add(GetMediaSource(info, hdhrId, channelInfo, "native"));
|
||||
list.Add(GetMediaSource(tuner, hdhrId, channel, "native"));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, ChannelInfo channelInfo, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
|
||||
protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
|
||||
{
|
||||
var tunerCount = info.TunerCount;
|
||||
var tunerCount = tunerHost.TunerCount;
|
||||
|
||||
if (tunerCount > 0)
|
||||
{
|
||||
var tunerHostId = info.Id;
|
||||
var tunerHostId = tunerHost.Id;
|
||||
var liveStreams = currentLiveStreams.Where(i => string.Equals(i.TunerHostId, tunerHostId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (liveStreams.Count() >= tunerCount)
|
||||
@@ -557,26 +553,26 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
|
||||
var profile = streamId.Split('_')[0];
|
||||
|
||||
Logger.LogInformation("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelInfo.Id, streamId, profile);
|
||||
Logger.LogInformation("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channel.Id, streamId, profile);
|
||||
|
||||
var hdhrId = GetHdHrIdFromChannelId(channelInfo.Id);
|
||||
var hdhrId = GetHdHrIdFromChannelId(channel.Id);
|
||||
|
||||
var hdhomerunChannel = channelInfo as HdHomerunChannelInfo;
|
||||
var hdhomerunChannel = channel as HdHomerunChannelInfo;
|
||||
|
||||
var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
|
||||
var modelInfo = await GetModelInfo(tunerHost, false, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (!modelInfo.SupportsTranscoding)
|
||||
{
|
||||
profile = "native";
|
||||
}
|
||||
|
||||
var mediaSource = GetMediaSource(info, hdhrId, channelInfo, profile);
|
||||
var mediaSource = GetMediaSource(tunerHost, hdhrId, channel, profile);
|
||||
|
||||
if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner)
|
||||
{
|
||||
return new HdHomerunUdpStream(
|
||||
mediaSource,
|
||||
info,
|
||||
tunerHost,
|
||||
streamId,
|
||||
new LegacyHdHomerunChannelCommands(hdhomerunChannel.Path),
|
||||
modelInfo.TunerCount,
|
||||
@@ -584,7 +580,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
Logger,
|
||||
Config,
|
||||
_appHost,
|
||||
_networkManager,
|
||||
_streamHelper);
|
||||
}
|
||||
|
||||
@@ -593,7 +588,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
{
|
||||
mediaSource.Protocol = MediaProtocol.Http;
|
||||
|
||||
var httpUrl = channelInfo.Path;
|
||||
var httpUrl = channel.Path;
|
||||
|
||||
// If raw was used, the tuner doesn't support params
|
||||
if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -605,7 +600,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
|
||||
return new SharedHttpStream(
|
||||
mediaSource,
|
||||
info,
|
||||
tunerHost,
|
||||
streamId,
|
||||
FileSystem,
|
||||
_httpClientFactory,
|
||||
@@ -617,7 +612,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
|
||||
return new HdHomerunUdpStream(
|
||||
mediaSource,
|
||||
info,
|
||||
tunerHost,
|
||||
streamId,
|
||||
new HdHomerunChannelCommands(hdhomerunChannel.Number, profile),
|
||||
modelInfo.TunerCount,
|
||||
@@ -625,7 +620,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
Logger,
|
||||
Config,
|
||||
_appHost,
|
||||
_networkManager,
|
||||
_streamHelper);
|
||||
}
|
||||
|
||||
@@ -661,7 +655,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
_modelCache.Clear();
|
||||
}
|
||||
|
||||
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(discoveryDurationMs).Token, cancellationToken).Token;
|
||||
using var timedCancellationToken = new CancellationTokenSource(discoveryDurationMs);
|
||||
using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timedCancellationToken.Token, cancellationToken);
|
||||
cancellationToken = linkedCancellationTokenSource.Token;
|
||||
var list = new List<TunerHostInfo>();
|
||||
|
||||
// Create udp broadcast discovery message
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -25,6 +27,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
{
|
||||
private string _channel;
|
||||
private string _program;
|
||||
|
||||
public LegacyHdHomerunChannelCommands(string url)
|
||||
{
|
||||
// parse url for channel and program
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -10,7 +12,6 @@ using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Dto;
|
||||
@@ -28,7 +29,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly IHdHomerunChannelCommands _channelCommands;
|
||||
private readonly int _numTuners;
|
||||
private readonly INetworkManager _networkManager;
|
||||
|
||||
public HdHomerunUdpStream(
|
||||
MediaSourceInfo mediaSource,
|
||||
@@ -40,12 +40,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
ILogger logger,
|
||||
IConfigurationManager configurationManager,
|
||||
IServerApplicationHost appHost,
|
||||
INetworkManager networkManager,
|
||||
IStreamHelper streamHelper)
|
||||
: base(mediaSource, tunerHostInfo, fileSystem, logger, configurationManager, streamHelper)
|
||||
{
|
||||
_appHost = appHost;
|
||||
_networkManager = networkManager;
|
||||
OriginalStreamId = originalStreamId;
|
||||
_channelCommands = channelCommands;
|
||||
_numTuners = numTuners;
|
||||
@@ -126,7 +124,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||
using (udpClient)
|
||||
using (hdHomerunManager)
|
||||
{
|
||||
if (!(ex is OperationCanceledException))
|
||||
if (ex is not OperationCanceledException)
|
||||
{
|
||||
Logger.LogError(ex, "Error opening live stream:");
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -150,7 +152,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
|
||||
public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token).Token;
|
||||
using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token);
|
||||
cancellationToken = linkedCancellationTokenSource.Token;
|
||||
|
||||
// use non-async filestream on windows along with read due to https://github.com/dotnet/corefx/issues/6039
|
||||
var allowAsync = Environment.OSVersion.Platform != PlatformID.Win32NT;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -27,6 +29,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
{
|
||||
public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
|
||||
{
|
||||
private static readonly string[] _disallowedSharedStreamExtensions =
|
||||
{
|
||||
".mkv",
|
||||
".mp4",
|
||||
".m3u8",
|
||||
".mpd"
|
||||
};
|
||||
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly INetworkManager _networkManager;
|
||||
@@ -61,12 +71,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
return ChannelIdPrefix + info.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
|
||||
protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken)
|
||||
{
|
||||
var channelIdPrefix = GetFullChannelIdPrefix(info);
|
||||
var channelIdPrefix = GetFullChannelIdPrefix(tuner);
|
||||
|
||||
return await new M3uParser(Logger, _httpClientFactory, _appHost)
|
||||
.Parse(info, channelIdPrefix, cancellationToken)
|
||||
return await new M3uParser(Logger, _httpClientFactory)
|
||||
.Parse(tuner, channelIdPrefix, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -86,21 +96,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
return Task.FromResult(list);
|
||||
}
|
||||
|
||||
private static readonly string[] _disallowedSharedStreamExtensions =
|
||||
protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
|
||||
{
|
||||
".mkv",
|
||||
".mp4",
|
||||
".m3u8",
|
||||
".mpd"
|
||||
};
|
||||
|
||||
protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, ChannelInfo channelInfo, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
|
||||
{
|
||||
var tunerCount = info.TunerCount;
|
||||
var tunerCount = tunerHost.TunerCount;
|
||||
|
||||
if (tunerCount > 0)
|
||||
{
|
||||
var tunerHostId = info.Id;
|
||||
var tunerHostId = tunerHost.Id;
|
||||
var liveStreams = currentLiveStreams.Where(i => string.Equals(i.TunerHostId, tunerHostId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (liveStreams.Count() >= tunerCount)
|
||||
@@ -109,7 +111,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
}
|
||||
}
|
||||
|
||||
var sources = await GetChannelStreamMediaSources(info, channelInfo, cancellationToken).ConfigureAwait(false);
|
||||
var sources = await GetChannelStreamMediaSources(tunerHost, channel, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var mediaSource = sources[0];
|
||||
|
||||
@@ -119,23 +121,23 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
|
||||
if (!_disallowedSharedStreamExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClientFactory, Logger, Config, _appHost, _streamHelper);
|
||||
return new SharedHttpStream(mediaSource, tunerHost, streamId, FileSystem, _httpClientFactory, Logger, Config, _appHost, _streamHelper);
|
||||
}
|
||||
}
|
||||
|
||||
return new LiveStream(mediaSource, info, FileSystem, Logger, Config, _streamHelper);
|
||||
return new LiveStream(mediaSource, tunerHost, FileSystem, Logger, Config, _streamHelper);
|
||||
}
|
||||
|
||||
public async Task Validate(TunerHostInfo info)
|
||||
{
|
||||
using (var stream = await new M3uParser(Logger, _httpClientFactory, _appHost).GetListingsStream(info, CancellationToken.None).ConfigureAwait(false))
|
||||
using (var stream = await new M3uParser(Logger, _httpClientFactory).GetListingsStream(info, CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected override Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, ChannelInfo channelInfo, CancellationToken cancellationToken)
|
||||
protected override Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, ChannelInfo channel, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new List<MediaSourceInfo> { CreateMediaSourceInfo(info, channelInfo) });
|
||||
return Task.FromResult(new List<MediaSourceInfo> { CreateMediaSourceInfo(tuner, channel) });
|
||||
}
|
||||
|
||||
protected virtual MediaSourceInfo CreateMediaSourceInfo(TunerHostInfo info, ChannelInfo channel)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -8,9 +10,9 @@ using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -19,15 +21,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
{
|
||||
public class M3uParser
|
||||
{
|
||||
private const string ExtInfPrefix = "#EXTINF:";
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
|
||||
public M3uParser(ILogger logger, IHttpClientFactory httpClientFactory, IServerApplicationHost appHost)
|
||||
public M3uParser(ILogger logger, IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_appHost = appHost;
|
||||
}
|
||||
|
||||
public async Task<List<ChannelInfo>> Parse(TunerHostInfo info, string channelIdPrefix, CancellationToken cancellationToken)
|
||||
@@ -35,79 +37,74 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
// Read the file and display it line by line.
|
||||
using (var reader = new StreamReader(await GetListingsStream(info, cancellationToken).ConfigureAwait(false)))
|
||||
{
|
||||
return GetChannels(reader, channelIdPrefix, info.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ChannelInfo> ParseString(string text, string channelIdPrefix, string tunerHostId)
|
||||
{
|
||||
// Read the file and display it line by line.
|
||||
using (var reader = new StringReader(text))
|
||||
{
|
||||
return GetChannels(reader, channelIdPrefix, tunerHostId);
|
||||
return await GetChannelsAsync(reader, channelIdPrefix, info.Id).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
|
||||
{
|
||||
if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||
if (info == null)
|
||||
{
|
||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
|
||||
if (!string.IsNullOrEmpty(info.UserAgent))
|
||||
{
|
||||
requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
|
||||
}
|
||||
|
||||
var response = await _httpClientFactory.CreateClient(NamedClient.Default)
|
||||
.SendAsync(requestMessage, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
|
||||
return File.OpenRead(info.Url);
|
||||
if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return File.OpenRead(info.Url);
|
||||
}
|
||||
|
||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
|
||||
if (!string.IsNullOrEmpty(info.UserAgent))
|
||||
{
|
||||
requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
|
||||
}
|
||||
|
||||
// Set HttpCompletionOption.ResponseHeadersRead to prevent timeouts on larger files
|
||||
var response = await _httpClientFactory.CreateClient(NamedClient.Default)
|
||||
.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
return await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private const string ExtInfPrefix = "#EXTINF:";
|
||||
|
||||
private List<ChannelInfo> GetChannels(TextReader reader, string channelIdPrefix, string tunerHostId)
|
||||
private async Task<List<ChannelInfo>> GetChannelsAsync(TextReader reader, string channelIdPrefix, string tunerHostId)
|
||||
{
|
||||
var channels = new List<ChannelInfo>();
|
||||
string line;
|
||||
string extInf = string.Empty;
|
||||
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
|
||||
{
|
||||
line = line.Trim();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
var trimmedLine = line.Trim();
|
||||
if (string.IsNullOrWhiteSpace(trimmedLine))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase))
|
||||
if (trimmedLine.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
if (trimmedLine.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
extInf = line.Substring(ExtInfPrefix.Length).Trim();
|
||||
_logger.LogInformation("Found m3u channel: {0}", extInf);
|
||||
extInf = trimmedLine.Substring(ExtInfPrefix.Length).Trim();
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith('#'))
|
||||
else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#'))
|
||||
{
|
||||
var channel = GetChannelnfo(extInf, tunerHostId, line);
|
||||
var channel = GetChannelnfo(extInf, tunerHostId, trimmedLine);
|
||||
if (string.IsNullOrWhiteSpace(channel.Id))
|
||||
{
|
||||
channel.Id = channelIdPrefix + line.GetMD5().ToString("N", CultureInfo.InvariantCulture);
|
||||
channel.Id = channelIdPrefix + trimmedLine.GetMD5().ToString("N", CultureInfo.InvariantCulture);
|
||||
}
|
||||
else
|
||||
{
|
||||
channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
channel.Path = line;
|
||||
channel.Path = trimmedLine;
|
||||
channels.Add(channel);
|
||||
_logger.LogInformation("Parsed channel: {ChannelName}", channel.Name);
|
||||
extInf = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -298,11 +295,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
}
|
||||
}
|
||||
|
||||
attributes.TryGetValue("tvg-name", out string name);
|
||||
string name = nameInExtInf;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
name = nameInExtInf;
|
||||
attributes.TryGetValue("tvg-name", out name);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -89,8 +91,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
_ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
|
||||
|
||||
// OpenedMediaSource.Protocol = MediaProtocol.File;
|
||||
@@ -118,7 +118,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
if (!taskCompletionSource.Task.Result)
|
||||
{
|
||||
Logger.LogWarning("Zero bytes copied from stream {0} to {1} but no exception raised", GetType().Name, TempFilePath);
|
||||
throw new EndOfStreamException(String.Format(CultureInfo.InvariantCulture, "Zero bytes copied from stream {0}", GetType().Name));
|
||||
throw new EndOfStreamException(string.Format(CultureInfo.InvariantCulture, "Zero bytes copied from stream {0}", GetType().Name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user