Clean up Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

This commit is contained in:
Bond_009 2019-12-04 21:39:27 +01:00
parent f3ca4631c3
commit 5cab79c839
11 changed files with 319 additions and 298 deletions

View File

@ -128,7 +128,7 @@ namespace Emby.Server.Implementations.Library
return streams; return streams;
} }
public async Task<List<MediaSourceInfo>> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken) public async Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
{ {
var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
@ -290,7 +290,7 @@ namespace Emby.Server.Implementations.Library
return await GetLiveStream(liveStreamId, cancellationToken).ConfigureAwait(false); return await GetLiveStream(liveStreamId, cancellationToken).ConfigureAwait(false);
} }
var sources = await GetPlayackMediaSources(item, null, false, enablePathSubstitution, cancellationToken).ConfigureAwait(false); var sources = await GetPlaybackMediaSources(item, null, false, enablePathSubstitution, cancellationToken).ConfigureAwait(false);
return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
@ -74,7 +75,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
DecompressionMethod = CompressionMethod.None DecompressionMethod = CompressionMethod.None
}; };
using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false)) using (var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false))
{ {
_logger.LogInformation("Opened recording stream from tuner provider"); _logger.LogInformation("Opened recording stream from tuner provider");

View File

@ -1,3 +1,6 @@
#pragma warning disable SA1600
#pragma warning disable CS1591
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@ -40,6 +43,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable
{ {
public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
private const int TunerDiscoveryDurationMs = 3000;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
@ -57,19 +64,21 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly IProviderManager _providerManager; private readonly IProviderManager _providerManager;
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly IProcessFactory _processFactory; private readonly IProcessFactory _processFactory;
private IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly IStreamHelper _streamHelper;
public static EmbyTV Current;
public event EventHandler<GenericEventArgs<TimerInfo>> TimerCreated;
public event EventHandler<GenericEventArgs<string>> TimerCancelled;
private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings = private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase); new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
private readonly IStreamHelper _streamHelper; private readonly ConcurrentDictionary<string, EpgChannelData> _epgChannels =
new ConcurrentDictionary<string, EpgChannelData>(StringComparer.OrdinalIgnoreCase);
public EmbyTV(IServerApplicationHost appHost, private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1);
private bool _disposed = false;
public EmbyTV(
IServerApplicationHost appHost,
IStreamHelper streamHelper, IStreamHelper streamHelper,
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
ILogger logger, ILogger logger,
@ -103,12 +112,40 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers.json")); _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers.json"));
_timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json")); _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json"));
_timerProvider.TimerFired += _timerProvider_TimerFired; _timerProvider.TimerFired += OnTimerProviderTimerFired;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
} }
private void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) public event EventHandler<GenericEventArgs<TimerInfo>> TimerCreated;
public event EventHandler<GenericEventArgs<string>> TimerCancelled;
public static EmbyTV Current { get; private set; }
/// <inheritdoc />
public string Name => "Emby";
public string DataPath => Path.Combine(_config.CommonApplicationPaths.DataPath, "livetv");
/// <inheritdoc />
public string HomePageUrl => "https://github.com/jellyfin/jellyfin";
private string DefaultRecordingPath => Path.Combine(DataPath, "recordings");
private string RecordingPath
{
get
{
var path = GetConfiguration().RecordingPath;
return string.IsNullOrWhiteSpace(path)
? DefaultRecordingPath
: path;
}
}
private void OnNamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
{ {
if (string.Equals(e.Key, "livetv", StringComparison.OrdinalIgnoreCase)) if (string.Equals(e.Key, "livetv", StringComparison.OrdinalIgnoreCase))
{ {
@ -116,11 +153,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
} }
public async Task Start() public Task Start()
{ {
_timerProvider.RestartTimers(); _timerProvider.RestartTimers();
await CreateRecordingFolders().ConfigureAwait(false); return CreateRecordingFolders();
} }
private async void OnRecordingFoldersChanged() private async void OnRecordingFoldersChanged()
@ -132,8 +169,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
try try
{ {
var recordingFolders = GetRecordingFolders(); var recordingFolders = GetRecordingFolders().ToArray();
var virtualFolders = _libraryManager.GetVirtualFolders() var virtualFolders = _libraryManager.GetVirtualFolders()
.ToList(); .ToList();
@ -241,26 +277,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
} }
public string Name => "Emby";
public string DataPath => Path.Combine(_config.CommonApplicationPaths.DataPath, "livetv");
private string DefaultRecordingPath => Path.Combine(DataPath, "recordings");
private string RecordingPath
{
get
{
var path = GetConfiguration().RecordingPath;
return string.IsNullOrWhiteSpace(path)
? DefaultRecordingPath
: path;
}
}
public string HomePageUrl => "https://github.com/jellyfin/jellyfin";
public async Task RefreshSeriesTimers(CancellationToken cancellationToken) public async Task RefreshSeriesTimers(CancellationToken cancellationToken)
{ {
var seriesTimers = await GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false); var seriesTimers = await GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
@ -339,7 +355,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
catch (NotSupportedException) catch (NotSupportedException)
{ {
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -351,7 +366,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return list; return list;
} }
private async Task AddMetadata(IListingsProvider provider, ListingsProviderInfo info, List<ChannelInfo> tunerChannels, bool enableCache, CancellationToken cancellationToken) private async Task AddMetadata(
IListingsProvider provider,
ListingsProviderInfo info,
IEnumerable<ChannelInfo> tunerChannels,
bool enableCache,
CancellationToken cancellationToken)
{ {
var epgChannels = await GetEpgChannels(provider, info, enableCache, cancellationToken).ConfigureAwait(false); var epgChannels = await GetEpgChannels(provider, info, enableCache, cancellationToken).ConfigureAwait(false);
@ -363,8 +383,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
if (!string.IsNullOrWhiteSpace(epgChannel.Name)) if (!string.IsNullOrWhiteSpace(epgChannel.Name))
{ {
//tunerChannel.Name = epgChannel.Name; // tunerChannel.Name = epgChannel.Name;
} }
if (!string.IsNullOrWhiteSpace(epgChannel.ImageUrl)) if (!string.IsNullOrWhiteSpace(epgChannel.ImageUrl))
{ {
tunerChannel.ImageUrl = epgChannel.ImageUrl; tunerChannel.ImageUrl = epgChannel.ImageUrl;
@ -373,10 +394,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
} }
private readonly ConcurrentDictionary<string, EpgChannelData> _epgChannels = private async Task<EpgChannelData> GetEpgChannels(
new ConcurrentDictionary<string, EpgChannelData>(StringComparer.OrdinalIgnoreCase); IListingsProvider provider,
ListingsProviderInfo info,
private async Task<EpgChannelData> GetEpgChannels(IListingsProvider provider, ListingsProviderInfo info, bool enableCache, CancellationToken cancellationToken) bool enableCache,
CancellationToken cancellationToken)
{ {
if (!enableCache || !_epgChannels.TryGetValue(info.Id, out var result)) if (!enableCache || !_epgChannels.TryGetValue(info.Id, out var result))
{ {
@ -394,59 +416,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return result; return result;
} }
private class EpgChannelData
{
public EpgChannelData(List<ChannelInfo> channels)
{
ChannelsById = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
ChannelsByNumber = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
ChannelsByName = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
foreach (var channel in channels)
{
ChannelsById[channel.Id] = channel;
if (!string.IsNullOrEmpty(channel.Number))
{
ChannelsByNumber[channel.Number] = channel;
}
var normalizedName = NormalizeName(channel.Name ?? string.Empty);
if (!string.IsNullOrWhiteSpace(normalizedName))
{
ChannelsByName[normalizedName] = channel;
}
}
}
private Dictionary<string, ChannelInfo> ChannelsById { get; set; }
private Dictionary<string, ChannelInfo> ChannelsByNumber { get; set; }
private Dictionary<string, ChannelInfo> ChannelsByName { get; set; }
public ChannelInfo GetChannelById(string id)
{
ChannelInfo result = null;
ChannelsById.TryGetValue(id, out result);
return result;
}
public ChannelInfo GetChannelByNumber(string number)
{
ChannelsByNumber.TryGetValue(number, out var result);
return result;
}
public ChannelInfo GetChannelByName(string name)
{
ChannelsByName.TryGetValue(name, out var result);
return result;
}
}
private async Task<ChannelInfo> GetEpgChannelFromTunerChannel(IListingsProvider provider, ListingsProviderInfo info, ChannelInfo tunerChannel, CancellationToken cancellationToken) private async Task<ChannelInfo> GetEpgChannelFromTunerChannel(IListingsProvider provider, ListingsProviderInfo info, ChannelInfo tunerChannel, CancellationToken cancellationToken)
{ {
var epgChannels = await GetEpgChannels(provider, info, true, cancellationToken).ConfigureAwait(false); var epgChannels = await GetEpgChannels(provider, info, true, cancellationToken).ConfigureAwait(false);
@ -463,6 +432,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return mapping.Value; return mapping.Value;
} }
} }
return channelId; return channelId;
} }
@ -476,7 +446,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return GetEpgChannelFromTunerChannel(info.ChannelMappings, tunerChannel, epgChannels); return GetEpgChannelFromTunerChannel(info.ChannelMappings, tunerChannel, epgChannels);
} }
private ChannelInfo GetEpgChannelFromTunerChannel(NameValuePair[] mappings, ChannelInfo tunerChannel, EpgChannelData epgChannelData) private ChannelInfo GetEpgChannelFromTunerChannel(
NameValuePair[] mappings,
ChannelInfo tunerChannel,
EpgChannelData epgChannelData)
{ {
if (!string.IsNullOrWhiteSpace(tunerChannel.Id)) if (!string.IsNullOrWhiteSpace(tunerChannel.Id))
{ {
@ -537,7 +510,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (!string.IsNullOrWhiteSpace(tunerChannel.Name)) if (!string.IsNullOrWhiteSpace(tunerChannel.Name))
{ {
var normalizedName = NormalizeName(tunerChannel.Name); var normalizedName = EpgChannelData.NormalizeName(tunerChannel.Name);
var channel = epgChannelData.GetChannelByName(normalizedName); var channel = epgChannelData.GetChannelByName(normalizedName);
@ -550,11 +523,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return null; return null;
} }
private static string NormalizeName(string value)
{
return value.Replace(" ", string.Empty).Replace("-", string.Empty);
}
public async Task<List<ChannelInfo>> GetChannelsForListingsProvider(ListingsProviderInfo listingsProvider, CancellationToken cancellationToken) public async Task<List<ChannelInfo>> GetChannelsForListingsProvider(ListingsProviderInfo listingsProvider, CancellationToken cancellationToken)
{ {
var list = new List<ChannelInfo>(); var list = new List<ChannelInfo>();
@ -600,6 +568,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
_seriesTimerProvider.Delete(remove); _seriesTimerProvider.Delete(remove);
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -689,6 +658,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
programInfo = GetProgramInfoFromCache(timer); programInfo = GetProgramInfoFromCache(timer);
} }
if (programInfo == null) if (programInfo == null)
{ {
_logger.LogInformation("Unable to find program with Id {0}. Will search using start date", timer.ProgramId); _logger.LogInformation("Unable to find program with Id {0}. Will search using start date", timer.ProgramId);
@ -703,10 +673,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
timer.IsManual = true; timer.IsManual = true;
_timerProvider.Add(timer); _timerProvider.Add(timer);
if (TimerCreated != null) TimerCreated?.Invoke(this, new GenericEventArgs<TimerInfo>(timer));
{
TimerCreated(this, new GenericEventArgs<TimerInfo>(timer));
}
return Task.FromResult(timer.Id); return Task.FromResult(timer.Id);
} }
@ -800,7 +767,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
// Only update if not currently active // Only update if not currently active
if (!_activeRecordings.TryGetValue(updatedTimer.Id, out var activeRecordingInfo)) if (!_activeRecordings.TryGetValue(updatedTimer.Id, out _))
{ {
existingTimer.PrePaddingSeconds = updatedTimer.PrePaddingSeconds; existingTimer.PrePaddingSeconds = updatedTimer.PrePaddingSeconds;
existingTimer.PostPaddingSeconds = updatedTimer.PostPaddingSeconds; existingTimer.PostPaddingSeconds = updatedTimer.PostPaddingSeconds;
@ -846,6 +813,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
return info.Path; return info.Path;
} }
return null; return null;
} }
@ -870,9 +838,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
return null; return null;
} }
return recording; return recording;
} }
} }
return null; return null;
} }
@ -1061,13 +1031,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
mediaSource.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture) + "_" + mediaSource.Id; mediaSource.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture) + "_" + mediaSource.Id;
//if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing)
//{
// var ticks = (DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value).Ticks - TimeSpan.FromSeconds(10).Ticks;
// ticks = Math.Max(0, ticks);
// mediaSource.Path += "?t=" + ticks.ToString(CultureInfo.InvariantCulture) + "&s=" + mediaSource.DateLiveStreamOpened.Value.Ticks.ToString(CultureInfo.InvariantCulture);
//}
return mediaSource; return mediaSource;
} }
@ -1091,7 +1054,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
catch (NotImplementedException) catch (NotImplementedException)
{ {
} }
} }
@ -1142,7 +1104,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return Task.CompletedTask; return Task.CompletedTask;
} }
async void _timerProvider_TimerFired(object sender, GenericEventArgs<TimerInfo> e) private async void OnTimerProviderTimerFired(object sender, GenericEventArgs<TimerInfo> e)
{ {
var timer = e.Argument; var timer = e.Argument;
@ -1177,7 +1139,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -1221,7 +1182,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (timer.SeasonNumber.HasValue) if (timer.SeasonNumber.HasValue)
{ {
folderName = string.Format("Season {0}", timer.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture)); folderName = string.Format(
CultureInfo.InvariantCulture,
"Season {0}",
timer.SeasonNumber.Value);
recordPath = Path.Combine(recordPath, folderName); recordPath = Path.Combine(recordPath, folderName);
} }
} }
@ -1275,6 +1239,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
recordPath = Path.Combine(recordPath, "Sports"); recordPath = Path.Combine(recordPath, "Sports");
} }
recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(timer.Name).Trim()); recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(timer.Name).Trim());
} }
else else
@ -1283,6 +1248,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
recordPath = Path.Combine(recordPath, "Other"); recordPath = Path.Combine(recordPath, "Other");
} }
recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(timer.Name).Trim()); recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(timer.Name).Trim());
} }
@ -1304,6 +1270,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
programInfo = GetProgramInfoFromCache(timer); programInfo = GetProgramInfoFromCache(timer);
} }
if (programInfo == null) if (programInfo == null)
{ {
_logger.LogInformation("Unable to find program with Id {0}. Will search using start date", timer.ProgramId); _logger.LogInformation("Unable to find program with Id {0}. Will search using start date", timer.ProgramId);
@ -1315,9 +1282,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
CopyProgramInfoToTimerInfo(programInfo, timer); CopyProgramInfoToTimerInfo(programInfo, timer);
} }
string seriesPath = null;
var remoteMetadata = await FetchInternetMetadata(timer, CancellationToken.None).ConfigureAwait(false); var remoteMetadata = await FetchInternetMetadata(timer, CancellationToken.None).ConfigureAwait(false);
var recordPath = GetRecordingPath(timer, remoteMetadata, out seriesPath); var recordPath = GetRecordingPath(timer, remoteMetadata, out string seriesPath);
var recordingStatus = RecordingStatus.New; var recordingStatus = RecordingStatus.New;
string liveStreamId = null; string liveStreamId = null;
@ -1326,19 +1292,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try try
{ {
var allMediaSources = await _mediaSourceManager.GetPlayackMediaSources(channelItem, null, true, false, CancellationToken.None).ConfigureAwait(false); var allMediaSources = await _mediaSourceManager.GetPlaybackMediaSources(channelItem, null, true, false, CancellationToken.None).ConfigureAwait(false);
var mediaStreamInfo = allMediaSources[0]; var mediaStreamInfo = allMediaSources[0];
IDirectStreamProvider directStreamProvider = null; IDirectStreamProvider directStreamProvider = null;
if (mediaStreamInfo.RequiresOpening) if (mediaStreamInfo.RequiresOpening)
{ {
var liveStreamResponse = await _mediaSourceManager.OpenLiveStreamInternal(new LiveStreamRequest var liveStreamResponse = await _mediaSourceManager.OpenLiveStreamInternal(
{ new LiveStreamRequest
ItemId = channelItem.Id, {
OpenToken = mediaStreamInfo.OpenToken ItemId = channelItem.Id,
OpenToken = mediaStreamInfo.OpenToken
}, CancellationToken.None).ConfigureAwait(false); },
CancellationToken.None).ConfigureAwait(false);
mediaStreamInfo = liveStreamResponse.Item1.MediaSource; mediaStreamInfo = liveStreamResponse.Item1.MediaSource;
liveStreamId = mediaStreamInfo.LiveStreamId; liveStreamId = mediaStreamInfo.LiveStreamId;
@ -1412,12 +1379,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (recordingStatus != RecordingStatus.Completed && DateTime.UtcNow < timer.EndDate && timer.RetryCount < 10) if (recordingStatus != RecordingStatus.Completed && DateTime.UtcNow < timer.EndDate && timer.RetryCount < 10)
{ {
const int retryIntervalSeconds = 60; const int RetryIntervalSeconds = 60;
_logger.LogInformation("Retrying recording in {0} seconds.", retryIntervalSeconds); _logger.LogInformation("Retrying recording in {0} seconds.", RetryIntervalSeconds);
timer.Status = RecordingStatus.New; timer.Status = RecordingStatus.New;
timer.PrePaddingSeconds = 0; timer.PrePaddingSeconds = 0;
timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds); timer.StartDate = DateTime.UtcNow.AddSeconds(RetryIntervalSeconds);
timer.RetryCount++; timer.RetryCount++;
_timerProvider.AddOrUpdate(timer); _timerProvider.AddOrUpdate(timer);
} }
@ -1538,6 +1505,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
return; return;
} }
if (string.IsNullOrWhiteSpace(seriesPath)) if (string.IsNullOrWhiteSpace(seriesPath))
{ {
return; return;
@ -1576,34 +1544,34 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
DeleteLibraryItemsForTimers(timersToDelete); DeleteLibraryItemsForTimers(timersToDelete);
var librarySeries = _libraryManager.FindByPath(seriesPath, true) as Folder; var librarySeries = _libraryManager.FindByPath(seriesPath, true) as Folder;
if (librarySeries == null) if (librarySeries == null)
{ {
return; return;
} }
var episodesToDelete = (librarySeries.GetItemList(new InternalItemsQuery var episodesToDelete = librarySeries.GetItemList(
{ new InternalItemsQuery
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) }, {
IsVirtualItem = false, OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
IsFolder = false, IsVirtualItem = false,
Recursive = true, IsFolder = false,
DtoOptions = new DtoOptions(true) Recursive = true,
DtoOptions = new DtoOptions(true)
})) }).Where(i => i.IsFileProtocol && File.Exists(i.Path))
.Where(i => i.IsFileProtocol && File.Exists(i.Path)) .Skip(seriesTimer.KeepUpTo - 1)
.Skip(seriesTimer.KeepUpTo - 1) .ToList();
.ToList();
foreach (var item in episodesToDelete) foreach (var item in episodesToDelete)
{ {
try try
{ {
_libraryManager.DeleteItem(item, new DeleteOptions _libraryManager.DeleteItem(
{ item,
DeleteFileLocation = true new DeleteOptions
{
}, true); DeleteFileLocation = true
},
true);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -1617,7 +1585,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
} }
private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1);
private void DeleteLibraryItemsForTimers(List<TimerInfo> timers) private void DeleteLibraryItemsForTimers(List<TimerInfo> timers)
{ {
foreach (var timer in timers) foreach (var timer in timers)
@ -1644,22 +1611,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (libraryItem != null) if (libraryItem != null)
{ {
_libraryManager.DeleteItem(libraryItem, new DeleteOptions _libraryManager.DeleteItem(
{ libraryItem,
DeleteFileLocation = true new DeleteOptions
{
}, true); DeleteFileLocation = true
},
true);
} }
else else
{ {
try if (File.Exists(timer.RecordingPath))
{ {
_fileSystem.DeleteFile(timer.RecordingPath); _fileSystem.DeleteFile(timer.RecordingPath);
} }
catch (IOException)
{
}
} }
_timerProvider.Delete(timer); _timerProvider.Delete(timer);
@ -1690,16 +1655,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return true; return true;
} }
var hasRecordingAtPath = _activeRecordings return _activeRecordings
.Values .Values
.ToList() .ToList()
.Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase)); .Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
if (hasRecordingAtPath)
{
return true;
}
return false;
} }
private IRecorder GetRecorder(MediaSourceInfo mediaSource) private IRecorder GetRecorder(MediaSourceInfo mediaSource)
@ -1756,17 +1715,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void Process_Exited(object sender, EventArgs e) private void Process_Exited(object sender, EventArgs e)
{ {
var process = (IProcess)sender; using (var process = (IProcess)sender)
try
{ {
_logger.LogInformation("Recording post-processing script completed with exit code {ExitCode}", process.ExitCode); _logger.LogInformation("Recording post-processing script completed with exit code {ExitCode}", process.ExitCode);
}
catch
{
process.Dispose();
} }
process.Dispose();
} }
private async Task SaveRecordingImage(string recordingPath, LiveTvProgram program, ItemImageInfo image) private async Task SaveRecordingImage(string recordingPath, LiveTvProgram program, ItemImageInfo image)
@ -1776,44 +1730,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
image = await _libraryManager.ConvertImageToLocal(program, image, 0).ConfigureAwait(false); image = await _libraryManager.ConvertImageToLocal(program, image, 0).ConfigureAwait(false);
} }
string imageSaveFilenameWithoutExtension = null; string imageSaveFilenameWithoutExtension = image.Type switch
switch (image.Type)
{ {
case ImageType.Primary: ImageType.Primary => program.IsSeries ? Path.GetFileNameWithoutExtension(recordingPath) + "-thumb" : "poster",
ImageType.Logo => "logo",
ImageType.Thumb => program.IsSeries ? Path.GetFileNameWithoutExtension(recordingPath) + "-thumb" : "landscape",
ImageType.Backdrop => "fanart",
_ => null
};
if (program.IsSeries) if (imageSaveFilenameWithoutExtension == null)
{
imageSaveFilenameWithoutExtension = Path.GetFileNameWithoutExtension(recordingPath) + "-thumb";
}
else
{
imageSaveFilenameWithoutExtension = "poster";
}
break;
case ImageType.Logo:
imageSaveFilenameWithoutExtension = "logo";
break;
case ImageType.Thumb:
if (program.IsSeries)
{
imageSaveFilenameWithoutExtension = Path.GetFileNameWithoutExtension(recordingPath) + "-thumb";
}
else
{
imageSaveFilenameWithoutExtension = "landscape";
}
break;
case ImageType.Backdrop:
imageSaveFilenameWithoutExtension = "fanart";
break;
default:
break;
}
if (string.IsNullOrWhiteSpace(imageSaveFilenameWithoutExtension))
{ {
return; return;
} }
@ -1897,7 +1823,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
Limit = 1, Limit = 1,
ExternalId = timer.ProgramId, ExternalId = timer.ProgramId,
DtoOptions = new DtoOptions(true) DtoOptions = new DtoOptions(true)
}).FirstOrDefault() as LiveTvProgram; }).FirstOrDefault() as LiveTvProgram;
// dummy this up // dummy this up
@ -1921,11 +1846,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
program.AddGenre("Sports"); program.AddGenre("Sports");
} }
if (timer.IsKids) if (timer.IsKids)
{ {
program.AddGenre("Kids"); program.AddGenre("Kids");
program.AddGenre("Children"); program.AddGenre("Children");
} }
if (timer.IsNews) if (timer.IsNews)
{ {
program.AddGenre("News"); program.AddGenre("News");
@ -1980,14 +1907,17 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
writer.WriteElementString("id", id); writer.WriteElementString("id", id);
} }
if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id)) if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id))
{ {
writer.WriteElementString("imdb_id", id); writer.WriteElementString("imdb_id", id);
} }
if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out id)) if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out id))
{ {
writer.WriteElementString("tmdbid", id); writer.WriteElementString("tmdbid", id);
} }
if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id)) if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id))
{ {
writer.WriteElementString("zap2itid", id); writer.WriteElementString("zap2itid", id);
@ -2014,7 +1944,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
} }
public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
private void SaveVideoNfo(TimerInfo timer, string recordingPath, BaseItem item, bool lockData) private void SaveVideoNfo(TimerInfo timer, string recordingPath, BaseItem item, bool lockData)
{ {
var nfoPath = Path.ChangeExtension(recordingPath, ".nfo"); var nfoPath = Path.ChangeExtension(recordingPath, ".nfo");
@ -2056,7 +1985,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
var formatString = options.ReleaseDateFormat; var formatString = options.ReleaseDateFormat;
writer.WriteElementString("aired", premiereDate.Value.ToLocalTime().ToString(formatString)); writer.WriteElementString(
"aired",
premiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
} }
if (item.IndexNumber.HasValue) if (item.IndexNumber.HasValue)
@ -2087,12 +2018,18 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
var formatString = options.ReleaseDateFormat; var formatString = options.ReleaseDateFormat;
writer.WriteElementString("premiered", item.PremiereDate.Value.ToLocalTime().ToString(formatString)); writer.WriteElementString(
writer.WriteElementString("releasedate", item.PremiereDate.Value.ToLocalTime().ToString(formatString)); "premiered",
item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
writer.WriteElementString(
"releasedate",
item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
} }
} }
writer.WriteElementString("dateadded", DateTime.UtcNow.ToLocalTime().ToString(DateAddedFormat)); writer.WriteElementString(
"dateadded",
DateTime.UtcNow.ToLocalTime().ToString(DateAddedFormat, CultureInfo.InvariantCulture));
if (item.ProductionYear.HasValue) if (item.ProductionYear.HasValue)
{ {
@ -2106,7 +2043,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var overview = (item.Overview ?? string.Empty) var overview = (item.Overview ?? string.Empty)
.StripHtml() .StripHtml()
.Replace("&quot;", "'"); .Replace("&quot;", "'", StringComparison.Ordinal);
writer.WriteElementString("plot", overview); writer.WriteElementString("plot", overview);
@ -2214,17 +2151,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
private static bool IsPersonType(PersonInfo person, string type) private static bool IsPersonType(PersonInfo person, string type)
{ => string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase)
return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase); || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
}
private void AddGenre(List<string> genres, string genre)
{
if (!genres.Contains(genre, StringComparer.OrdinalIgnoreCase))
{
genres.Add(genre);
}
}
private LiveTvProgram GetProgramInfoFromCache(string programId) private LiveTvProgram GetProgramInfoFromCache(string programId)
{ {
@ -2283,25 +2211,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return false; return false;
} }
if (!seriesTimer.RecordAnyTime) if (!seriesTimer.RecordAnyTime
&& Math.Abs(seriesTimer.StartDate.TimeOfDay.Ticks - timer.StartDate.TimeOfDay.Ticks) >= TimeSpan.FromMinutes(10).Ticks)
{ {
if (Math.Abs(seriesTimer.StartDate.TimeOfDay.Ticks - timer.StartDate.TimeOfDay.Ticks) >= TimeSpan.FromMinutes(10).Ticks) return true;
{
return true;
}
} }
//if (!seriesTimer.Days.Contains(timer.StartDate.ToLocalTime().DayOfWeek))
//{
// return true;
//}
if (seriesTimer.RecordNewOnly && timer.IsRepeat) if (seriesTimer.RecordNewOnly && timer.IsRepeat)
{ {
return true; return true;
} }
if (!seriesTimer.RecordAnyChannel && !string.Equals(timer.ChannelId, seriesTimer.ChannelId, StringComparison.OrdinalIgnoreCase)) if (!seriesTimer.RecordAnyChannel
&& !string.Equals(timer.ChannelId, seriesTimer.ChannelId, StringComparison.OrdinalIgnoreCase))
{ {
return true; return true;
} }
@ -2346,7 +2268,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
var allTimers = GetTimersForSeries(seriesTimer).ToList(); var allTimers = GetTimersForSeries(seriesTimer).ToList();
var enabledTimersForSeries = new List<TimerInfo>(); var enabledTimersForSeries = new List<TimerInfo>();
foreach (var timer in allTimers) foreach (var timer in allTimers)
{ {
@ -2369,10 +2290,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
enabledTimersForSeries.Add(timer); enabledTimersForSeries.Add(timer);
} }
_timerProvider.Add(timer); _timerProvider.Add(timer);
TimerCreated?.Invoke(this, new GenericEventArgs<TimerInfo>(timer)); TimerCreated?.Invoke(this, new GenericEventArgs<TimerInfo>(timer));
} }
// Only update if not currently active - test both new timer and existing in case Id's are different // Only update if not currently active - test both new timer and existing in case Id's are different
// Id's could be different if the timer was created manually prior to series timer creation // Id's could be different if the timer was created manually prior to series timer creation
else if (!_activeRecordings.TryGetValue(timer.Id, out _) && !_activeRecordings.TryGetValue(existingTimer.Id, out _)) else if (!_activeRecordings.TryGetValue(timer.Id, out _) && !_activeRecordings.TryGetValue(existingTimer.Id, out _))
@ -2508,13 +2431,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
if (!tempChannelCache.TryGetValue(parent.ChannelId, out LiveTvChannel channel)) if (!tempChannelCache.TryGetValue(parent.ChannelId, out LiveTvChannel channel))
{ {
channel = _libraryManager.GetItemList(new InternalItemsQuery channel = _libraryManager.GetItemList(
{ new InternalItemsQuery
IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name }, {
ItemIds = new[] { parent.ChannelId }, IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name },
DtoOptions = new DtoOptions() ItemIds = new[] { parent.ChannelId },
DtoOptions = new DtoOptions()
}).Cast<LiveTvChannel>().FirstOrDefault(); }).FirstOrDefault() as LiveTvChannel;
if (channel != null && !string.IsNullOrWhiteSpace(channel.ExternalId)) if (channel != null && !string.IsNullOrWhiteSpace(channel.ExternalId))
{ {
@ -2567,13 +2490,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
if (!tempChannelCache.TryGetValue(programInfo.ChannelId, out LiveTvChannel channel)) if (!tempChannelCache.TryGetValue(programInfo.ChannelId, out LiveTvChannel channel))
{ {
channel = _libraryManager.GetItemList(new InternalItemsQuery channel = _libraryManager.GetItemList(
{ new InternalItemsQuery
IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name }, {
ItemIds = new[] { programInfo.ChannelId }, IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name },
DtoOptions = new DtoOptions() ItemIds = new[] { programInfo.ChannelId },
DtoOptions = new DtoOptions()
}).Cast<LiveTvChannel>().FirstOrDefault(); }).FirstOrDefault() as LiveTvChannel;
if (channel != null && !string.IsNullOrWhiteSpace(channel.ExternalId)) if (channel != null && !string.IsNullOrWhiteSpace(channel.ExternalId))
{ {
@ -2618,10 +2541,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var providerId in timerInfo.ProviderIds) foreach (var providerId in timerInfo.ProviderIds)
{ {
var srch = "Series"; const string Search = "Series";
if (providerId.Key.StartsWith(srch, StringComparison.OrdinalIgnoreCase)) if (providerId.Key.StartsWith(Search, StringComparison.OrdinalIgnoreCase))
{ {
seriesProviderIds[providerId.Key.Substring(srch.Length)] = providerId.Value; seriesProviderIds[providerId.Key.Substring(Search.Length)] = providerId.Value;
} }
} }
@ -2632,12 +2555,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
if ((program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue) || !string.IsNullOrWhiteSpace(program.EpisodeTitle)) if ((program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue) || !string.IsNullOrWhiteSpace(program.EpisodeTitle))
{ {
var seriesIds = _libraryManager.GetItemIds(new InternalItemsQuery var seriesIds = _libraryManager.GetItemIds(
{ new InternalItemsQuery
IncludeItemTypes = new[] { typeof(Series).Name }, {
Name = program.Name IncludeItemTypes = new[] { typeof(Series).Name },
Name = program.Name
}).ToArray(); }).ToArray();
if (seriesIds.Length == 0) if (seriesIds.Length == 0)
{ {
@ -2666,59 +2589,70 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return false; return false;
} }
private bool _disposed; /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_disposed = true; Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
_recordingDeleteSemaphore.Dispose();
}
foreach (var pair in _activeRecordings.ToList()) foreach (var pair in _activeRecordings.ToList())
{ {
pair.Value.CancellationTokenSource.Cancel(); pair.Value.CancellationTokenSource.Cancel();
} }
_disposed = true;
} }
public List<VirtualFolderInfo> GetRecordingFolders() public IEnumerable<VirtualFolderInfo> GetRecordingFolders()
{ {
var list = new List<VirtualFolderInfo>();
var defaultFolder = RecordingPath; var defaultFolder = RecordingPath;
var defaultName = "Recordings"; var defaultName = "Recordings";
if (Directory.Exists(defaultFolder)) if (Directory.Exists(defaultFolder))
{ {
list.Add(new VirtualFolderInfo yield return new VirtualFolderInfo
{ {
Locations = new string[] { defaultFolder }, Locations = new string[] { defaultFolder },
Name = defaultName Name = defaultName
}); };
} }
var customPath = GetConfiguration().MovieRecordingPath; var customPath = GetConfiguration().MovieRecordingPath;
if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath)) if (!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase) && Directory.Exists(customPath))
{ {
list.Add(new VirtualFolderInfo yield return new VirtualFolderInfo
{ {
Locations = new string[] { customPath }, Locations = new string[] { customPath },
Name = "Recorded Movies", Name = "Recorded Movies",
CollectionType = CollectionType.Movies CollectionType = CollectionType.Movies
}); };
} }
customPath = GetConfiguration().SeriesRecordingPath; customPath = GetConfiguration().SeriesRecordingPath;
if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath)) if (!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase) && Directory.Exists(customPath))
{ {
list.Add(new VirtualFolderInfo yield return new VirtualFolderInfo
{ {
Locations = new string[] { customPath }, Locations = new string[] { customPath },
Name = "Recorded Shows", Name = "Recorded Shows",
CollectionType = CollectionType.TvShows CollectionType = CollectionType.TvShows
}); };
} }
return list;
} }
private const int TunerDiscoveryDurationMs = 3000;
public async Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken) public async Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
{ {
var list = new List<TunerHostInfo>(); var list = new List<TunerHostInfo>();
@ -2737,6 +2671,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
discoveredDevices = discoveredDevices.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparer.OrdinalIgnoreCase)) discoveredDevices = discoveredDevices.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparer.OrdinalIgnoreCase))
.ToList(); .ToList();
} }
list.AddRange(discoveredDevices); list.AddRange(discoveredDevices);
} }
@ -2773,11 +2708,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
} }
private async Task<List<TunerHostInfo>> DiscoverDevices(ITunerHost host, int discoveryDuationMs, CancellationToken cancellationToken) private async Task<List<TunerHostInfo>> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken)
{ {
try try
{ {
var discoveredDevices = await host.DiscoverDevices(discoveryDuationMs, cancellationToken).ConfigureAwait(false); var discoveredDevices = await host.DiscoverDevices(discoveryDurationMs, cancellationToken).ConfigureAwait(false);
foreach (var device in discoveredDevices) foreach (var device in discoveredDevices)
{ {
@ -2794,11 +2729,4 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
} }
} }
} }
public static class ConfigurationExtension
{
public static XbmcMetadataOptions GetNfoConfiguration(this IConfigurationManager manager)
{
return manager.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
}
}
} }

View File

@ -0,0 +1,68 @@
#pragma warning disable SA1600
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using MediaBrowser.Controller.LiveTv;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
internal class EpgChannelData
{
public EpgChannelData(IEnumerable<ChannelInfo> channels)
{
ChannelsById = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
ChannelsByNumber = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
ChannelsByName = new Dictionary<string, ChannelInfo>(StringComparer.OrdinalIgnoreCase);
foreach (var channel in channels)
{
ChannelsById[channel.Id] = channel;
if (!string.IsNullOrEmpty(channel.Number))
{
ChannelsByNumber[channel.Number] = channel;
}
var normalizedName = NormalizeName(channel.Name ?? string.Empty);
if (!string.IsNullOrWhiteSpace(normalizedName))
{
ChannelsByName[normalizedName] = channel;
}
}
}
private Dictionary<string, ChannelInfo> ChannelsById { get; set; }
private Dictionary<string, ChannelInfo> ChannelsByNumber { get; set; }
private Dictionary<string, ChannelInfo> ChannelsByName { get; set; }
public ChannelInfo GetChannelById(string id)
{
ChannelsById.TryGetValue(id, out var result);
return result;
}
public ChannelInfo GetChannelByNumber(string number)
{
ChannelsByNumber.TryGetValue(number, out var result);
return result;
}
public ChannelInfo GetChannelByName(string name)
{
ChannelsByName.TryGetValue(name, out var result);
return result;
}
public static string NormalizeName(string value)
{
return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
}
}
}

View File

@ -0,0 +1,19 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
/// <summary>
/// Class containing extension methods for working with the nfo configuration.
/// </summary>
public static class NfoConfigurationExtensions
{
/// <summary>
/// Gets the nfo configuration.
/// </summary>
/// <param name="configurationManager">The configuration manager.</param>
/// <returns>The nfo configuration.</returns>
public static XbmcMetadataOptions GetNfoConfiguration(this IConfigurationManager configurationManager)
=> configurationManager.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
}
}

View File

@ -1,3 +1,6 @@
#pragma warning disable SA1600
#pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -35,7 +38,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv namespace Emby.Server.Implementations.LiveTv
{ {
/// <summary> /// <summary>
/// Class LiveTvManager /// Class LiveTvManager.
/// </summary> /// </summary>
public class LiveTvManager : ILiveTvManager, IDisposable public class LiveTvManager : ILiveTvManager, IDisposable
{ {

View File

@ -768,7 +768,7 @@ namespace MediaBrowser.Api.Playback
if (mediaSource == null) if (mediaSource == null)
{ {
var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList(); var mediaSources = (await MediaSourceManager.GetPlaybackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList();
mediaSource = string.IsNullOrEmpty(request.MediaSourceId) mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources[0] ? mediaSources[0]

View File

@ -270,7 +270,7 @@ namespace MediaBrowser.Api.Playback
try try
{ {
// TODO handle supportedLiveMediaTypes ? // TODO handle supportedLiveMediaTypes ?
mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false); mediaSources = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -39,9 +39,9 @@ namespace MediaBrowser.Controller.Library
List<MediaStream> GetMediaStreams(MediaStreamQuery query); List<MediaStream> GetMediaStreams(MediaStreamQuery query);
/// <summary> /// <summary>
/// Gets the playack media sources. /// Gets the playback media sources.
/// </summary> /// </summary>
Task<List<MediaSourceInfo>> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken); Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the static media sources. /// Gets the static media sources.

View File

@ -126,7 +126,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw new ArgumentNullException(nameof(mediaSourceId)); throw new ArgumentNullException(nameof(mediaSourceId));
} }
var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false); var mediaSources = await _mediaSourceManager.GetPlaybackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false);
var mediaSource = mediaSources var mediaSource = mediaSources
.First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));

View File

@ -31,7 +31,9 @@
<Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design"> <Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design">
<!-- disable warning CA1031: Do not catch general exception types --> <!-- disable warning CA1031: Do not catch general exception types -->
<Rule Id="CA1031" Action="Info" /> <Rule Id="CA1031" Action="Info" />
<!-- disable warning CA1062: Validate arguments of public methods --> <!-- disable warning CA1056: URI properties should not be strings -->
<Rule Id="CA1056" Action="Info" />
<!-- disable warning CA1720: Identifiers should not contain type names -->
<Rule Id="CA1062" Action="Info" /> <Rule Id="CA1062" Action="Info" />
<!-- disable warning CA1720: Identifiers should not contain type names --> <!-- disable warning CA1720: Identifiers should not contain type names -->
<Rule Id="CA1720" Action="Info" /> <Rule Id="CA1720" Action="Info" />