update tuner setup

This commit is contained in:
Luke Pulverenti 2015-07-23 09:23:22 -04:00
parent f9f29de05e
commit 3fda8ec5c2
14 changed files with 262 additions and 65 deletions

View file

@ -1,4 +1,5 @@
using MediaBrowser.Common.Configuration; using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
@ -345,6 +346,31 @@ namespace MediaBrowser.Api.LiveTv
public string Id { get; set; } public string Id { get; set; }
} }
[Route("/LiveTv/ListingProviders", "POST", Summary = "Adds a listing provider")]
[Authenticated]
public class AddListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo>
{
}
[Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")]
[Authenticated]
public class DeleteListingProvider : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "DELETE")]
public string Id { get; set; }
}
[Route("/LiveTv/ListingProviders/Lineups", "GET", Summary = "Gets available lineups")]
[Authenticated]
public class GetLineups : IReturn<List<NameIdPair>>
{
[ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Id { get; set; }
[ApiMember(Name = "Location", Description = "Location", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Location { get; set; }
}
public class LiveTvService : BaseApiService public class LiveTvService : BaseApiService
{ {
private readonly ILiveTvManager _liveTvManager; private readonly ILiveTvManager _liveTvManager;
@ -373,20 +399,27 @@ namespace MediaBrowser.Api.LiveTv
} }
} }
public void Post(AddTunerHost request) public object Post(AddListingProvider request)
{
var result = _liveTvManager.SaveListingProvider(request).Result;
return ToOptimizedResult(result);
}
public void Delete(DeleteListingProvider request)
{ {
var config = GetConfiguration(); var config = GetConfiguration();
config.TunerHosts.Add(new TunerHostInfo config.ListingProviders = config.ListingProviders.Where(i => !string.Equals(request.Id, i.Id, StringComparison.OrdinalIgnoreCase)).ToList();
{
Id = Guid.NewGuid().ToString("N"),
Url = request.Url,
Type = request.Type
});
_config.SaveConfiguration("livetv", config); _config.SaveConfiguration("livetv", config);
} }
public void Post(AddTunerHost request)
{
var task = _liveTvManager.SaveTunerHost(request);
Task.WaitAll(task);
}
public void Delete(DeleteTunerHost request) public void Delete(DeleteTunerHost request)
{ {
var config = GetConfiguration(); var config = GetConfiguration();
@ -401,6 +434,13 @@ namespace MediaBrowser.Api.LiveTv
return _config.GetConfiguration<LiveTvOptions>("livetv"); return _config.GetConfiguration<LiveTvOptions>("livetv");
} }
public async Task<object> Get(GetLineups request)
{
var info = await _liveTvManager.GetLineups(request.Id, request.Location).ConfigureAwait(false);
return ToOptimizedSerializedResultUsingCache(info);
}
public async Task<object> Get(GetLiveTvInfo request) public async Task<object> Get(GetLiveTvInfo request)
{ {
var info = await _liveTvManager.GetLiveTvInfo(CancellationToken.None).ConfigureAwait(false); var info = await _liveTvManager.GetLiveTvInfo(CancellationToken.None).ConfigureAwait(false);

View file

@ -1,4 +1,5 @@
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
@ -9,7 +10,10 @@ namespace MediaBrowser.Controller.LiveTv
public interface IListingsProvider public interface IListingsProvider
{ {
string Name { get; } string Name { get; }
string Type { get; }
Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, ChannelInfo channel, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken); Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, ChannelInfo channel, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken);
Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken); Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken);
Task Validate(ListingsProviderInfo info);
Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string location);
} }
} }

View file

@ -56,12 +56,14 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="id">The identifier.</param> /// <param name="id">The identifier.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task CancelSeriesTimer(string id); Task CancelSeriesTimer(string id);
/// <summary> /// <summary>
/// Adds the parts. /// Adds the parts.
/// </summary> /// </summary>
/// <param name="services">The services.</param> /// <param name="services">The services.</param>
void AddParts(IEnumerable<ILiveTvService> services); /// <param name="tunerHosts">The tuner hosts.</param>
/// <param name="listingProviders">The listing providers.</param>
void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders);
/// <summary> /// <summary>
/// Gets the channels. /// Gets the channels.
@ -337,5 +339,24 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="dto">The dto.</param> /// <param name="dto">The dto.</param>
/// <param name="user">The user.</param> /// <param name="user">The user.</param>
void AddInfoToProgramDto(BaseItem item, BaseItemDto dto, User user = null); void AddInfoToProgramDto(BaseItem item, BaseItemDto dto, User user = null);
/// <summary>
/// Saves the tuner host.
/// </summary>
/// <param name="info">The information.</param>
/// <returns>Task.</returns>
Task SaveTunerHost(TunerHostInfo info);
/// <summary>
/// Saves the listing provider.
/// </summary>
/// <param name="info">The information.</param>
/// <returns>Task.</returns>
Task<ListingsProviderInfo> SaveListingProvider(ListingsProviderInfo info);
/// <summary>
/// Gets the lineups.
/// </summary>
/// <param name="providerId">The provider identifier.</param>
/// <param name="location">The location.</param>
/// <returns>Task&lt;List&lt;NameIdPair&gt;&gt;.</returns>
Task<List<NameIdPair>> GetLineups(string providerId, string location);
} }
} }

View file

@ -46,5 +46,11 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns> /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken); Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken);
/// <summary>
/// Validates the specified information.
/// </summary>
/// <param name="info">The information.</param>
/// <returns>Task.</returns>
Task Validate(TunerHostInfo info);
} }
} }

View file

@ -28,10 +28,11 @@ namespace MediaBrowser.Model.LiveTv
public class ListingsProviderInfo public class ListingsProviderInfo
{ {
public string ProviderName { get; set; } public string Id { get; set; }
public string Type { get; set; }
public string Username { get; set; } public string Username { get; set; }
public string Password { get; set; } public string Password { get; set; }
public string ZipCode { get; set; }
public string ListingsId { get; set; } public string ListingsId { get; set; }
public string ZipCode { get; set; }
} }
} }

View file

@ -30,18 +30,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private readonly ItemDataProvider<SeriesTimerInfo> _seriesTimerProvider; private readonly ItemDataProvider<SeriesTimerInfo> _seriesTimerProvider;
private readonly TimerManager _timerProvider; private readonly TimerManager _timerProvider;
private readonly List<ITunerHost> _tunerHosts = new List<ITunerHost>(); private readonly LiveTvManager _liveTvManager;
private readonly List<IListingsProvider> _listingProviders = new List<IListingsProvider>();
public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IConfigurationManager config) public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IConfigurationManager config, ILiveTvManager liveTvManager)
{ {
_appHpst = appHost; _appHpst = appHost;
_logger = logger; _logger = logger;
_httpClient = httpClient; _httpClient = httpClient;
_config = config; _config = config;
_liveTvManager = (LiveTvManager)liveTvManager;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_tunerHosts.AddRange(appHost.GetExports<ITunerHost>());
_listingProviders.AddRange(appHost.GetExports<IListingsProvider>());
_recordingProvider = new ItemDataProvider<RecordingInfo>(jsonSerializer, _logger, Path.Combine(DataPath, "recordings"), (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)); _recordingProvider = new ItemDataProvider<RecordingInfo>(jsonSerializer, _logger, Path.Combine(DataPath, "recordings"), (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase));
_seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers")); _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
@ -76,7 +74,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
var status = new LiveTvServiceStatusInfo(); var status = new LiveTvServiceStatusInfo();
var list = new List<LiveTvTunerInfo>(); var list = new List<LiveTvTunerInfo>();
foreach (var host in _tunerHosts) foreach (var host in _liveTvManager.TunerHosts)
{ {
foreach (var hostInstance in host.GetTunerHosts()) foreach (var hostInstance in host.GetTunerHosts())
{ {
@ -104,7 +102,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{ {
var list = new List<ChannelInfo>(); var list = new List<ChannelInfo>();
foreach (var host in _tunerHosts) foreach (var host in _liveTvManager.TunerHosts)
{ {
foreach (var hostInstance in host.GetTunerHosts()) foreach (var hostInstance in host.GetTunerHosts())
{ {
@ -288,7 +286,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
return GetConfiguration().ListingProviders return GetConfiguration().ListingProviders
.Select(i => .Select(i =>
{ {
var provider = _listingProviders.FirstOrDefault(l => string.Equals(l.Name, i.ProviderName, StringComparison.OrdinalIgnoreCase)); var provider = _liveTvManager.ListingProviders.FirstOrDefault(l => string.Equals(l.Type, i.Type, StringComparison.OrdinalIgnoreCase));
return provider == null ? null : new Tuple<IListingsProvider, ListingsProviderInfo>(provider, i); return provider == null ? null : new Tuple<IListingsProvider, ListingsProviderInfo>(provider, i);
}) })

View file

@ -422,7 +422,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
return images; return images;
} }
public async Task<List<NameIdPair>> GetHeadends(ListingsProviderInfo info, CancellationToken cancellationToken) public async Task<List<NameIdPair>> GetHeadends(ListingsProviderInfo info, string location, CancellationToken cancellationToken)
{ {
var token = await GetToken(info, cancellationToken); var token = await GetToken(info, cancellationToken);
@ -437,7 +437,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
var options = new HttpRequestOptions() var options = new HttpRequestOptions()
{ {
Url = ApiUrl + "/headends?country=USA&postalcode=" + info.ZipCode, Url = ApiUrl + "/headends?country=USA&postalcode=" + location,
UserAgent = UserAgent, UserAgent = UserAgent,
CancellationToken = cancellationToken CancellationToken = cancellationToken
}; };
@ -484,43 +484,43 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
private async Task<string> GetToken(ListingsProviderInfo info, CancellationToken cancellationToken) private async Task<string> GetToken(ListingsProviderInfo info, CancellationToken cancellationToken)
{ {
var username = info.Username;
// Reset the token if there's no username
if (string.IsNullOrWhiteSpace(username))
{
return null;
}
var password = info.Password;
if (string.IsNullOrWhiteSpace(password))
{
return null;
}
NameValuePair savedToken = null;
if (!_tokens.TryGetValue(username, out savedToken))
{
savedToken = new NameValuePair();
_tokens.TryAdd(username, savedToken);
}
if (!string.IsNullOrWhiteSpace(savedToken.Name) && !string.IsNullOrWhiteSpace(savedToken.Value))
{
long ticks;
if (long.TryParse(savedToken.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out ticks))
{
// If it's under 24 hours old we can still use it
if ((DateTime.UtcNow.Ticks - ticks) < TimeSpan.FromHours(24).Ticks)
{
return savedToken.Name;
}
}
}
await _tokenSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); await _tokenSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try try
{ {
var username = info.Username;
// Reset the token if there's no username
if (string.IsNullOrWhiteSpace(username))
{
return null;
}
var password = info.Password;
if (string.IsNullOrWhiteSpace(password))
{
return null;
}
NameValuePair savedToken = null;
if (!_tokens.TryGetValue(username, out savedToken))
{
savedToken = new NameValuePair();
_tokens.TryAdd(username, savedToken);
}
if (!string.IsNullOrWhiteSpace(savedToken.Name) && !string.IsNullOrWhiteSpace(savedToken.Value))
{
long ticks;
if (long.TryParse(savedToken.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out ticks))
{
// If it's under 24 hours old we can still use it
if ((DateTime.UtcNow.Ticks - ticks) < TimeSpan.FromHours(24).Ticks)
{
return savedToken.Name;
}
}
}
var result = await GetTokenInternal(username, password, cancellationToken).ConfigureAwait(false); var result = await GetTokenInternal(username, password, cancellationToken).ConfigureAwait(false);
savedToken.Name = result; savedToken.Name = result;
savedToken.Value = DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture); savedToken.Value = DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture);
@ -563,6 +563,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
get { return "Schedules Direct"; } get { return "Schedules Direct"; }
} }
public string Type
{
get { return "SchedulesDirect"; }
}
public class ScheduleDirect public class ScheduleDirect
{ {
public class Token public class Token
@ -842,5 +847,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
} }
public async Task Validate(ListingsProviderInfo info)
{
}
public Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string location)
{
return GetHeadends(info, location, CancellationToken.None);
}
} }
} }

View file

@ -57,6 +57,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly ConcurrentDictionary<Guid, Guid> _refreshedPrograms = new ConcurrentDictionary<Guid, Guid>(); private readonly ConcurrentDictionary<Guid, Guid> _refreshedPrograms = new ConcurrentDictionary<Guid, Guid>();
private readonly List<ITunerHost> _tunerHosts = new List<ITunerHost>();
private readonly List<IListingsProvider> _listingProviders = new List<IListingsProvider>();
public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager) public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager)
{ {
_config = config; _config = config;
@ -92,9 +95,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv
/// Adds the parts. /// Adds the parts.
/// </summary> /// </summary>
/// <param name="services">The services.</param> /// <param name="services">The services.</param>
public void AddParts(IEnumerable<ILiveTvService> services) /// <param name="tunerHosts">The tuner hosts.</param>
/// <param name="listingProviders">The listing providers.</param>
public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders)
{ {
_services.AddRange(services); _services.AddRange(services);
_tunerHosts.AddRange(tunerHosts);
_listingProviders.AddRange(listingProviders);
foreach (var service in _services) foreach (var service in _services)
{ {
@ -102,6 +109,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv
} }
} }
public List<ITunerHost> TunerHosts
{
get { return _tunerHosts; }
}
public List<IListingsProvider> ListingProviders
{
get { return _listingProviders; }
}
void service_DataSourceChanged(object sender, EventArgs e) void service_DataSourceChanged(object sender, EventArgs e)
{ {
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>(); _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
@ -2154,5 +2171,84 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var user = _userManager.GetUserById(userId); var user = _userManager.GetUserById(userId);
return await _libraryManager.GetNamedView(user, name, "livetv", "zz_" + name, cancellationToken).ConfigureAwait(false); return await _libraryManager.GetNamedView(user, name, "livetv", "zz_" + name, cancellationToken).ConfigureAwait(false);
} }
public async Task SaveTunerHost(TunerHostInfo info)
{
info = (TunerHostInfo)_jsonSerializer.DeserializeFromString(_jsonSerializer.SerializeToString(info), typeof(TunerHostInfo));
var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
if (provider == null)
{
throw new ResourceNotFoundException();
}
await provider.Validate(info).ConfigureAwait(false);
var config = GetConfiguration();
var index = config.TunerHosts.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
{
info.Id = Guid.NewGuid().ToString("N");
config.TunerHosts.Add(info);
}
else
{
config.TunerHosts[index] = info;
}
_config.SaveConfiguration("livetv", config);
}
public async Task<ListingsProviderInfo> SaveListingProvider(ListingsProviderInfo info)
{
info = (ListingsProviderInfo)_jsonSerializer.DeserializeFromString(_jsonSerializer.SerializeToString(info), typeof(ListingsProviderInfo));
var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
if (provider == null)
{
throw new ResourceNotFoundException();
}
await provider.Validate(info).ConfigureAwait(false);
var config = GetConfiguration();
var index = config.ListingProviders.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
{
info.Id = Guid.NewGuid().ToString("N");
config.ListingProviders.Add(info);
}
else
{
config.ListingProviders[index] = info;
}
_config.SaveConfiguration("livetv", config);
return info;
}
public Task<List<NameIdPair>> GetLineups(string providerId, string location)
{
var config = GetConfiguration();
var info = config.ListingProviders.FirstOrDefault(i => string.Equals(i.Id, providerId, StringComparison.OrdinalIgnoreCase));
var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
if (provider == null)
{
throw new ResourceNotFoundException();
}
return provider.GetLineups(info, location);
}
} }
} }

View file

@ -3,7 +3,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
@ -15,7 +14,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
{ {
@ -222,5 +220,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
} }
throw new ApplicationException("Channel not found."); throw new ApplicationException("Channel not found.");
} }
public async Task Validate(TunerHostInfo info)
{
await GetChannels(info, CancellationToken.None).ConfigureAwait(false);
}
} }
} }

View file

@ -185,5 +185,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
{ {
} }
} }
public async Task Validate(TunerHostInfo info)
{
if (!File.Exists(info.Url))
{
throw new FileNotFoundException();
}
}
} }
} }

View file

@ -820,5 +820,8 @@
"MessageConfirmDeleteTunerDevice": "Are you sure you wish to delete this device?", "MessageConfirmDeleteTunerDevice": "Are you sure you wish to delete this device?",
"MessageConfirmDeleteGuideProvider": "Are you sure you wish to delete this guide provider?", "MessageConfirmDeleteGuideProvider": "Are you sure you wish to delete this guide provider?",
"HeaderDeleteProvider": "Delete Provider", "HeaderDeleteProvider": "Delete Provider",
"HeaderAddProvider": "Add Provider" "HeaderAddProvider": "Add Provider",
"ErrorAddingTunerDevice": "There was an error adding the tuner device. Please ensure it is accessible and try again.",
"ErrorSavingTvProvider": "There was an error saving the TV provider. Please ensure it is accessible and try again.",
"ErrorGettingTvLineups": "There was an error downloading tv lineups. Please ensure your username and password are correct and try again."
} }

View file

@ -1483,5 +1483,8 @@
"TabTuners": "Tuners", "TabTuners": "Tuners",
"HeaderGuideProviders": "Guide Providers", "HeaderGuideProviders": "Guide Providers",
"AddGuideProviderHelp": "Add a source for TV Guide information", "AddGuideProviderHelp": "Add a source for TV Guide information",
"LabelZipCode": "Zip Code" "LabelZipCode": "Zip Code:",
"GuideProviderListingsStep": "Step 2: Select Listings",
"GuideProviderLoginStep": "Step 1: Login",
"LabelLineup": "Lineup"
} }

View file

@ -784,7 +784,7 @@ namespace MediaBrowser.Server.Startup.Common
ImageProcessor.AddParts(GetExports<IImageEnhancer>()); ImageProcessor.AddParts(GetExports<IImageEnhancer>());
LiveTvManager.AddParts(GetExports<ILiveTvService>()); LiveTvManager.AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
SubtitleManager.AddParts(GetExports<ISubtitleProvider>()); SubtitleManager.AddParts(GetExports<ISubtitleProvider>());
ChapterManager.AddParts(GetExports<IChapterProvider>()); ChapterManager.AddParts(GetExports<IChapterProvider>());

View file

@ -183,7 +183,7 @@
<Content Include="dashboard-ui\livetvguide.html"> <Content Include="dashboard-ui\livetvguide.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\livetvguidesettings.html"> <Content Include="dashboard-ui\livetvguideprovider-scd.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\livetvrecordings.html"> <Content Include="dashboard-ui\livetvrecordings.html">
@ -207,7 +207,7 @@
<Content Include="dashboard-ui\scripts\homeupcoming.js"> <Content Include="dashboard-ui\scripts\homeupcoming.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\livetvguidesettings.js"> <Content Include="dashboard-ui\scripts\livetvguideprovider-scd.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\mypreferenceshome.js"> <Content Include="dashboard-ui\scripts\mypreferenceshome.js">