aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/LiveTv
diff options
context:
space:
mode:
authorEric Reed <ebr@mediabrowser3.com>2013-12-04 15:07:56 -0500
committerEric Reed <ebr@mediabrowser3.com>2013-12-04 15:07:56 -0500
commit6819be81601f6a95a60ce2735474ae0015d19bff (patch)
tree7e2743455e53d4a028fae789f2fc74a7c5ae87b9 /MediaBrowser.Server.Implementations/LiveTv
parent190be6311fbdf3a73f9c8e330f44edafe7764284 (diff)
parentcb882a4b48e9cf03cd363c54d93338ad62153e7e (diff)
Merge branch 'master' of https://github.com/MediaBrowser/MediaBrowser
Diffstat (limited to 'MediaBrowser.Server.Implementations/LiveTv')
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs95
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs553
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs59
3 files changed, 702 insertions, 5 deletions
diff --git a/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs
new file mode 100644
index 0000000000..322948bade
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs
@@ -0,0 +1,95 @@
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using System;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.LiveTv
+{
+ public class ChannelImageProvider : BaseMetadataProvider
+ {
+ private readonly ILiveTvManager _liveTvManager;
+ private readonly IProviderManager _providerManager;
+
+ public ChannelImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, ILiveTvManager liveTvManager, IProviderManager providerManager)
+ : base(logManager, configurationManager)
+ {
+ _liveTvManager = liveTvManager;
+ _providerManager = providerManager;
+ }
+
+ public override bool Supports(BaseItem item)
+ {
+ return item is Channel;
+ }
+
+ protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
+ {
+ return !item.HasImage(ImageType.Primary);
+ }
+
+ public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
+ {
+ if (item.HasImage(ImageType.Primary))
+ {
+ SetLastRefreshed(item, DateTime.UtcNow);
+ return true;
+ }
+
+ try
+ {
+ await DownloadImage(item, cancellationToken).ConfigureAwait(false);
+ }
+ catch (HttpException ex)
+ {
+ // Don't fail the provider on a 404
+ if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
+ {
+ throw;
+ }
+ }
+
+
+ SetLastRefreshed(item, DateTime.UtcNow);
+ return true;
+ }
+
+ private async Task DownloadImage(BaseItem item, CancellationToken cancellationToken)
+ {
+ var channel = (Channel)item;
+
+ var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, channel.ServiceName, StringComparison.OrdinalIgnoreCase));
+
+ if (service != null)
+ {
+ var response = await service.GetChannelImageAsync(channel.ChannelId, cancellationToken).ConfigureAwait(false);
+
+ // Dummy up the original url
+ var url = channel.ServiceName + channel.ChannelId;
+
+ await _providerManager.SaveImage(channel, response.Stream, response.MimeType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ public override MetadataProviderPriority Priority
+ {
+ get { return MetadataProviderPriority.Second; }
+ }
+
+ public override ItemUpdateType ItemUpdateType
+ {
+ get
+ {
+ return ItemUpdateType.ImageUpdate;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 05bac17c3b..4fd1f0e43b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1,6 +1,23 @@
-using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.Localization;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
+using System;
using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv
{
@@ -9,7 +26,35 @@ namespace MediaBrowser.Server.Implementations.LiveTv
/// </summary>
public class LiveTvManager : ILiveTvManager
{
+ private readonly IServerApplicationPaths _appPaths;
+ private readonly IFileSystem _fileSystem;
+ private readonly ILogger _logger;
+ private readonly IItemRepository _itemRepo;
+ private readonly IImageProcessor _imageProcessor;
+
+ private readonly IUserManager _userManager;
+ private readonly ILocalizationManager _localization;
+ private readonly IUserDataManager _userDataManager;
+ private readonly IDtoService _dtoService;
+
private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
+
+ private List<Channel> _channels = new List<Channel>();
+ private List<ProgramInfoDto> _programs = new List<ProgramInfoDto>();
+
+ public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserManager userManager, ILocalizationManager localization, IUserDataManager userDataManager, IDtoService dtoService)
+ {
+ _appPaths = appPaths;
+ _fileSystem = fileSystem;
+ _logger = logger;
+ _itemRepo = itemRepo;
+ _imageProcessor = imageProcessor;
+ _userManager = userManager;
+ _localization = localization;
+ _userDataManager = userDataManager;
+ _dtoService = dtoService;
+ }
+
/// <summary>
/// Gets the services.
/// </summary>
@@ -32,17 +77,515 @@ namespace MediaBrowser.Server.Implementations.LiveTv
/// Gets the channel info dto.
/// </summary>
/// <param name="info">The info.</param>
+ /// <param name="user">The user.</param>
/// <returns>ChannelInfoDto.</returns>
- public ChannelInfoDto GetChannelInfoDto(ChannelInfo info)
+ public ChannelInfoDto GetChannelInfoDto(Channel info, User user)
{
- return new ChannelInfoDto
+ var dto = new ChannelInfoDto
{
Name = info.Name,
ServiceName = info.ServiceName,
ChannelType = info.ChannelType,
- Id = info.Id,
- Number = info.Number
+ Number = info.ChannelNumber,
+ Type = info.GetType().Name,
+ Id = info.Id.ToString("N"),
+ MediaType = info.MediaType
+ };
+
+ if (user != null)
+ {
+ dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, info.GetUserDataKey()));
+ }
+
+ var imageTag = GetLogoImageTag(info);
+
+ if (imageTag.HasValue)
+ {
+ dto.ImageTags[ImageType.Primary] = imageTag.Value;
+ }
+
+ return dto;
+ }
+
+ private Guid? GetLogoImageTag(Channel info)
+ {
+ var path = info.PrimaryImagePath;
+
+ if (string.IsNullOrEmpty(path))
+ {
+ return null;
+ }
+
+ try
+ {
+ return _imageProcessor.GetImageCacheTag(info, ImageType.Primary, path);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting channel image info for {0}", ex, info.Name);
+ }
+
+ return null;
+ }
+
+ public QueryResult<ChannelInfoDto> GetChannels(ChannelQuery query)
+ {
+ var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
+
+ IEnumerable<Channel> channels = _channels;
+
+ if (user != null)
+ {
+ channels = channels.Where(i => i.IsParentalAllowed(user, _localization))
+ .OrderBy(i =>
+ {
+ double number = 0;
+
+ if (!string.IsNullOrEmpty(i.ChannelNumber))
+ {
+ double.TryParse(i.ChannelNumber, out number);
+ }
+
+ return number;
+
+ });
+ }
+
+ var returnChannels = channels.OrderBy(i =>
+ {
+ double number = 0;
+
+ if (!string.IsNullOrEmpty(i.ChannelNumber))
+ {
+ double.TryParse(i.ChannelNumber, out number);
+ }
+
+ return number;
+
+ }).ThenBy(i => i.Name)
+ .Select(i => GetChannelInfoDto(i, user))
+ .ToArray();
+
+ return new QueryResult<ChannelInfoDto>
+ {
+ Items = returnChannels,
+ TotalRecordCount = returnChannels.Length
+ };
+ }
+
+ public Channel GetChannel(string id)
+ {
+ var guid = new Guid(id);
+
+ return _channels.FirstOrDefault(i => i.Id == guid);
+ }
+
+ public ChannelInfoDto GetChannelInfoDto(string id, string userId)
+ {
+ var channel = GetChannel(id);
+
+ var user = string.IsNullOrEmpty(userId) ? null : _userManager.GetUserById(new Guid(userId));
+
+ return channel == null ? null : GetChannelInfoDto(channel, user);
+ }
+
+ private ProgramInfoDto GetProgramInfoDto(ProgramInfo program, Channel channel)
+ {
+ var id = GetInternalProgramIdId(channel.ServiceName, program.Id).ToString("N");
+
+ return new ProgramInfoDto
+ {
+ ChannelId = channel.Id.ToString("N"),
+ Description = program.Description,
+ EndDate = program.EndDate,
+ Genres = program.Genres,
+ ExternalId = program.Id,
+ Id = id,
+ Name = program.Name,
+ ServiceName = channel.ServiceName,
+ StartDate = program.StartDate,
+ OfficialRating = program.OfficialRating,
+ Quality = program.Quality,
+ OriginalAirDate = program.OriginalAirDate,
+ Audio = program.Audio,
+ CommunityRating = program.CommunityRating,
+ AspectRatio = program.AspectRatio,
+ IsRepeat = program.IsRepeat,
+ EpisodeTitle = program.EpisodeTitle
+ };
+ }
+
+ private Guid GetInternalChannelId(string serviceName, string externalChannelId, string channelName)
+ {
+ var name = serviceName + externalChannelId + channelName;
+
+ return name.ToLower().GetMBId(typeof(Channel));
+ }
+
+ private Guid GetInternalProgramIdId(string serviceName, string externalProgramId)
+ {
+ var name = serviceName + externalProgramId;
+
+ return name.ToLower().GetMD5();
+ }
+
+ private async Task<Channel> GetChannel(ChannelInfo channelInfo, string serviceName, CancellationToken cancellationToken)
+ {
+ var path = Path.Combine(_appPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(serviceName), _fileSystem.GetValidFilename(channelInfo.Name));
+
+ var fileInfo = new DirectoryInfo(path);
+
+ var isNew = false;
+
+ if (!fileInfo.Exists)
+ {
+ Directory.CreateDirectory(path);
+ fileInfo = new DirectoryInfo(path);
+
+ if (!fileInfo.Exists)
+ {
+ throw new IOException("Path not created: " + path);
+ }
+
+ isNew = true;
+ }
+
+ var id = GetInternalChannelId(serviceName, channelInfo.Id, channelInfo.Name);
+
+ var item = _itemRepo.RetrieveItem(id) as Channel;
+
+ if (item == null)
+ {
+ item = new Channel
+ {
+ Name = channelInfo.Name,
+ Id = id,
+ DateCreated = _fileSystem.GetCreationTimeUtc(fileInfo),
+ DateModified = _fileSystem.GetLastWriteTimeUtc(fileInfo),
+ Path = path,
+ ChannelId = channelInfo.Id,
+ ChannelNumber = channelInfo.Number,
+ ServiceName = serviceName
+ };
+
+ isNew = true;
+ }
+
+ // Set this now so we don't cause additional file system access during provider executions
+ item.ResetResolveArgs(fileInfo);
+
+ await item.RefreshMetadata(cancellationToken, forceSave: isNew, resetResolveArgs: false);
+
+ return item;
+ }
+
+ public async Task<QueryResult<ProgramInfoDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken)
+ {
+ IEnumerable<ProgramInfoDto> programs = _programs
+ .OrderBy(i => i.StartDate)
+ .ThenBy(i => i.EndDate);
+
+ if (!string.IsNullOrEmpty(query.ServiceName))
+ {
+ programs = programs.Where(i => string.Equals(i.ServiceName, query.ServiceName, StringComparison.OrdinalIgnoreCase));
+ }
+
+ if (query.ChannelIdList.Length > 0)
+ {
+ var guids = query.ChannelIdList.Select(i => new Guid(i)).ToList();
+
+ programs = programs.Where(i => guids.Contains(new Guid(i.ChannelId)));
+ }
+
+ var returnArray = programs.ToArray();
+
+ return new QueryResult<ProgramInfoDto>
+ {
+ Items = returnArray,
+ TotalRecordCount = returnArray.Length
+ };
+ }
+
+ internal async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ // Avoid implicitly captured closure
+ var currentCancellationToken = cancellationToken;
+
+ var channelTasks = _services.Select(i => GetChannels(i, currentCancellationToken));
+
+ progress.Report(10);
+
+ var results = await Task.WhenAll(channelTasks).ConfigureAwait(false);
+
+ var allChannels = results.SelectMany(i => i).ToList();
+
+ var list = new List<Channel>();
+ var programs = new List<ProgramInfoDto>();
+
+ var numComplete = 0;
+
+ foreach (var channelInfo in allChannels)
+ {
+ try
+ {
+ var item = await GetChannel(channelInfo.Item2, channelInfo.Item1, cancellationToken).ConfigureAwait(false);
+
+ var service = _services.First(i => string.Equals(channelInfo.Item1, i.Name, StringComparison.OrdinalIgnoreCase));
+
+ var channelPrograms = await service.GetProgramsAsync(channelInfo.Item2.Id, cancellationToken).ConfigureAwait(false);
+
+ programs.AddRange(channelPrograms.Select(program => GetProgramInfoDto(program, item)));
+
+ list.Add(item);
+ }
+ catch (OperationCanceledException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting channel information for {0}", ex, channelInfo.Item2.Name);
+ }
+
+ numComplete++;
+ double percent = numComplete;
+ percent /= allChannels.Count;
+
+ progress.Report(90 * percent + 10);
+ }
+
+ _programs = programs;
+ _channels = list;
+ }
+
+ private async Task<IEnumerable<Tuple<string, ChannelInfo>>> GetChannels(ILiveTvService service, CancellationToken cancellationToken)
+ {
+ var channels = await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false);
+
+ return channels.Select(i => new Tuple<string, ChannelInfo>(service.Name, i));
+ }
+
+ private async Task<IEnumerable<RecordingInfoDto>> GetRecordings(ILiveTvService service, CancellationToken cancellationToken)
+ {
+ var recordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
+
+ return recordings.Select(i => GetRecordingInfoDto(i, service));
+ }
+
+ private RecordingInfoDto GetRecordingInfoDto(RecordingInfo info, ILiveTvService service)
+ {
+ var id = service.Name + info.ChannelId + info.Id;
+ id = id.GetMD5().ToString("N");
+
+ var dto = new RecordingInfoDto
+ {
+ ChannelName = info.ChannelName,
+ Description = info.Description,
+ EndDate = info.EndDate,
+ Name = info.Name,
+ StartDate = info.StartDate,
+ Id = id,
+ ExternalId = info.Id,
+ ChannelId = GetInternalChannelId(service.Name, info.ChannelId, info.ChannelName).ToString("N"),
+ Status = info.Status,
+ Path = info.Path,
+ Genres = info.Genres,
+ IsRepeat = info.IsRepeat,
+ EpisodeTitle = info.EpisodeTitle,
+ ChannelType = info.ChannelType,
+ MediaType = info.ChannelType == ChannelType.Radio ? MediaType.Audio : MediaType.Video,
+ CommunityRating = info.CommunityRating,
+ OfficialRating = info.OfficialRating
};
+
+ var duration = info.EndDate - info.StartDate;
+ dto.DurationMs = Convert.ToInt32(duration.TotalMilliseconds);
+
+ if (!string.IsNullOrEmpty(info.ProgramId))
+ {
+ dto.ProgramId = GetInternalProgramIdId(service.Name, info.ProgramId).ToString("N");
+ }
+
+ return dto;
+ }
+
+ public async Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, CancellationToken cancellationToken)
+ {
+ var list = new List<RecordingInfoDto>();
+
+ foreach (var service in GetServices(query.ServiceName, query.ChannelId))
+ {
+ var recordings = await GetRecordings(service, cancellationToken).ConfigureAwait(false);
+
+ list.AddRange(recordings);
+ }
+
+ if (!string.IsNullOrEmpty(query.ChannelId))
+ {
+ list = list.Where(i => string.Equals(i.ChannelId, query.ChannelId))
+ .ToList();
+ }
+
+ var returnArray = list.OrderByDescending(i => i.StartDate)
+ .ToArray();
+
+ return new QueryResult<RecordingInfoDto>
+ {
+ Items = returnArray,
+ TotalRecordCount = returnArray.Length
+ };
+ }
+
+ private IEnumerable<ILiveTvService> GetServices(string serviceName, string channelId)
+ {
+ IEnumerable<ILiveTvService> services = _services;
+
+ if (string.IsNullOrEmpty(serviceName) && !string.IsNullOrEmpty(channelId))
+ {
+ var channelIdGuid = new Guid(channelId);
+
+ serviceName = _channels.Where(i => i.Id == channelIdGuid)
+ .Select(i => i.ServiceName)
+ .FirstOrDefault();
+ }
+
+ if (!string.IsNullOrEmpty(serviceName))
+ {
+ services = services.Where(i => string.Equals(i.Name, serviceName, StringComparison.OrdinalIgnoreCase));
+ }
+
+ return services;
+ }
+
+ public Task ScheduleRecording(string programId)
+ {
+ throw new NotImplementedException();
+ }
+
+ public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
+ {
+ var list = new List<TimerInfoDto>();
+
+ foreach (var service in GetServices(query.ServiceName, query.ChannelId))
+ {
+ var timers = await GetTimers(service, cancellationToken).ConfigureAwait(false);
+
+ list.AddRange(timers);
+ }
+
+ if (!string.IsNullOrEmpty(query.ChannelId))
+ {
+ list = list.Where(i => string.Equals(i.ChannelId, query.ChannelId))
+ .ToList();
+ }
+
+ var returnArray = list.OrderByDescending(i => i.StartDate)
+ .ToArray();
+
+ return new QueryResult<TimerInfoDto>
+ {
+ Items = returnArray,
+ TotalRecordCount = returnArray.Length
+ };
+ }
+
+ private async Task<IEnumerable<TimerInfoDto>> GetTimers(ILiveTvService service, CancellationToken cancellationToken)
+ {
+ var timers = await service.GetTimersAsync(cancellationToken).ConfigureAwait(false);
+
+ return timers.Select(i => GetTimerInfoDto(i, service));
+ }
+
+ private TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service)
+ {
+ var id = service.Name + info.ChannelId + info.Id;
+ id = id.GetMD5().ToString("N");
+
+ var dto = new TimerInfoDto
+ {
+ ChannelName = info.ChannelName,
+ Description = info.Description,
+ EndDate = info.EndDate,
+ Name = info.Name,
+ StartDate = info.StartDate,
+ Id = id,
+ ExternalId = info.Id,
+ ChannelId = GetInternalChannelId(service.Name, info.ChannelId, info.ChannelName).ToString("N"),
+ Status = info.Status,
+ SeriesTimerId = info.SeriesTimerId,
+ PrePaddingSeconds = info.PrePaddingSeconds,
+ PostPaddingSeconds = info.PostPaddingSeconds
+ };
+
+ var duration = info.EndDate - info.StartDate;
+ dto.DurationMs = Convert.ToInt32(duration.TotalMilliseconds);
+
+ if (!string.IsNullOrEmpty(info.ProgramId))
+ {
+ dto.ProgramId = GetInternalProgramIdId(service.Name, info.ProgramId).ToString("N");
+ }
+
+ return dto;
+ }
+
+ public async Task DeleteRecording(string recordingId)
+ {
+ var recordings = await GetRecordings(new RecordingQuery
+ {
+
+ }, CancellationToken.None).ConfigureAwait(false);
+
+ var recording = recordings.Items
+ .FirstOrDefault(i => string.Equals(recordingId, i.Id, StringComparison.OrdinalIgnoreCase));
+
+ if (recording == null)
+ {
+ throw new ResourceNotFoundException(string.Format("Recording with Id {0} not found", recordingId));
+ }
+
+ var channel = GetChannel(recording.ChannelId);
+
+ var service = GetServices(channel.ServiceName, null)
+ .First();
+
+ await service.DeleteRecordingAsync(recording.ExternalId, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ public async Task CancelTimer(string id)
+ {
+ var timers = await GetTimers(new TimerQuery
+ {
+
+ }, CancellationToken.None).ConfigureAwait(false);
+
+ var timer = timers.Items
+ .FirstOrDefault(i => string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase));
+
+ if (timer == null)
+ {
+ throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
+ }
+
+ var channel = GetChannel(timer.ChannelId);
+
+ var service = GetServices(channel.ServiceName, null)
+ .First();
+
+ await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ public async Task<RecordingInfoDto> GetRecording(string id, CancellationToken cancellationToken)
+ {
+ var results = await GetRecordings(new RecordingQuery(), cancellationToken).ConfigureAwait(false);
+
+ return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.CurrentCulture));
+ }
+
+ public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
+ {
+ var results = await GetTimers(new TimerQuery(), cancellationToken).ConfigureAwait(false);
+
+ return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.CurrentCulture));
}
}
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
new file mode 100644
index 0000000000..00bf9e55ba
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
@@ -0,0 +1,59 @@
+using MediaBrowser.Common.ScheduledTasks;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Tasks;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.LiveTv
+{
+ class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
+ {
+ private readonly ILiveTvManager _liveTvManager;
+
+ public RefreshChannelsScheduledTask(ILiveTvManager liveTvManager)
+ {
+ _liveTvManager = liveTvManager;
+ }
+
+ public string Name
+ {
+ get { return "Refresh Guide"; }
+ }
+
+ public string Description
+ {
+ get { return "Downloads channel information from live tv services."; }
+ }
+
+ public string Category
+ {
+ get { return "Live TV"; }
+ }
+
+ public Task Execute(System.Threading.CancellationToken cancellationToken, IProgress<double> progress)
+ {
+ var manager = (LiveTvManager)_liveTvManager;
+
+ return manager.RefreshChannels(progress, cancellationToken);
+ }
+
+ public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ {
+ return new ITaskTrigger[]
+ {
+
+ new StartupTrigger(),
+
+ new SystemEventTrigger{ SystemEvent = SystemEvent.WakeFromSleep},
+
+ new IntervalTrigger{ Interval = TimeSpan.FromHours(2)}
+ };
+ }
+
+ public bool IsHidden
+ {
+ get { return _liveTvManager.Services.Count == 0; }
+ }
+ }
+}