|
@@ -14,20 +14,16 @@ using Jellyfin.Data.Enums;
|
|
|
using Jellyfin.Data.Events;
|
|
|
using Jellyfin.LiveTv.Configuration;
|
|
|
using MediaBrowser.Common.Extensions;
|
|
|
-using MediaBrowser.Common.Progress;
|
|
|
using MediaBrowser.Controller.Channels;
|
|
|
using MediaBrowser.Controller.Configuration;
|
|
|
using MediaBrowser.Controller.Dto;
|
|
|
using MediaBrowser.Controller.Entities;
|
|
|
using MediaBrowser.Controller.Library;
|
|
|
using MediaBrowser.Controller.LiveTv;
|
|
|
-using MediaBrowser.Controller.Persistence;
|
|
|
-using MediaBrowser.Controller.Providers;
|
|
|
using MediaBrowser.Controller.Sorting;
|
|
|
using MediaBrowser.Model.Dto;
|
|
|
using MediaBrowser.Model.Entities;
|
|
|
using MediaBrowser.Model.Globalization;
|
|
|
-using MediaBrowser.Model.IO;
|
|
|
using MediaBrowser.Model.LiveTv;
|
|
|
using MediaBrowser.Model.Querying;
|
|
|
using MediaBrowser.Model.Tasks;
|
|
@@ -40,24 +36,16 @@ namespace Jellyfin.LiveTv
|
|
|
/// </summary>
|
|
|
public class LiveTvManager : ILiveTvManager
|
|
|
{
|
|
|
- private const int MaxGuideDays = 14;
|
|
|
- private const string ExternalServiceTag = "ExternalServiceId";
|
|
|
-
|
|
|
- private const string EtagKey = "ProgramEtag";
|
|
|
-
|
|
|
private readonly IServerConfigurationManager _config;
|
|
|
private readonly ILogger<LiveTvManager> _logger;
|
|
|
- private readonly IItemRepository _itemRepo;
|
|
|
private readonly IUserManager _userManager;
|
|
|
private readonly IDtoService _dtoService;
|
|
|
private readonly IUserDataManager _userDataManager;
|
|
|
private readonly ILibraryManager _libraryManager;
|
|
|
private readonly ITaskManager _taskManager;
|
|
|
private readonly ILocalizationManager _localization;
|
|
|
- private readonly IFileSystem _fileSystem;
|
|
|
private readonly IChannelManager _channelManager;
|
|
|
private readonly LiveTvDtoService _tvDtoService;
|
|
|
- private readonly ITunerHostManager _tunerHostManager;
|
|
|
|
|
|
private ILiveTvService[] _services = Array.Empty<ILiveTvService>();
|
|
|
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
|
|
@@ -65,31 +53,25 @@ namespace Jellyfin.LiveTv
|
|
|
public LiveTvManager(
|
|
|
IServerConfigurationManager config,
|
|
|
ILogger<LiveTvManager> logger,
|
|
|
- IItemRepository itemRepo,
|
|
|
IUserDataManager userDataManager,
|
|
|
IDtoService dtoService,
|
|
|
IUserManager userManager,
|
|
|
ILibraryManager libraryManager,
|
|
|
ITaskManager taskManager,
|
|
|
ILocalizationManager localization,
|
|
|
- IFileSystem fileSystem,
|
|
|
IChannelManager channelManager,
|
|
|
- LiveTvDtoService liveTvDtoService,
|
|
|
- ITunerHostManager tunerHostManager)
|
|
|
+ LiveTvDtoService liveTvDtoService)
|
|
|
{
|
|
|
_config = config;
|
|
|
_logger = logger;
|
|
|
- _itemRepo = itemRepo;
|
|
|
_userManager = userManager;
|
|
|
_libraryManager = libraryManager;
|
|
|
_taskManager = taskManager;
|
|
|
_localization = localization;
|
|
|
- _fileSystem = fileSystem;
|
|
|
_dtoService = dtoService;
|
|
|
_userDataManager = userDataManager;
|
|
|
_channelManager = channelManager;
|
|
|
_tvDtoService = liveTvDtoService;
|
|
|
- _tunerHostManager = tunerHostManager;
|
|
|
}
|
|
|
|
|
|
public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
|
|
@@ -400,355 +382,6 @@ namespace Jellyfin.LiveTv
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private async Task<LiveTvChannel> GetChannelAsync(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
|
|
|
- {
|
|
|
- var parentFolderId = parentFolder.Id;
|
|
|
- var isNew = false;
|
|
|
- var forceUpdate = false;
|
|
|
-
|
|
|
- var id = _tvDtoService.GetInternalChannelId(serviceName, channelInfo.Id);
|
|
|
-
|
|
|
- var item = _libraryManager.GetItemById(id) as LiveTvChannel;
|
|
|
-
|
|
|
- if (item is null)
|
|
|
- {
|
|
|
- item = new LiveTvChannel
|
|
|
- {
|
|
|
- Name = channelInfo.Name,
|
|
|
- Id = id,
|
|
|
- DateCreated = DateTime.UtcNow
|
|
|
- };
|
|
|
-
|
|
|
- isNew = true;
|
|
|
- }
|
|
|
-
|
|
|
- if (channelInfo.Tags is not null)
|
|
|
- {
|
|
|
- if (!channelInfo.Tags.SequenceEqual(item.Tags, StringComparer.OrdinalIgnoreCase))
|
|
|
- {
|
|
|
- isNew = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.Tags = channelInfo.Tags;
|
|
|
- }
|
|
|
-
|
|
|
- if (!item.ParentId.Equals(parentFolderId))
|
|
|
- {
|
|
|
- isNew = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.ParentId = parentFolderId;
|
|
|
-
|
|
|
- item.ChannelType = channelInfo.ChannelType;
|
|
|
- item.ServiceName = serviceName;
|
|
|
-
|
|
|
- if (!string.Equals(item.GetProviderId(ExternalServiceTag), serviceName, StringComparison.OrdinalIgnoreCase))
|
|
|
- {
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.SetProviderId(ExternalServiceTag, serviceName);
|
|
|
-
|
|
|
- if (!string.Equals(channelInfo.Id, item.ExternalId, StringComparison.Ordinal))
|
|
|
- {
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.ExternalId = channelInfo.Id;
|
|
|
-
|
|
|
- if (!string.Equals(channelInfo.Number, item.Number, StringComparison.Ordinal))
|
|
|
- {
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.Number = channelInfo.Number;
|
|
|
-
|
|
|
- if (!string.Equals(channelInfo.Name, item.Name, StringComparison.Ordinal))
|
|
|
- {
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.Name = channelInfo.Name;
|
|
|
-
|
|
|
- if (!item.HasImage(ImageType.Primary))
|
|
|
- {
|
|
|
- if (!string.IsNullOrWhiteSpace(channelInfo.ImagePath))
|
|
|
- {
|
|
|
- item.SetImagePath(ImageType.Primary, channelInfo.ImagePath);
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
- else if (!string.IsNullOrWhiteSpace(channelInfo.ImageUrl))
|
|
|
- {
|
|
|
- item.SetImagePath(ImageType.Primary, channelInfo.ImageUrl);
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (isNew)
|
|
|
- {
|
|
|
- _libraryManager.CreateItem(item, parentFolder);
|
|
|
- }
|
|
|
- else if (forceUpdate)
|
|
|
- {
|
|
|
- await _libraryManager.UpdateItemAsync(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
|
|
|
- }
|
|
|
-
|
|
|
- return item;
|
|
|
- }
|
|
|
-
|
|
|
- private (LiveTvProgram Item, bool IsNew, bool IsUpdated) GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel)
|
|
|
- {
|
|
|
- var id = _tvDtoService.GetInternalProgramId(info.Id);
|
|
|
-
|
|
|
- var isNew = false;
|
|
|
- var forceUpdate = false;
|
|
|
-
|
|
|
- if (!allExistingPrograms.TryGetValue(id, out LiveTvProgram item))
|
|
|
- {
|
|
|
- isNew = true;
|
|
|
- item = new LiveTvProgram
|
|
|
- {
|
|
|
- Name = info.Name,
|
|
|
- Id = id,
|
|
|
- DateCreated = DateTime.UtcNow,
|
|
|
- DateModified = DateTime.UtcNow
|
|
|
- };
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(info.Etag))
|
|
|
- {
|
|
|
- item.SetProviderId(EtagKey, info.Etag);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!string.Equals(info.ShowId, item.ShowId, StringComparison.OrdinalIgnoreCase))
|
|
|
- {
|
|
|
- item.ShowId = info.ShowId;
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
-
|
|
|
- var seriesId = info.SeriesId;
|
|
|
-
|
|
|
- if (!item.ParentId.Equals(channel.Id))
|
|
|
- {
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.ParentId = channel.Id;
|
|
|
-
|
|
|
- item.Audio = info.Audio;
|
|
|
- item.ChannelId = channel.Id;
|
|
|
- item.CommunityRating ??= info.CommunityRating;
|
|
|
- if ((item.CommunityRating ?? 0).Equals(0))
|
|
|
- {
|
|
|
- item.CommunityRating = null;
|
|
|
- }
|
|
|
-
|
|
|
- item.EpisodeTitle = info.EpisodeTitle;
|
|
|
- item.ExternalId = info.Id;
|
|
|
-
|
|
|
- if (!string.IsNullOrWhiteSpace(seriesId) && !string.Equals(item.ExternalSeriesId, seriesId, StringComparison.Ordinal))
|
|
|
- {
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.ExternalSeriesId = seriesId;
|
|
|
-
|
|
|
- var isSeries = info.IsSeries || !string.IsNullOrEmpty(info.EpisodeTitle);
|
|
|
-
|
|
|
- if (isSeries || !string.IsNullOrEmpty(info.EpisodeTitle))
|
|
|
- {
|
|
|
- item.SeriesName = info.Name;
|
|
|
- }
|
|
|
-
|
|
|
- var tags = new List<string>();
|
|
|
- if (info.IsLive)
|
|
|
- {
|
|
|
- tags.Add("Live");
|
|
|
- }
|
|
|
-
|
|
|
- if (info.IsPremiere)
|
|
|
- {
|
|
|
- tags.Add("Premiere");
|
|
|
- }
|
|
|
-
|
|
|
- if (info.IsNews)
|
|
|
- {
|
|
|
- tags.Add("News");
|
|
|
- }
|
|
|
-
|
|
|
- if (info.IsSports)
|
|
|
- {
|
|
|
- tags.Add("Sports");
|
|
|
- }
|
|
|
-
|
|
|
- if (info.IsKids)
|
|
|
- {
|
|
|
- tags.Add("Kids");
|
|
|
- }
|
|
|
-
|
|
|
- if (info.IsRepeat)
|
|
|
- {
|
|
|
- tags.Add("Repeat");
|
|
|
- }
|
|
|
-
|
|
|
- if (info.IsMovie)
|
|
|
- {
|
|
|
- tags.Add("Movie");
|
|
|
- }
|
|
|
-
|
|
|
- if (isSeries)
|
|
|
- {
|
|
|
- tags.Add("Series");
|
|
|
- }
|
|
|
-
|
|
|
- item.Tags = tags.ToArray();
|
|
|
-
|
|
|
- item.Genres = info.Genres.ToArray();
|
|
|
-
|
|
|
- if (info.IsHD ?? false)
|
|
|
- {
|
|
|
- item.Width = 1280;
|
|
|
- item.Height = 720;
|
|
|
- }
|
|
|
-
|
|
|
- item.IsMovie = info.IsMovie;
|
|
|
- item.IsRepeat = info.IsRepeat;
|
|
|
-
|
|
|
- if (item.IsSeries != isSeries)
|
|
|
- {
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.IsSeries = isSeries;
|
|
|
-
|
|
|
- item.Name = info.Name;
|
|
|
- item.OfficialRating ??= info.OfficialRating;
|
|
|
- item.Overview ??= info.Overview;
|
|
|
- item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
|
|
|
- item.ProviderIds = info.ProviderIds;
|
|
|
-
|
|
|
- foreach (var providerId in info.SeriesProviderIds)
|
|
|
- {
|
|
|
- info.ProviderIds["Series" + providerId.Key] = providerId.Value;
|
|
|
- }
|
|
|
-
|
|
|
- if (item.StartDate != info.StartDate)
|
|
|
- {
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.StartDate = info.StartDate;
|
|
|
-
|
|
|
- if (item.EndDate != info.EndDate)
|
|
|
- {
|
|
|
- forceUpdate = true;
|
|
|
- }
|
|
|
-
|
|
|
- item.EndDate = info.EndDate;
|
|
|
-
|
|
|
- item.ProductionYear = info.ProductionYear;
|
|
|
-
|
|
|
- if (!isSeries || info.IsRepeat)
|
|
|
- {
|
|
|
- item.PremiereDate = info.OriginalAirDate;
|
|
|
- }
|
|
|
-
|
|
|
- item.IndexNumber = info.EpisodeNumber;
|
|
|
- item.ParentIndexNumber = info.SeasonNumber;
|
|
|
-
|
|
|
- if (!item.HasImage(ImageType.Primary))
|
|
|
- {
|
|
|
- if (!string.IsNullOrWhiteSpace(info.ImagePath))
|
|
|
- {
|
|
|
- item.SetImage(
|
|
|
- new ItemImageInfo
|
|
|
- {
|
|
|
- Path = info.ImagePath,
|
|
|
- Type = ImageType.Primary
|
|
|
- },
|
|
|
- 0);
|
|
|
- }
|
|
|
- else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
|
|
|
- {
|
|
|
- item.SetImage(
|
|
|
- new ItemImageInfo
|
|
|
- {
|
|
|
- Path = info.ImageUrl,
|
|
|
- Type = ImageType.Primary
|
|
|
- },
|
|
|
- 0);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!item.HasImage(ImageType.Thumb))
|
|
|
- {
|
|
|
- if (!string.IsNullOrWhiteSpace(info.ThumbImageUrl))
|
|
|
- {
|
|
|
- item.SetImage(
|
|
|
- new ItemImageInfo
|
|
|
- {
|
|
|
- Path = info.ThumbImageUrl,
|
|
|
- Type = ImageType.Thumb
|
|
|
- },
|
|
|
- 0);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!item.HasImage(ImageType.Logo))
|
|
|
- {
|
|
|
- if (!string.IsNullOrWhiteSpace(info.LogoImageUrl))
|
|
|
- {
|
|
|
- item.SetImage(
|
|
|
- new ItemImageInfo
|
|
|
- {
|
|
|
- Path = info.LogoImageUrl,
|
|
|
- Type = ImageType.Logo
|
|
|
- },
|
|
|
- 0);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!item.HasImage(ImageType.Backdrop))
|
|
|
- {
|
|
|
- if (!string.IsNullOrWhiteSpace(info.BackdropImageUrl))
|
|
|
- {
|
|
|
- item.SetImage(
|
|
|
- new ItemImageInfo
|
|
|
- {
|
|
|
- Path = info.BackdropImageUrl,
|
|
|
- Type = ImageType.Backdrop
|
|
|
- },
|
|
|
- 0);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var isUpdated = false;
|
|
|
- if (isNew)
|
|
|
- {
|
|
|
- }
|
|
|
- else if (forceUpdate || string.IsNullOrWhiteSpace(info.Etag))
|
|
|
- {
|
|
|
- isUpdated = true;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- var etag = info.Etag;
|
|
|
-
|
|
|
- if (!string.Equals(etag, item.GetProviderId(EtagKey), StringComparison.OrdinalIgnoreCase))
|
|
|
- {
|
|
|
- item.SetProviderId(EtagKey, etag);
|
|
|
- isUpdated = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (isNew || isUpdated)
|
|
|
- {
|
|
|
- item.OnMetadataChanged();
|
|
|
- }
|
|
|
-
|
|
|
- return (item, isNew, isUpdated);
|
|
|
- }
|
|
|
-
|
|
|
public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
|
|
|
{
|
|
|
var program = _libraryManager.GetItemById(id);
|
|
@@ -1000,293 +633,6 @@ namespace Jellyfin.LiveTv
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- internal Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
|
|
|
- {
|
|
|
- return RefreshChannelsInternal(progress, cancellationToken);
|
|
|
- }
|
|
|
-
|
|
|
- private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken)
|
|
|
- {
|
|
|
- await EmbyTV.EmbyTV.Current.CreateRecordingFolders().ConfigureAwait(false);
|
|
|
-
|
|
|
- await _tunerHostManager.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
|
|
|
-
|
|
|
- var numComplete = 0;
|
|
|
- double progressPerService = _services.Length == 0
|
|
|
- ? 0
|
|
|
- : 1.0 / _services.Length;
|
|
|
-
|
|
|
- var newChannelIdList = new List<Guid>();
|
|
|
- var newProgramIdList = new List<Guid>();
|
|
|
-
|
|
|
- var cleanDatabase = true;
|
|
|
-
|
|
|
- foreach (var service in _services)
|
|
|
- {
|
|
|
- cancellationToken.ThrowIfCancellationRequested();
|
|
|
-
|
|
|
- _logger.LogDebug("Refreshing guide from {Name}", service.Name);
|
|
|
-
|
|
|
- try
|
|
|
- {
|
|
|
- var innerProgress = new ActionableProgress<double>();
|
|
|
- innerProgress.RegisterAction(p => progress.Report(p * progressPerService));
|
|
|
-
|
|
|
- var idList = await RefreshChannelsInternal(service, innerProgress, cancellationToken).ConfigureAwait(false);
|
|
|
-
|
|
|
- newChannelIdList.AddRange(idList.Item1);
|
|
|
- newProgramIdList.AddRange(idList.Item2);
|
|
|
- }
|
|
|
- catch (OperationCanceledException)
|
|
|
- {
|
|
|
- throw;
|
|
|
- }
|
|
|
- catch (Exception ex)
|
|
|
- {
|
|
|
- cleanDatabase = false;
|
|
|
- _logger.LogError(ex, "Error refreshing channels for service");
|
|
|
- }
|
|
|
-
|
|
|
- numComplete++;
|
|
|
- double percent = numComplete;
|
|
|
- percent /= _services.Length;
|
|
|
-
|
|
|
- progress.Report(100 * percent);
|
|
|
- }
|
|
|
-
|
|
|
- if (cleanDatabase)
|
|
|
- {
|
|
|
- CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { BaseItemKind.LiveTvChannel }, progress, cancellationToken);
|
|
|
- CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { BaseItemKind.LiveTvProgram }, progress, cancellationToken);
|
|
|
- }
|
|
|
-
|
|
|
- var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
|
|
|
-
|
|
|
- if (coreService is not null)
|
|
|
- {
|
|
|
- await coreService.RefreshSeriesTimers(cancellationToken).ConfigureAwait(false);
|
|
|
- await coreService.RefreshTimers(cancellationToken).ConfigureAwait(false);
|
|
|
- }
|
|
|
-
|
|
|
- // Load these now which will prefetch metadata
|
|
|
- var dtoOptions = new DtoOptions();
|
|
|
- var fields = dtoOptions.Fields.ToList();
|
|
|
- dtoOptions.Fields = fields.ToArray();
|
|
|
-
|
|
|
- progress.Report(100);
|
|
|
- }
|
|
|
-
|
|
|
- private async Task<Tuple<List<Guid>, List<Guid>>> RefreshChannelsInternal(ILiveTvService service, ActionableProgress<double> progress, CancellationToken cancellationToken)
|
|
|
- {
|
|
|
- progress.Report(10);
|
|
|
-
|
|
|
- var allChannelsList = (await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false))
|
|
|
- .Select(i => new Tuple<string, ChannelInfo>(service.Name, i))
|
|
|
- .ToList();
|
|
|
-
|
|
|
- var list = new List<LiveTvChannel>();
|
|
|
-
|
|
|
- var numComplete = 0;
|
|
|
- var parentFolder = GetInternalLiveTvFolder(cancellationToken);
|
|
|
-
|
|
|
- foreach (var channelInfo in allChannelsList)
|
|
|
- {
|
|
|
- cancellationToken.ThrowIfCancellationRequested();
|
|
|
-
|
|
|
- try
|
|
|
- {
|
|
|
- var item = await GetChannelAsync(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken).ConfigureAwait(false);
|
|
|
-
|
|
|
- list.Add(item);
|
|
|
- }
|
|
|
- catch (OperationCanceledException)
|
|
|
- {
|
|
|
- throw;
|
|
|
- }
|
|
|
- catch (Exception ex)
|
|
|
- {
|
|
|
- _logger.LogError(ex, "Error getting channel information for {Name}", channelInfo.Item2.Name);
|
|
|
- }
|
|
|
-
|
|
|
- numComplete++;
|
|
|
- double percent = numComplete;
|
|
|
- percent /= allChannelsList.Count;
|
|
|
-
|
|
|
- progress.Report((5 * percent) + 10);
|
|
|
- }
|
|
|
-
|
|
|
- progress.Report(15);
|
|
|
-
|
|
|
- numComplete = 0;
|
|
|
- var programs = new List<Guid>();
|
|
|
- var channels = new List<Guid>();
|
|
|
-
|
|
|
- var guideDays = GetGuideDays();
|
|
|
-
|
|
|
- _logger.LogInformation("Refreshing guide with {0} days of guide data", guideDays);
|
|
|
-
|
|
|
- cancellationToken.ThrowIfCancellationRequested();
|
|
|
-
|
|
|
- foreach (var currentChannel in list)
|
|
|
- {
|
|
|
- channels.Add(currentChannel.Id);
|
|
|
- cancellationToken.ThrowIfCancellationRequested();
|
|
|
-
|
|
|
- try
|
|
|
- {
|
|
|
- var start = DateTime.UtcNow.AddHours(-1);
|
|
|
- var end = start.AddDays(guideDays);
|
|
|
-
|
|
|
- var isMovie = false;
|
|
|
- var isSports = false;
|
|
|
- var isNews = false;
|
|
|
- var isKids = false;
|
|
|
- var iSSeries = false;
|
|
|
-
|
|
|
- var channelPrograms = (await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false)).ToList();
|
|
|
-
|
|
|
- var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
|
|
|
- {
|
|
|
- IncludeItemTypes = new[] { BaseItemKind.LiveTvProgram },
|
|
|
- ChannelIds = new Guid[] { currentChannel.Id },
|
|
|
- DtoOptions = new DtoOptions(true)
|
|
|
- }).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
|
|
|
-
|
|
|
- var newPrograms = new List<LiveTvProgram>();
|
|
|
- var updatedPrograms = new List<BaseItem>();
|
|
|
-
|
|
|
- foreach (var program in channelPrograms)
|
|
|
- {
|
|
|
- var programTuple = GetProgram(program, existingPrograms, currentChannel);
|
|
|
- var programItem = programTuple.Item;
|
|
|
-
|
|
|
- if (programTuple.IsNew)
|
|
|
- {
|
|
|
- newPrograms.Add(programItem);
|
|
|
- }
|
|
|
- else if (programTuple.IsUpdated)
|
|
|
- {
|
|
|
- updatedPrograms.Add(programItem);
|
|
|
- }
|
|
|
-
|
|
|
- programs.Add(programItem.Id);
|
|
|
-
|
|
|
- isMovie |= program.IsMovie;
|
|
|
- iSSeries |= program.IsSeries;
|
|
|
- isSports |= program.IsSports;
|
|
|
- isNews |= program.IsNews;
|
|
|
- isKids |= program.IsKids;
|
|
|
- }
|
|
|
-
|
|
|
- _logger.LogDebug("Channel {0} has {1} new programs and {2} updated programs", currentChannel.Name, newPrograms.Count, updatedPrograms.Count);
|
|
|
-
|
|
|
- if (newPrograms.Count > 0)
|
|
|
- {
|
|
|
- _libraryManager.CreateItems(newPrograms, null, cancellationToken);
|
|
|
- }
|
|
|
-
|
|
|
- if (updatedPrograms.Count > 0)
|
|
|
- {
|
|
|
- await _libraryManager.UpdateItemsAsync(
|
|
|
- updatedPrograms,
|
|
|
- currentChannel,
|
|
|
- ItemUpdateType.MetadataImport,
|
|
|
- cancellationToken).ConfigureAwait(false);
|
|
|
- }
|
|
|
-
|
|
|
- currentChannel.IsMovie = isMovie;
|
|
|
- currentChannel.IsNews = isNews;
|
|
|
- currentChannel.IsSports = isSports;
|
|
|
- currentChannel.IsSeries = iSSeries;
|
|
|
-
|
|
|
- if (isKids)
|
|
|
- {
|
|
|
- currentChannel.AddTag("Kids");
|
|
|
- }
|
|
|
-
|
|
|
- await currentChannel.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
|
|
|
- await currentChannel.RefreshMetadata(
|
|
|
- new MetadataRefreshOptions(new DirectoryService(_fileSystem))
|
|
|
- {
|
|
|
- ForceSave = true
|
|
|
- },
|
|
|
- cancellationToken).ConfigureAwait(false);
|
|
|
- }
|
|
|
- catch (OperationCanceledException)
|
|
|
- {
|
|
|
- throw;
|
|
|
- }
|
|
|
- catch (Exception ex)
|
|
|
- {
|
|
|
- _logger.LogError(ex, "Error getting programs for channel {Name}", currentChannel.Name);
|
|
|
- }
|
|
|
-
|
|
|
- numComplete++;
|
|
|
- double percent = numComplete / (double)allChannelsList.Count;
|
|
|
-
|
|
|
- progress.Report((85 * percent) + 15);
|
|
|
- }
|
|
|
-
|
|
|
- progress.Report(100);
|
|
|
- return new Tuple<List<Guid>, List<Guid>>(channels, programs);
|
|
|
- }
|
|
|
-
|
|
|
- private void CleanDatabaseInternal(Guid[] currentIdList, BaseItemKind[] validTypes, IProgress<double> progress, CancellationToken cancellationToken)
|
|
|
- {
|
|
|
- var list = _itemRepo.GetItemIdsList(new InternalItemsQuery
|
|
|
- {
|
|
|
- IncludeItemTypes = validTypes,
|
|
|
- DtoOptions = new DtoOptions(false)
|
|
|
- });
|
|
|
-
|
|
|
- var numComplete = 0;
|
|
|
-
|
|
|
- foreach (var itemId in list)
|
|
|
- {
|
|
|
- cancellationToken.ThrowIfCancellationRequested();
|
|
|
-
|
|
|
- if (itemId.Equals(default))
|
|
|
- {
|
|
|
- // Somehow some invalid data got into the db. It probably predates the boundary checking
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if (!currentIdList.Contains(itemId))
|
|
|
- {
|
|
|
- var item = _libraryManager.GetItemById(itemId);
|
|
|
-
|
|
|
- if (item is not null)
|
|
|
- {
|
|
|
- _libraryManager.DeleteItem(
|
|
|
- item,
|
|
|
- new DeleteOptions
|
|
|
- {
|
|
|
- DeleteFileLocation = false,
|
|
|
- DeleteFromExternalProvider = false
|
|
|
- },
|
|
|
- false);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- numComplete++;
|
|
|
- double percent = numComplete / (double)list.Count;
|
|
|
-
|
|
|
- progress.Report(100 * percent);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private double GetGuideDays()
|
|
|
- {
|
|
|
- var config = _config.GetLiveTvConfiguration();
|
|
|
-
|
|
|
- if (config.GuideDays.HasValue)
|
|
|
- {
|
|
|
- return Math.Max(1, Math.Min(config.GuideDays.Value, MaxGuideDays));
|
|
|
- }
|
|
|
-
|
|
|
- return 7;
|
|
|
- }
|
|
|
-
|
|
|
private async Task<QueryResult<BaseItem>> GetEmbyRecordingsAsync(RecordingQuery query, DtoOptions dtoOptions, User user)
|
|
|
{
|
|
|
if (user is null)
|
|
@@ -2056,18 +1402,6 @@ namespace Jellyfin.LiveTv
|
|
|
await service.UpdateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
|
|
|
}
|
|
|
|
|
|
- public GuideInfo GetGuideInfo()
|
|
|
- {
|
|
|
- var startDate = DateTime.UtcNow;
|
|
|
- var endDate = startDate.AddDays(GetGuideDays());
|
|
|
-
|
|
|
- return new GuideInfo
|
|
|
- {
|
|
|
- StartDate = startDate,
|
|
|
- EndDate = endDate
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
private LiveTvServiceInfo[] GetServiceInfos()
|
|
|
{
|
|
|
return Services.Select(GetServiceInfo).ToArray();
|